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