Files
mesh-controller/cmd/mesh-controller/states_announced_integration_test.go
T

119 lines
4.1 KiB
Go

//go:build integration
package main
import (
"context"
"slices"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/announce"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"github.com/nats-io/nats.go/jetstream"
"github.com/novox/mesh-controller/internal/broker"
"github.com/novox/mesh-controller/internal/inventory"
"github.com/novox/mesh-controller/internal/testbus"
)
// D1-D3 whole against a real bus and the store (ADR 0495 checks §2, §3, §5; ADR 0513 §3; issue 1790): the controller
// says what it keeps and keeps what every module says; a reader of a state its owner announced readable is granted it,
// one its owner announced and did not make readable is refused and said, and one of an owner that announced nothing
// yet is granted as before and named as not yet announced.
func TestTheStatesAnnouncedGrantTheReads(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
inv := open.inventory
js, err := broker.Dial(testbus.URL(t))
if err != nil {
t.Fatal(err)
}
t.Cleanup(js.Close)
if err := broker.AssertMeshStreams(js); err != nil {
t.Fatal(err)
}
if err := js.EnsureControllerBuckets(); err != nil {
t.Fatal(err)
}
servingBus.Store(js)
t.Cleanup(func() { servingBus.Store(nil) })
was := inventory.Announcements
inventory.Announcements = readAnnouncements
t.Cleanup(func() { inventory.Announcements = was })
// The owner keeps two states, one readable; the reader reads both and a state of a module that says nothing.
register := func(m catalogue.Manifest) {
t.Helper()
if err := inv.RegisterModule(ctx, m, inventory.Source{Repository: "novox/mesh-catalog", Seat: "git",
Path: "modules/" + m.Module, BuiltFrom: "c0"}); err != nil {
t.Fatal(err)
}
}
register(catalogue.Manifest{Module: "owner", Version: "1", State: []catalogue.StateDeclaration{{Name: "open"}, {Name: "closed"}}})
register(catalogue.Manifest{Module: "quiet", Version: "1", State: []catalogue.StateDeclaration{{Name: "any"}}})
register(catalogue.Manifest{Module: "reader", Version: "1", Reads: []string{"owner.open", "owner.closed", "quiet.any"}})
if _, err := inv.AddNode(ctx, "one"); err != nil {
t.Fatal(err)
}
for _, m := range []string{"owner", "quiet", "reader"} {
if _, err := inv.Assign(ctx, "one", m); err != nil {
t.Fatal(err)
}
}
watching, stop := context.WithCancel(context.Background())
t.Cleanup(stop)
if err := announceOwnStates(ctx, js); err != nil {
t.Fatal(err)
}
if err := keepAnnouncements(watching, js, open); err != nil {
t.Fatal(err)
}
api, _ := jetstream.New(js.Conn())
word, _ := announce.Encode(announce.States{Module: "owner", Node: "one",
States: []announce.State{{Name: "open", Readable: true}, {Name: "closed"}}})
if _, err := api.Publish(ctx, announce.Subject("owner"), word); err != nil {
t.Fatal(err)
}
becomes(t, 10*time.Second, "both words kept", func() bool {
a, err := readAnnouncements(ctx)
return err == nil && len(a) == 2 && a["owner"].Readable("open") && a[broker.ControllerSeat].Readable(broker.PlansBuilding)
})
records, err := inv.BusRecords(ctx)
if err != nil {
t.Fatal(err)
}
var reader broker.Declared
for _, d := range records.Assigned["one"] {
if d.Module == "reader" {
reader = d
}
}
if !slices.Equal(reader.Reads, []string{"owner.open", "quiet.any"}) || !slices.Equal(reader.ReadsRefused, []string{"owner.closed"}) {
t.Fatalf("0495 §3: the reader is granted %v and refused %v", reader.Reads, reader.ReadsRefused)
}
if got := broker.NotAnnounced(records); !slices.Equal(got, []string{"quiet"}) {
t.Fatalf("not yet announced: %v", got)
}
users, err := broker.Users(records)
if err != nil {
t.Fatal(err)
}
for _, u := range users {
if u.Kind != broker.KindModule || u.Module != "reader" {
continue
}
perms, err := broker.PermissionsFor(u)
if err != nil {
t.Fatal(err)
}
if slices.ContainsFunc(perms.Publish, func(s string) bool { return s == "$JS.API.DIRECT.GET.KV_owner_closed.>" }) {
t.Fatal("0495 §3: the reader may read the state its owner did not announce readable")
}
if !slices.Contains(perms.Publish, "$JS.API.DIRECT.GET.KV_owner_open.>") {
t.Fatal("0495 §3: the reader may not read the state its owner announced readable")
}
}
}