test(stealth): await complete main-thread admin callbacks
Release / release (push) Successful in 3m10s
CI / build (push) Successful in 1m31s

This commit is contained in:
dmg
2026-09-12 13:02:36 -04:00
parent b7db69d010
commit 74b9ac59ff
@@ -80,7 +80,10 @@ class StealthAdminCommandTest {
fixture.command.onCommand( fixture.command.onCommand(
sender, command, "stealthadmin", new String[] {"sleepcount", "include"}); sender, command, "stealthadmin", new String[] {"sleepcount", "include"});
verify(sender, org.mockito.Mockito.timeout(1000)).sendMessage(contains("now included")); verify(sender, never()).sendMessage(contains("now included"));
assertEquals(List.of(), fixture.audit);
fixture.runMainThreadCallback();
verify(sender).sendMessage(contains("now included"));
assertEquals(SleepCountPolicy.INCLUDE, fixture.manager.snapshot().sleepCountPolicy()); assertEquals(SleepCountPolicy.INCLUDE, fixture.manager.snapshot().sleepCountPolicy());
verify(fixture.presentation).refreshSleepCountPolicy(); verify(fixture.presentation).refreshSleepCountPolicy();
org.junit.jupiter.api.Assertions.assertTrue(fixture.audit.stream() org.junit.jupiter.api.Assertions.assertTrue(fixture.audit.stream()
@@ -125,7 +128,10 @@ class StealthAdminCommandTest {
try (Fixture fixture = fixture(playerId)) { try (Fixture fixture = fixture(playerId)) {
CommandSender sender = sender(true); CommandSender sender = sender(true);
fixture.command.onCommand(sender, mock(Command.class), "stealthadmin", new String[] {"grant", playerId.toString()}); fixture.command.onCommand(sender, mock(Command.class), "stealthadmin", new String[] {"grant", playerId.toString()});
verify(sender, org.mockito.Mockito.timeout(1000)).sendMessage(contains("granted")); verify(sender, never()).sendMessage(contains("granted"));
assertEquals(List.of(), fixture.audit);
fixture.runMainThreadCallback();
verify(sender).sendMessage(contains("granted"));
org.junit.jupiter.api.Assertions.assertTrue(fixture.manager.snapshot().player(playerId).unlocked()); org.junit.jupiter.api.Assertions.assertTrue(fixture.manager.snapshot().player(playerId).unlocked());
org.junit.jupiter.api.Assertions.assertTrue(fixture.audit.stream().anyMatch(message -> message.contains("grant") && message.contains(playerId.toString()))); org.junit.jupiter.api.Assertions.assertTrue(fixture.audit.stream().anyMatch(message -> message.contains("grant") && message.contains(playerId.toString())));
} }
@@ -167,16 +173,30 @@ class StealthAdminCommandTest {
manager, progression, presentation, ignored -> { }, List::of); manager, progression, presentation, ignored -> { }, List::of);
KnownPlayerResolver resolver = new KnownPlayerResolver(manager::snapshot, List::of); KnownPlayerResolver resolver = new KnownPlayerResolver(manager::snapshot, List::of);
ArrayList<String> audit = new ArrayList<>(); ArrayList<String> audit = new ArrayList<>();
var mainThread = new java.util.concurrent.LinkedBlockingQueue<Runnable>();
StealthAdminCommand command = new StealthAdminCommand( StealthAdminCommand command = new StealthAdminCommand(
administration, resolver, Runnable::run, audit::add, Duration.ofHours(8)); administration, resolver, mainThread::add, audit::add, Duration.ofHours(8));
return new Fixture(manager, command, presentation, audit); return new Fixture(manager, command, presentation, audit, mainThread);
} }
private record Fixture( private record Fixture(
StealthStateManager manager, StealthStateManager manager,
StealthAdminCommand command, StealthAdminCommand command,
IdentityPresentation presentation, IdentityPresentation presentation,
ArrayList<String> audit) implements AutoCloseable { ArrayList<String> audit,
java.util.concurrent.BlockingQueue<Runnable> mainThread) implements AutoCloseable {
void runMainThreadCallback() {
// A reply is emitted before the audit append. Await and run the whole scheduled callback,
// not just the reply invocation, and never read the ArrayList concurrently with the I/O thread.
try {
Runnable callback = mainThread.poll(5, java.util.concurrent.TimeUnit.SECONDS);
org.junit.jupiter.api.Assertions.assertNotNull(callback, "Expected a scheduled main-thread completion");
callback.run();
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
throw new AssertionError("Interrupted waiting for command completion", exception);
}
}
@Override public void close() { manager.close(); } @Override public void close() { manager.close(); }
} }
} }