fleetd #612 Shape A r9+r11: pin capacitySource, healthCoverageSource, coordinator.peers #647
@@ -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<String> coordinatorPeers() {
|
||||
return peers;
|
||||
}
|
||||
|
||||
// --- tool logic (thin adapters over the services; unit-testable) ---------------------------
|
||||
|
||||
/**
|
||||
|
||||
+338
@@ -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()}).
|
||||
*
|
||||
* <p>{@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.
|
||||
*
|
||||
* <p>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<String, String> 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<InboxMessage> 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<LeadMessage> 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<String> 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()");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user