Files
mesh-catalog/modules/nats/cmd/nats-tools/keep_test.go
T
jschoubben 905c34a537 CAT-N2: the keeper's process: the first view from indexer_bus_state, then manifest-accepted on its durable consumer
nats-tools keep is the process keeper, on its own account (own secret
broker-keeper): it asks the indexer's indexer_bus_state once, on a backoff
from 10 s doubling to 5 min while it does not answer (unhealthy meanwhile,
nothing created or orphaned), then binds its durable consumer on EVENTS
(<node>_nats_keeper, the indexer's manifest-accepted) and applies each verdict
only if newer by the indexer's revision; a manifest-accepted carrying no
revision yet is read as 0. A fill pass every minute, waiting entries made
again on the same backoff. It says its events on its own stream and its
conditions through the condition keeper, and serves nothing (ADR 0436 §3).

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 12:34:59 +01:00

66 lines
2.6 KiB
Go

package main
import (
"fmt"
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/catalogue"
)
// The keeper's inputs (design 25 § 12.2; CAT-N2): a manifest-accepted read with the indexer's revision when it carries
// one (0 when not yet), its entries whole; the first view of indexer_bus_state read bare or as the tool runner wraps it,
// an error answer an error; anything else refused, naming why.
func TestTheKeepersInputsAreRead(t *testing.T) {
for _, row := range []struct {
name, body, want, refused string
view bool
}{
{name: "accepted with its revision", body: `{"v":1,"module":"m","digest":"sha256:a","judge":"j","revision":7,"bus-state":[{"name":"drafts","storage":"key-value","template":"state"}]}`,
want: "m sha256:a 7 [drafts/key-value/state]"},
{name: "accepted before the event carries a revision", body: `{"v":1,"module":"m","digest":"sha256:b","judge":"j","bus-state":[]}`,
want: "m sha256:b 0 []"},
{name: "accepted naming no module", body: `{"v":1,"digest":"x"}`, refused: "names no module"},
{name: "first view as the tool runner answers it", view: true,
body: `{"result":{"revision":12,"modules":[{"module":"m","digest":"d","revision":11,"bus-state":[{"name":"log","storage":"stream","template":"log"}]}]}}`,
want: "12: m d 11 [log/stream/log]"},
{name: "first view bare", view: true, body: `{"revision":3,"modules":[]}`, want: "3:"},
{name: "first view answering an error", view: true, body: `{"error":"not granted"}`, refused: "not granted"},
{name: "first view that is none", view: true, body: `[1,2]`, refused: "is not a first view"},
} {
t.Run(row.name, func(t *testing.T) {
var got string
var err error
if row.view {
v, e := FirstViewOf([]byte(row.body))
err = e
got = fmt.Sprintf("%d:", v.Revision)
for _, m := range v.Modules {
got += " " + line(m.Module, m.Digest, m.Revision, m.BusState)
}
} else {
a, e := AcceptedOf([]byte(row.body))
err = e
got = line(a.Module, a.Digest, a.Revision, a.BusState)
}
if row.refused != "" {
if err == nil || !strings.Contains(err.Error(), row.refused) {
t.Fatalf("read %s (%v): refused, naming %q", got, err, row.refused)
}
return
}
if err != nil || got != row.want {
t.Errorf("read %q (%v), want %q", got, err, row.want)
}
})
}
}
func line(module, digest string, revision uint64, entries []catalogue.BusStateEntry) string {
es := []string{}
for _, e := range entries {
es = append(es, e.Name+"/"+e.Storage+"/"+e.Template)
}
return fmt.Sprintf("%s %s %d [%s]", module, digest, revision, strings.Join(es, " "))
}