From dd28dab0f2801ff601f3c3b445c7e80d8987e68d Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Fri, 2 Oct 2026 04:20:21 +0200 Subject: [PATCH] fleetd #612 Shape A r9+r11: pin capacitySource, healthCoverageSource, coordinator.peers at the assembly call site MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FleetdAssembly's new FleetMcp(...) call wires three sources at :481/:482/:489 that no test drove through the real assembly before: Fleetd.capacitySource, Fleetd.healthCoverageSource, and the cfg.coordinator() == null ? List.of() : cfg.coordinator().peers() ternary. The two factory methods already had isolated unit tests (FleetdCapacitySourceWiringTest, FleetdHealthCoverageSourceWiringTest), but neither proved FleetdAssembly's constructor call actually receives their output rather than an inert stand-in or an unrelated config ref. FleetdCapacityHealthCoveragePeersAssemblyTest drives the real FleetdAssembly.assembleAndStart and reads each source straight off the real FleetMcp FleetdRuntime.mcp() owns. That needed three small, read-only public accessors on FleetMcp (capacitySource(), healthCoverageSource(), coordinatorPeers()) — the same cross-package shape quarantineSource()/leadSeatSource()/ leadRollover() already use (fleetd #612 B3), added because driving fleet_list itself through a real MCP/HTTP round trip is a dead end here: FleetdAssembly builds its ConnectionIdentity with a hardcoded real LsofPeerPidLookup, which excludes its own pid, so a same-JVM test caller always resolves ANONYMOUS and never reaches fleet_list's READ gate (FleetdAssemblyFleetAppTest's own javadoc documents this exact dead end for GET /sessions). Each of the three assertions was verified independently: a loud control (flip the expected value, observe RED, revert) plus two full-suite mutation cycles per site (inert stand-in, then a mis-wire that keeps every call-site symbol and only swaps the collaborator identity) — each mutation failed exactly the one named test and left the other two green, then was reverted clean. Full suite: mvn clean install, Tests run: 1895, Failures: 0, Errors: 0, Skipped: 0, BUILD SUCCESS. --- .../java/dev/ltms/fleet/mcp/FleetMcp.java | 35 ++ ...pacityHealthCoveragePeersAssemblyTest.java | 338 ++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 fleetd/src/test/java/dev/ltms/fleet/FleetdCapacityHealthCoveragePeersAssemblyTest.java diff --git a/fleetd/src/main/java/dev/ltms/fleet/mcp/FleetMcp.java b/fleetd/src/main/java/dev/ltms/fleet/mcp/FleetMcp.java index 82e3367..d006a3e 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/mcp/FleetMcp.java +++ b/fleetd/src/main/java/dev/ltms/fleet/mcp/FleetMcp.java @@ -837,6 +837,41 @@ public final class FleetMcp { return leadRollover; } + /** + * fleetd #612 Shape A r9+r11 — {@code public} for the same cross-package reason as {@link + * #quarantineSource()}, for the real {@link CapacitySource} this daemon was assembled with. + * {@code fleet_list}'s {@code capacity} row is gated on {@link CapacitySource#available()}, + * which is {@code false} whenever {@code configuredProfiles} is empty — exactly what {@link + * CapacitySource#none()} (the inert stand-in) always reports, so a test can tell a real, + * populated source apart from one swapped for the inert variant at the {@code new FleetMcp(...)} + * call site in {@code FleetdAssembly}. + */ + public CapacitySource capacitySource() { + return capacity; + } + + /** + * fleetd #612 Shape A r9+r11 — {@code public} for the same cross-package reason as {@link + * #quarantineSource()}, for the real {@link HealthCoverageSource} this daemon was assembled + * with. {@code fleet_list}'s {@code healthCoverage} field reads {@link + * HealthCoverageSource#value()} straight off this exact instance. + */ + public HealthCoverageSource healthCoverageSource() { + return healthCoverage; + } + + /** + * fleetd #612 Shape A r9+r11 — {@code public} for the same cross-package reason as {@link + * #quarantineSource()}, for the real {@code coordinator.peers} list this daemon was assembled + * with (see {@link CoordinationSource#peers()}). {@code fleet_list}'s {@code coordinator.peers} + * row reports exactly this list. Both an absent {@code coordinator:} block and a mis-wired call + * site report {@code List.of()}, so a test must configure a real {@code coordinator:} block + * with peers to tell a working wiring from the inert one. + */ + public List coordinatorPeers() { + return peers; + } + // --- tool logic (thin adapters over the services; unit-testable) --------------------------- /** diff --git a/fleetd/src/test/java/dev/ltms/fleet/FleetdCapacityHealthCoveragePeersAssemblyTest.java b/fleetd/src/test/java/dev/ltms/fleet/FleetdCapacityHealthCoveragePeersAssemblyTest.java new file mode 100644 index 0000000..134e803 --- /dev/null +++ b/fleetd/src/test/java/dev/ltms/fleet/FleetdCapacityHealthCoveragePeersAssemblyTest.java @@ -0,0 +1,338 @@ +package dev.ltms.fleet; + +import dev.ltms.fleet.config.ConfigRef; +import dev.ltms.fleet.config.FleetConfig; +import dev.ltms.fleet.guard.SubscriptionGuard; +import dev.ltms.fleet.herdr.FakeHerdr; +import dev.ltms.fleet.herdr.HerdrClient; +import dev.ltms.fleet.mcp.FleetMcp; +import dev.ltms.fleet.msg.LeadChannel; +import dev.ltms.fleet.msg.LeadChannelHandle; +import dev.ltms.fleet.msg.LeadMessage; +import dev.ltms.fleet.msg.ReplyInbox; +import io.javalin.Javalin; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.LongSupplier; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * fleetd #612 Shape A, unit r9+r11 — replaces nothing (these three call sites were never covered + * by the deleted source-text tests), and pins the three call sites {@code FleetdAssembly}'s + * {@code new FleetMcp(...)} wires at {@code :481} ({@link Fleetd#capacitySource}), {@code :482} + * ({@link Fleetd#healthCoverageSource}), and {@code :489} ({@code cfg.coordinator() == null ? + * List.of() : cfg.coordinator().peers()}). + * + *

{@link FleetdCapacitySourceWiringTest} and {@link FleetdHealthCoverageSourceWiringTest} + * already pin the two factory methods themselves in isolation — calling {@code Fleetd.capacitySource} + * / {@code Fleetd.healthCoverageSource} directly with a hand-built {@link ConfigRef}/{@link + * FleetConfig}. Neither proves that {@code FleetdAssembly}'s {@code new FleetMcp(...)} call + * actually receives what those factories return, as opposed to e.g. {@link + * FleetMcp.CapacitySource#none()} or a {@link ConfigRef}/{@link FleetConfig} that does not match + * what the rest of the daemon was assembled with. The coordinator-peers call site has no isolated + * factory at all — it is a plain ternary at the call site itself, so a test can only pin it at the + * assembly level. + * + *

This class drives the real {@link FleetdAssembly#assembleAndStart}, then reads the resulting + * sources straight off the exact {@link FleetMcp} instance {@link FleetdRuntime#mcp()} owns — + * never a copy built alongside it. That needed three small, read-only, public accessors on {@link + * FleetMcp} ({@code capacitySource()}, {@code healthCoverageSource()}, {@code coordinatorPeers()}), + * added in this same change for the identical cross-package reason {@code quarantineSource()}/ + * {@code leadSeatSource()}/{@code leadRollover()} already exist (fleetd #612 B3): {@code fleet_list} + * itself is reachable only through a real, authorized MCP/HTTP round trip, and driving the real + * assembly's {@link dev.ltms.fleet.mcp.ConnectionIdentity} (built with a hardcoded real {@code + * LsofPeerPidLookup}) always resolves a same-JVM test caller as {@code ANONYMOUS} — {@code + * LsofPeerPidLookup} excludes its own pid, and a JUnit test's HTTP client shares the daemon's JVM + * pid. {@code FleetdAssemblyFleetAppTest}'s own javadoc documents this exact dead end for {@code + * GET /sessions}; {@code fleet_list} needs the identical {@code Authz.Action.READ} gate. See the + * PR body for why this one change to {@code FleetMcp.java} was necessary rather than reshaping + * {@code FleetdAssembly} itself. + */ +class FleetdCapacityHealthCoveragePeersAssemblyTest { + + /** Minimal fake {@link ResourcePorts}: real {@link FakeHerdr}, a sentinel reply inbox, and an + * optional fake lead channel for the one test that configures a {@code coordinator:} block. */ + private static final class RecordingResourcePorts implements ResourcePorts { + + final FakeHerdr herdr = new FakeHerdr(); + final SentinelReplyInbox replyInbox = new SentinelReplyInbox(); + /** null unless a test wants the {@code coordinator:} path to actually open. */ + LeadChannelHandle leadChannel; + + @Override + public Map environment() { + return Map.of(); + } + + @Override + public HerdrClient connectHerdr(Path socketPath) { + return herdr; + } + + @Override + public Fleetd.AmqpOpener replyInboxOpener() { + return (uri, prefetch) -> replyInbox; + } + + @Override + public Fleetd.LeadMailboxOpener leadMailboxOpener() { + return (uri, selfCoordId, prefetch) -> { + if (leadChannel == null) { + throw new UnsupportedOperationException( + "leadMailboxOpener must not be called — no coordinator: block is configured"); + } + return leadChannel; + }; + } + + @Override + public LongSupplier nanoClock() { + return System::nanoTime; + } + + @Override + public LongSupplier wallClockNanos() { + return System::nanoTime; + } + + @Override + public ScheduledExecutorService newScheduler(String purpose) { + return Executors.newSingleThreadScheduledExecutor(); + } + + @Override + public void addShutdownHook(Runnable hook) { + } + + @Override + public void startHttp(Javalin app, String host, int port) { + } + + @Override + public Runnable herdrPollWait() { + // Never invoked: this test's FakeHerdr answers immediately, so awaitHerdr never polls. + return () -> { + throw new UnsupportedOperationException("herdrPollWait must not be called — herdr is healthy"); + }; + } + } + + private static final class SentinelReplyInbox implements ReplyInbox, AutoCloseable { + @Override + public void own(String target) { + } + + @Override + public void release(String target) { + } + + @Override + public void publish(String target, String msgId, String content) { + } + + @Override + public List peek(String target) { + return List.of(); + } + + @Override + public boolean ack(String target, String msgId) { + return false; + } + + @Override + public void close() { + } + } + + /** A fake {@link LeadChannelHandle}: never touches a broker. */ + private static final class FakeLeadChannel implements LeadChannelHandle { + private final String selfCoordId; + + FakeLeadChannel(String selfCoordId) { + this.selfCoordId = selfCoordId; + } + + @Override + public void publish(String toCoordId, LeadMessage m) { + } + + @Override + public List peek() { + return List.of(); + } + + @Override + public void ack(String msgId) { + } + + @Override + public String selfCoordId() { + return selfCoordId; + } + + @Override + public boolean heldDurable() { + return true; + } + + @Override + public LeadChannel.MailboxState inspect(String coordId) { + return LeadChannel.MailboxState.unknown(coordId); + } + + @Override + public void close() { + } + } + + private static FleetConfig writeConfig(Path dir, String yaml) throws Exception { + Path f = dir.resolve("fleetd.yaml"); + Files.writeString(f, yaml); + return FleetConfig.load(f); + } + + // --- rank 9: Fleetd.capacitySource, FleetdAssembly.java:481 --------------------------------- + + /** + * fleetd #612 rank 9. The inert stand-in {@link FleetMcp.CapacitySource#none()} always reports + * an empty {@code configuredProfiles} set, which makes {@code fleet_list} report ZERO + * configured profiles — loud, but cheap to pin, and this is the property named in the ticket. + */ + @Test + @DisplayName("[rank 9, BEHAVIOURAL] the real assembled CapacitySource reports the configured " + + "profile with its real maxLoad, not an empty/inert source") + void capacitySourceReportsTheConfiguredProfileWithItsRealCapacity(@TempDir Path dir) throws Exception { + FleetConfig cfg = writeConfig(dir, """ + bind: + host: 127.0.0.1 + port: 8765 + idleSleepGuard: + enabled: false + profiles: + terra: + baseUrl: http://gx00.gw:8000 + model: terra + maxLoad: 5 + guard: + offSubscriptionHosts: + - gx00.gw + """); + ConfigRef config = new ConfigRef(dir.resolve("fleetd.yaml"), cfg); + SubscriptionGuard guard = new SubscriptionGuard(cfg.guard().hostSet()); + RecordingResourcePorts ports = new RecordingResourcePorts(); + + try (FleetdRuntime runtime = FleetdAssembly.assembleAndStart(new AssemblyInputs(cfg, config, guard), ports)) { + FleetMcp.CapacitySource capacity = runtime.mcp().capacitySource(); + + assertTrue(capacity.configuredProfiles().get().contains("terra"), + "the real assembled CapacitySource must report the configured profile 'terra' — " + + "CapacitySource.none() (the inert stand-in a mis-wired call site could " + + "swap in) always reports an empty set, which would make fleet_list " + + "report ZERO configured profiles"); + assertEquals(5, capacity.maxLoad().apply("terra"), + "the real assembled CapacitySource must report the configured profile's real " + + "maxLoad (5), not null (CapacitySource.none()'s maxLoad always answers " + + "null, whatever the profile)"); + + // Loud control, same instance: a profile nobody configured must NOT be reported, so this + // assertion is not vacuously true against a source that reports everything. + assertFalse(capacity.configuredProfiles().get().contains("no-such-profile")); + } + } + + // --- rank 11: Fleetd.healthCoverageSource, FleetdAssembly.java:482 -------------------------- + + /** + * fleetd #612 rank 11. The live daemon currently reports {@code healthCoverage: "detection-only"} + * for exactly this shape of config (health enabled, no notifications configured) — a real, + * non-default value this test can lean on, per the ticket's own note. + */ + @Test + @DisplayName("[rank 11, BEHAVIOURAL] the real assembled HealthCoverageSource reports the " + + "configured coverage value") + void healthCoverageSourceReportsTheConfiguredValue(@TempDir Path dir) throws Exception { + FleetConfig cfg = writeConfig(dir, """ + bind: + host: 127.0.0.1 + port: 8765 + idleSleepGuard: + enabled: false + health: + enabled: true + """); + ConfigRef config = new ConfigRef(dir.resolve("fleetd.yaml"), cfg); + SubscriptionGuard guard = new SubscriptionGuard(cfg.guard().hostSet()); + RecordingResourcePorts ports = new RecordingResourcePorts(); + + try (FleetdRuntime runtime = FleetdAssembly.assembleAndStart(new AssemblyInputs(cfg, config, guard), ports)) { + FleetMcp.HealthCoverageSource healthCoverage = runtime.mcp().healthCoverageSource(); + + assertEquals("detection-only", healthCoverage.value().get(), + "the real assembled HealthCoverageSource must report 'detection-only' for " + + "health.enabled: true with no notifications configured — a mis-wired " + + "call site (e.g. a HealthCoverageSource built off an unrelated/empty " + + "ConfigRef) would report 'off' here instead, since an absent health: " + + "block also reports 'off'"); + } + } + + // --- rank 11: coordinator peers, FleetdAssembly.java:489 ------------------------------------ + + /** + * fleetd #612 rank 11, the ternary trap the ticket calls out by name: {@code cfg.coordinator() + * == null ? List.of() : cfg.coordinator().peers()}. An ABSENT {@code coordinator:} block and a + * MIS-WIRED call site both report {@code List.of()} — indistinguishable unless a test actually + * configures a {@code coordinator:} block with peers and checks the real, non-empty answer. + */ + @Test + @DisplayName("[rank 11, BEHAVIOURAL] the real assembled FleetMcp reports coordinator.peers from " + + "a configured coordinator: block, not the no-config empty answer") + void coordinatorPeersReportsTheConfiguredPeers(@TempDir Path dir) throws Exception { + FleetConfig cfg = writeConfig(dir, """ + bind: + host: 127.0.0.1 + port: 8765 + idleSleepGuard: + enabled: false + coordinator: + uri: "amqp://fake-lead-broker/vh" + selfId: "test-lead" + peers: + - "peer-one" + - "peer-two" + """); + ConfigRef config = new ConfigRef(dir.resolve("fleetd.yaml"), cfg); + SubscriptionGuard guard = new SubscriptionGuard(cfg.guard().hostSet()); + RecordingResourcePorts ports = new RecordingResourcePorts(); + ports.leadChannel = new FakeLeadChannel("test-lead"); + + try (FleetdRuntime runtime = FleetdAssembly.assembleAndStart(new AssemblyInputs(cfg, config, guard), ports)) { + List peers = runtime.mcp().coordinatorPeers(); + + assertEquals(List.of("peer-one", "peer-two"), peers, + "the real assembled FleetMcp must report exactly the peers declared under " + + "coordinator.peers, in order"); + + // Loud, named control: the trap is that List.of() is ALSO the correct answer with no + // coordinator: block configured at all, so an assertion of emptiness here could never + // tell a working wiring from the inert/no-config one. This expectation is non-empty, + // which only the real wiring (not the ternary's other branch) can produce. + assertFalse(peers.isEmpty(), + "this test's own expected value must be non-empty, or it could not tell a real " + + "coordinator.peers wiring from cfg.coordinator() == null, which reports " + + "the identical List.of()"); + } + } +}