package stdio import ( "bufio" "encoding/json" "errors" "io" "testing" "time" ) // A Go bundle serves as the module the runtime names, answers calls concurrently, and emits through // the runtime with the TypeScript SDK's envelope (novox/hq ADR 0193). func TestABundleEmitsThroughTheRuntimeAndServesAsItsModule(t *testing.T) { t.Setenv(ServedAs, "chime") t.Setenv("MESH_MODULE", "chime") t.Setenv("MESH_NODE", "anchor") inR, inW := io.Pipe() outR, outW := io.Pipe() go func() { _ = ServeOn("", []Tool{{Name: "ring", Description: "rings", Run: func(map[string]any) (any, error) { if err := Emit("rang", map[string]any{"times": 1}); err != nil { return nil, err } return map[string]any{"rang": true}, 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')) } send(map[string]any{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": map[string]any{}}) if got := next()["result"].(map[string]any)["serverInfo"].(map[string]any)["name"]; got != "chime" { t.Fatalf("served as %v, want the module the runtime named", got) } send(map[string]any{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": map[string]any{"name": "ring", "arguments": map[string]any{}}}) asked := next() if asked["method"] != "mesh/publish" { t.Fatalf("the tool's emit was not asked of the runtime: %v", asked) } p := asked["params"].(map[string]any) h := p["headers"].(map[string]any) if p["key"] != "rang" || p["node"] != "anchor" || h["x-source"] != "chime" || h["content-type"] != "application/json" || len(h["x-event-id"].(string)) != 36 { t.Errorf("the envelope is not the SDK's: %v", p) } send(map[string]any{"jsonrpc": "2.0", "id": asked["id"], "result": map[string]any{}}) answered := next() text := answered["result"].(map[string]any)["content"].([]any)[0].(map[string]any)["text"] if answered["id"] != float64(2) || text != `{"rang":true}` { t.Errorf("the call answered %v", answered) } _ = inW.Close() } // A Go bundle subscribes and asks through the runtime (novox/hq ADR 0198): the event is answered only // after the handler took it, a handler's error is answered as one, and a tool call is asked as the module. func TestABundleSubscribesAndAsksThroughTheRuntime(t *testing.T) { t.Setenv(ServedAs, "watcher") inR, inW := io.Pipe() outR, outW := io.Pipe() ready := make(chan struct{}) go func() { _ = ServeOn("", []Tool{{Name: "start", Run: func(map[string]any) (any, error) { go func() { _ = Subscribe("database.*", func(e Envelope) error { if e.Key == "database.bad" { return errors.New("not taken") } return nil }) close(ready) }() answer, err := Ask("postgres.list", map[string]any{}) return json.RawMessage(answer), err }}}, 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')) } send(map[string]any{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": map[string]any{"name": "start"}}) // Two asks arrive, in either order: the subscription and the tool call. asked := map[string]map[string]any{} for len(asked) < 2 { m := next() asked[m["method"].(string)] = m } sub, ask := asked["mesh/subscribe"], asked["mesh/ask"] if sub["params"].(map[string]any)["pattern"] != "database.*" || ask["params"].(map[string]any)["key"] != "postgres.list" { t.Fatalf("asked %v", asked) } send(map[string]any{"jsonrpc": "2.0", "id": sub["id"], "result": map[string]any{}}) send(map[string]any{"jsonrpc": "2.0", "id": ask["id"], "result": []string{"a", "b"}}) if r := next(); r["id"] != float64(1) || r["result"].(map[string]any)["content"].([]any)[0].(map[string]any)["text"] != `["a","b"]` { t.Fatalf("the call answered %v", r) } <-ready send(map[string]any{"jsonrpc": "2.0", "id": 7, "method": "mesh/event", "params": map[string]any{"envelope": map[string]any{"key": "database.provisioned", "node": "anchor", "body": map[string]any{}}}}) if r := next(); r["id"] != float64(7) || r["error"] != nil { t.Errorf("a taken event was answered %v", r) } send(map[string]any{"jsonrpc": "2.0", "id": 8, "method": "mesh/event", "params": map[string]any{"envelope": map[string]any{"key": "database.bad", "node": "anchor", "body": map[string]any{}}}}) if r := next(); r["id"] != float64(8) || r["error"] == nil { t.Errorf("a refused event was answered %v, want an error so the runtime does not acknowledge it", r) } _ = inW.Close() if !TopicMatches("a.#", "a.b.c") || TopicMatches("a.*", "a.b.c") || !TopicMatches("#", "x") { t.Error("topic matching differs from the TypeScript SDK's") } }