package stdio import ( "bufio" "encoding/json" "io" "testing" "time" ) // 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 { 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)}, 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 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"` } if len(m.Result.Content) > 0 { _ = json.Unmarshal([]byte(m.Result.Content[0].Text), &answer) } done <- answer.Caller } }() select { case got := <-done: return got 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) } } // 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 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) } }