Why: token budget let the current tool batch run, compaction could split a tool pair or race an append, writes were not crash-atomic, and several MCP and session edges only existed as comments. Evidence: GOWORK=off go test -count=1 -race ./runtime/ ./runtime/llm/ ./runtime/session/ ./runtime/tools/ ./runtime/mcp/ ./adapters/native/ ./adapters/intentgate/ passed, including the TestR2* cases for budget, replacement, cancel, compaction, SSE, UTF-8, schema, MCP retry, session terminate, poison reload, and registry reload. Impact: over-budget tool calls are skipped and paired; Stop.Replacement is appended to the session; the intent gate can follow Registry.Reload. Refs: N5, N6, N7, N8, N9, N11, N12, N15, N16, N17, N18, N19, N21, I9 Co-authored-by: Cursor <cursoragent@cursor.com>
67 lines
1.8 KiB
Go
67 lines
1.8 KiB
Go
package mcp
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
type flakyClient struct {
|
|
fail bool
|
|
calls int
|
|
}
|
|
|
|
func (f *flakyClient) Initialize(context.Context) (initializeResult, error) {
|
|
return initializeResult{}, nil
|
|
}
|
|
func (f *flakyClient) ListTools(context.Context) ([]toolInfo, error) { return nil, nil }
|
|
func (f *flakyClient) Close() error { return nil }
|
|
func (f *flakyClient) CallTool(context.Context, string, json.RawMessage) (callToolResult, error) {
|
|
f.calls++
|
|
if f.fail {
|
|
return callToolResult{}, fmt.Errorf("down")
|
|
}
|
|
return callToolResult{Content: []contentBlock{{Type: "text", Text: "back"}}}, nil
|
|
}
|
|
|
|
func TestR2MCPRetriesOnceAfterReconnect(t *testing.T) {
|
|
first := &flakyClient{fail: true}
|
|
second := &flakyClient{}
|
|
tool := &bridgeTool{
|
|
client: first,
|
|
toolName: "echo",
|
|
declName: "mcp__s__echo",
|
|
redial: func(context.Context) (Client, error) {
|
|
return second, nil
|
|
},
|
|
}
|
|
res, err := tool.Execute(context.Background(), []byte(`{}`))
|
|
if err != nil || res.IsError {
|
|
t.Fatalf("retry: %+v %v", res, err)
|
|
}
|
|
if first.calls != 1 || second.calls != 1 || !strings.Contains(res.Content, "back") {
|
|
t.Fatalf("calls first=%d second=%d content=%q", first.calls, second.calls, res.Content)
|
|
}
|
|
}
|
|
|
|
func TestR2HTTPCloseTerminatesSession(t *testing.T) {
|
|
var method, sid string
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
method = r.Method
|
|
sid = r.Header.Get("Mcp-Session-Id")
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer srv.Close()
|
|
c := &httpClient{cfg: ServerConfig{URL: srv.URL}, client: &http.Client{}, sessionID: "sess-1"}
|
|
if err := c.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if method != http.MethodDelete || sid != "sess-1" {
|
|
t.Fatalf("terminate %s session %q", method, sid)
|
|
}
|
|
}
|