diff --git a/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigTest.java b/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigTest.java index 47e93b22..11002d31 100644 --- a/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigTest.java +++ b/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigTest.java @@ -800,6 +800,33 @@ class FleetConfigTest { assertTrue(e.getMessage().contains("sonnet"), "the message must name the other offending lead"); } + /** + * fleetd #693: the guard matches tabs case-insensitively, because + * {@code LeadTabScanner} keys its tab map on a lowercased label — two tabs differing only in + * case collide there too, and the guard must catch that independently of the exact-match case + * above. + */ + @Test + void twoLeadsSharingTheSameTabInDifferentCaseRefusesToStart(@TempDir Path dir) throws Exception { + Path f = dir.resolve("shared-tab-case.yaml"); + Files.writeString(f, """ + bind: + port: 8080 + fleet: + leaders: + opus: + tab: "Shared Tab" + sonnet: + tab: "shared tab" + """); + FleetConfig cfg = FleetConfig.load(f); + + IllegalStateException e = + assertThrows(IllegalStateException.class, cfg::validateLeadTabPrefixes); + assertTrue(e.getMessage().contains("opus"), "the message must name one offending lead"); + assertTrue(e.getMessage().contains("sonnet"), "the message must name the other offending lead"); + } + /** Control for {@link #twoLeadsSharingTheSameExactTabRefusesToStart}: distinct tabs load cleanly. */ @Test void twoLeadsWithDistinctExactTabsAreAllowed(@TempDir Path dir) throws Exception { diff --git a/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigValidateAllTest.java b/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigValidateAllTest.java index eb4b0e54..cdaa108c 100644 --- a/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigValidateAllTest.java +++ b/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigValidateAllTest.java @@ -19,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests the reflective validator sweep and {@link FleetConfig#validateAll()} reachability. * - *

{@link #theSweepMechanismIsGenericNotHardcodedToFleetConfigsSixNames()} and its neighbours + *

{@link #theSweepRunsEveryValidateMethodOnAnUnrelatedClass()} and its neighbours * prove that {@link FleetConfig#invokeAllValidators} runs each public, no-arg, void * {@code validateXxx()} method on its target. {@link #fleetConfigDeclaresExactlyTheseValidatorsToday()} * is the canary for the validator set. {@link #validateAllReachesEveryOneOfTodaysRealValidators()} @@ -50,7 +50,7 @@ class FleetConfigValidateAllTest { } @Test - void theSweepMechanismIsGenericNotHardcodedToFleetConfigsSixNames() { + void theSweepRunsEveryValidateMethodOnAnUnrelatedClass() { ThreeValidators target = new ThreeValidators(); FleetConfig.invokeAllValidators(target); assertEquals(List.of("validateAlpha", "validateBeta", "validateGamma"), target.ran,