The controller's seat is mesh-controller; second-node assignment refused #28

Merged
jschoubben merged 1 commits from multi-node/foundation-seats into main 2026-09-17 00:16:05 +00:00
2 changed files with 24 additions and 1 deletions
+23
View File
@@ -224,6 +224,29 @@ func TestAMeshWideClaimIsHeldByOneNode(t *testing.T) {
} }
} }
func TestAFoundationModuleCannotBeRaisedOnASecondNode(t *testing.T) {
// novox/hq 04-ISSUES/056. The store and broker are adopted in place on the control-node; each
// foundation module claims a mesh-scoped seat named after its server — the same mechanism that
// keeps one controller — so a second `assign` to another node is refused rather than silently
// raising a second postgres or broker that holds none of the first's data.
for _, tc := range []struct{ module, seat string }{
{"postgres", "mesh-store"},
{"lavinmq", "mesh-broker"},
{"mesh-controller", "mesh-controller"},
} {
_, err := Resolve(
shelf(mod(tc.module, nil, nil, nil, Claim{Name: tc.seat, Scope: ScopeMesh})),
[]string{tc.module}, workstation(),
World{Held: []Held{{Claim: tc.seat, Scope: ScopeMesh, Node: "anchor", Module: tc.module}}})
if err == nil {
t.Fatalf("%s was raised on a second node though %s is a mesh-wide seat", tc.module, tc.seat)
}
if !strings.Contains(err.Error(), "one per mesh") {
t.Errorf("%s: the refusal does not say it is one per mesh: %v", tc.module, err)
}
}
}
func TestASiteClaimOnlyCollidesWithinThatSite(t *testing.T) { func TestASiteClaimOnlyCollidesWithinThatSite(t *testing.T) {
// A DHCP server per segment. Two of them is a fault at one site and perfectly ordinary // A DHCP server per segment. Two of them is a fault at one site and perfectly ordinary
// across two, and treating site as mesh would forbid the ordinary case. // across two, and treating site as mesh would forbid the ordinary case.
+1 -1
View File
@@ -7,7 +7,7 @@
], ],
"claims": [ "claims": [
{ {
"name": "the-controller", "name": "mesh-controller",
"scope": "mesh" "scope": "mesh"
} }
], ],