Files
mesh-controller/internal/broker/composed_test.go
T
jschoubben 1350c428ed Let the operator's hand path replace a file over 64 KiB in a node's object bucket (issue 1945; the review of A1, H1)
The object store reads an object's metadata and purges its old chunks when it replaces one, so the hand path is
granted the direct and message gets and the purge of each node's object stream; a test on a bus running the composed
user list walks the hand path end to end.
2026-10-24 18:25:46 +02:00

89 lines
4.0 KiB
Go

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)
}
}
}
}