Files
mesh-catalog/modules/nats/cmd/nats-tools/requests_test.go
T
jschoubben 0d6d7acaae nats: a warrant's spend record in the keeper's own state spent; the fill pass while the consumer binds; partial says kept
Review round 2 of the registry milestone:
- security F2: the spend record leaves requests, which the tools write as the tool runner's account, for state spent,
  named in the keeper's bus.state and declared read-only (the lead's 18:1x ruling: the tools read it, never write);
  nats_requests lists the warrants spent from it;
- running F1: the fill pass also runs while the manifest-accepted consumer is bound again;
- health note 3: an event said on the seat's subject and refused by its history is kept with its one id and said again
  on the history alone, never again with a new id;
- seams note 3, health note 4: words unsaid with a status that cannot be written raise the keeper's own condition
  status-unwritten, cleared when it is written; the health names what a restart would lose.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 18:13:36 +01:00

148 lines
6.0 KiB
Go

package main
import (
"errors"
"runtime"
"strings"
"testing"
"time"
occ "git.novox.be/novox/mesh-contracts/go/clients/operatorchannelclient"
oc "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/handacts"
"nats-tools/internal/keeper"
)
// A person's acting tools hand their request to the keeper (design 25 § 12.3; CAT-N8): stamped with the caller the
// tool runner proved, whatever name the arguments give; a create's end read back; a drop raises the operator's ask
// first, binding exactly that act, and is not acted on by the tool.
func TestAPersonsRequestIsHandedToTheKeeperAsTheProvedCaller(t *testing.T) {
held := map[string]keeper.Request{}
var raised []oc.Ask
var acts []handacts.Request
rs := requestStore{
Put: func(key string, r keeper.Request) error {
if r.Verb == keeper.VerbCreate {
// The keeper's end, as its watch writes it.
r.Done, r.Outcome = time.Unix(1, 0), "created"
}
held[key] = r
return nil
},
Get: func(key string) (*keeper.Request, error) {
r, ok := held[key]
if !ok {
return nil, nil
}
return &r, nil
},
Keys: func() ([]string, error) {
var out []string
for k := range held {
out = append(out, k)
}
return out, nil
},
Delete: func(key string) error { delete(held, key); return nil },
Spent: func() ([]keeper.Acted, error) {
return []keeper.Acted{{Ask: "bus-old", Request: "r0", Verb: "drop"}}, nil
},
Ask: func(a oc.Ask) error { raised = append(raised, a); return nil },
Now: func() time.Time { return time.Date(2026, 11, 4, 12, 0, 0, 0, time.UTC) },
Record: func(q handacts.Request) error { acts = append(acts, q); return nil },
}
tools := map[string]stdio.Tool{}
for _, tl := range requestTools(rs) {
tools[tl.Name] = tl
}
out, err := tools["nats_create"].Run(map[string]any{"module": "m", "local": "log", "why": "a test", "caller": "person.mallory"})
if err != nil {
t.Fatal(err)
}
if r := out.(*keeper.Request); r.Caller != stdio.Unproven || r.Outcome != "created" {
t.Errorf("answered %+v: acted for the caller the runtime proved (here none, %q), never person.mallory", r, stdio.Unproven)
}
out, err = tools["nats_drop"].Run(map[string]any{"module": "m", "local": "old", "why": "no manifest asks for it"})
if err != nil {
t.Fatal(err)
}
answer := out.(map[string]any)
r := held[answer["request"].(string)]
if len(raised) != 1 || r.Ask != raised[0].ID || !r.Done.IsZero() {
t.Fatalf("raised %+v, held %+v: the ask raised, the request waiting for it", raised, r)
}
binds, _ := occ.ActDigest(occ.Act{"verb": "drop", "module": "m", "local": "old"})
if o, _ := occ.OptionOf(raised[0], "go"); o.Level != occ.Destroy || o.Binds != binds || !strings.Contains(raised[0].Explanation, "snapshot") {
t.Errorf("the ask is %+v: its option destroys exactly this drop, after a snapshot", raised[0])
}
if _, err := tools["nats_drop"].Run(map[string]any{"module": "m", "local": "old"}); err == nil || !strings.Contains(err.Error(), "says why") {
t.Errorf("a drop without why: %v", err)
}
if got, _ := tools["nats_drop"].Run(map[string]any{"request": r.ID}); got.(*keeper.Request).ID != r.ID {
t.Errorf("reading the request back answered %+v", got)
}
// Review round 1 (tools F3): nats_requests reads what waits, what ended and each warrant spent, and drops a waiting
// request by hand, recorded; an ended one is not dropped.
out, err = tools["nats_requests"].Run(map[string]any{})
if err != nil {
t.Fatal(err)
}
list := out.(map[string]any)
if w, e, spent := list["waiting"].([]keeper.Request), list["ended"].([]keeper.Request), list["warrants-spent"].([]keeper.Acted); len(w) != 1 ||
w[0].ID != r.ID || len(e) != 1 || len(spent) != 1 || spent[0].Ask != "bus-old" {
t.Errorf("nats_requests answered %+v: the drop waiting, the create ended, one warrant spent", list)
}
if _, err := tools["nats_requests"].Run(map[string]any{"drop": r.ID}); err == nil || !strings.Contains(err.Error(), "says why") {
t.Errorf("a drop by hand without why: %v", err)
}
for id, r := range held {
if r.Verb == keeper.VerbCreate {
if _, err := tools["nats_requests"].Run(map[string]any{"drop": id, "why": "x"}); err == nil || !strings.Contains(err.Error(), "only a request that waits") {
t.Errorf("dropping an ended request: %v", err)
}
}
}
if _, err := tools["nats_requests"].Run(map[string]any{"drop": r.ID, "why": "nobody will answer"}); err != nil || len(acts) != 1 ||
!strings.Contains(acts[0].What, "dropped the waiting request "+r.ID) {
t.Errorf("dropped by hand (%v), recorded %+v", err, acts)
}
if _, still := held[r.ID]; still {
t.Error("the dropped request is still held")
}
// The ask first: an ask that cannot be raised hands no request to the keeper.
before := len(held)
rs.Ask = func(oc.Ask) error { return errors.New("no responders") }
tools = map[string]stdio.Tool{}
for _, tl := range requestTools(rs) {
tools[tl.Name] = tl
}
if _, err := tools["nats_recreate"].Run(map[string]any{"module": "m", "local": "log", "why": "drifted"}); err == nil || len(held) != before {
t.Errorf("an ask not raised: %v, %d requests held (was %d)", err, len(held), before)
}
// A request's end is heard on the one watch of the state, handed to the call that waits for it.
var handle func(string, []byte)
watches := 0
e := newEnds(func(h func(string, []byte)) error { watches++; handle = h; return nil })
got := make(chan *keeper.Request, 1)
go func() { got <- e.wait("r1", time.Minute) }()
for {
e.mu.Lock()
ready := len(e.waiting["r1"]) == 1 && handle != nil
e.mu.Unlock()
if ready {
break
}
runtime.Gosched()
}
handle("r1", []byte(`{"id":"r1"}`))
handle("r1", []byte(`{"id":"r1","done":"2026-11-04T12:00:01Z","outcome":"created"}`))
if r := <-got; r == nil || r.Outcome != "created" || watches != 1 {
t.Errorf("the end heard %+v on %d watch(es): created, on one", r, watches)
}
if r := e.wait("r2", time.Millisecond); r != nil || watches != 1 {
t.Errorf("an end not written: %+v on %d watch(es)", r, watches)
}
}