Task 1.3 of novox/hq ADR 0116. On AMQP an account was an HTTP call; on NATS it is text the controller composes and the server reloads (ADR 0106). Pure, so the mesh's whole authority model is testable as strings. NATS closes a gap management.go recorded rather than hid: LavinMQ has no topic permissions, so an emitter was granted the events exchange whole and ADR 0042's origin reservation was "stamped by the sdk, not enforced here". Per-subject permissions make it the server's refusal. Two things found by composing a real file rather than reading the design: - a scoped inbox leaves a responder unable to reply, because the answer goes to the caller's inbox. allow_responses is the answer — one reply to the subject of a message actually received — and only principals that serve are granted it. Recorded in design 25 §4. - composition must be deterministic: the module's entrypoint reloads on the file's digest, so an order-dependent composer would reload the whole bus on every controller restart. Covered by a test. The golden fixture is the exact text `nats-server -t` accepts, so the syntax is the server's rather than one we invented.
50 lines
1.8 KiB
Go
50 lines
1.8 KiB
Go
package broker
|
|
|
|
import (
|
|
"flag"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
var update = flag.Bool("update", false, "rewrite the golden composition")
|
|
|
|
// The composed file is the mesh's whole authority model, so a change to it should be visible in a
|
|
// review rather than inferred from a diff of Go. The fixture is also the exact text checked
|
|
// against the real server's parser (`nats-server -t`), which is what says this syntax is the
|
|
// server's and not one we invented.
|
|
func TestTheComposedConfigMatchesTheGolden(t *testing.T) {
|
|
seat := Seat{Name: "telegram-sender", Accepts: []string{"send"}, Emits: []string{"delivered", "failed"}}
|
|
got, err := Compose(
|
|
Server{ClientPort: 4222, MonitoringPort: 8222, StoreDir: "/data",
|
|
TLSCert: "/tls/tls.crt", TLSKey: "/tls/tls.key", TLSCA: "/tls/ca.crt"},
|
|
[]Principal{
|
|
{Kind: KindController, PasswordHash: "$2a$11$cccccccccccccccccccccc"},
|
|
{Kind: KindEnrolment, PasswordHash: "$2a$11$eeeeeeeeeeeeeeeeeeeeee"},
|
|
{Kind: KindNode, Node: "one", PasswordHash: "$2a$11$nnnnnnnnnnnnnnnnnnnnnn"},
|
|
{Kind: KindModule, Node: "one", Module: "telegram", Holds: []Seat{seat},
|
|
Serves: []string{"status"}, PasswordHash: "$2a$11$tttttttttttttttttttttt"},
|
|
{Kind: KindModule, Node: "two", Module: "shop", Uses: []Seat{seat},
|
|
Emits: []string{"order.placed"}, PasswordHash: "$2a$11$ssssssssssssssssssssss"},
|
|
{Kind: KindModule, Node: "two", Module: "audit",
|
|
Consumes: []string{"shop.order.placed"}, PasswordHash: "$2a$11$aaaaaaaaaaaaaaaaaaaaaa"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
golden := filepath.Join("testdata", "composed.conf")
|
|
if *update {
|
|
if err := os.WriteFile(golden, []byte(got), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return
|
|
}
|
|
want, err := os.ReadFile(golden)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != string(want) {
|
|
t.Errorf("composition changed; re-run with -update and read the diff:\n%s", got)
|
|
}
|
|
}
|