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
148 lines
6.0 KiB
Go
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)
|
|
}
|
|
}
|