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
66 lines
2.6 KiB
Go
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, " "))
|
|
}
|