package catalogue import ( "strings" "testing" ) // **A seat's holder on record settles who holds it, and lets the next holder stand beside the // current one** (novox/hq ADR 0131, design 28 task 5.3). Until the record existed, two assignments // whose modules both claimed a seat were refused outright — which left no way to hand a seat over // without a moment where nobody held it, and the control plane finds its own bus through one of // these seats. That moment was the outage of 2026-09-27. func busSeatDelivering(t *testing.T, delivers string) { t.Helper() was := Seats() t.Cleanup(func() { UseSeats(was) }) UseSeats([]Seat{{Name: "mesh-broker", Scope: ScopeMesh, Delivers: delivers, Decision: "test"}}) } func oldBroker() Manifest { return Manifest{Module: "old-broker", Provides: []Offer{{Name: "mesh-bus", Scope: ScopeMesh}}, Claims: []Claim{{Name: "mesh-broker", Scope: ScopeMesh}}} } func newBroker() Manifest { return Manifest{Module: "new-broker", Provides: []Offer{{Name: "mesh-bus", Scope: ScopeMesh}}, Claims: []Claim{{Name: "mesh-broker", Scope: ScopeMesh}}} } // Nothing on record: exactly the old rule. One claimant holds; two are refused. func TestWithNoHolderOnRecordTheSoleClaimantHoldsAndTwoAreRefused(t *testing.T) { busSeatDelivering(t, "mesh-bus") node := Node{Name: "anchor"} held, problems := checkClaims([]Manifest{oldBroker()}, node, nil, nil) if len(problems) != 0 || len(held) != 1 || held[0].Module != "old-broker" { t.Fatalf("a sole claimant did not hold the seat: held=%v problems=%v", held, problems) } _, problems = checkClaims([]Manifest{oldBroker(), newBroker()}, node, nil, nil) if len(problems) != 1 || !strings.Contains(problems[0], "both claim") { t.Fatalf("two claimants with nothing on record were not refused: %v", problems) } } // With a holder on record, the other eligible assignment is silent: not refused, and not holding. func TestTheHolderOnRecordHoldsAndTheOtherClaimantStandsBesideIt(t *testing.T) { busSeatDelivering(t, "mesh-bus") node := Node{Name: "anchor"} record := []Held{{Claim: "mesh-broker", Scope: ScopeMesh, Node: "anchor", Module: "new-broker"}} held, problems := checkClaims([]Manifest{oldBroker(), newBroker()}, node, nil, record) if len(problems) != 0 { t.Fatalf("the assignment beside the holder was refused: %v", problems) } if len(held) != 1 || held[0].Module != "new-broker" { t.Fatalf("the holder on record is not the one holding: %v", held) } } // The record names a node too: an eligible module on another machine holds nothing, and its // machine's set still resolves. func TestAHolderOnRecordElsewhereLeavesThisMachinesClaimantSilent(t *testing.T) { busSeatDelivering(t, "mesh-bus") record := []Held{{Claim: "mesh-broker", Scope: ScopeMesh, Node: "anchor", Module: "new-broker"}} held, problems := checkClaims([]Manifest{oldBroker()}, Node{Name: "laptop"}, nil, record) if len(problems) != 0 || len(held) != 0 { t.Fatalf("a claimant elsewhere than the recorded holder was not simply silent: held=%v problems=%v", held, problems) } } // A record naming a seat's former name still applies to it after a rename (ADR 0122). func TestAHolderRecordedUnderAFormerNameStillHolds(t *testing.T) { busSeatDelivering(t, "mesh-bus") wasAliases := aliases t.Cleanup(func() { UseAliases(wasAliases) }) UseAliases(map[string]string{"the-broker": "mesh-broker"}) record := []Held{{Claim: "the-broker", Scope: ScopeMesh, Node: "anchor", Module: "new-broker"}} held, problems := checkClaims([]Manifest{oldBroker(), newBroker()}, Node{Name: "anchor"}, nil, record) if len(problems) != 0 || len(held) != 1 || held[0].Module != "new-broker" { t.Fatalf("a record under the former name did not settle the seat: held=%v problems=%v", held, problems) } } // CanHold is the one judgement registration and the handover share, against the store's row. func TestCanHoldJudgesClaimScopeAndWhatTheSeatDelivers(t *testing.T) { busSeatDelivering(t, "mesh-bus") seat, _ := SeatNamed("mesh-broker") if err := CanHold(newBroker(), seat); err != nil { t.Fatalf("a module that claims the seat and provides what it delivers was refused: %v", err) } noClaim := Manifest{Module: "quiet", Provides: []Offer{{Name: "mesh-bus", Scope: ScopeMesh}}} if err := CanHold(noClaim, seat); err == nil || !strings.Contains(err.Error(), "does not claim") { t.Fatalf("a module that never claimed the seat was allowed to hold it: %v", err) } wrongScope := newBroker() wrongScope.Claims[0].Scope = ScopeNode if err := CanHold(wrongScope, seat); err == nil || !strings.Contains(err.Error(), "scope") { t.Fatalf("a claim at the wrong scope was allowed: %v", err) } cannotAnswer := Manifest{Module: "amqp-only", Provides: []Offer{{Name: "amqp", Scope: ScopeMesh}}, Claims: []Claim{{Name: "mesh-broker", Scope: ScopeMesh}}} if err := CanHold(cannotAnswer, seat); err == nil || !strings.Contains(err.Error(), `does not provide "mesh-bus"`) { t.Fatalf("a holder that cannot answer for the seat was allowed: %v", err) } // And the judgement follows the store's row, not a compiled copy. busSeatDelivering(t, "amqp") seat, _ = SeatNamed("mesh-broker") if err := CanHold(cannotAnswer, seat); err != nil { t.Fatalf("with the row saying amqp, an amqp provider was refused: %v", err) } } // A machine being moved is handed its membership for the new bus as a sealed file in its own // declaration — the machine's, not any module's (design 28, task 5.2). func TestAMembershipForTheNewBusIsComposedAsASealedFile(t *testing.T) { r := Resolution{Node: "anchor"} got, err := r.Compose(Rendering{BusMembership: "sealed-blob"}) if err != nil { t.Fatal(err) } var found map[string]any for _, res := range got.Resources { if res["id"] == BusMembershipID() { found = res } } if found == nil { t.Fatalf("no membership resource in %v", got.Resources) } if found["path"] != BusMembershipPath || found["sealed"] != "sealed-blob" || found["mode"] != "0600" { t.Fatalf("the membership is not a sealed 0600 file where the host reads it: %v", found) } // And a machine not being moved is handed nothing. got, _ = r.Compose(Rendering{}) for _, res := range got.Resources { if res["id"] == BusMembershipID() { t.Fatal("a machine with no membership on record was handed one") } } } // The store's seat rows have no protocol columns yet; loading them must not drop the protocol the // bus is derived from, or no role's work queue is ever raised (found live, 2026-09-28). func TestAStoreRowWithoutAProtocolKeepsTheCompiledOne(t *testing.T) { was := Seats() t.Cleanup(func() { UseSeats(was) }) UseSeats([]Seat{{Name: "mesh-build-machine", Scope: ScopeMesh, Decision: "row"}}) got, ok := SeatNamed("mesh-build-machine") if !ok || len(got.Accepts) == 0 { t.Fatalf("the build machine's seat lost what it accepts when loaded from the store: %+v", got) } if got.Decision != "row" { t.Fatalf("the store's own columns were not kept: %+v", got) } }