diff --git a/fleetd/src/test/java/dev/ltms/fleet/FleetdConfigRefWiringTest.java b/fleetd/src/test/java/dev/ltms/fleet/FleetdConfigRefWiringTest.java index 3585d6b..6da002e 100644 --- a/fleetd/src/test/java/dev/ltms/fleet/FleetdConfigRefWiringTest.java +++ b/fleetd/src/test/java/dev/ltms/fleet/FleetdConfigRefWiringTest.java @@ -24,8 +24,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * {@code ConfigRefTest} and {@code FleetdConfigRefCharterToolSurfaceWiringTest} case — because * neither of those tests constructs its {@code ConfigRef} through {@code main}; both build their own * instance directly, wired with the check by hand. That silent regression is exactly the shape - * {@link FleetdBackendQuarantineWiringTest}, {@link FleetdLeadSeatWiringTest} and {@link - * FleetdCompletionResolverWiringTest} already guard against for their own constructor arguments — + * {@link FleetdBackendQuarantineAssemblyTest}, {@link FleetdLeadSeatAssemblyTest} and {@link + * FleetdCompletionResolverAssemblyTest} already guard against for their own constructor arguments — * this class is the same class of gap for fleetd #474's {@code extraValidation} argument, following * their approach. * diff --git a/fleetd/src/test/java/dev/ltms/fleet/FleetdHerdrControlConstructionTest.java b/fleetd/src/test/java/dev/ltms/fleet/FleetdHerdrControlConstructionTest.java index 01dedf3..f95c5c9 100644 --- a/fleetd/src/test/java/dev/ltms/fleet/FleetdHerdrControlConstructionTest.java +++ b/fleetd/src/test/java/dev/ltms/fleet/FleetdHerdrControlConstructionTest.java @@ -2,16 +2,67 @@ package dev.ltms.fleet; import java.nio.file.Files; import java.nio.file.Path; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +/** + * {@code AgentControl} caches {@code paneByTerminal}, so {@code HerdrRouter} must be its only + * production factory — a second instance means a second cache; the same reasoning applies to + * {@code WorkspaceControl}. {@code HerdrRouter}'s constructor is the one place both are built. + * + *

This test checks source text, not runtime behaviour. It never constructs a {@code + * HerdrRouter} and never runs {@code FleetdAssembly.assembleAndStart} — a green result proves only + * that neither watched file's text contains {@code new AgentControl(} or {@code new + * WorkspaceControl(}. It does not prove the instances {@code HerdrRouter} does build are the ones + * actually wired through the rest of the daemon, and it does not cover a bypass written into a + * production file other than the two this test reads. + */ class FleetdHerdrControlConstructionTest { + + private static String source(String relativePath) throws Exception { + return Files.readString(Path.of(relativePath)); + } + @Test + @DisplayName("[SOURCE TEXT] Fleetd.java never constructs AgentControl or WorkspaceControl directly") void fleetdDelegatesStatefulControlsToTheRouter() throws Exception { - // AgentControl caches paneByTerminal, so the router must be its only production factory. - String source = Files.readString(Path.of("src/main/java/dev/ltms/fleet/Fleetd.java")); - assertFalse(source.contains("new AgentControl(")); - assertFalse(source.contains("new WorkspaceControl(")); + String source = source("src/main/java/dev/ltms/fleet/Fleetd.java"); + + // A broken read (wrong working directory, wrong path, a file that came back empty) would + // make the assertFalse checks below pass vacuously — a "clean" negative check that actually + // checked nothing. Guard against that first, with an anchor that has nothing to do with + // this mutation, so a bad read fails loudly here instead of silently proving nothing below. + assertTrue(source.contains("public final class Fleetd"), + "the read of Fleetd.java did not come back containing its own class declaration — " + + "the assertFalse checks below would pass vacuously on a broken read; fix the " + + "read before trusting this test."); + + assertFalse(source.contains("new AgentControl("), + "Fleetd.java must not construct AgentControl directly — HerdrRouter is its only " + + "production factory"); + assertFalse(source.contains("new WorkspaceControl("), + "Fleetd.java must not construct WorkspaceControl directly — HerdrRouter is its only " + + "production factory"); + } + + @Test + @DisplayName("[SOURCE TEXT] FleetdAssembly.java never constructs AgentControl or WorkspaceControl directly") + void fleetdAssemblyDelegatesStatefulControlsToTheRouter() throws Exception { + String source = source("src/main/java/dev/ltms/fleet/FleetdAssembly.java"); + + assertTrue(source.contains("final class FleetdAssembly"), + "the read of FleetdAssembly.java did not come back containing its own class " + + "declaration — the assertFalse checks below would pass vacuously on a broken " + + "read; fix the read before trusting this test."); + + assertFalse(source.contains("new AgentControl("), + "FleetdAssembly.java must not construct AgentControl directly — HerdrRouter is its " + + "only production factory"); + assertFalse(source.contains("new WorkspaceControl("), + "FleetdAssembly.java must not construct WorkspaceControl directly — HerdrRouter is " + + "its only production factory"); } }