package stdio import ( "bufio" "encoding/json" "io" "strings" "testing" "time" ) // A Go bundle reads its values at runtime through the runtime (novox/hq ADR 0349 ยง14, ADR 0370, issue 685): Config, // Bound and their watches ask the runtime, which asks as the module; a watch's value now is handled before it // returns and each change after is named by the watch's id; what the module does not declare and every secret come // back refused, in the runtime's words; a change for no watch of this bundle is refused. func TestABundleReadsAndWatchesItsValuesThroughTheRuntime(t *testing.T) { t.Setenv(ServedAs, "claude-code") inR, inW := io.Pipe() outR, outW := io.Pipe() seen := make(chan string, 16) watched := make(chan error, 1) answers := make(chan string, 4) go func() { _ = ServeOn("", []Tool{ {Name: "start", Run: func(map[string]any) (any, error) { go func() { watched <- WatchConfig("managed_settings", func(c ProvisionChange) error { seen <- c.Kind + " " + c.Key + " " + string(c.Value) + c.Refused return nil }) }() return "started", nil }}, {Name: "read", Run: func(a map[string]any) (any, error) { var v json.RawMessage var err error switch a["what"] { case "config": v, err = Config(a["key"].(string)) case "bound": v, err = Bound("mcp-endpoint", "url") case "secret": var s []byte s, err = Secret("gitea-read") v = json.RawMessage(s) } if err != nil { answers <- "refused: " + err.Error() } else { answers <- string(v) } return "read", nil }}, }, inR, outW) }() lines := bufio.NewScanner(outR) next := func() map[string]any { done := make(chan map[string]any, 1) go func() { if lines.Scan() { var m map[string]any _ = json.Unmarshal(lines.Bytes(), &m) done <- m } }() select { case m := <-done: return m case <-time.After(3 * time.Second): t.Fatal("the bundle said nothing") return nil } } send := func(m map[string]any) { b, _ := json.Marshal(m); _, _ = inW.Write(append(b, '\n')) } // asked is the bundle's next ask of the runtime, beside its answers to this tool call and those before. asked := func(callID int) map[string]any { t.Helper() var ask map[string]any for ask == nil { m := next() if m["method"] != nil { ask = m } else if id, ok := m["id"].(float64); !ok || id > float64(callID) || m["error"] != nil { t.Fatalf("said %v", m) } } return ask } // The watch: asked of the runtime with the kind and the key and no module, the module being the runtime's word. send(map[string]any{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": map[string]any{"name": "start"}}) watch := asked(1) if watch["method"] != "mesh/provision.watch" { t.Fatalf("asked %v", watch) } if p := watch["params"].(map[string]any); p["kind"] != "config" || p["key"] != "managed_settings" || len(p) != 2 { t.Fatalf("asked to watch %v", p) } send(map[string]any{"jsonrpc": "2.0", "id": 50, "method": "mesh/provision", "params": map[string]any{"watch": watch["id"], "change": map[string]any{"kind": "config", "key": "managed_settings", "value": map[string]any{"spinnerTipsEnabled": false}}}}) for { if r := next(); r["id"] == float64(50) { if r["error"] != nil { t.Fatalf("the value now was answered %v", r) } break } } if s := <-seen; s != `config managed_settings {"spinnerTipsEnabled":false}` { t.Fatalf("handled %q", s) } select { case err := <-watched: t.Fatalf("the watch returned before it was answered: %v", err) default: } send(map[string]any{"jsonrpc": "2.0", "id": watch["id"], "result": map[string]any{}}) if err := <-watched; err != nil { t.Fatal(err) } // A change, and then the value no longer answered. send(map[string]any{"jsonrpc": "2.0", "id": 51, "method": "mesh/provision", "params": map[string]any{"watch": watch["id"], "change": map[string]any{"kind": "config", "key": "managed_settings", "value": map[string]any{"spinnerTipsEnabled": true}}}}) if r := next(); r["id"] != float64(51) || r["error"] != nil { t.Fatalf("a change was answered %v", r) } if s := <-seen; s != `config managed_settings {"spinnerTipsEnabled":true}` { t.Fatalf("handled %q", s) } send(map[string]any{"jsonrpc": "2.0", "id": 52, "method": "mesh/provision", "params": map[string]any{"watch": watch["id"], "change": map[string]any{"kind": "config", "key": "managed_settings", "refused": "claude-code is not placed on g14"}}}) if r := next(); r["id"] != float64(52) || r["error"] != nil { t.Fatalf("a refusal was answered %v", r) } if s := <-seen; s != `config managed_settings claude-code is not placed on g14` { t.Fatalf("handled %q", s) } // A change for a watch this bundle never asked for is refused. send(map[string]any{"jsonrpc": "2.0", "id": 53, "method": "mesh/provision", "params": map[string]any{"watch": "bundle-999", "change": map[string]any{"kind": "config", "key": "x"}}}) if r := next(); r["id"] != float64(53) || !strings.Contains(r["error"].(map[string]any)["message"].(string), "names no watch") { t.Fatalf("a change for no watch was answered %v", r) } // Each reader asks the runtime, and hands back its answer or its refusal. for i, c := range []struct { what, key, kind, askedKey string result any refusal string want string }{ {what: "config", key: "role", kind: "config", askedKey: "role", result: "the build machine", want: `"the build machine"`}, {what: "config", key: "nothing", kind: "config", askedKey: "nothing", refusal: "claude-code declares no config value nothing", want: "refused: claude-code declares no config value nothing"}, {what: "bound", kind: "bound", askedKey: "mcp-endpoint:url", result: "http://127.0.0.1:9000", want: `"http://127.0.0.1:9000"`}, {what: "secret", kind: "secret", askedKey: "gitea-read", refusal: "the vault hands a secret only to a caller the bus proves", want: "refused: the vault hands a secret only to a caller the bus proves"}, } { id := 10 + i send(map[string]any{"jsonrpc": "2.0", "id": id, "method": "tools/call", "params": map[string]any{"name": "read", "arguments": map[string]any{"what": c.what, "key": c.key}}}) get := asked(id) if p := get["params"].(map[string]any); get["method"] != "mesh/provision.get" || p["kind"] != c.kind || p["key"] != c.askedKey || len(p) != 2 { t.Fatalf("%s: asked %v", c.what, get) } if c.refusal != "" { send(map[string]any{"jsonrpc": "2.0", "id": get["id"], "error": map[string]any{"code": -32000, "message": c.refusal}}) } else { send(map[string]any{"jsonrpc": "2.0", "id": get["id"], "result": c.result}) } if got := <-answers; got != c.want { t.Errorf("%s %s: %q, want %q", c.what, c.key, got, c.want) } } _ = inW.Close() }