279 lines
10 KiB
Java
279 lines
10 KiB
Java
package games.dmg.spigotquestboard;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.verify;
|
|
|
|
import java.io.IOException;
|
|
import java.time.Clock;
|
|
import java.time.Instant;
|
|
import java.time.ZoneOffset;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
import org.bukkit.entity.Player;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
final class QuestBoardDialogUiTest {
|
|
private static final Instant NOW = Instant.parse("2026-09-05T03:00:00Z");
|
|
|
|
@Test
|
|
void disabledPlayerCommandsDoNotGateBoardUiGateways() throws Exception {
|
|
PlayerCommandSettings settings = new PlayerCommandSettings(
|
|
new PlayerCommandSettingsRepository() {
|
|
@Override public boolean loadEnabled() { return false; }
|
|
@Override public void saveEnabled(boolean enabled) { }
|
|
}
|
|
);
|
|
RecordingCreator creator = new RecordingCreator(false);
|
|
RecordingCompleter completer = new RecordingCompleter();
|
|
RecordingCanceller canceller = new RecordingCanceller();
|
|
java.util.concurrent.atomic.AtomicReference<Player> claimantPlayer =
|
|
new java.util.concurrent.atomic.AtomicReference<>();
|
|
QuestClaimGateway claimant = player -> {
|
|
claimantPlayer.set(player);
|
|
return new ClaimCollectionResult(0, 0);
|
|
};
|
|
QuestBoardDialogUi ui = new QuestBoardDialogUi(
|
|
creator, now -> List.of(creator.quest(NOW)), completer, canceller, claimant,
|
|
Clock.fixed(NOW, ZoneOffset.UTC)
|
|
);
|
|
Player player = mock(Player.class);
|
|
|
|
ui.listingText();
|
|
ui.submit(player, "stone", "1");
|
|
ui.submitCompletion(player, "complete-id");
|
|
ui.submitCancellation(player, "cancel-id");
|
|
ui.submitClaim(player);
|
|
|
|
org.junit.jupiter.api.Assertions.assertFalse(settings.enabled());
|
|
assertEquals("stone", creator.material);
|
|
assertEquals(player, completer.player);
|
|
assertEquals(player, canceller.player);
|
|
assertEquals(player, claimantPlayer.get());
|
|
}
|
|
|
|
@Test
|
|
void dialogSubmissionUsesEquivalentCreationFlow() {
|
|
RecordingCreator creator = new RecordingCreator(false);
|
|
QuestBoardDialogUi ui = new QuestBoardDialogUi(
|
|
creator, now -> List.of(), Clock.fixed(NOW, ZoneOffset.UTC)
|
|
);
|
|
Player player = mock(Player.class);
|
|
|
|
ui.submit(player, "stone", "64");
|
|
|
|
assertEquals("stone", creator.material);
|
|
assertEquals(64, creator.quantity);
|
|
assertEquals(NOW, creator.createdAt);
|
|
verify(player).sendMessage(
|
|
"Quest 00000000-0000-0000-0000-000000000010 created. "
|
|
+ "Your exact held stack is now escrowed."
|
|
);
|
|
}
|
|
|
|
@Test
|
|
void completionActionUsesEquivalentCompletionFlow() {
|
|
RecordingCreator creator = new RecordingCreator(false);
|
|
RecordingCompleter completer = new RecordingCompleter();
|
|
QuestBoardDialogUi ui = new QuestBoardDialogUi(
|
|
creator, now -> List.of(), completer, Clock.fixed(NOW, ZoneOffset.UTC)
|
|
);
|
|
Player player = mock(Player.class);
|
|
String id = UUID.randomUUID().toString();
|
|
|
|
ui.submitCompletion(player, id);
|
|
|
|
assertEquals(player, completer.player);
|
|
assertEquals(id, completer.questId);
|
|
assertEquals(NOW, completer.completedAt);
|
|
}
|
|
|
|
@Test
|
|
void claimActionUsesEquivalentCollectionFlowAtABoard() {
|
|
RecordingCreator creator = new RecordingCreator(false);
|
|
java.util.concurrent.atomic.AtomicReference<Player> claimantPlayer =
|
|
new java.util.concurrent.atomic.AtomicReference<>();
|
|
QuestClaimGateway claimant = player -> {
|
|
claimantPlayer.set(player);
|
|
return new ClaimCollectionResult(1, 0);
|
|
};
|
|
QuestBoardDialogUi ui = new QuestBoardDialogUi(
|
|
creator, now -> List.of(), null, null, claimant,
|
|
Clock.fixed(NOW, ZoneOffset.UTC)
|
|
);
|
|
Player player = mock(Player.class);
|
|
|
|
ui.submitClaim(player);
|
|
|
|
assertEquals(player, claimantPlayer.get());
|
|
}
|
|
|
|
@Test
|
|
void cancellationActionUsesEquivalentCancellationFlow() {
|
|
RecordingCreator creator = new RecordingCreator(false);
|
|
RecordingCanceller canceller = new RecordingCanceller();
|
|
QuestBoardDialogUi ui = new QuestBoardDialogUi(
|
|
creator, now -> List.of(), null, canceller, Clock.fixed(NOW, ZoneOffset.UTC)
|
|
);
|
|
Player player = mock(Player.class);
|
|
String id = UUID.randomUUID().toString();
|
|
|
|
ui.submitCancellation(player, id);
|
|
|
|
assertEquals(player, canceller.player);
|
|
assertEquals(id, canceller.questId);
|
|
assertEquals(NOW, canceller.cancelledAt);
|
|
}
|
|
|
|
@Test
|
|
void boardListingDistinguishesDeliveredBlocksFromReturnedRewards() {
|
|
RecordingCreator creator = new RecordingCreator(false);
|
|
Player player = mock(Player.class);
|
|
UUID owner = UUID.randomUUID();
|
|
org.mockito.Mockito.when(player.getUniqueId()).thenReturn(owner);
|
|
QuestClaimGateway claimant = new QuestClaimGateway() {
|
|
@Override
|
|
public ClaimCollectionResult collect(Player ignored) {
|
|
return new ClaimCollectionResult(0, 0);
|
|
}
|
|
|
|
@Override
|
|
public List<QuestClaim> pendingClaims(Player ignored) {
|
|
return List.of(
|
|
new QuestClaim(
|
|
UUID.randomUUID(), UUID.randomUUID(), owner,
|
|
List.of(new EscrowItem("STONE", 2, null)), NOW,
|
|
QuestClaimSource.COMPLETION
|
|
),
|
|
new QuestClaim(
|
|
UUID.randomUUID(), UUID.randomUUID(), owner,
|
|
List.of(new EscrowItem("DIAMOND", 1, null)), NOW,
|
|
QuestClaimSource.EXPIRATION
|
|
)
|
|
);
|
|
}
|
|
};
|
|
QuestBoardDialogUi ui = new QuestBoardDialogUi(
|
|
creator, now -> List.of(), null, null, claimant,
|
|
Clock.fixed(NOW, ZoneOffset.UTC)
|
|
);
|
|
|
|
String listing = ui.claimListingText(player);
|
|
|
|
org.junit.jupiter.api.Assertions.assertTrue(listing.contains("DELIVERED BLOCKS — 2 STONE"));
|
|
org.junit.jupiter.api.Assertions.assertTrue(
|
|
listing.contains("RETURNED REWARD (EXPIRATION) — 1 DIAMOND")
|
|
);
|
|
}
|
|
|
|
@Test
|
|
void boardOnlyOffersCancellationActionsForPlayersOwnedActiveQuests() {
|
|
RecordingCreator creator = new RecordingCreator(false);
|
|
Quest owned = creator.quest(NOW);
|
|
Quest other = new Quest(
|
|
UUID.randomUUID(), UUID.randomUUID(), "Other", "DIRT", 1,
|
|
List.of(new EscrowItem("COAL", 1, null)), NOW, NOW.plusSeconds(604800)
|
|
);
|
|
QuestBoardDialogUi ui = new QuestBoardDialogUi(
|
|
creator, now -> List.of(owned, other), null, new RecordingCanceller(),
|
|
Clock.fixed(NOW, ZoneOffset.UTC)
|
|
);
|
|
Player issuer = mock(Player.class);
|
|
org.mockito.Mockito.when(issuer.getUniqueId()).thenReturn(owned.issuerId());
|
|
|
|
assertEquals(List.of(owned.id().toString()), ui.cancellableQuestIds(issuer));
|
|
}
|
|
|
|
@Test
|
|
void everyOpeningBuildsAListingFromTheCurrentActiveQuests() {
|
|
RecordingCreator creator = new RecordingCreator(false);
|
|
Quest active = creator.quest(NOW);
|
|
QuestBoardDialogUi ui = new QuestBoardDialogUi(
|
|
creator, now -> List.of(active), Clock.fixed(NOW, ZoneOffset.UTC)
|
|
);
|
|
|
|
assertEquals(QuestListingFormatter.format(active, NOW), ui.listingText());
|
|
}
|
|
|
|
@Test
|
|
void persistenceFailureExplainsThatRewardWasRestored() {
|
|
QuestBoardDialogUi ui = new QuestBoardDialogUi(
|
|
new RecordingCreator(true), now -> List.of(), Clock.fixed(NOW, ZoneOffset.UTC)
|
|
);
|
|
Player player = mock(Player.class);
|
|
|
|
ui.submit(player, "stone", "64");
|
|
|
|
verify(player).sendMessage(
|
|
"The quest could not be saved. Your held reward was restored."
|
|
);
|
|
}
|
|
|
|
private static final class RecordingCanceller implements QuestCancellationGateway {
|
|
private Player player;
|
|
private String questId;
|
|
private Instant cancelledAt;
|
|
|
|
@Override
|
|
public QuestClaim cancel(Player cancellingPlayer, String id, Instant instant) {
|
|
player = cancellingPlayer;
|
|
questId = id;
|
|
cancelledAt = instant;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static final class RecordingCompleter implements QuestCompletionGateway {
|
|
private Player player;
|
|
private String questId;
|
|
private Instant completedAt;
|
|
|
|
@Override
|
|
public QuestCompletion complete(Player completingPlayer, String id, Instant instant) {
|
|
player = completingPlayer;
|
|
questId = id;
|
|
completedAt = instant;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static final class RecordingCreator implements QuestCreationGateway {
|
|
private final boolean fail;
|
|
private String material;
|
|
private int quantity;
|
|
private Instant createdAt;
|
|
|
|
private RecordingCreator(boolean fail) {
|
|
this.fail = fail;
|
|
}
|
|
|
|
@Override
|
|
public Quest create(
|
|
Player player, String requestedMaterial, int requestedAmount, Instant instant
|
|
) throws IOException {
|
|
if (fail) {
|
|
throw new IOException("disk full");
|
|
}
|
|
material = requestedMaterial;
|
|
quantity = requestedAmount;
|
|
createdAt = instant;
|
|
return quest(instant);
|
|
}
|
|
|
|
private Quest quest(Instant instant) {
|
|
return new Quest(
|
|
UUID.fromString("00000000-0000-0000-0000-000000000010"),
|
|
UUID.fromString("00000000-0000-0000-0000-000000000001"),
|
|
"Issuer", "STONE", 64,
|
|
List.of(new EscrowItem("DIAMOND", 1, null)),
|
|
instant, instant.plusSeconds(604800)
|
|
);
|
|
}
|
|
|
|
@Override
|
|
public List<String> suggestBlockMaterials(String prefix) {
|
|
return List.of();
|
|
}
|
|
}
|
|
}
|