feat(participation): add opt-out and role status
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class ParticipationServiceTest {
|
||||
private static final UUID PLAYER = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
private static final UUID TYRANT = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
private static final Instant NOW = Instant.parse("2026-08-14T12:00:00Z");
|
||||
private final ParticipationService service = new ParticipationService(Duration.ofDays(7));
|
||||
|
||||
@Test
|
||||
void optingOutClearsOrdinaryClassAndFollowerButKeepsPlayerOutAfterCooldown() {
|
||||
PlayerState assigned = new PlayerState(
|
||||
PLAYER, "Player", Optional.of(NOW), Optional.empty(), TyrantClass.ASSASSIN,
|
||||
Optional.of(UUID.randomUUID()),
|
||||
Map.of(Ability.ASSASSIN_INVISIBILITY, NOW.plusSeconds(30)),
|
||||
Set.of(Ability.ASSASSIN_INVISIBILITY), java.util.List.of()
|
||||
);
|
||||
GameState game = runningGame();
|
||||
|
||||
ParticipationResult result = service.optOut(game, assigned, NOW);
|
||||
|
||||
assertEquals(ParticipationStatus.OPTED_OUT, result.status());
|
||||
assertEquals(Optional.of(NOW.plus(Duration.ofDays(7))),
|
||||
result.player().optedOutUntil());
|
||||
assertEquals(TyrantClass.NONE, result.player().tyrantClass());
|
||||
assertEquals(Optional.empty(), result.player().followerOf());
|
||||
assertEquals(Map.of(), result.player().cooldownEnds());
|
||||
assertEquals(ParticipationStatus.OPTED_OUT,
|
||||
service.status(result.player(), NOW.plus(Duration.ofDays(8))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void optingBackInIsBlockedUntilSevenDaysAndThenClearsOptOut() {
|
||||
PlayerState player = service.optOut(
|
||||
runningGame(), PlayerState.newPlayer(PLAYER, "Player"), NOW
|
||||
).player();
|
||||
|
||||
ParticipationResult early = service.optIn(player, NOW.plus(Duration.ofDays(6)));
|
||||
ParticipationResult ready = service.optIn(player, NOW.plus(Duration.ofDays(7)));
|
||||
|
||||
assertEquals(ParticipationStatus.COOLDOWN_ACTIVE, early.status());
|
||||
assertEquals(ParticipationStatus.OPTED_IN, ready.status());
|
||||
assertEquals(Optional.empty(), ready.player().optedOutUntil());
|
||||
}
|
||||
|
||||
@Test
|
||||
void currentCentralRoleMustRelinquishBeforeOptingOut() {
|
||||
GameState game = new GameState(
|
||||
GameLifecycle.RUNNING, Optional.of(PLAYER), Optional.empty(), Optional.empty(),
|
||||
Optional.empty(), Optional.empty(), Duration.ZERO, 0, 1, Set.of()
|
||||
);
|
||||
|
||||
ParticipationResult result = service.optOut(
|
||||
game, PlayerState.newPlayer(PLAYER, "Tyrant"), NOW
|
||||
);
|
||||
|
||||
assertEquals(ParticipationStatus.MUST_RELINQUISH_ROLE, result.status());
|
||||
assertEquals(Optional.empty(), result.player().optedOutUntil());
|
||||
}
|
||||
|
||||
private static GameState runningGame() {
|
||||
return new GameState(
|
||||
GameLifecycle.RUNNING, Optional.of(TYRANT), Optional.empty(), Optional.empty(),
|
||||
Optional.empty(), Optional.empty(), Duration.ZERO, 0, 1, Set.of()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class PlayerStatusMessagesTest {
|
||||
private static final UUID PLAYER = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
private static final Instant NOW = Instant.parse("2026-08-14T12:00:00Z");
|
||||
|
||||
@Test
|
||||
void assignedPlayerSeesGameRoleClassAbilitiesAndCooldowns() {
|
||||
GameState game = new GameState(
|
||||
GameLifecycle.RUNNING, Optional.empty(), Optional.of(PLAYER), Optional.empty(),
|
||||
Optional.empty(), Optional.empty(), Duration.ZERO, 0, 0, Set.of()
|
||||
);
|
||||
PlayerState player = new PlayerState(
|
||||
PLAYER, "Player", Optional.of(NOW), Optional.empty(), TyrantClass.ASSASSIN,
|
||||
Optional.empty(), Map.of(Ability.ASSASSIN_INVISIBILITY, NOW.plusSeconds(60)),
|
||||
Set.of(), List.of()
|
||||
);
|
||||
|
||||
List<String> messages = PlayerStatusMessages.forJoin(game, player, NOW);
|
||||
|
||||
assertTrue(messages.stream().anyMatch(message -> message.contains("Game: RUNNING")));
|
||||
assertTrue(messages.stream().anyMatch(message -> message.contains("Role: VIGILANTE")));
|
||||
assertTrue(messages.stream().anyMatch(message -> message.contains("Class: ASSASSIN")));
|
||||
assertTrue(messages.stream().anyMatch(message -> message.contains("ASSASSIN_INVISIBILITY")
|
||||
&& message.contains("60s")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unassignedPlayerIsToldHowToOptOut() {
|
||||
List<String> messages = PlayerStatusMessages.forJoin(
|
||||
GameState.empty(), PlayerState.newPlayer(PLAYER, "Player"), NOW
|
||||
);
|
||||
|
||||
assertTrue(messages.stream().anyMatch(message -> message.contains("/tyrant optout")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.random.RandomGenerator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class RoleRelinquishmentServiceTest {
|
||||
@Test
|
||||
void vigilanteCanRelinquishAndTriggerNormalReroll() {
|
||||
UUID tyrant = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
UUID vigilante = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
UUID candidate = UUID.fromString("33333333-3333-3333-3333-333333333333");
|
||||
Instant now = Instant.parse("2026-08-14T12:00:00Z");
|
||||
RoleCandidateSelector selector = new RoleCandidateSelector();
|
||||
RandomGenerator random = RandomGenerator.of("L64X128MixRandom");
|
||||
TyrantSuccessionService succession = new TyrantSuccessionService(
|
||||
selector, Duration.ofHours(24), Duration.ofHours(24), random
|
||||
);
|
||||
RoleMaintenanceService maintenance = new RoleMaintenanceService(
|
||||
selector, succession, Duration.ofHours(48), Duration.ofHours(24),
|
||||
Duration.ofHours(24), random
|
||||
);
|
||||
RoleRelinquishmentService service = new RoleRelinquishmentService(
|
||||
succession, maintenance
|
||||
);
|
||||
GameState game = new GameState(
|
||||
GameLifecycle.RUNNING, Optional.of(tyrant), Optional.of(vigilante),
|
||||
Optional.empty(), Optional.empty(), Optional.empty(), Duration.ZERO,
|
||||
0, 0, Set.of()
|
||||
);
|
||||
PlayerState tyrantState = active(tyrant, now);
|
||||
PlayerState vigilanteState = active(vigilante, now);
|
||||
PlayerState candidateState = active(candidate, now);
|
||||
|
||||
RelinquishmentResult result = service.relinquish(
|
||||
new PersistentState(game, Map.of(
|
||||
tyrant, tyrantState, vigilante, vigilanteState, candidate, candidateState
|
||||
)),
|
||||
vigilante,
|
||||
Set.of(tyrant, candidate),
|
||||
now
|
||||
);
|
||||
|
||||
assertEquals(RelinquishmentStatus.RELINQUISHED, result.status());
|
||||
assertEquals(Optional.of(candidate), result.state().game().vigilanteId());
|
||||
}
|
||||
|
||||
private static PlayerState active(UUID id, Instant now) {
|
||||
PlayerState player = PlayerState.newPlayer(id, id.toString());
|
||||
return new PlayerState(
|
||||
id, player.latestName(), Optional.of(now.minusSeconds(60)), Optional.empty(),
|
||||
TyrantClass.NONE, Optional.empty(), Map.of(), Set.of(), java.util.List.of()
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user