Files
mesh-catalog/modules/nats/cmd/nats-tools/broker_test.go
T
jschoubben ae056a44dd
mesh/delivery merged: source.main moved to novox/mesh-catalog@9dfb2eae0eb3
mesh/delivery-group group fix/1966-bindings-first-view merged: every member is merged
mesh/check checking ae056a44dd72 on 9dfb2eae0eb3 | novox/mesh-catalog#656 in group fix/1966-bindings-first-view
The bus module's list, show and orphans answer each held entry's binding
Held.binding is the entry's plan binding, the value its created and recreated
carry, present when the bus holds the entry's objects and absent on one it does
not (waiting, refused). Table rows in the reading verbs' test: the binding of a
held entry equals the one its created carries; a waiting entry has none.

Builds: design 25 bindings-first-view §2, §7 (hq d0760629, PR 543)

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
2026-11-11 10:19:42 +01:00

117 lines
5.5 KiB
Go

package main
import (
"encoding/json"
"fmt"
"reflect"
"strings"
"testing"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"nats-tools/internal/keeper"
)
// The seat's reading verbs and nats_history (design 25 § 12.3; CAT-N7): list and show answer every entry the keeper
// made with its settings asked and held and its fill against its cap; check names what is missing or differs; orphans
// what no manifest asks for; history one entry's events in order. Each from the keeper's own state and the bus's
// monitoring endpoint.
func TestTheSeatsReadingVerbsAnswerFromTheKeeperAndTheBus(t *testing.T) {
plan := func(local, storage, template string, options string) keeper.Plan {
e := catalogue.BusStateEntry{Name: local, Storage: storage, Template: template}
if options != "" {
_ = json.Unmarshal([]byte(options), &e.Options)
}
p, err := keeper.PlanOf("m", e)
if err != nil {
t.Fatal(err)
}
return p
}
held := map[string]keeper.Asked{"m": {Module: "m", Made: map[string]keeper.Made{
"drafts": {Plan: plan("drafts", "key-value", "state", ""), Created: true},
"log": {Plan: plan("log", "stream", "log", `{"max-mib":16}`), Created: true, Drift: "m_log: max-bytes asked 16777216, held 33554432"},
"old": {Plan: plan("old", "stream", "events", ""), Created: true, Orphaned: true},
"later": {Plan: plan("later", "key-value", "state", ""), Waiting: "a grant not composed yet"},
}}}
jsz := `{"account_details":[{"stream_detail":[
{"name":"KV_m_drafts","config":{"name":"KV_m_drafts","retention":"limits","discard":"new","max_bytes":67108864,"max_msgs_per_subject":1,"max_msg_size":262144},"state":{"messages":3,"bytes":1048576}},
{"name":"m_log","config":{"name":"m_log","retention":"limits","discard":"new","max_bytes":33554432,"max_msg_size":262144},"state":{"messages":9,"bytes":9000}}]}]}`
tools := map[string]stdio.Tool{}
for _, tl := range brokerTools(func() (map[string]keeper.Asked, error) { return held, nil },
func() ([]byte, error) { return []byte(jsz), nil },
func(module, local string, limit int) ([]keeper.Event, error) {
return []keeper.Event{{Module: module, Local: local, Why: "first"}, {Module: module, Local: local, Why: "second"}}[:limit], nil
}) {
tools[tl.Name] = tl
}
for _, row := range []struct {
tool string
args map[string]any
want []string
err string
}{
// Each answers the seat's contract (mesh-contracts meshbroker ListAnswer, Held, CheckAnswer, OrphansAnswer).
{"mesh-broker.list", nil, []string{`{"entries":[`, `"bus-name":"m_drafts"`, `"local-name":"drafts"`, `"storage-type":"key-value"`,
`"KV_m_drafts":{"max-bytes":67108864`, `"fill":{"bytes":1048576,"cap":67108864,"count":3}`, `"options":{"max-mib":16}`,
// The binding of an entry the bus holds is the one its created carries (design 25 bindings-first-view §2).
`"binding":{"bucket":"m_drafts","local":"drafts","storage":"key-value","template":"state"}`}, ""},
{"mesh-broker.show", map[string]any{"module": "m", "local-name": "log"}, []string{`"bus-name":"m_log"`, `"template":"log"`,
`"m_log":{"max-bytes":33554432`}, ""},
{"mesh-broker.show", map[string]any{"module": "m", "local-name": "later"}, []string{`"local-name":"later"`}, ""},
{"mesh-broker.show", map[string]any{"module": "m", "local-name": "none"}, nil, `made no entry "none" of "m"`},
{"mesh-broker.check", nil, []string{`{"differences":[{`, `"local-name":"log"`, `"asked":{"m_log":{"max-bytes":16777216`,
`"held":{"m_log":{"max-bytes":33554432`}, ""},
{"mesh-broker.check", map[string]any{"module": "x"}, []string{`{"differences":[]}`}, ""},
{"mesh-broker.orphans", nil, []string{`{"entries":[{`, `"local-name":"old"`}, ""},
{"nats_history", map[string]any{"module": "m", "local": "log", "limit": float64(1)}, []string{`"why":"first"`}, ""},
{"nats_history", map[string]any{"module": "m"}, nil, "names one entry"},
} {
t.Run(fmt.Sprintf("%s %v", row.tool, row.args), func(t *testing.T) {
args := row.args
if args == nil {
args = map[string]any{}
}
out, err := tools[row.tool].Run(args)
if row.err != "" {
if err == nil || !strings.Contains(err.Error(), row.err) {
t.Fatalf("answered %v (%v): refused, naming %q", out, err, row.err)
}
return
}
raw, _ := json.Marshal(out)
for _, w := range row.want {
if !strings.Contains(string(raw), w) {
t.Errorf("%s lacks %s", raw, w)
}
}
// list's binding is, field by field, the binding the entry's created carries (the event's body takes the plan's binding whole).
if row.tool == "mesh-broker.list" {
var got struct {
Entries []struct {
Local string `json:"local-name"`
Binding map[string]any `json:"binding"`
} `json:"entries"`
}
var created map[string]any
wire, _ := json.Marshal(held["m"].Made["drafts"].Plan.Binding)
_ = json.Unmarshal(raw, &got)
_ = json.Unmarshal(wire, &created)
for _, e := range got.Entries {
if e.Local == "drafts" && !reflect.DeepEqual(e.Binding, created) {
t.Errorf("list names the binding %v for drafts, its created carries %v", e.Binding, created)
}
}
}
// An entry the bus does not hold has no binding.
if row.args["local-name"] == "later" && strings.Contains(string(raw), `"binding"`) {
t.Errorf("show names a binding for later, whose objects the bus does not hold: %s", raw)
}
if row.tool == "mesh-broker.check" && strings.Contains(string(raw), `"local-name":"drafts"`) {
t.Errorf("check names drafts, which the bus holds as asked: %s", raw)
}
})
}
}