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) } }