Files
purpur-quest-board/src/test/java/games/dmg/spigotquestboard/QuestServiceTest.java
T
dmg a09a0a3163
Release / release (push) Successful in 2m17s
CI / build (push) Successful in 1m7s
feat(quests): add shared active quest browsing
2026-09-04 23:51:34 -04:00

143 lines
6.1 KiB
Java

package games.dmg.spigotquestboard;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.Test;
final class QuestServiceTest {
@Test
void createsSevenDayQuestWithEscrowedRewards() throws Exception {
MemoryQuestRepository repository = new MemoryQuestRepository();
QuestService service = new QuestService(repository);
Instant createdAt = Instant.parse("2026-09-05T00:00:00Z");
EscrowItem reward = new EscrowItem("DIAMOND", 3, null);
Quest quest = service.create(
UUID.fromString("00000000-0000-0000-0000-000000000001"),
"Issuer", "STONE", 64, List.of(reward), createdAt
);
assertEquals(createdAt.plus(7, ChronoUnit.DAYS), quest.expiresAt());
assertEquals(List.of(reward), quest.reward());
assertEquals(createdAt, quest.createdAt());
assertEquals("STONE", quest.requestedMaterial());
assertEquals(64, quest.requestedAmount());
assertEquals("Issuer", quest.issuerName());
assertEquals(quest, repository.state.quests().get(quest.id()));
}
@Test
void createsUniqueIdentifiers() throws Exception {
MemoryQuestRepository repository = new MemoryQuestRepository();
QuestService service = new QuestService(repository);
UUID issuer = UUID.fromString("00000000-0000-0000-0000-000000000001");
List<EscrowItem> reward = List.of(new EscrowItem("DIAMOND", 1, null));
Quest first = service.create(issuer, "Issuer", "STONE", 1, reward, Instant.EPOCH);
Quest second = service.create(issuer, "Issuer", "DIRT", 2, reward, Instant.EPOCH);
assertNotEquals(first.id(), second.id());
assertEquals(2, service.state().quests().size());
}
@Test
void rejectsInvalidInputWithoutSaving() throws Exception {
MemoryQuestRepository repository = new MemoryQuestRepository();
QuestService service = new QuestService(repository);
UUID issuer = UUID.randomUUID();
List<EscrowItem> reward = List.of(new EscrowItem("DIAMOND", 1, null));
assertThrows(IllegalArgumentException.class,
() -> service.create(issuer, "Issuer", "AIR", 1, reward, Instant.EPOCH));
assertThrows(IllegalArgumentException.class,
() -> service.create(issuer, "Issuer", "STONE", 0, reward, Instant.EPOCH));
assertThrows(IllegalArgumentException.class,
() -> service.create(issuer, "Issuer", "STONE", 1, List.of(), Instant.EPOCH));
assertTrue(repository.state.quests().isEmpty());
assertEquals(0, repository.saveCount);
}
@Test
void listsOnlyQuestsThatHaveNotReachedTheirExpiration() throws Exception {
MemoryQuestRepository repository = new MemoryQuestRepository();
QuestService service = new QuestService(repository);
Instant createdAt = Instant.parse("2026-09-05T00:00:00Z");
Quest quest = service.create(
UUID.randomUUID(), "Issuer", "STONE", 1,
List.of(new EscrowItem("DIAMOND", 1, null)), createdAt
);
assertEquals(List.of(quest), service.activeQuests(quest.expiresAt().minusNanos(1)));
assertTrue(service.activeQuests(quest.expiresAt()).isEmpty());
}
@Test
void activeListingExcludesNonActiveLifecycleStatuses() throws Exception {
Instant now = Instant.parse("2026-09-06T00:00:00Z");
Quest active = questWithStatus(QuestStatus.ACTIVE);
Quest completed = questWithStatus(QuestStatus.COMPLETED);
Quest cancelled = questWithStatus(QuestStatus.CANCELLED);
Quest expired = questWithStatus(QuestStatus.EXPIRED);
MemoryQuestRepository repository = new MemoryQuestRepository();
repository.state = new QuestState(java.util.stream.Stream.of(
active, completed, cancelled, expired
).collect(java.util.stream.Collectors.toMap(
Quest::id, quest -> quest, (left, right) -> left, java.util.LinkedHashMap::new
)));
QuestService service = new QuestService(repository);
assertEquals(List.of(active), service.activeQuests(now));
assertEquals(List.of(active.id().toString()), service.completableQuestIds(now));
assertEquals(
List.of(active.id().toString()), service.cancellableQuestIds(active.issuerId(), now)
);
assertTrue(service.cancellableQuestIds(UUID.randomUUID(), now).isEmpty());
}
@Test
void persistenceFailureDoesNotPublishQuest() throws Exception {
QuestRepository repository = new QuestRepository() {
@Override public QuestState load() { return QuestState.empty(); }
@Override public void save(QuestState state) throws IOException {
throw new IOException("disk full");
}
};
QuestService service = new QuestService(repository);
assertThrows(IOException.class, () -> service.create(
UUID.randomUUID(), "Issuer", "STONE", 1,
List.of(new EscrowItem("DIAMOND", 1, null)), Instant.EPOCH
));
assertTrue(service.state().quests().isEmpty());
}
private static Quest questWithStatus(QuestStatus status) {
Instant createdAt = Instant.parse("2026-09-05T00:00:00Z");
return new Quest(
UUID.randomUUID(), UUID.randomUUID(), "Issuer", "STONE", 1,
List.of(new EscrowItem("DIAMOND", 1, null)), createdAt,
createdAt.plus(7, ChronoUnit.DAYS), status
);
}
private static final class MemoryQuestRepository implements QuestRepository {
private QuestState state = QuestState.empty();
private int saveCount;
@Override public QuestState load() { return state; }
@Override public void save(QuestState state) {
this.state = state;
saveCount++;
}
}
}