186 lines
7.8 KiB
Go
186 lines
7.8 KiB
Go
package stdio
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"io"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/buscalls"
|
|
)
|
|
|
|
// callWith serves one tool that answers the caller CallerOf reads from its arguments, sends it one tools/call
|
|
// with the params given, and returns the caller it answered.
|
|
func callWith(t *testing.T, params map[string]any) string {
|
|
caller, _ := callAnswering(t, params)
|
|
return caller
|
|
}
|
|
|
|
// callAnswering is callWith answering the call's id CallIDOf read too.
|
|
func callAnswering(t *testing.T, params map[string]any) (string, string) {
|
|
servesAlone(t)
|
|
t.Helper()
|
|
inR, inW := io.Pipe()
|
|
outR, outW := io.Pipe()
|
|
go func() {
|
|
_ = ServeOn("tracker", []Tool{{Name: "who", Run: func(args map[string]any) (any, error) {
|
|
return map[string]any{"caller": CallerOf(args), "call": CallIDOf(args)}, nil
|
|
}}}, inR, outW)
|
|
}()
|
|
defer inW.Close()
|
|
b, _ := json.Marshal(map[string]any{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": params})
|
|
_, _ = inW.Write(append(b, '\n'))
|
|
lines := bufio.NewScanner(outR)
|
|
done := make(chan [2]string, 1)
|
|
go func() {
|
|
if lines.Scan() {
|
|
var m struct {
|
|
Result struct {
|
|
Content []struct {
|
|
Text string `json:"text"`
|
|
} `json:"content"`
|
|
} `json:"result"`
|
|
}
|
|
_ = json.Unmarshal(lines.Bytes(), &m)
|
|
var answer struct {
|
|
Caller string `json:"caller"`
|
|
Call string `json:"call"`
|
|
}
|
|
if len(m.Result.Content) > 0 {
|
|
_ = json.Unmarshal([]byte(m.Result.Content[0].Text), &answer)
|
|
}
|
|
done <- [2]string{answer.Caller, answer.Call}
|
|
}
|
|
}()
|
|
select {
|
|
case got := <-done:
|
|
return got[0], got[1]
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("the bundle did not answer the call")
|
|
return "", ""
|
|
}
|
|
}
|
|
|
|
// **A tool reads its caller from the runtime, never from the call's arguments** (novox/hq issue 365): the
|
|
// runtime names the caller it took from the subject the call arrived on in the call's `_meta`, and CallerOf
|
|
// hands it to the tool by the arguments the tool was given.
|
|
func TestAToolReadsTheCallerTheRuntimeNamed(t *testing.T) {
|
|
got := callWith(t, map[string]any{"name": "who", "arguments": map[string]any{"title": "x"},
|
|
"_meta": map[string]any{MetaCaller: "shanks.node-tools"}})
|
|
if got != "shanks.node-tools" {
|
|
t.Fatalf("the tool read caller %q, want the one the runtime named", got)
|
|
}
|
|
// The call's id is the runtime's too (ADR 0597 §7): read from `_meta`, never from the arguments, and none when it
|
|
// is not one token.
|
|
for _, c := range []struct {
|
|
args, meta map[string]any
|
|
want string
|
|
}{
|
|
{map[string]any{}, map[string]any{MetaCaller: "shanks.node-tools", MetaCall: "c-7Hq2"}, "c-7Hq2"},
|
|
{map[string]any{}, map[string]any{MetaCaller: "shanks.node-tools"}, ""},
|
|
{map[string]any{MetaCall: "c-forged"}, map[string]any{}, ""},
|
|
{map[string]any{}, map[string]any{MetaCall: "a b"}, ""},
|
|
{map[string]any{}, map[string]any{MetaCall: 7}, ""},
|
|
} {
|
|
if _, call := callAnswering(t, map[string]any{"name": "who", "arguments": c.args, "_meta": c.meta}); call != c.want {
|
|
t.Errorf("args %v, meta %v: the tool read call %q, want %q", c.args, c.meta, call, c.want)
|
|
}
|
|
}
|
|
if CallIDOf(map[string]any{"x": 1}) != "" || CallIDOf(nil) != "" {
|
|
t.Error("arguments no call was handed have a call id")
|
|
}
|
|
if MetaCall != buscalls.MetaCall {
|
|
t.Errorf("the call id's place is %q here and %q in buscalls", MetaCall, buscalls.MetaCall)
|
|
}
|
|
}
|
|
|
|
// A caller named in the arguments is not the caller: an argument is the caller's to write.
|
|
func TestACallerInTheArgumentsIsNeverTheCaller(t *testing.T) {
|
|
got := callWith(t, map[string]any{"name": "who", "arguments": map[string]any{"caller": "novox.node-tools",
|
|
"_meta": map[string]any{MetaCaller: "novox.node-tools"}, MetaCaller: "novox.node-tools"}})
|
|
if got != Unproven {
|
|
t.Fatalf("a caller the arguments claimed was read as %q, want %q", got, Unproven)
|
|
}
|
|
got = callWith(t, map[string]any{"name": "who", "arguments": map[string]any{"caller": "novox.node-tools"},
|
|
"_meta": map[string]any{MetaCaller: "shanks.node-tools"}})
|
|
if got != "shanks.node-tools" {
|
|
t.Fatalf("with a caller in the arguments the tool read %q, want the runtime's", got)
|
|
}
|
|
// A module's trusted list of callers (issue 774, ADR 0563 §8) grants the callers it names as the runtime proved them:
|
|
// a module on any machine, <machine>.<module> exactly, person.<name> exactly; no wildcard, never one unproven.
|
|
callers, err := ParseCallers([]byte(`{"callers":["node-tools","ace.claude-code","person.jo","*"]}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for caller, want := range map[string]bool{"shanks.node-tools": true, "ace.claude-code": true, "g14.claude-code": false,
|
|
"person.jo": true, "person.node-tools": false, Unproven: false, "": false, "novox.mesh-vault": false} {
|
|
if callers.Allows(caller) != want {
|
|
t.Errorf("%q allowed: %v, want %v", caller, !want, want)
|
|
}
|
|
}
|
|
if _, err := ParseCallers([]byte(`{"callers":["x"],"more":1}`)); err == nil {
|
|
t.Error("a list with a field it does not know was read")
|
|
}
|
|
// A holder's list per verb (ADR 0627 §2): each verb's entries as above, plus every proved caller (AnyCaller) or
|
|
// every operator's session (AnyPerson); an unproven caller never, a verb the holder does not serve refused.
|
|
verbs, err := ParseVerbCallers(`{"publish":["mesh-build-go"],"versions":["*"],"release":["operator","sweep"]}`,
|
|
[]string{"publish", "versions", "release", "remove"}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, c := range []struct {
|
|
verb, caller string
|
|
want bool
|
|
}{{"publish", "ace.mesh-build-go", true}, {"publish", "ace.mesh-build-node", false}, {"versions", "g14.anything", true},
|
|
{"versions", Unproven, false}, {"release", "person.jo", true}, {"release", "ace.operator", false},
|
|
{"release", "novox.sweep", true}, {"remove", "novox.sweep", false}} {
|
|
if got := verbs.Allowed(c.verb, c.caller) == nil; got != c.want {
|
|
t.Errorf("%s by %q allowed: %v, want %v", c.verb, c.caller, got, c.want)
|
|
}
|
|
}
|
|
if _, err := ParseVerbCallers(`{"mirror":["x"]}`, []string{"publish"}, nil); err == nil {
|
|
t.Error("a list naming a verb the holder does not serve was read")
|
|
}
|
|
if d, _ := ParseVerbCallers(" ", nil, VerbCallers{"publish": {"x"}}); len(d["publish"]) != 1 {
|
|
t.Error("an empty value is not the holder's defaults")
|
|
}
|
|
// As mesh-config resolves the value: the object itself, or written as a string; null is the defaults.
|
|
for _, raw := range []string{`{"publish":["a"]}`, `"{\"publish\":[\"a\"]}"`, `null`} {
|
|
c, err := parseConfiguredVerbCallers(json.RawMessage(raw), []string{"publish"}, VerbCallers{"publish": {"a"}})
|
|
if err != nil || c.Allowed("publish", "ace.a") != nil {
|
|
t.Errorf("the config value %s read as %v, %v", raw, c, err)
|
|
}
|
|
}
|
|
if ModuleOf("ace.mesh-build-go") != "mesh-build-go" {
|
|
t.Error("ModuleOf does not read the module of a proved caller")
|
|
}
|
|
}
|
|
|
|
// A runtime that names no caller — one from before issue 365, or a call on the subject that names none — is
|
|
// said as Unproven, explicitly; a caller that is not a bus user's name is Unproven too.
|
|
func TestACallTheRuntimeNamedNoCallerForIsUnproven(t *testing.T) {
|
|
for _, params := range []map[string]any{
|
|
{"name": "who", "arguments": map[string]any{}},
|
|
{"name": "who"},
|
|
{"name": "who", "arguments": map[string]any{}, "_meta": map[string]any{}},
|
|
{"name": "who", "arguments": map[string]any{}, "_meta": map[string]any{MetaCaller: ""}},
|
|
{"name": "who", "arguments": map[string]any{}, "_meta": map[string]any{MetaCaller: "a b"}},
|
|
{"name": "who", "arguments": map[string]any{}, "_meta": map[string]any{MetaCaller: 7}},
|
|
} {
|
|
if got := callWith(t, params); got != Unproven {
|
|
t.Errorf("%v: the tool read caller %q, want %q", params, got, Unproven)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Arguments the runtime never handed a tool have no caller.
|
|
func TestArgumentsOfNoCallHaveNoCaller(t *testing.T) {
|
|
if got := CallerOf(map[string]any{"x": 1}); got != Unproven {
|
|
t.Fatalf("CallerOf of a map no call was given answered %q", got)
|
|
}
|
|
if got := CallerOf(nil); got != Unproven {
|
|
t.Fatalf("CallerOf(nil) answered %q", got)
|
|
}
|
|
}
|