The seat verb approvals is the gate's own, served wherever a marked verb is: the runner answers it by caller from the calls its gate holds, with no bundle naming it; a marked verb already reaches the gate by caller. Tested with zsh's node-login-shell execute as the case. Builds: design 47 G §677, §697; lead answers A-264, A-265, A-266, A-271 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
80 lines
3.4 KiB
Go
80 lines
3.4 KiB
Go
package runtime
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/approval"
|
|
"git.novox.be/novox/mesh-sdk/go/seatdef"
|
|
|
|
"github.com/novox/mesh-tools/node-tools/internal/bus"
|
|
mt "github.com/novox/mesh-tools/node-tools/internal/meshtest"
|
|
)
|
|
|
|
// gateHolding is the runner's gate as far as the runtime reaches it: it records the calls it is run for and answers
|
|
// `approvals` from them.
|
|
type gateHolding struct{ calls []approval.Call }
|
|
|
|
func (g *gateHolding) Unserved(string, string, []string) []string { return nil }
|
|
func (g *gateHolding) Run(c approval.Call, run func() (any, error)) (any, error) {
|
|
g.calls = append(g.calls, c)
|
|
return run()
|
|
}
|
|
func (g *gateHolding) Approvals(caller string) (approval.Approvals, error) {
|
|
out := approval.Approvals{Calls: []approval.ListedCall{}, Acts: []approval.ListedAct{}, Claims: []string{}}
|
|
for i, c := range g.calls {
|
|
l := approval.ListedCall{Call: string(rune('a' + i)), Caller: c.Caller, Verb: c.Seat + "." + c.Name, State: "waiting"}
|
|
if caller == c.Caller {
|
|
l.Arguments = c.Args
|
|
}
|
|
out.Calls = append(out.Calls, l)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// **The tool runner answers a held seat's marked verb by caller and serves the seat's `approvals`** (design 47 G §677; lead
|
|
// answers A-265, A-271), with zsh's node-login-shell `execute` as the case: the marked verb reaches the gate with the caller
|
|
// its subject names, and `approvals` is answered from the runner's own record of the calls its gate holds, with the
|
|
// arguments for the call's own caller only, though no bundle names the verb.
|
|
func TestTheRunnerServesApprovalsForTheSeatsItsBundlesHold(t *testing.T) {
|
|
mesh := mt.New(t)
|
|
mesh.Issue(t, mt.MembershipOf("zsh", "anchor", true, map[string][]string{"node-login-shell": {"execute", seatdef.ApprovalsVerb}}))
|
|
nodeTools := connect(t, "node-tools", "anchor")
|
|
gate := &gateHolding{}
|
|
defined := WithSeatDefinitions(func() map[string]seatdef.Definition {
|
|
return map[string]seatdef.Definition{"node-login-shell": {Seat: "node-login-shell", Verbs: []seatdef.Verb{
|
|
{Name: "execute", NeedsApproval: true}, {Name: seatdef.ApprovalsVerb}}}}
|
|
})
|
|
stop, err := Run(nodeTools, []Served{{"zsh", []string{mt.Fixture("login-shell-execute.py")}}}, nil, func(string, ...any) {}, defined, WithGate(gate))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer stop()
|
|
asker, err := bus.Connect(bus.Credential{URL: mt.URL(t), Module: "node-tools", Node: "shanks", User: "shanks.node-tools", Password: "unused"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
asker.Logf = func(string, ...any) {}
|
|
defer asker.Close()
|
|
|
|
mt.Until(t, func() error {
|
|
_, err := asker.Ask("seat:node-login-shell.execute@anchor", map[string]any{"command": "ls"}, "")
|
|
return err
|
|
})
|
|
if len(gate.calls) != 1 || gate.calls[0].Seat != "node-login-shell" || gate.calls[0].Name != "execute" || gate.calls[0].Caller != "shanks.node-tools" {
|
|
t.Fatalf("the marked verb reached the gate as %+v, want the seat's execute by shanks.node-tools", gate.calls)
|
|
}
|
|
var got approval.Approvals
|
|
mt.Until(t, func() error {
|
|
answered, err := asker.Ask("seat:node-login-shell.approvals@anchor", map[string]any{}, "")
|
|
if err == nil {
|
|
err = json.Unmarshal(answered.Result, &got)
|
|
}
|
|
return err
|
|
})
|
|
if len(got.Calls) != 1 || got.Calls[0].Caller != "shanks.node-tools" || got.Calls[0].Verb != "node-login-shell.execute" ||
|
|
string(got.Calls[0].Arguments) != `{"command":"ls"}` {
|
|
t.Fatalf("approvals answered %+v, want the one call the gate holds, with its caller's arguments", got)
|
|
}
|
|
}
|