test(fantasy-stick): fence owned audit work in native fixtures
Release / release (push) Failing after 3h10m53s

This commit is contained in:
dmg committed 2026-10-06 17:17:45 -04:00
1 parent 496b20a3a4
commit 715f3cc6ef
3 files changed
+140 -5

No files matched your search

@@ -30,8 +30,7 @@ class ArmorConfigurationEventsNativeTest {
f.adminCommand.execute(sender.port, "fstickadmin", new String[]{"config", key});
assertTrue(sender.messages.stream().anyMatch(message -> message.startsWith(key + "=" + settings.value(key))));
}
for (int attempt = 0; attempt < 100 && rows("config.read").size() < GearSettings.keys().size(); attempt++) Thread.sleep(10);
assertEquals(GearSettings.keys().size(), rows("config.read").size(), "Every actual registered gear read needs one owner JSONL record");
awaitGearReads(f, GearSettings.keys().size());
for (var row : rows("config.read")) {
var values = row.getAsJsonObject("data"); var key = values.get("key").getAsString();
assertTrue(GearSettings.keys().contains(key)); assertEquals(settings.value(key), values.get("after").getAsString());
@@ -128,6 +127,67 @@ class ArmorConfigurationEventsNativeTest {
f.plugin.setEnabled(false); f.awaitWorkers();
}
}
@Test void visibleRegisteredGearReadDoesNotAcknowledgeItsHeldOwnerCallback() throws Exception {
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
f.plugin.setEnabled(true);
((ArmorMarket) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "armorMarket"))
.current().toCompletableFuture().get(5, TimeUnit.SECONDS);
FixtureAuditAcknowledgmentNativeTest.awaitOwnQuiescence(f);
var owner = FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "armorAuditEvents");
var outbox = FantasyStickLifecycleNativeTest.Fixture.field(owner, "outbox");
var sinkField = outbox.getClass().getDeclaredField("sink"); sinkField.setAccessible(true);
var original = FixtureAuditAcknowledgmentNativeTest.sink(outbox);
var held = new java.util.concurrent.CompletableFuture<games.dmg.fantasystick.events.LocalEventLog.Delivery>();
var exported = new java.util.concurrent.CountDownLatch(1);
var returned = new java.util.concurrent.CountDownLatch(1);
var premature = new java.util.concurrent.atomic.AtomicBoolean();
java.util.concurrent.CompletableFuture<Void> observer = null;
try {
java.util.function.Function<games.dmg.fantasystick.events.EventInput,
java.util.concurrent.CompletionStage<games.dmg.fantasystick.events.LocalEventLog.Delivery>> sink = input ->
original.apply(input).thenCompose(delivery -> {
if (!input.type().equals("config.read")) return java.util.concurrent.CompletableFuture.completedStage(delivery);
assertEquals(games.dmg.fantasystick.events.LocalEventLog.Delivery.EXPORTED, delivery);
exported.countDown(); return held;
});
sinkField.set(outbox, sink);
var sender = new EquipmentAdminCommandNativeTest.Sender();
var settings = (GearSettings) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "gearSettings");
var before = settings.value(GearSettings.VARIANCE);
f.adminCommand.execute(sender.port, "fstickadmin", new String[]{"config", GearSettings.VARIANCE});
assertTrue(sender.messages.stream().anyMatch(message -> message.startsWith(GearSettings.VARIANCE + "=" + before)));
assertTrue(exported.await(5, TimeUnit.SECONDS));
assertEquals(1, rows("config.read").size(), "Actual registered read is already visibly exported");
assertEquals(1, ((java.util.Set<?>) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "inFlight")).size());
observer = java.util.concurrent.CompletableFuture.runAsync(() -> {
try { premature.set(returned.await(200, TimeUnit.MILLISECONDS)); }
catch (InterruptedException interrupted) { Thread.currentThread().interrupt(); throw new AssertionError(interrupted); }
finally { held.complete(games.dmg.fantasystick.events.LocalEventLog.Delivery.EXPORTED); }
});
awaitGearReads(f, 1); returned.countDown(); observer.get(5, TimeUnit.SECONDS);
assertFalse(premature.get(), "Read fixture returned while its real exported row still had an unacknowledged owned callback");
assertEquals(before, settings.value(GearSettings.VARIANCE));
assertEquals(1, rows("config.read").size()); assertTrue(rows("config.changed").isEmpty());
assertEquals(0, ((java.util.Set<?>) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "inFlight")).size());
} finally {
held.complete(games.dmg.fantasystick.events.LocalEventLog.Delivery.EXPORTED);
sinkField.set(outbox, original);
if (observer != null) observer.get(5, TimeUnit.SECONDS);
((java.util.concurrent.ExecutorService) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "worker"))
.submit(() -> {}).get(5, TimeUnit.SECONDS);
}
}
}
private void awaitGearReads(FantasyStickLifecycleNativeTest.Fixture f, int expected) throws Exception {
// Accepted commands publish asynchronously: settle their owned setup before the unchanged row assertion.
FixtureAuditAcknowledgmentNativeTest.awaitOwnQuiescence(f);
for (int attempt = 0; attempt < 100 && rows("config.read").size() < expected; attempt++) Thread.sleep(10);
assertEquals(expected, rows("config.read").size(),
() -> "Every actual registered gear read needs one owner JSONL record; "
+ FixtureAuditAcknowledgmentNativeTest.ownerState(FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "armorAuditEvents")));
}
private boolean hasStartupCatalog() {
try { return ReskillEventsNativeTest.records(data).stream().anyMatch(row -> row.get("type").getAsString().endsWith("armor.catalog_refreshed")); }
catch (Exception failure) { throw new AssertionError("Cannot parse actual armor exports", failure); }
@@ -55,8 +55,16 @@ class CharacterBossMetadataSafetyNativeTest {
@Test void authenticVeryLargePersistedStatReachesBoundedOriginalCustodyAndNativeCastProofThenExactOfflineReplay() throws Exception {
var huge=BigInteger.ONE.shiftLeft(60_000);List<java.util.Properties> originals;byte[] definitions,character;Path subjectFile;String expected;
try(var fixture=new CharacterBossAuditNativeFixture(data,List.of("speed_i","low_light_vision","quicker_regeneration_i"),Map.of(Stat.INT,huge))) {
var b=fixture.base;((LocalEventLog)FantasyStickLifecycleNativeTest.Fixture.field(b.f.plugin,"eventLog")).close();fixture.authorAndAccept();subjectFile=data.resolve("characters").resolve(fixture.template.id()+".yml");character=Files.readAllBytes(subjectFile);b.enter(b.boss);var shell=fixture.shell();assertTrue(shell.entity.isValid());assertEquals(70,shell.health[0]);b.actor.damage=(amount,source)->{};fixture.damage(b.actor,shell,3,false);b.shared.pulse();assertEquals(fixture.custody,b.directory.custody(fixture.template.id()).orElseThrow());expected=huge.multiply(BigInteger.TWO).toString();
b.f.plugin.setEnabled(false);b.f.awaitWorkers();WitchAuditTestSupport.drain(b.f,"characterBossDrain");originals=WitchAuditTestSupport.intents(data.resolve("character-boss-event-intents"));
var b=fixture.base;((LocalEventLog)FantasyStickLifecycleNativeTest.Fixture.field(b.f.plugin,"eventLog")).close();fixture.authorAndAccept();subjectFile=data.resolve("characters").resolve(fixture.template.id()+".yml");character=Files.readAllBytes(subjectFile);FixtureAuditAcknowledgmentNativeTest.awaitOwnQuiescence(b.f);b.enter(b.boss);var shell=fixture.shell();assertTrue(shell.entity.isValid());assertEquals(70,shell.health[0]);FixtureAuditAcknowledgmentNativeTest.awaitOwnQuiescence(b.f);b.actor.damage=(amount,source)->{};fixture.damage(b.actor,shell,3,false);b.shared.pulse();assertEquals(fixture.custody,b.directory.custody(fixture.template.id()).orElseThrow());expected=huge.multiply(BigInteger.TWO).toString();
var owner=FantasyStickLifecycleNativeTest.Fixture.field(b.f.plugin,"characterBossEvents");
System.out.println("boss-before-close " + FixtureAuditAcknowledgmentNativeTest.ownerState(owner));
try { shutdownAfterMetadata(b.f); }
catch (java.util.concurrent.TimeoutException timeout) {
System.out.println("boss-at-deadline " + FixtureAuditAcknowledgmentNativeTest.ownerState(owner)
+ " dungeon-authority-drained=" + b.directory.drained().toCompletableFuture().isDone());
throw timeout;
}
originals=WitchAuditTestSupport.intents(data.resolve("character-boss-event-intents"));
for(var type:List.of("dungeon.character_boss.assigned","dungeon.entity.spawned","ability.cast","ability.resolved"))assertTrue(originals.stream().anyMatch(p->p.getProperty("type").equals(type)),"Supported exact large stat must not drop every independent owner: "+type);
try(var paths=Files.list(data.resolve("character-boss-event-intents"))) {for(var path:paths.filter(p->p.toString().endsWith(".pending")).toList())assertTrue(Files.size(path)<=16_384,"Shared durable intent bound stays unchanged");}
definitions=Files.readAllBytes(data.resolve("dungeons.yml"));assertArrayEquals(character,Files.readAllBytes(subjectFile));
@@ -80,6 +88,60 @@ class CharacterBossMetadataSafetyNativeTest {
}
assertArrayEquals(definitions,Files.readAllBytes(data.resolve("dungeons.yml")));assertArrayEquals(character,Files.readAllBytes(subjectFile));
}
@Test void acceptedBossMetadataMustSettleBeforeFixtureStartsBoundedShutdown() throws Exception {
try (var fixture = new CharacterBossAuditNativeFixture(data)) {
var b = fixture.base;
FixtureAuditAcknowledgmentNativeTest.awaitOwnQuiescence(b.f);
((LocalEventLog) FantasyStickLifecycleNativeTest.Fixture.field(b.f.plugin, "eventLog")).close();
var owner = FantasyStickLifecycleNativeTest.Fixture.field(b.f.plugin, "characterBossEvents");
var outbox = FantasyStickLifecycleNativeTest.Fixture.field(owner, "outbox");
var worker = (java.util.concurrent.ThreadPoolExecutor) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "worker");
var ownerClosed = (java.util.concurrent.atomic.AtomicBoolean) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "closed");
var entered = new java.util.concurrent.CountDownLatch(1);
var release = new java.util.concurrent.CountDownLatch(1);
var returned = new java.util.concurrent.CountDownLatch(1);
var prematureShutdown = new java.util.concurrent.atomic.AtomicBoolean();
java.util.concurrent.Future<?> heldWork = null;
java.util.concurrent.CompletableFuture<Void> observer = null;
try {
heldWork = worker.submit(() -> {
entered.countDown();
try { release.await(); }
catch (InterruptedException interrupted) { Thread.currentThread().interrupt(); throw new AssertionError(interrupted); }
});
assertTrue(entered.await(5, java.util.concurrent.TimeUnit.SECONDS));
fixture.authorAndAccept();
assertTrue(worker.getQueue().size() > 0, "Real registered custody/native consumption queued its independent original metadata behind this owned hold");
assertEquals(fixture.custody, b.directory.custody(fixture.template.id()).orElseThrow());
var subjectFile = data.resolve("characters").resolve(fixture.template.id() + ".yml");
var subjectBytes = Files.readAllBytes(subjectFile);
observer = java.util.concurrent.CompletableFuture.runAsync(() -> {
try {
returned.await(200, java.util.concurrent.TimeUnit.MILLISECONDS);
prematureShutdown.set(ownerClosed.get() || worker.isShutdown());
} catch (InterruptedException interrupted) {
Thread.currentThread().interrupt(); throw new AssertionError(interrupted);
} finally { release.countDown(); }
});
shutdownAfterMetadata(b.f); returned.countDown(); observer.get(5, java.util.concurrent.TimeUnit.SECONDS);
assertFalse(prematureShutdown.get(), "Fixture started bounded shutdown before its already accepted original metadata writes settled");
assertTrue(worker.isTerminated()); assertArrayEquals(subjectBytes, Files.readAllBytes(subjectFile));
var originals = WitchAuditTestSupport.intents(data.resolve("character-boss-event-intents"));
assertEquals(1, originals.stream().filter(intent -> intent.getProperty("type").equals("dungeon.character_boss.assigned")).count());
} finally {
release.countDown();
if (observer != null) observer.get(5, java.util.concurrent.TimeUnit.SECONDS);
if (heldWork != null) heldWork.get(5, java.util.concurrent.TimeUnit.SECONDS);
}
}
}
private static void shutdownAfterMetadata(FantasyStickLifecycleNativeTest.Fixture f) throws Exception {
// Test-owned accepted metadata settles before starting the unchanged production shutdown attempt.
FixtureAuditAcknowledgmentNativeTest.awaitOwnQuiescence(f);
f.plugin.setEnabled(false); f.awaitWorkers(); WitchAuditTestSupport.drain(f, "characterBossDrain");
}
private static void command(FantasyStickLifecycleNativeTest.Fixture f,FantasyStickLifecycleNativeTest.Actor actor,String... args) throws Exception {DungeonAuthoringEventsNativeTest.command(f,actor,args);}
private static void fence(Object owner) throws Exception {((java.util.concurrent.ExecutorService)FantasyStickLifecycleNativeTest.Fixture.field(owner,"worker")).submit(()->{}).get(5,java.util.concurrent.TimeUnit.SECONDS);}
}
@@ -159,13 +159,26 @@ class FixtureAuditAcknowledgmentNativeTest {
} catch (ReflectiveOperationException failure) { throw new AssertionError(failure); }
}
/** Bounded scalar diagnosis for one explicit owner; no global enumeration or payload capture. */
static String ownerState(Object bridge) {
var outbox = FantasyStickLifecycleNativeTest.Fixture.field(bridge, "outbox");
var worker = (ThreadPoolExecutor) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "worker");
return "active=" + worker.getActiveCount() + " queued=" + worker.getQueue().size()
+ " completed=" + worker.getCompletedTaskCount() + "/" + worker.getTaskCount()
+ " terminated=" + worker.isTerminated()
+ " inFlight=" + ((Set<?>) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "inFlight")).size()
+ " closed=" + ((AtomicBoolean) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "closed")).get()
+ " diagnosing=" + ((AtomicBoolean) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "diagnosing")).get()
+ " drained=" + ((CompletableFuture<?>) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "drained")).isDone();
}
static int available(Object outbox) throws Exception {
var field = outbox.getClass().getDeclaredField("HANDOFFS"); field.setAccessible(true);
return ((java.util.concurrent.Semaphore) field.get(null)).availablePermits();
}
@SuppressWarnings("unchecked") // Existing typed final sink, restored after this explicitly owned gate.
private static Function<EventInput, CompletionStage<LocalEventLog.Delivery>> sink(Object outbox) {
static Function<EventInput, CompletionStage<LocalEventLog.Delivery>> sink(Object outbox) {
return (Function<EventInput, CompletionStage<LocalEventLog.Delivery>>) FantasyStickLifecycleNativeTest.Fixture.field(outbox, "sink");
}
}