A-3 judge and claim: the same call again runs once on the router's record of its Approve
The gate sits on both handlers the runner serves, a module's tools (its ledger repairs among them) and the seat verbs its modules hold, before the bundle. The same call again is judged on the router's own record of its act's newest ask: held to the warrant (operatorchannelclient.For), the option and the act it bound (Performs), within 30 minutes of the choice, and claimed by compare-and-set (Client.Take) before it runs, so one approval runs one call. A later call is told it still waits, was declined (not asked again for 10 minutes), or asks anew after expired, approved too long ago or already run. A runner's own tool marked is refused unasked; with no state approvals bound a marked call is refused and an unmarked one runs. The gate's reach (state, publish, the router's record) is the runner's own connection, in one place; it waits for the catalog half's first view on the events the runner hears. Builds: design 46 7-approval-mark §13, §14, §15, §17, §20 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -145,6 +145,24 @@ func newWorld() *world {
|
||||
return w
|
||||
}
|
||||
|
||||
// decide writes the router's record of the newest ask as the router writes a person's choice: its state and warrant.
|
||||
func (w *world) decide(option string, at time.Time) seat.Ask {
|
||||
a := w.asked[len(w.asked)-1]
|
||||
key := "shell." + a.ID
|
||||
rec := w.records[key]
|
||||
rec.State = "chosen"
|
||||
rec.Warrant = &seat.Warrant{Ask: a.ID, Asker: "shell", Outcome: operatorchannelclient.OutcomeChosen, Option: option,
|
||||
Level: operatorchannelclient.Approve, At: operatorchannelclient.FormatTime(at), AskDigest: operatorchannelclient.Digest(a),
|
||||
By: &seat.Person{Who: operatorchannelclient.Operator, Kind: "telegram", Identity: "42", Verified: "user id verified"}}
|
||||
w.records[key] = rec
|
||||
return a
|
||||
}
|
||||
|
||||
// do makes a call through the gate, counting the runs.
|
||||
func (w *world) do(c Call) (any, error) {
|
||||
return w.g.Run(c, func() (any, error) { w.runs++; return "done", nil })
|
||||
}
|
||||
|
||||
// call is a fixture call of the marked tool wipe, or of another tool of the module.
|
||||
func call(name, args string) Call {
|
||||
return Call{Address: "shell." + name, Module: "shell", Name: name, Fingerprint: "src2:1", Caller: "agent", Args: json.RawMessage(args),
|
||||
@@ -270,6 +288,150 @@ func TestTheRefusalsAndAnswers(t *testing.T) {
|
||||
t.Fatalf("answer %v after %s", err, w.now.Sub(start))
|
||||
}
|
||||
}},
|
||||
{"§10, §17: a second identical call publishes nothing and is told it still waits", func(t *testing.T, w *world) {
|
||||
_, _ = w.do(call("wipe", `{"dir":"scratch"}`))
|
||||
_, err := w.do(call("wipe", `{"dir":"scratch"}`))
|
||||
if len(w.asked) != 1 || err == nil || !strings.HasPrefix(err.Error(), "Not run: still waiting for the operator's answer") || w.runs != 0 {
|
||||
t.Fatalf("asked %d, %v, runs %d", len(w.asked), err, w.runs)
|
||||
}
|
||||
}},
|
||||
{"§17: an ask the router holds back as the fourth open one still waits", func(t *testing.T, w *world) {
|
||||
_, _ = w.do(call("wipe", `{}`))
|
||||
a := w.asked[0]
|
||||
rec := w.records["shell."+a.ID]
|
||||
rec.State, rec.HeldSince = "held-back", operatorchannelclient.FormatTime(w.now)
|
||||
w.records["shell."+a.ID] = rec
|
||||
if _, err := w.do(call("wipe", `{}`)); err == nil || !strings.Contains(err.Error(), "still waiting") || len(w.asked) != 1 {
|
||||
t.Fatalf("%v, asked %d", err, len(w.asked))
|
||||
}
|
||||
}},
|
||||
{"§14, §15: approved, the same call runs once; made again it is already run and asks anew", func(t *testing.T, w *world) {
|
||||
_, _ = w.do(call("wipe", `{"dir":"scratch"}`))
|
||||
w.decide(OptionApprove, w.now.Add(time.Minute))
|
||||
w.now = w.now.Add(2 * time.Minute)
|
||||
if got, err := w.do(call("wipe", `{"dir":"scratch"}`)); err != nil || got != "done" || w.runs != 1 {
|
||||
t.Fatalf("%v %v, runs %d", got, err, w.runs)
|
||||
}
|
||||
_, err := w.do(call("wipe", `{"dir":"scratch"}`))
|
||||
if err == nil || !strings.Contains(err.Error(), "was already run; asked again") || w.runs != 1 || len(w.asked) != 2 {
|
||||
t.Fatalf("%v, runs %d, asked %d", err, w.runs, len(w.asked))
|
||||
}
|
||||
}},
|
||||
{"§15: two calls at the same moment run it once", func(t *testing.T, w *world) {
|
||||
_, _ = w.do(call("wipe", `{}`))
|
||||
w.decide(OptionApprove, w.now)
|
||||
var wg sync.WaitGroup
|
||||
var mu sync.Mutex
|
||||
runs := 0
|
||||
for i := 0; i < 2; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_, _ = w.g.Run(call("wipe", `{}`), func() (any, error) { mu.Lock(); runs++; mu.Unlock(); return "done", nil })
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
if runs != 1 {
|
||||
t.Fatalf("ran %d times", runs)
|
||||
}
|
||||
}},
|
||||
{"§14: approved for other arguments runs nothing", func(t *testing.T, w *world) {
|
||||
_, _ = w.do(call("wipe", `{"dir":"scratch"}`))
|
||||
a := w.decide(OptionApprove, w.now)
|
||||
rec := w.records["shell."+a.ID]
|
||||
other, _ := ActOf(call("wipe", `{"dir":"home"}`), "n1")
|
||||
d, _ := operatorchannelclient.ActDigest(other)
|
||||
a.Options[0].Binds = d // the ask the person was shown bound another act
|
||||
rec.Warrant.AskDigest = operatorchannelclient.Digest(a)
|
||||
w.records["shell."+a.ID] = rec
|
||||
var k operatorchannelclient.Kept
|
||||
_ = json.Unmarshal(w.state.vals["n1.ask."+a.ID], &k)
|
||||
k.Ask = a
|
||||
raw, _ := json.Marshal(k)
|
||||
w.state.vals["n1.ask."+a.ID] = raw
|
||||
if _, err := w.do(call("wipe", `{"dir":"scratch"}`)); err == nil || w.runs != 0 || !strings.Contains(err.Error(), "nothing is done") {
|
||||
t.Fatalf("%v, runs %d", err, w.runs)
|
||||
}
|
||||
}},
|
||||
{"§14: approved 31 minutes ago asks anew", func(t *testing.T, w *world) {
|
||||
_, _ = w.do(call("wipe", `{}`))
|
||||
w.decide(OptionApprove, w.now)
|
||||
w.now = w.now.Add(31 * time.Minute)
|
||||
if _, err := w.do(call("wipe", `{}`)); err == nil || !strings.Contains(err.Error(), "approved too long ago") || w.runs != 0 || len(w.asked) != 2 {
|
||||
t.Fatalf("%v, runs %d, asked %d", err, w.runs, len(w.asked))
|
||||
}
|
||||
}},
|
||||
{"§14, §17: declined, the same call is not asked again for 10 minutes", func(t *testing.T, w *world) {
|
||||
_, _ = w.do(call("wipe", `{}`))
|
||||
w.decide(OptionDecline, w.now)
|
||||
w.now = w.now.Add(9 * time.Minute)
|
||||
if _, err := w.do(call("wipe", `{}`)); err == nil || !strings.Contains(err.Error(), "declined") || len(w.asked) != 1 || w.runs != 0 {
|
||||
t.Fatalf("%v, asked %d", err, len(w.asked))
|
||||
}
|
||||
w.now = w.now.Add(2 * time.Minute)
|
||||
if _, err := w.do(call("wipe", `{}`)); err == nil || !strings.Contains(err.Error(), "asked again") || len(w.asked) != 2 {
|
||||
t.Fatalf("%v, asked %d", err, len(w.asked))
|
||||
}
|
||||
}},
|
||||
{"§14, §17: expired asks anew", func(t *testing.T, w *world) {
|
||||
_, _ = w.do(call("wipe", `{}`))
|
||||
a := w.asked[0]
|
||||
rec := w.records["shell."+a.ID]
|
||||
rec.State = "expired"
|
||||
w.records["shell."+a.ID] = rec
|
||||
if _, err := w.do(call("wipe", `{}`)); err == nil || !strings.Contains(err.Error(), "ended expired; asked again") || len(w.asked) != 2 {
|
||||
t.Fatalf("%v, asked %d", err, len(w.asked))
|
||||
}
|
||||
}},
|
||||
{"§14: another asker's warrant runs nothing", func(t *testing.T, w *world) {
|
||||
_, _ = w.do(call("wipe", `{}`))
|
||||
a := w.decide(OptionApprove, w.now)
|
||||
rec := w.records["shell."+a.ID]
|
||||
rec.Warrant.Asker = "mail"
|
||||
w.records["shell."+a.ID] = rec
|
||||
if _, err := w.do(call("wipe", `{}`)); err == nil || w.runs != 0 || !strings.Contains(err.Error(), "its warrant does not hold") {
|
||||
t.Fatalf("%v, runs %d", err, w.runs)
|
||||
}
|
||||
}},
|
||||
{"§17: the router's refusal is said with its words", func(t *testing.T, w *world) {
|
||||
_, _ = w.do(call("wipe", `{}`))
|
||||
a := w.asked[0]
|
||||
rec := w.records["shell."+a.ID]
|
||||
rec.State, rec.Answer = "refused", "the asker has too many open asks"
|
||||
w.records["shell."+a.ID] = rec
|
||||
if _, err := w.do(call("wipe", `{}`)); err == nil || !strings.Contains(err.Error(), "the asker has too many open asks") {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
}},
|
||||
{"§20: a runner's own tool marked is refused unasked, never run", func(t *testing.T, w *world) {
|
||||
c := call("wipe", `{}`)
|
||||
c.Own = true
|
||||
if _, err := w.do(c); err == nil || w.runs != 0 || len(w.asked) != 0 {
|
||||
t.Fatalf("%v, runs %d, asked %d", err, w.runs, len(w.asked))
|
||||
}
|
||||
}},
|
||||
{"§13: with no binding, an unmarked call runs and a marked one is refused", func(t *testing.T, w *world) {
|
||||
w.g.d.State = nil
|
||||
if got, err := w.do(call("tidy", `{}`)); err != nil || got != "done" {
|
||||
t.Fatalf("unmarked: %v %v", got, err)
|
||||
}
|
||||
if _, err := w.do(call("wipe", `{}`)); err == nil || !strings.Contains(err.Error(), "binds no state approvals") || w.runs != 1 {
|
||||
t.Fatalf("marked: %v, runs %d", err, w.runs)
|
||||
}
|
||||
}},
|
||||
{"§13: an ask over 256 KiB is refused naming the cause", func(t *testing.T, w *world) {
|
||||
c := call("wipe", `{}`)
|
||||
c.Description = strings.Repeat("w", 300*1024)
|
||||
if _, err := w.do(c); err == nil || !strings.Contains(err.Error(), "more than the 262144 a value of the state approvals holds") || len(w.asked) != 0 {
|
||||
t.Fatalf("%v, asked %d", err, len(w.asked))
|
||||
}
|
||||
}},
|
||||
{"§3, §2: a seat verb its seat marks is asked for its holder", func(t *testing.T, w *world) {
|
||||
c := Call{Address: "node-login-shell.execute", Module: "shell", Seat: "node-login-shell", Name: "execute", Fingerprint: "src2:1", Args: json.RawMessage(`{"command":"ls"}`)}
|
||||
if _, err := w.do(c); err == nil || !strings.HasPrefix(err.Error(), "Not run") || len(w.asked) != 1 || w.asked[0].Headline != "Run node-login-shell.execute on n1?" {
|
||||
t.Fatalf("%v, asked %d", err, len(w.asked))
|
||||
}
|
||||
}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) { tc.run(t, newWorld()) })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user