diff --git a/internal/catalogue/broker_seat_test.go b/internal/catalogue/broker_seat_test.go new file mode 100644 index 0000000..13ca8b3 --- /dev/null +++ b/internal/catalogue/broker_seat_test.go @@ -0,0 +1,65 @@ +package catalogue + +import ( + "strings" + "testing" +) + +// The mesh's bus is one per mesh, read from the catalogue beside this checkout. +// +// **This is step 2's claim, and it is checked here rather than in a bed** (novox/hq ADR 0116): +// adoption puts the NATS server into the `mesh-broker` seat on a mesh that is already running, +// and the property that matters is that a second one anywhere is refused *when it is assigned*, +// not discovered later as two servers holding different halves of the mesh's traffic. A second +// bus is not a degraded mesh; it is two meshes that both believe they are the one. +func TestASecondMeshBusAnywhereIsRefusedByName(t *testing.T) { + nats := catalogueManifest(t, "nats") + + if _, err := Resolve(shelf(nats), []string{"nats"}, workstation(), World{}); err != nil { + t.Fatalf("the bus alone does not resolve: %v", err) + } + + elsewhere := World{Held: []Held{{Claim: "mesh-broker", Scope: ScopeMesh, + Node: "anchor", Module: "nats"}}} + other := workstation() + other.Name = "laptop" + _, err := Resolve(shelf(nats), []string{"nats"}, other, elsewhere) + if err == nil { + t.Fatal("a second bus was accepted on another machine") + } + if !strings.Contains(err.Error(), "mesh-broker") || !strings.Contains(err.Error(), "one per mesh") { + t.Fatalf("refused without naming the seat: %v", err) + } +} + +// The seat is the server's role, not the product's name (novox/hq ADR 0079). A different +// implementation of the bus claims the same seat, and the mesh refuses it for the same reason — +// which is the property that lets the bus be replaced at all. +func TestTheSeatRefusesADifferentBusToo(t *testing.T) { + nats := catalogueManifest(t, "nats") + held := World{Held: []Held{{Claim: "mesh-broker", Scope: ScopeMesh, + Node: "anchor", Module: "some-other-broker"}}} + other := workstation() + other.Name = "laptop" + if _, err := Resolve(shelf(nats), []string{"nats"}, other, held); err == nil { + t.Fatal("the seat admitted a second holder because the module's name differed") + } +} + +// The old broker no longer claims the seat: it is an ordinary provider of `amqp` +// (novox/hq ADR 0119), so it can sit on the same mesh as the bus without contending for it. +func TestTheAmqpBrokerDoesNotContendForTheSeat(t *testing.T) { + lavinmq := catalogueManifest(t, "lavinmq") + for _, c := range lavinmq.Claims { + if c.Name == "mesh-broker" { + t.Fatal("the amqp broker still claims mesh-broker; it is a provider, not foundation") + } + } + busHeld := World{Held: []Held{{Claim: "mesh-broker", Scope: ScopeMesh, + Node: "anchor", Module: "nats"}}} + other := workstation() + other.Name = "laptop" + if _, err := Resolve(shelf(lavinmq), []string{"lavinmq"}, other, busHeld); err != nil { + t.Fatalf("the amqp broker was refused beside the mesh bus: %v", err) + } +}