fleetd #612 r5: pin FleetdAssembly's leadConfigDirSource call site #643

Merged
ltms merged 1 commits from worker/612-a-r5-leadconfigdir-9e70cf-6 into main 2026-10-02 04:05:50 +02:00
@@ -0,0 +1,213 @@
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.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.lang.reflect.Field;
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.assertNull;
/**
* fleetd #612 Shape A, unit r5 — {@code FleetdAssembly.java:488} wires {@link
* FleetMcp.LeadConfigDirSource} with {@code Fleetd.leadConfigDirSource(() -> config.get().profiles(),
* leaders)}. {@link FleetdLeadConfigDirSourceWiringTest} already pins that the FACTORY itself
* delegates to the real {@link Fleetd#leadConfigDirLookup} — but, by its own javadoc, it "does not
* and structurally cannot cover" whether the real call site in {@code FleetdAssembly} still calls
* that factory at all. Measured there: swapping that one-line call for a bare {@code
* FleetMcp.LeadConfigDirSource.none()} compiles with 0 errors and leaves the full suite green.
*
* <p>This is the literal fleetd #602/#606 defect, one call site away from its own fix: {@code main}
* (now {@code FleetdAssembly}) used to build {@code LeadConfigDirSource.none()} inline, the whole
* suite passed, and the live daemon reported {@code "state":"unknown"} for every lead's context,
* forever, with no test noticing. The fix extracted the factory; this test is the one that proves
* {@code FleetdAssembly}'s own call site still reaches it.
*
* <p>This test drives the REAL {@link FleetMcp} the real {@link FleetdAssembly#assembleAndStart}
* builds, reached through {@link FleetdRuntime#mcp()}, and reads the {@code leadConfigDirs} field it
* was constructed with via reflection — {@code FleetMcp} exposes no public accessor for it (unlike
* {@code quarantineSource()}/{@code leadSeatSource()}), so there is no non-reflective route to the
* live instance. The assertion resolves a REAL lead name against a REAL configured {@code
* configDir:}: {@link FleetMcp.LeadConfigDirSource#none()} (the historical defect, and the
* mis-wire this test's mutation cycles reintroduce) always returns {@code null} regardless of the
* input, so a non-null, config-matching answer is a property {@code none()} can never produce by
* accident.
*/
class FleetdLeadConfigDirSourceAssemblyTest {
private static final String LEAD_NAME = "opus";
private static final String LEAD_TAB = "lead: opus";
private static final String LEAD_PROFILE = "sonnet";
private static final class RecordingResourcePorts implements ResourcePorts {
final FakeHerdr herdr = new FakeHerdr();
final SentinelReplyInbox replyInbox = new SentinelReplyInbox();
@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) -> {
throw new UnsupportedOperationException(
"leadMailboxOpener must not be called — no coordinator: block is configured");
};
}
@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() {
}
}
private static FleetConfig writeConfig(Path dir, String configDir) throws Exception {
Path f = dir.resolve("fleetd.yaml");
Files.writeString(f, """
bind:
host: 127.0.0.1
port: 8765
idleSleepGuard:
enabled: false
broker:
uri: "amqp://fake-test-broker/vh"
fleet:
leaders:
%s:
tab: "%s"
profile: %s
profiles:
%s:
subscription: true
argv: ["ccs", "sonnet"]
configDir: "%s"
""".formatted(LEAD_NAME, LEAD_TAB, LEAD_PROFILE, LEAD_PROFILE, configDir));
return FleetConfig.load(f);
}
@SuppressWarnings("unchecked")
private static FleetMcp.LeadConfigDirSource leadConfigDirSourceOf(FleetMcp mcp) throws Exception {
Field field = FleetMcp.class.getDeclaredField("leadConfigDirs");
field.setAccessible(true);
return (FleetMcp.LeadConfigDirSource) field.get(mcp);
}
@Test
@DisplayName("[BEHAVIOURAL] the real assembled LeadConfigDirSource resolves a lead's REAL "
+ "configured configDir, not the none() stand-in's hardcoded null")
void assembledLeadConfigDirSourceResolvesTheRealConfiguredConfigDir(@TempDir Path dir) throws Exception {
String configuredConfigDir = "/mnt/fake-lead-configdir";
FleetConfig cfg = writeConfig(dir, configuredConfigDir);
ConfigRef config = new ConfigRef(dir.resolve("fleetd.yaml"), cfg);
SubscriptionGuard guard = new SubscriptionGuard(cfg.guard().hostSet());
RecordingResourcePorts ports = new RecordingResourcePorts();
// Label FakeHerdr's own default pane's tab (term_a / w2:p7 / w2:t7, already carrying a live
// agent) to match fleet.leaders.opus.tab exactly, so LeadLauncher.ensureLeads() sees the
// lead as already live and does not try to auto-launch a second one.
ports.herdr.withTab("w2", "w2:t7", LEAD_TAB);
FleetdRuntime runtime = FleetdAssembly.assembleAndStart(new AssemblyInputs(cfg, config, guard), ports);
try {
FleetMcp.LeadConfigDirSource source = leadConfigDirSourceOf(runtime.mcp());
assertEquals(configuredConfigDir, source.configDirFor().apply(LEAD_NAME),
"fleet.leaders." + LEAD_NAME + ".profile (" + LEAD_PROFILE + ") configures "
+ "configDir: " + configuredConfigDir + " — the real assembled source must "
+ "resolve it. FleetMcp.LeadConfigDirSource.none() (the inert stand-in "
+ "this test's mutation cycles swap the call site for, and the historical "
+ "fleetd #602/#606 defect) always reports null here, whatever the input");
// A lead name the config does not recognise still resolves to null, not a crash — the
// same source, applied to an input that must stay at the inert answer even on the real,
// non-inert instance.
assertNull(source.configDirFor().apply("no-such-lead"));
} finally {
// Surefire runs the whole suite in one JVM fork (fleetd/pom.xml sets no forkCount /
// reuseForks), so the scheduler/loops this assembly starts must be torn down here, on the
// failure path too — hence try/finally rather than a bare statement at the end.
runtime.close();
}
}
}