package broker import ( "slices" "strings" "testing" "github.com/nats-io/nats.go/jetstream" ) // 1945 (the review's A1): the bus's user list today's controller composes, which is the one the bus runs after release // 6's switch, already grants every subject the new controller, every new node-engine and the assigner use: the // controller writes each node's compositions and the digests; a node-engine reads its own compositions and wants, asks // its own backup holder and hears its word, and nothing of another node's; the assigner reads the digests, announced // readable. func TestTheUserListBeforeTheSwitchGrantsWhatReleaseSixUses(t *testing.T) { controller, err := PermissionsFor(Principal{Kind: KindController, PasswordHash: "x", FilesFor: []string{"ace", "g14"}}) if err != nil { t.Fatal(err) } for _, s := range []string{"$KV.composed-ace.>", "$KV.composed-g14.>", "$KV.mesh-controller_composed.>", "mesh.seat.mesh-controller.event.bucket-deleted"} { has(t, controller.Publish, s) } node, err := PermissionsFor(Principal{Kind: KindNode, Node: "ace", PasswordHash: "x"}) if err != nil { t.Fatal(err) } for _, s := range []string{ "$JS.API.STREAM.INFO.KV_composed-ace", "$JS.API.DIRECT.GET.KV_composed-ace.>", "$JS.API.CONSUMER.CREATE.KV_composed-ace.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.ace.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.ace.>", "mesh.seat.node-backup.tool.now.ace", "mesh.seat.node-backup.tool.backed-up.ace", } { has(t, node.Publish, s) } has(t, node.Subscribe, "mesh.mod.restic.event.backed-up") for _, p := range node.Publish { if strings.Contains(p, "composed-g14") || strings.Contains(p, "mesh-controller_composed") || strings.Contains(p, "want.g14") || strings.HasPrefix(p, "$KV.composed-") || strings.HasSuffix(p, "node-backup.tool.now.*") { t.Errorf("a node-engine of ace may publish %s", p) } } if !slices.Contains(controllerReadableStates(), ControllerComposed) || !slices.Contains(ControllerBuckets(), ControllerComposedBucket) || !IsControllerBucket(ControllerComposedBucket) { t.Fatalf("the digests are not announced readable among the controller's buckets") } if !ControllerAnnouncement("novox").Readable(ControllerComposed) { t.Fatalf("the controller does not announce %s readable", ControllerComposed) } assigner, err := PermissionsFor(Principal{Kind: KindModule, Node: "novox", Module: "mesh-delivery-assigner", PasswordHash: "x", Reads: []string{"mesh-controller.composed"}}) if err != nil { t.Fatal(err) } has(t, assigner.Publish, "$JS.API.STREAM.INFO.KV_mesh-controller_composed") has(t, assigner.Publish, "$JS.API.CONSUMER.CREATE.KV_mesh-controller_composed.>") if c := ComposedDigestsConfig(); c.Bucket != "mesh-controller_composed" || c.History != 5 || c.Storage != jetstream.FileStorage { t.Errorf("the digests' bucket is %+v", c) } } // 1945 (the review of A1, (b)): the operator's account, a person who may invoke every tool, writes release 6's hand // path: a want of any node, each node's compositions and the digests; a person with fewer tools writes none of it. func TestTheOperatorWritesTheHandPathAheadOfTheSwitch(t *testing.T) { users, err := Users(Records{Nodes: []string{"ace", "novox"}, People: map[string][]string{"jochen": {"*"}, "guest": {"mesh-controller.status"}}}) if err != nil { t.Fatal(err) } for _, u := range users { if u.Kind != KindPerson { continue } perms, err := PermissionsFor(u) if err != nil { t.Fatal(err) } hand := []string{"$KV.mesh-delivery-assigner_wants.want.>", "$KV.composed-ace.>", "$KV.composed-novox.>", "$KV.mesh-controller_composed.>", "$O.declaration-files-ace.>", "$JS.API.DIRECT.GET.OBJ_declaration-files-ace.>", "$JS.API.STREAM.MSG.GET.OBJ_declaration-files-ace", "$JS.API.STREAM.PURGE.OBJ_declaration-files-ace"} for _, s := range hand { if got := slices.Contains(perms.Publish, s); got != (u.Module == "jochen") { t.Errorf("%s may publish %s: %v", u.Module, s, got) } } } }