Where serveSeats answers a seat's verb it reads the verb from the runner's seat list (seatdef's EffectiveCallers): an unproven caller is refused, and every caller while the seat's definition is unread, names no such verb or opens it to none; a proven caller is among them, since the grants part composes the call subject only for the effective callers. A module's tool on the subject naming no caller is unchanged. Builds: design 47 G §610; lead answers A-165, A-179 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
130 lines
5.5 KiB
Go
130 lines
5.5 KiB
Go
package runtime
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/seatdef"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
|
|
"github.com/novox/mesh-tools/node-tools/internal/bus"
|
|
mt "github.com/novox/mesh-tools/node-tools/internal/meshtest"
|
|
)
|
|
|
|
type saidCaller struct {
|
|
Caller string `json:"caller"`
|
|
Call string `json:"call"`
|
|
Arguments map[string]any `json:"arguments"`
|
|
}
|
|
|
|
// **A module is told who called it, from the subject the call arrived on** (novox/hq issue 365): a call on the
|
|
// caller-named subject reaches the bundle with that caller in the call's `_meta`, for a module's tool, on its
|
|
// machine's address, and for a seat's verb; a caller named in the arguments is only an argument.
|
|
func TestACallOnTheCallerNamedSubjectReachesTheModuleWithItsCaller(t *testing.T) {
|
|
mesh := mt.New(t)
|
|
mesh.Issue(t, mt.MembershipOf("ledger", "anchor", true, map[string][]string{"node-desk": {"who"}}))
|
|
nodeTools := connect(t, "node-tools", "anchor")
|
|
stop, err := Run(nodeTools, []Served{{"ledger", []string{mt.Fixture("caller-echo.py")}}}, nil, func(string, ...any) {}, deskDefined)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer stop() // returns once the bundle's exit is handled (novox/hq issue 476)
|
|
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()
|
|
|
|
calls := map[string]bool{}
|
|
for _, key := range []string{"ledger.who", "ledger.who@anchor", "seat:node-desk.who@anchor"} {
|
|
var got saidCaller
|
|
mt.Until(t, func() error {
|
|
answered, err := asker.Ask(key, map[string]any{"caller": "novox.node-tools",
|
|
"_meta": map[string]any{"mesh/caller": "novox.controller"}}, "")
|
|
if err == nil {
|
|
err = json.Unmarshal(answered.Result, &got)
|
|
}
|
|
return err
|
|
})
|
|
if got.Caller != "shanks.node-tools" {
|
|
t.Errorf("%s reached the module with caller %q, want the asker's bus user", key, got.Caller)
|
|
}
|
|
// And the id of the call in flight, one per call, which a ledger's apply names (novox/hq ADR 0597 §7).
|
|
if got.Call == "" || calls[got.Call] {
|
|
t.Errorf("%s reached the module with call id %q, want one of its own", key, got.Call)
|
|
}
|
|
calls[got.Call] = true
|
|
if got.Arguments["caller"] != "novox.node-tools" {
|
|
t.Errorf("%s: the arguments did not reach the module as sent: %v", key, got.Arguments)
|
|
}
|
|
}
|
|
}
|
|
|
|
// deskDefined is the seat list's definition of the fixture seat node-desk, its verb who taking its class's default.
|
|
var deskDefined = WithSeatDefinitions(func() map[string]seatdef.Definition {
|
|
return map[string]seatdef.Definition{"node-desk": {Seat: "node-desk", Verbs: []seatdef.Verb{{Name: "who"}}}}
|
|
})
|
|
|
|
// The subject that names no caller is still served for a module's tool, for one release (retired by hq issue 464), and says
|
|
// so: the module is named no caller, whatever the body claims. A seat's verb refuses it before the bundle runs (design 47 G
|
|
// §610; lead answers A-165, A-179), and refuses every caller while the seat's definition is unread, names no such verb or
|
|
// opens it to none.
|
|
func TestTheSubjectThatNamesNoCallerStillWorksAndNamesNone(t *testing.T) {
|
|
mesh := mt.New(t)
|
|
mesh.Issue(t, mt.MembershipOf("ledger", "anchor", true, map[string][]string{"node-desk": {"who"}}))
|
|
nodeTools := connect(t, "node-tools", "anchor")
|
|
stop, err := Run(nodeTools, []Served{{"ledger", []string{mt.Fixture("caller-echo.py")}}}, nil, func(string, ...any) {}, deskDefined)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer stop() // returns once the bundle's exit is handled (novox/hq issue 476)
|
|
old, err := nats.Connect(mt.URL(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer old.Close()
|
|
body, _ := json.Marshal(map[string]any{"_meta": map[string]any{"mesh/caller": "novox.controller"}})
|
|
for _, subject := range []string{"mesh.mod.ledger.tool.who", "mesh.mod.ledger.tool.who.anchor", "mesh.seat.node-desk.tool.who.anchor"} {
|
|
var reply struct {
|
|
Result saidCaller `json:"result"`
|
|
Error string `json:"error"`
|
|
}
|
|
mt.Until(t, func() error {
|
|
msg, err := old.Request(subject, body, 5*time.Second)
|
|
if err == nil {
|
|
err = json.Unmarshal(msg.Data, &reply)
|
|
}
|
|
return err
|
|
})
|
|
if strings.HasPrefix(subject, "mesh.seat.") {
|
|
if !strings.Contains(reply.Error, "node-desk.who takes no unproven caller") || reply.Result.Call != "" {
|
|
t.Errorf("%s: an unproven caller of a seat's verb was answered %+v (error %q), want refused before the bundle", subject, reply.Result, reply.Error)
|
|
}
|
|
continue
|
|
}
|
|
if reply.Error != "" || reply.Result.Caller != "" {
|
|
t.Errorf("%s: the module was named caller %q (error %q), want none", subject, reply.Result.Caller, reply.Error)
|
|
}
|
|
}
|
|
for _, c := range []struct {
|
|
defs map[string]seatdef.Definition
|
|
says string
|
|
}{
|
|
{nil, "is not yet read"},
|
|
{map[string]seatdef.Definition{"node-desk": {Seat: "node-desk"}}, "names no verb who"},
|
|
{map[string]seatdef.Definition{"node-desk": {Seat: "node-desk", Verbs: []seatdef.Verb{{Name: "who", Callers: &seatdef.Callers{List: []string{}}}}}}, "open to no caller"},
|
|
{map[string]seatdef.Definition{"node-desk": {Seat: "node-desk", Verbs: []seatdef.Verb{{Name: "who", Class: seatdef.ClassAct}}}}, ""},
|
|
} {
|
|
defs := c.defs
|
|
err := (&consumers{definitions: func() map[string]seatdef.Definition { return defs }}).admitted("node-desk", "who", "shanks.node-tools")
|
|
if c.says == "" && err != nil || c.says != "" && (err == nil || !strings.Contains(err.Error(), c.says)) {
|
|
t.Errorf("a proven caller with the definitions %v: %v, want %q", c.defs, err, c.says)
|
|
}
|
|
}
|
|
}
|