feat(stealth): conceal identity for prepared sessions
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package games.dmg.spigotstealth;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scoreboard.Scoreboard;
|
||||
import org.bukkit.scoreboard.Team;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class BukkitIdentityPresentationTest {
|
||||
@Test
|
||||
void concealRemovesTabEntryAndHidesNameTagWithoutHidingEntity() {
|
||||
UUID targetId = UUID.randomUUID();
|
||||
Player target = player(targetId, "Alex");
|
||||
Player observer = player(UUID.randomUUID(), "Morgan");
|
||||
Scoreboard scoreboard = mock(Scoreboard.class);
|
||||
Team team = mock(Team.class);
|
||||
when(scoreboard.getTeam(BukkitIdentityPresentation.teamName(targetId))).thenReturn(null);
|
||||
when(scoreboard.registerNewTeam(BukkitIdentityPresentation.teamName(targetId))).thenReturn(team);
|
||||
TabListController tabLists = mock(TabListController.class);
|
||||
BukkitIdentityPresentation presentation = new BukkitIdentityPresentation(
|
||||
() -> List.of(target, observer), scoreboard, tabLists);
|
||||
|
||||
presentation.conceal(target);
|
||||
|
||||
verify(team).setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.NEVER);
|
||||
verify(team).addEntry("Alex");
|
||||
verify(tabLists).remove(observer, targetId);
|
||||
verify(observer, never()).hidePlayer(org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void newObserverHasEveryConcealedPlayerRemovedFromTab() {
|
||||
UUID targetId = UUID.randomUUID();
|
||||
Player target = player(targetId, "Alex");
|
||||
Player observer = player(UUID.randomUUID(), "Morgan");
|
||||
Scoreboard scoreboard = mock(Scoreboard.class);
|
||||
Team team = mock(Team.class);
|
||||
when(scoreboard.getTeam(BukkitIdentityPresentation.teamName(targetId))).thenReturn(team);
|
||||
TabListController tabLists = mock(TabListController.class);
|
||||
BukkitIdentityPresentation presentation = new BukkitIdentityPresentation(
|
||||
() -> List.of(target, observer), scoreboard, tabLists);
|
||||
presentation.conceal(target);
|
||||
|
||||
presentation.refreshForObserver(observer);
|
||||
|
||||
verify(tabLists, org.mockito.Mockito.atLeastOnce()).remove(observer, targetId);
|
||||
}
|
||||
|
||||
private static Player player(UUID playerId, String name) {
|
||||
Player player = mock(Player.class);
|
||||
when(player.getUniqueId()).thenReturn(playerId);
|
||||
when(player.getName()).thenReturn(name);
|
||||
return player;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ class PluginMetadataTest {
|
||||
assertEquals("games.dmg.spigotstealth.SpigotStealthPlugin", metadata.get("main"));
|
||||
assertNotNull(metadata.get("commands"));
|
||||
assertNotNull(metadata.get("permissions"));
|
||||
assertEquals(java.util.List.of("ProtocolLib"), metadata.get("depend"));
|
||||
} catch (java.io.IOException exception) {
|
||||
throw new AssertionError(exception);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package games.dmg.spigotstealth;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class StealthSessionListenerTest {
|
||||
@Test
|
||||
void preparedLoginSuppressesAnnouncementAndConcealsIdentityForSession() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
try (StealthStateManager manager = manager()) {
|
||||
manager.update(state -> state.withPlayer(unlocked(playerId).withSession(true, false))).join();
|
||||
QualifyingInvisibilityService progression = new QualifyingInvisibilityService(
|
||||
manager, Duration.ofHours(8), System::nanoTime, ignored -> { });
|
||||
IdentityPresentation presentation = mock(IdentityPresentation.class);
|
||||
StealthSessionListener listener = new StealthSessionListener(
|
||||
new StealthSessionService(manager, progression), presentation, "Stealth active");
|
||||
Player player = player(playerId);
|
||||
PlayerJoinEvent event = mock(PlayerJoinEvent.class);
|
||||
when(event.getPlayer()).thenReturn(player);
|
||||
|
||||
listener.onJoin(event);
|
||||
|
||||
verify(event).setJoinMessage(null);
|
||||
verify(presentation).conceal(player);
|
||||
verify(player).sendMessage("Stealth active");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void ordinaryLoginRetainsAnnouncementAndIdentity() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
try (StealthStateManager manager = manager()) {
|
||||
QualifyingInvisibilityService progression = new QualifyingInvisibilityService(
|
||||
manager, Duration.ofHours(8), System::nanoTime, ignored -> { });
|
||||
IdentityPresentation presentation = mock(IdentityPresentation.class);
|
||||
StealthSessionListener listener = new StealthSessionListener(
|
||||
new StealthSessionService(manager, progression), presentation, "Stealth active");
|
||||
Player player = player(playerId);
|
||||
PlayerJoinEvent event = mock(PlayerJoinEvent.class);
|
||||
when(event.getPlayer()).thenReturn(player);
|
||||
|
||||
listener.onJoin(event);
|
||||
|
||||
verify(event, never()).setJoinMessage(null);
|
||||
verify(presentation).reveal(player);
|
||||
verify(player, never()).sendMessage("Stealth active");
|
||||
}
|
||||
}
|
||||
|
||||
private static Player player(UUID playerId) {
|
||||
Player player = mock(Player.class);
|
||||
when(player.getUniqueId()).thenReturn(playerId);
|
||||
when(player.getName()).thenReturn("Alex");
|
||||
return player;
|
||||
}
|
||||
|
||||
private static PlayerStealthState unlocked(UUID playerId) {
|
||||
return new PlayerStealthState(playerId, "Alex", Duration.ofHours(8).toMillis(), true, false, false, null, Map.of());
|
||||
}
|
||||
|
||||
private static StealthStateManager manager() {
|
||||
StealthStateRepository repository = new StealthStateRepository() {
|
||||
@Override public PersistentStealthState load() { return new PersistentStealthState(Map.of(), Map.of()); }
|
||||
@Override public void save(PersistentStealthState state) { }
|
||||
};
|
||||
return new StealthStateManager(repository, new PersistentStealthState(Map.of(), Map.of()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package games.dmg.spigotstealth;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class StealthSessionServiceTest {
|
||||
@Test
|
||||
void unlockedQualifyingDisconnectPreparesAndConsumesOneConcealedLogin() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
try (StealthStateManager manager = manager()) {
|
||||
manager.update(state -> state.withPlayer(unlocked(playerId))).join();
|
||||
QualifyingInvisibilityService progression = new QualifyingInvisibilityService(
|
||||
manager, Duration.ofHours(8), System::nanoTime, ignored -> { });
|
||||
progression.begin(playerId, "Alex", Instant.EPOCH).join();
|
||||
StealthSessionService sessions = new StealthSessionService(manager, progression);
|
||||
|
||||
sessions.disconnect(playerId).join();
|
||||
assertTrue(manager.snapshot().player(playerId).preparedLogin());
|
||||
assertTrue(sessions.login(playerId, "Alex").concealed());
|
||||
assertFalse(manager.snapshot().player(playerId).preparedLogin());
|
||||
assertTrue(manager.snapshot().player(playerId).concealed());
|
||||
|
||||
sessions.disconnect(playerId).join();
|
||||
assertFalse(sessions.login(playerId, "Alex").concealed());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void lockedPlayerCannotPrepareConcealedLogin() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
try (StealthStateManager manager = manager()) {
|
||||
QualifyingInvisibilityService progression = new QualifyingInvisibilityService(
|
||||
manager, Duration.ofHours(8), System::nanoTime, ignored -> { });
|
||||
progression.begin(playerId, "Alex", Instant.EPOCH).join();
|
||||
StealthSessionService sessions = new StealthSessionService(manager, progression);
|
||||
sessions.disconnect(playerId).join();
|
||||
assertFalse(sessions.login(playerId, "Alex").concealed());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void ordinaryDisconnectClearsEarlierPreparation() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
try (StealthStateManager manager = manager()) {
|
||||
manager.update(state -> state.withPlayer(unlocked(playerId).withSession(true, false))).join();
|
||||
QualifyingInvisibilityService progression = new QualifyingInvisibilityService(
|
||||
manager, Duration.ofHours(8), System::nanoTime, ignored -> { });
|
||||
StealthSessionService sessions = new StealthSessionService(manager, progression);
|
||||
sessions.disconnect(playerId).join();
|
||||
assertFalse(manager.snapshot().player(playerId).preparedLogin());
|
||||
}
|
||||
}
|
||||
|
||||
private static PlayerStealthState unlocked(UUID playerId) {
|
||||
return new PlayerStealthState(playerId, "Alex", Duration.ofHours(8).toMillis(), true, false, false, null, Map.of());
|
||||
}
|
||||
|
||||
private static StealthStateManager manager() {
|
||||
StealthStateRepository repository = new StealthStateRepository() {
|
||||
@Override public PersistentStealthState load() { return new PersistentStealthState(Map.of(), Map.of()); }
|
||||
@Override public void save(PersistentStealthState state) { }
|
||||
};
|
||||
return new StealthStateManager(repository, new PersistentStealthState(Map.of(), Map.of()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user