309 lines
25 KiB
Java
309 lines
25 KiB
Java
package games.dmg.fantasystick;
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
import org.bukkit.command.CommandSender;
|
|
import org.junit.jupiter.api.BeforeAll;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.junit.jupiter.api.io.TempDir;
|
|
|
|
/** Actual packaged PluginCommand permission/dispatch; only the absent console is a port. */
|
|
class EquipmentAdminCommandNativeTest {
|
|
@TempDir Path data;
|
|
@BeforeAll static void bootstrap() throws Exception { NativeRuntime.bootstrap(); }
|
|
|
|
@Test void registeredGivePersistsOneFreshSheetThenDeliversActivatesAndReloads() throws Exception {
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true);
|
|
var target = new FantasyStickLifecycleNativeTest.Actor(data); f.players.put(target.core.id, target.player);
|
|
var before = target.core.snapshot(); var sender = new Sender();
|
|
assertTrue(f.command.execute(sender.port, "fantasystick", new String[]{"give", "Account"}));
|
|
assertTrue(sender.messages.isEmpty(), "Queued persistence is not successful delivery");
|
|
((games.dmg.fantasystick.persistence.CharacterStorage) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "characters"))
|
|
.find(java.util.UUID.randomUUID()).get(5, java.util.concurrent.TimeUnit.SECONDS);
|
|
var callback = f.tasks.poll(5, java.util.concurrent.TimeUnit.SECONDS); assertNotNull(callback); callback.run(); callback.run();
|
|
assertTrue(sender.messages.getLast().contains("Issued one new character sheet"));
|
|
assertFalse(java.util.Arrays.equals(before, target.core.snapshot()));
|
|
var sheets = new games.dmg.fantasystick.items.CharacterSheets();
|
|
var delivered = java.util.stream.IntStream.range(0, 36).mapToObj(target.core.inventory::getItem)
|
|
.filter(item -> sheets.identify(item).isPresent()).filter(item -> !sheets.identify(item).orElseThrow().equals(target.core.record.id())).toList();
|
|
assertEquals(1, delivered.size(), "Repeated callback must not duplicate delivery");
|
|
assertEquals(target.core.record.id(),sheets.identify(target.core.inventory.getItemInOffHand()).orElseThrow());
|
|
assertTrue(((games.dmg.fantasystick.session.EquipmentSessions) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin,"sessions")).active(target.core.id).isEmpty());
|
|
var id = sheets.identify(delivered.getFirst()).orElseThrow();
|
|
var storage = (games.dmg.fantasystick.persistence.CharacterStorage) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "characters");
|
|
var record = storage.find(id).get(5, java.util.concurrent.TimeUnit.SECONDS).orElseThrow();
|
|
assertEquals(java.math.BigInteger.valueOf(40), record.unspentPoints()); assertEquals(1, record.level());
|
|
assertEquals(java.math.BigInteger.ZERO, record.experience()); assertFalse(record.dead()); assertTrue(record.learned().isEmpty());
|
|
assertTrue(record.stats().values().stream().allMatch(java.math.BigInteger.ZERO::equals));assertEquals(new java.math.BigDecimal("10"),record.health());
|
|
assertEquals(java.util.Collections.nCopies(7,""),record.loadout());assertTrue(record.cooldowns().isEmpty());assertFalse(record.name().isBlank());
|
|
target.core.inventory.remove(delivered.getFirst()); target.core.inventory.setItemInOffHand(delivered.getFirst());
|
|
f.activate(target); assertEquals(id, ((games.dmg.fantasystick.session.EquipmentSessions) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "sessions")).active(target.core.id).orElseThrow().character());
|
|
f.plugin.setEnabled(false); f.plugin.setEnabled(true);
|
|
assertEquals(record, ((games.dmg.fantasystick.persistence.CharacterStorage) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "characters")).find(id).get(5, java.util.concurrent.TimeUnit.SECONDS).orElseThrow());
|
|
}
|
|
}
|
|
|
|
@Test void giveCompletionAndRejectedExecutionArePermissionAwareStrictAndReadOnly() throws Exception {
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true); var target = new FantasyStickLifecycleNativeTest.Actor(data); f.players.put(target.core.id,target.player);
|
|
var sender=new Sender();
|
|
assertEquals(List.of("config","experience","give","inspect","party-registrar","recover","shop","training-dummy"),f.command.tabComplete(sender.port,"fantasystick",new String[]{""}));
|
|
assertEquals(List.of("give"),f.command.tabComplete(sender.port,"fantasystick",new String[]{"g"}));
|
|
assertEquals(List.of("Account"),f.command.tabComplete(sender.port,"fantasystick",new String[]{"give",""}));
|
|
assertEquals(List.of("Account"),f.command.tabComplete(sender.port,"fantasystick",new String[]{"give","Acc"}));
|
|
assertTrue(f.command.tabComplete(sender.port,"fantasystick",new String[]{"give","Account",""}).isEmpty());
|
|
sender.allowed=false;assertTrue(f.command.tabComplete(sender.port,"fantasystick",new String[]{"give",""}).isEmpty());
|
|
var original=target.core.snapshot();f.command.getExecutor().onCommand(sender.port,f.command,"fantasystick",new String[]{"give","Account"});
|
|
sender.allowed=true;for(String[] args:List.of(new String[]{"give"},new String[]{"give","Missing"},new String[]{"give","Account","extra"}))f.command.execute(sender.port,"fantasystick",args);
|
|
assertArrayEquals(original,target.core.snapshot());assertTrue(f.tasks.isEmpty());
|
|
assertEquals(1,java.nio.file.Files.list(data.resolve("characters")).filter(path->path.toString().endsWith(".yml")).count());
|
|
for(int slot=0;slot<36;slot++)target.core.inventory.setItem(slot,new org.bukkit.inventory.ItemStack(org.bukkit.Material.STONE));
|
|
var full=target.core.snapshot();f.command.execute(sender.port,"fantasystick",new String[]{"give","Account"});assertTrue(sender.messages.getLast().contains("no inventory space"));
|
|
assertArrayEquals(full,target.core.snapshot());assertTrue(f.tasks.isEmpty());assertEquals(1,java.nio.file.Files.list(data.resolve("characters")).filter(path->path.toString().endsWith(".yml")).count());
|
|
}
|
|
}
|
|
|
|
@Test void giveSaveFailureDisconnectSlotRaceAndLifecycleInterruptionNeverDuplicateOrOverwrite() throws Exception {
|
|
for(String route:List.of("save","disconnect","slot","close")){var dir=data.resolve(route);java.nio.file.Files.createDirectories(dir);try(var f=new FantasyStickLifecycleNativeTest.Fixture(dir)){
|
|
f.plugin.setEnabled(true);var target=new FantasyStickLifecycleNativeTest.Actor(dir);f.players.put(target.core.id,target.player);var sender=new Sender();var original=target.core.snapshot();
|
|
var storage=(games.dmg.fantasystick.persistence.CharacterStorage)FantasyStickLifecycleNativeTest.Fixture.field(f.plugin,"characters");
|
|
var worker=(java.util.concurrent.ExecutorService)FantasyStickLifecycleNativeTest.Fixture.field(storage,"worker");var entered=new java.util.concurrent.CountDownLatch(1);var release=new java.util.concurrent.CountDownLatch(1);worker.execute(()->{entered.countDown();try{release.await(5,java.util.concurrent.TimeUnit.SECONDS);}catch(InterruptedException e){Thread.currentThread().interrupt();}});assertTrue(entered.await(5,java.util.concurrent.TimeUnit.SECONDS));
|
|
f.command.execute(sender.port,"fantasystick",new String[]{"give","Account"});
|
|
if(route.equals("save")){var issuer=FantasyStickLifecycleNativeTest.Fixture.field(f.command.getExecutor(),"characterIssuer");var pending=(java.util.Map<?,?>)FantasyStickLifecycleNativeTest.Fixture.field(issuer,"pending");var attempt=pending.values().iterator().next();var record=(games.dmg.fantasystick.persistence.CharacterRecord)FantasyStickLifecycleNativeTest.Fixture.field(attempt,"record");java.nio.file.Files.createDirectory(dir.resolve("characters").resolve(record.id()+".yml"));}
|
|
release.countDown();storage.find(java.util.UUID.randomUUID()).get(5,java.util.concurrent.TimeUnit.SECONDS);var callback=f.tasks.poll(5,java.util.concurrent.TimeUnit.SECONDS);assertNotNull(callback);
|
|
if(route.equals("disconnect"))target.core.online=false;if(route.equals("slot"))target.core.inventory.setItem(1,new org.bukkit.inventory.ItemStack(org.bukkit.Material.DIAMOND));if(route.equals("close"))f.plugin.setEnabled(false);callback.run();
|
|
if(!route.equals("close"))storage.find(java.util.UUID.randomUUID()).get(5,java.util.concurrent.TimeUnit.SECONDS);
|
|
assertFalse(sender.messages.stream().anyMatch(message->message.startsWith("Issued one")));assertEquals(route.equals("slot")?org.bukkit.Material.DIAMOND:original[1]==null?null:original[1].getType(),target.core.inventory.getItem(1)==null?null:target.core.inventory.getItem(1).getType());
|
|
if(!route.equals("save"))assertEquals(1,java.nio.file.Files.list(dir.resolve("characters")).filter(path->path.toString().endsWith(".yml")).count());
|
|
release.countDown();}}
|
|
}
|
|
|
|
@Test void recoveryCompletionIsPermissionAwarePrefixFilteredUuidOnlyAndReadOnly() throws Exception {
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true);
|
|
var first = new FantasyStickLifecycleNativeTest.Actor(data);
|
|
var second = new FantasyStickLifecycleNativeTest.Actor(data);
|
|
var offline = new FantasyStickLifecycleNativeTest.Actor(data);
|
|
offline.core.online = false;
|
|
f.players.put(first.core.id, first.player); f.players.put(second.core.id, second.player); f.players.put(offline.core.id, offline.player);
|
|
var sender = new Sender();
|
|
var expected = java.util.stream.Stream.of(first.core.id.toString(), second.core.id.toString()).sorted().toList();
|
|
assertEquals(List.of("config", "experience", "give", "inspect", "party-registrar", "recover", "shop", "training-dummy"), f.command.tabComplete(sender.port, "fantasystick", new String[]{""}));
|
|
assertEquals(List.of("inspect"), f.command.tabComplete(sender.port, "fantasystick", new String[]{"in"}));
|
|
assertEquals(List.of("recover"), f.command.tabComplete(sender.port, "fantasystick", new String[]{"rec"}));
|
|
assertEquals(expected, f.command.tabComplete(sender.port, "fantasystick", new String[]{"inspect", ""}));
|
|
String prefix = expected.getFirst().substring(0, 8);
|
|
assertEquals(expected.stream().filter(id -> id.startsWith(prefix)).toList(),
|
|
f.command.tabComplete(sender.port, "fantasystick", new String[]{"recover", prefix}));
|
|
assertFalse(f.command.tabComplete(sender.port, "fantasystick", new String[]{"inspect", ""}).contains("Account"));
|
|
assertTrue(f.command.tabComplete(sender.port, "fantasystick", new String[]{"inspect", expected.getFirst(), ""}).isEmpty());
|
|
assertTrue(f.command.tabComplete(sender.port, "fantasystick", new String[]{"unknown", ""}).isEmpty());
|
|
assertEquals(List.of("adventure-mode"), f.command.tabComplete(sender.port, "fantasystick", new String[]{"config", "adventure-m"}));
|
|
assertEquals(List.of("false", "true"), f.command.tabComplete(sender.port, "fantasystick", new String[]{"config", "adventure-mode", ""}));
|
|
assertEquals(List.of("true"), f.command.tabComplete(sender.port, "fantasystick", new String[]{"config", "adventure-mode", "t"}));
|
|
assertTrue(f.command.tabComplete(sender.port, "fantasystick", new String[]{"config", "adventure-mode", "true", ""}).isEmpty());
|
|
sender.allowed = false;
|
|
for (String[] args : List.of(new String[]{""}, new String[]{"inspect", ""}, new String[]{"config", ""},
|
|
new String[]{"config", "adventure-mode", ""})) {
|
|
assertTrue(f.command.tabComplete(sender.port, "fantasystick", args).isEmpty());
|
|
}
|
|
assertTrue(f.tasks.isEmpty()); assertFalse(Files.exists(data.resolve("sessions")));
|
|
}
|
|
}
|
|
|
|
@Test void adventureConfigurationCommandReadsValidatesPersistsAndReopensWithoutTouchingPlayers() throws Exception {
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true);
|
|
var sender = new Sender();
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"config", "adventure-mode"});
|
|
assertTrue(sender.messages.getLast().contains("adventure-mode=true"));
|
|
sender.messages.clear();
|
|
for (String value : List.of("yes", "1", "TRUE", "")) {
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"config", "adventure-mode", value});
|
|
assertTrue(sender.messages.getLast().contains("true|false"));
|
|
}
|
|
assertTrue(f.tasks.isEmpty());
|
|
assertFalse(Files.exists(data.resolve("config.yml")));
|
|
sender.allowed = false;
|
|
f.command.getExecutor().onCommand(sender.port, f.command, "fantasystick", new String[]{"config", "adventure-mode", "false"});
|
|
assertFalse(Files.exists(data.resolve("config.yml")));
|
|
sender.allowed = true; sender.messages.clear();
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"config", "adventure-mode", "false"});
|
|
assertTrue(sender.messages.isEmpty(), "Do not report success before acknowledgment");
|
|
var reply = f.tasks.poll(5, java.util.concurrent.TimeUnit.SECONDS); assertNotNull(reply); reply.run();
|
|
assertTrue(sender.messages.getLast().contains("adventure-mode=false"));
|
|
assertTrue(sender.messages.getLast().contains("new sessions"));
|
|
assertEquals(0, f.playerLookups);
|
|
f.plugin.setEnabled(false); f.plugin.setEnabled(true);
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"config", "adventure-mode"});
|
|
assertTrue(sender.messages.getLast().contains("adventure-mode=false"));
|
|
}
|
|
}
|
|
|
|
@Test void reenableWaitsOffThreadForAcceptedConfigurationWritesBeforeReadingNewPolicy() throws Exception {
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true);
|
|
var old = FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "adventureSettings");
|
|
var worker = (java.util.concurrent.ExecutorService) FantasyStickLifecycleNativeTest.Fixture.field(old, "worker");
|
|
var entered = new java.util.concurrent.CountDownLatch(1);
|
|
var release = new java.util.concurrent.CountDownLatch(1);
|
|
worker.execute(() -> {
|
|
entered.countDown();
|
|
try { release.await(5, java.util.concurrent.TimeUnit.SECONDS); }
|
|
catch (InterruptedException error) { Thread.currentThread().interrupt(); }
|
|
});
|
|
try {
|
|
assertTrue(entered.await(5, java.util.concurrent.TimeUnit.SECONDS));
|
|
var sender = new Sender();
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"config", "adventure-mode", "false"});
|
|
f.plugin.setEnabled(false); f.plugin.setEnabled(true);
|
|
assertNull(FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "sessions"), "Do not activate from a stale config snapshot");
|
|
release.countDown();
|
|
f.tick();
|
|
assertNotNull(FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "sessions"));
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"config", "adventure-mode"});
|
|
assertTrue(sender.messages.getLast().contains("adventure-mode=false"));
|
|
} finally { release.countDown(); }
|
|
}
|
|
}
|
|
|
|
@Test void permissionAndStrictShapeRejectBeforePersistenceOrNativeLookup() throws Exception {
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true);
|
|
assertNotNull(f.command, "Packaged descriptor must install the actual command");
|
|
var sender = new Sender();
|
|
sender.allowed = false;
|
|
assertTrue(f.command.execute(sender.port, "fantasystick", new String[]{"inspect", UUID.randomUUID().toString()}));
|
|
// Executor must also defend direct dispatch, not rely solely on PluginCommand.
|
|
assertTrue(f.command.getExecutor().onCommand(sender.port, f.command, "fantasystick",
|
|
new String[]{"recover", UUID.randomUUID().toString()}));
|
|
sender.allowed = true;
|
|
for (String[] args : List.of(new String[]{}, new String[]{"inspect"},
|
|
new String[]{"reset", UUID.randomUUID().toString()}, new String[]{"inspect", "1-1-1-1-1"},
|
|
new String[]{"recover", "not-a-uuid"}, new String[]{"inspect", UUID.randomUUID().toString(), "extra"})) {
|
|
assertTrue(f.command.execute(sender.port, "fantasystick", args));
|
|
}
|
|
assertFalse(Files.exists(data.resolve("sessions")));
|
|
assertTrue(f.tasks.isEmpty());
|
|
assertEquals(0, f.playerLookups, "Rejected commands must not inspect native players");
|
|
assertFalse(sender.messages.isEmpty());
|
|
}
|
|
assertFalse(Files.exists(data.resolve("sessions")), "No queued IO after worker drain either");
|
|
}
|
|
|
|
@Test void inspectIsDeferredSanitizedAndReturnsToMainThreadAndCloseDropsResponses() throws Exception {
|
|
try (var receipts = new games.dmg.fantasystick.persistence.EquipmentSessionStore(data, java.time.Clock.systemUTC());
|
|
var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
UUID owner = UUID.randomUUID(), session = UUID.randomUUID(), character = UUID.randomUUID();
|
|
receipts.prepare(owner, session, character).toCompletableFuture().get(5, java.util.concurrent.TimeUnit.SECONDS);
|
|
receipts.recordExit(owner, session, java.time.Duration.ofSeconds(900)).toCompletableFuture().get(5, java.util.concurrent.TimeUnit.SECONDS);
|
|
f.plugin.setEnabled(true);
|
|
var sender = new Sender();
|
|
assertNotNull(f.command);
|
|
assertTrue(f.command.execute(sender.port, "fantasystick", new String[]{"inspect", owner.toString()}));
|
|
assertTrue(sender.messages.isEmpty(), "No response until the server callback");
|
|
var response = f.tasks.poll(5, java.util.concurrent.TimeUnit.SECONDS); assertNotNull(response);
|
|
response.run();
|
|
String text = String.join("\n", sender.messages);
|
|
assertTrue(text.contains(owner.toString()), text);
|
|
assertTrue(text.contains(session.toString()), text);
|
|
assertTrue(text.contains(character.toString()), text);
|
|
assertTrue(text.contains("pending") && text.contains("deadline") && text.contains("exit"), text);
|
|
assertFalse(text.contains("schema-version") || text.contains(data.toString()), text);
|
|
sender.messages.clear();
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"inspect", owner.toString()});
|
|
var stale = f.tasks.poll(5, java.util.concurrent.TimeUnit.SECONDS); assertNotNull(stale);
|
|
f.plugin.setEnabled(false);
|
|
f.plugin.setEnabled(true);
|
|
stale.run();
|
|
assertTrue(sender.messages.isEmpty(), "Previous lifetime must not reply after re-enable");
|
|
}
|
|
}
|
|
|
|
@Test void onlineRecoveryUsesNativeProofAndReportsOnlyARequestAndOfflineNeverQueuesIO() throws Exception {
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true);
|
|
var sender = new Sender(); UUID offline = UUID.randomUUID();
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"recover", offline.toString()});
|
|
assertTrue(sender.messages.getLast().contains("must be online"));
|
|
assertFalse(Files.exists(data.resolve("sessions"))); assertTrue(f.tasks.isEmpty());
|
|
var a = new FantasyStickLifecycleNativeTest.Actor(data); f.players.put(a.core.id, a.player);
|
|
var original = a.core.snapshot(); UUID session = UUID.randomUUID();
|
|
f.receipts().prepare(a.core.id, session, a.core.record.id()).toCompletableFuture().get(5, java.util.concurrent.TimeUnit.SECONDS);
|
|
assertEquals(games.dmg.fantasystick.items.EquipmentStorage.PackResult.PACKED,
|
|
new games.dmg.fantasystick.items.EquipmentStorage().pack(a.core.inventory, a.core.id, session));
|
|
a.core.marker(session, "PACKED"); var packed = a.core.snapshot();
|
|
sender.messages.clear();
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"recover", a.core.id.toString()});
|
|
assertEquals(1, sender.messages.size());
|
|
assertTrue(sender.messages.getFirst().contains("requested") && sender.messages.getFirst().contains("not confirmation"));
|
|
assertArrayEquals(packed, a.core.snapshot(), "Request is not premature success");
|
|
f.tick();
|
|
assertArrayEquals(original, a.core.snapshot()); assertTrue(a.core.marker().endsWith("|RESTORED"));
|
|
assertNull(f.receipts().find(a.core.id).toCompletableFuture().get(5, java.util.concurrent.TimeUnit.SECONDS).orElseThrow().pending());
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"recover", a.core.id.toString()}); f.tick();
|
|
assertArrayEquals(original, a.core.snapshot());
|
|
}
|
|
}
|
|
|
|
@Test void ambiguousRecoveryRetainsEveryItemAndReceiptAndInspectionNeverPrintsRawMarker() throws Exception {
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true);
|
|
var a = new FantasyStickLifecycleNativeTest.Actor(data); f.players.put(a.core.id, a.player);
|
|
UUID session = UUID.randomUUID();
|
|
var saved = f.receipts().prepare(a.core.id, session, a.core.record.id()).toCompletableFuture().get(5, java.util.concurrent.TimeUnit.SECONDS);
|
|
new games.dmg.fantasystick.items.EquipmentStorage().pack(a.core.inventory, a.core.id, session);
|
|
a.core.pdc.set(new org.bukkit.NamespacedKey("fantasystick", "equipment_session"),
|
|
org.bukkit.persistence.PersistentDataType.STRING, "secret-not-a-valid-marker");
|
|
var before = a.core.snapshot(); var marker = a.core.pdc.serializeToBytes();
|
|
var sender = new Sender();
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"recover", a.core.id.toString()}); f.tick();
|
|
assertArrayEquals(before, a.core.snapshot()); assertArrayEquals(marker, a.core.pdc.serializeToBytes());
|
|
assertEquals(saved, f.receipts().find(a.core.id).toCompletableFuture().get(5, java.util.concurrent.TimeUnit.SECONDS).orElseThrow());
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"inspect", a.core.id.toString()}); f.tick();
|
|
var text = String.join("\n", sender.messages);
|
|
assertTrue(text.contains("malformed (retained)")); assertTrue(text.contains("active=none"));
|
|
assertFalse(text.contains("secret-not-a-valid-marker"));
|
|
}
|
|
}
|
|
|
|
@Test void inspectObservesCurrentOnlineActiveStateAndPermissionRevocationDropsDeferredResponse() throws Exception {
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true);
|
|
var a = new FantasyStickLifecycleNativeTest.Actor(data); f.players.put(a.core.id, a.player); f.activate(a);
|
|
var sender = new Sender();
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"inspect", a.core.id.toString()}); f.tick();
|
|
assertTrue(sender.messages.stream().anyMatch(message -> message.contains("active=session=")));
|
|
assertTrue(sender.messages.stream().anyMatch(message -> message.contains("phase=PACKED")));
|
|
sender.messages.clear();
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"inspect", a.core.id.toString()});
|
|
var pending = f.tasks.poll(5, java.util.concurrent.TimeUnit.SECONDS); assertNotNull(pending);
|
|
sender.allowed = false; pending.run(); assertTrue(sender.messages.isEmpty());
|
|
sender.allowed = true;
|
|
f.command.execute(sender.port, "fantasystick", new String[]{"inspect", a.core.id.toString()});
|
|
pending = f.tasks.poll(5, java.util.concurrent.TimeUnit.SECONDS); assertNotNull(pending);
|
|
((AutoCloseable) f.command.getExecutor()).close(); pending.run(); assertTrue(sender.messages.isEmpty());
|
|
}
|
|
}
|
|
|
|
static final class Sender {
|
|
final Thread main = Thread.currentThread();
|
|
final List<String> messages = new ArrayList<>();
|
|
boolean allowed = true;
|
|
final CommandSender port = FantasyStickLifecycleNativeTest.port(CommandSender.class, (name, args) -> {
|
|
assertSame(main, Thread.currentThread(), "Worker accessed command sender: " + name);
|
|
return switch (name) {
|
|
case "hasPermission" -> { assertEquals("fantasystick.admin", args[0]); yield allowed; }
|
|
case "sendMessage" -> { messages.add(String.valueOf(args[0])); yield null; }
|
|
default -> throw new AssertionError("Unexpected sender call " + name);
|
|
};
|
|
});
|
|
}
|
|
}
|