package broker import ( "strings" "testing" ) // Deriving the bus's user list from the mesh's records. // // Every test here is about a way the list could be wrong that the server would not tell anybody // about: a user missing, a user named twice, a user with authority it did not declare. func someRecords() Records { return Records{ Nodes: []string{"two", "one"}, Assigned: map[string][]Declared{ "one": {{Module: "telegram", Serves: []string{"status"}}}, "two": {{Module: "shop", Emits: []string{"order.placed"}}}, }, Enrolling: []string{"three"}, People: map[string][]string{"ada": {"mesh-catalog.catalog_tools"}}, } } func namesOf(t *testing.T, r Records) []string { t.Helper() users, err := Users(r) if err != nil { t.Fatal(err) } out := make([]string, 0, len(users)) for _, u := range users { out = append(out, u.Username()) } return out } // The controller is always there. A mesh whose own controller is not in the file is a mesh that // cannot be told anything, and there is no state of the records in which that is correct. func TestTheControllerIsAlwaysInTheList(t *testing.T) { for _, r := range []Records{{}, someRecords()} { names := namesOf(t, r) if len(names) == 0 || names[0] != "controller" { t.Fatalf("the controller is not first in %v", names) } } } // One user per node, one per module per node, one per live token and one per person — and nothing // else, because a user nobody derived is a user nobody can explain. func TestEveryRecordBecomesExactlyOneUser(t *testing.T) { names := namesOf(t, someRecords()) want := []string{ "controller", "node.one", "one.telegram", "node.two", "two.shop", "enrol.three", "person.ada", } if strings.Join(names, ",") != strings.Join(want, ",") { t.Fatalf("derived %v\n want %v", names, want) } } // Two users with one name is a file the server reads as one of them, and which one depends on the // order. Refused here, where both can be named, rather than left to be whichever the server picked. func TestTwoUsersWithOneNameAreRefused(t *testing.T) { r := someRecords() r.Assigned["one"] = append(r.Assigned["one"], Declared{Module: "telegram"}) _, err := Users(r) if err == nil { t.Fatal("a module assigned twice to one node composed two users with one name") } if !strings.Contains(err.Error(), "one.telegram") { t.Fatalf("the refusal does not name the user: %v", err) } } // A module's authority is what it declared and nothing more, carried through the derivation intact — // because this is the step where a mistake would grant something no manifest asked for. func TestAModulesAuthorityIsWhatItDeclared(t *testing.T) { seat := Seat{Name: "telegram-sender", Accepts: []string{"send"}, Emits: []string{"delivered"}} users, err := Users(Records{ Nodes: []string{"one"}, Assigned: map[string][]Declared{"one": {{ Module: "shop", Emits: []string{"order.placed"}, Uses: []Seat{seat}, }}}, }) if err != nil { t.Fatal(err) } perms, err := PermissionsFor(users[len(users)-1]) if err != nil { t.Fatal(err) } has(t, perms.Publish, "mesh.mod.shop.event.order.placed") has(t, perms.Publish, "mesh.seat.telegram-sender.accept.send") // A seat it uses, not one it holds: it may submit work and may not publish the seat's own // events, or it could lie about outcomes on a role somebody else fills. hasNot(t, perms.Publish, "mesh.seat.telegram-sender.event.delivered") hasNot(t, perms.Subscribe, "mesh.seat.telegram-sender.accept.send") } // A user the mesh has never minted a password for is named rather than silently dropped or // composed as a user anybody is. It is an ordinary situation — a module assigned a moment ago — and // the remedy is to mint one, so the caller decides whether to write a partial file. func TestAUserWithNoPasswordIsNamedRatherThanWritten(t *testing.T) { users, err := Users(someRecords()) if err != nil { t.Fatal(err) } filled, missing := WithPasswords(users, map[string]string{ "controller": "$2a$hash", "node.one": "$2a$hash", }) if len(filled) != 2 { t.Fatalf("composed %d users from two hashes", len(filled)) } if len(missing) != len(users)-2 { t.Fatalf("%d users are missing a password, of %d: %v", len(missing), len(users), missing) } for _, p := range filled { if p.PasswordHash == "" { t.Fatalf("%s was kept with no password, which is a user anybody is", p.Username()) } } } // And the whole thing composes: records in, a file the server would read out. func TestRecordsComposeIntoAFile(t *testing.T) { users, err := Users(someRecords()) if err != nil { t.Fatal(err) } hashes := map[string]string{} for _, u := range users { hashes[u.Username()] = "$2a$11$" + strings.Repeat("x", 22) } filled, missing := WithPasswords(users, hashes) if len(missing) != 0 { t.Fatalf("users with no password: %v", missing) } got, err := Compose(Server{ClientPort: 4222, MonitoringPort: 8222, StoreDir: "/data", TLSCert: "/tls/tls.crt", TLSKey: "/tls/tls.key", TLSCA: "/tls/ca.crt"}, filled) if err != nil { t.Fatal(err) } for _, want := range []string{ `user: "controller"`, `user: "node.one"`, `user: "one.telegram"`, `user: "enrol.three"`, `user: "person.ada"`, `"_INBOX.enrol.three.>"`, `"mesh.mod.mesh-catalog.tool.catalog_tools"`, } { if !strings.Contains(got, want) { t.Errorf("the composed file does not contain %s", want) } } } // The accounts block alone is what the mesh writes, and it holds nothing about the server. // // **The split is the whole design decision** (ComposeAccounts): ports, TLS paths and a store // directory are properties of the container the module raises, and a controller that wrote them // would have to be kept in step with a Dockerfile it never sees. So this test says what must not be // in the file as plainly as what must. func TestWhatTheMeshWritesIsUsersAndNothingAboutTheServer(t *testing.T) { users, err := Users(someRecords()) if err != nil { t.Fatal(err) } hashes := map[string]string{} for _, u := range users { hashes[u.Username()] = "$2a$11$" + strings.Repeat("x", 22) } filled, missing := WithPasswords(users, hashes) if len(missing) != 0 { t.Fatalf("users with no password: %v", missing) } got, err := ComposeAccounts(filled) if err != nil { t.Fatal(err) } for _, want := range []string{"accounts {", `user: "controller"`, `user: "one.telegram"`} { if !strings.Contains(got, want) { t.Errorf("the accounts file does not contain %s", want) } } // None of the server's own settings. Each of these in the mesh's file is a value the controller // would then own, and the module could no longer change its own image without the mesh agreeing. // `jetstream {` is the server's block (its store, its limits); `jetstream: enabled` inside the // account is the account's, and the mesh owns the account — a user in it is told "JetStream // not enabled for account" without it (2026-09-28). if !strings.Contains(got, "jetstream: enabled") { t.Errorf("the account does not enable JetStream, so no user in it can bind a consumer") } for _, absent := range []string{"port:", "http:", "jetstream {", "tls {", "store_dir", "cert_file"} { if strings.Contains(got, absent) { t.Errorf("the accounts file contains %q, which belongs to the module that raises the "+ "server, not to the mesh", absent) } } } // A user with no password is refused here too, not only by the whole-file composition: this is the // function the controller actually calls, and a user without a password is a user anybody is. func TestTheAccountsFileRefusesAUserWithNoPassword(t *testing.T) { if _, err := ComposeAccounts([]Principal{{Kind: KindController}}); err == nil { t.Fatal("a user with no password hash was written") } } // **A user list is composed for a bus the mesh has not moved onto yet**, and that is the whole of // step 2 (novox/hq ADR 0116): the server stands in the mesh carrying nothing, on its own ports, while // every node is still on the bus it was on. // // Pinned because the first version of the composing step got it backwards — it wrote the list only // once the controller was already on the new bus, which is a step that cannot be taken: the module // comes up, finds no accounts file, and waits for one the controller had decided not to write. func TestAUserListIsComposedBeforeAnythingMovesOntoTheBus(t *testing.T) { // Exactly the records of a mesh mid-change: everything running, nothing on the new bus. users, err := Users(Records{ Nodes: []string{"anchor"}, Assigned: map[string][]Declared{"anchor": {{Module: "nats"}}}, }) if err != nil { t.Fatal(err) } hashes := map[string]string{} for _, u := range users { hashes[u.Username()] = "$2a$11$" + strings.Repeat("x", 22) } filled, missing := WithPasswords(users, hashes) if len(missing) != 0 { t.Fatalf("users with no credential: %v", missing) } accounts, err := ComposeAccounts(filled) if err != nil { t.Fatal(err) } // The controller's own user above all: a file without it is a bus its writer cannot connect to, // which is what the server would be left holding the moment it starts. if !strings.Contains(accounts, `user: "controller"`) { t.Fatalf("the composed list does not contain the controller:\n%s", accounts) } if !strings.Contains(accounts, `user: "node.anchor"`) { t.Errorf("the composed list does not contain the machine running the bus") } }