mesh/merge-gate head 75a27ec1a4bc merged into trunk 85af245f1af1 — pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus…
mesh/repo-check head 75a27ec1a4bc merged into trunk 85af245f1af1 — pass: its merge-check.sh passed
mesh/delivery delivered
A node's own key carries its profile and was refused at 4 KiB on novox and g14 (nats: message size exceeds maximum allowed).
116 lines
5.0 KiB
Go
116 lines
5.0 KiB
Go
package broker
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
"github.com/nats-io/nats.go/jetstream"
|
|
)
|
|
|
|
// 1136/AC1: the controller's start asserts node-engine_have with the history delivery keeps, and the bucket is one of
|
|
// the controller's own, so raising the buckets does not name it undeclared (ADR 0429 §1).
|
|
func TestTheHaveBucketIsTheControllersAndKeepsTwentyRevisions(t *testing.T) { // 1136/AC1
|
|
cfg := HaveBucketConfig()
|
|
if cfg.Bucket != delivery.HaveBucket || cfg.History != delivery.HaveHistory || delivery.HaveHistory != 20 {
|
|
t.Fatalf("the have bucket is asserted as %q with history %d, want %q with 20", cfg.Bucket, cfg.History, delivery.HaveBucket)
|
|
}
|
|
if cfg.Storage != jetstream.FileStorage || cfg.MaxBytes <= 0 || cfg.MaxValueSize <= 0 {
|
|
t.Fatalf("the have bucket is not bounded on disk: %+v", cfg)
|
|
}
|
|
if !IsControllerBucket(delivery.HaveBucket) || !slices.Contains(ControllerBuckets(), delivery.HaveBucket) {
|
|
t.Fatalf("%s is not among the controller's buckets", delivery.HaveBucket)
|
|
}
|
|
undeclared, err := RaiseBuckets(fakeBuckets{names: []string{delivery.HaveBucket}}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(undeclared) != 0 {
|
|
t.Fatalf("%v are raised as undeclared", undeclared)
|
|
}
|
|
}
|
|
|
|
// 1136/AC2: a node may write and read the keys of its own node of the have bucket, with what a get, create, update and
|
|
// watch of them need, and no other key of it; the controller does not write it; an unsafe node name is refused.
|
|
func TestANodeWritesAndReadsOnlyItsOwnHaveKeys(t *testing.T) { // 1136/AC2
|
|
perms, err := PermissionsFor(Principal{Kind: KindNode, Node: "one", PasswordHash: "x"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, stream := delivery.HaveBucket, "KV_"+delivery.HaveBucket
|
|
for _, want := range []string{
|
|
"$KV." + b + ".one.>",
|
|
"$JS.API.STREAM.INFO." + stream,
|
|
"$JS.API.DIRECT.GET." + stream + ".$KV." + b + ".one.>",
|
|
"$JS.API.CONSUMER.CREATE." + stream + ".*.$KV." + b + ".one.>",
|
|
"$JS.API.CONSUMER.DELETE." + stream + ".>",
|
|
"$JS.FC." + stream + ".>",
|
|
} {
|
|
has(t, perms.Publish, want)
|
|
}
|
|
for _, p := range perms.Publish {
|
|
if !strings.Contains(p, delivery.HaveBucket) {
|
|
continue
|
|
}
|
|
if strings.Contains(p, "KV."+b+".>") || strings.Contains(p, "KV."+b+".two") || strings.HasSuffix(p, "."+b+".*") ||
|
|
strings.Contains(p, "DIRECT.GET."+stream+".>") || strings.Contains(p, "CONSUMER.CREATE."+stream+".>") {
|
|
t.Errorf("the node is granted %q, which reaches keys of other nodes", p)
|
|
}
|
|
}
|
|
if err := CheckWriters(Principal{Kind: KindNode, Node: "one"}, perms.Publish); err != nil {
|
|
t.Fatalf("the node's own have keys are refused as a second writer: %v", err)
|
|
}
|
|
controller, err := PermissionsFor(Principal{Kind: KindController, PasswordHash: "x"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, p := range controller.Publish {
|
|
if SubjectsOverlap(p, "$KV."+b+".one.zsh") {
|
|
t.Errorf("the controller may write the have keys through %q", p)
|
|
}
|
|
}
|
|
for _, bad := range []string{"on.e", "one.>", "*", "o>ne"} {
|
|
if _, err := PermissionsFor(Principal{Kind: KindNode, Node: bad, PasswordHash: "x"}); err == nil {
|
|
t.Errorf("node %q was accepted as part of a have subject", bad)
|
|
}
|
|
}
|
|
// A principal that is not a node writes no have key: the writers table refuses it.
|
|
if err := CheckWriters(Principal{Kind: KindModule, Module: "billing", Node: "one"}, []string{"$KV." + b + ".one.>"}); err == nil {
|
|
t.Error("a module granted the have keys of a node is not refused as a second writer")
|
|
}
|
|
if err := CheckWriters(Principal{Kind: KindNode, Node: "two"}, []string{"$KV." + b + ".one.>"}); err == nil {
|
|
t.Error("a node granted another node's have keys is not refused as a second writer")
|
|
}
|
|
}
|
|
|
|
// 1136/AC3: a module whose manifest reads node-engine.have, mesh-controller.assignments and mesh-controller.builds may
|
|
// watch and get every key of the three and write none.
|
|
func TestAModuleReadingHaveAssignmentsAndBuildsReadsAndWritesNone(t *testing.T) { // 1136/AC3
|
|
perms, err := PermissionsFor(Principal{Kind: KindModule, Node: "one", Module: "mesh-delivery-view", PasswordHash: "x",
|
|
Reads: []string{"node-engine.have", "mesh-controller.assignments", "mesh-controller.builds"}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, b := range []string{delivery.HaveBucket, AssignmentsBucket, BuildsBucket} {
|
|
stream := "KV_" + b
|
|
has(t, perms.Publish, "$JS.API.STREAM.INFO."+stream)
|
|
has(t, perms.Publish, "$JS.API.DIRECT.GET."+stream+".>")
|
|
has(t, perms.Publish, "$JS.API.CONSUMER.CREATE."+stream+".>")
|
|
for _, p := range perms.Publish {
|
|
if SubjectsOverlap(p, "$KV."+b+".any.key") {
|
|
t.Errorf("the reading module may write %s through %q", b, p)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// A node's own key carries its whole facts (its profile among them), several kilobytes on a desktop: the have
|
|
// bucket takes any value the chain's buckets take, since a value is never cut (novox/hq ADR 0408 §6, issue 1162).
|
|
// 1162/AC1
|
|
func TestTheHaveBucketTakesAValueAsLargeAsTheChains(t *testing.T) {
|
|
if got := HaveBucketConfig().MaxValueSize; got != ChainValueBytes {
|
|
t.Fatalf("the have bucket takes values up to %d bytes, the chain's up to %d", got, ChainValueBytes)
|
|
}
|
|
}
|