Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

91 lines
2.2 KiB
Go

package browser
import (
"context"
"encoding/json"
"sync"
"testing"
"time"
agentruntime "github.com/tutti-os/tutti/packages/agent/daemon/runtime"
)
type lockedScriptedConn struct {
mu sync.Mutex
frames chan agentruntime.ProcessFrame
sent [][]byte
}
func newLockedScriptedConn() *lockedScriptedConn {
return &lockedScriptedConn{frames: make(chan agentruntime.ProcessFrame, 64)}
}
func (c *lockedScriptedConn) push(v map[string]any) {
data, _ := json.Marshal(v)
c.frames <- agentruntime.ProcessFrame{Stdout: append(data, '\n')}
}
func (c *lockedScriptedConn) Send(data []byte) error {
c.mu.Lock()
c.sent = append(c.sent, append([]byte(nil), data...))
c.mu.Unlock()
var msg map[string]any
if err := json.Unmarshal(data, &msg); err != nil {
return err
}
method, _ := msg["method"].(string)
id := msg["id"]
switch method {
case "initialize":
c.push(map[string]any{"jsonrpc": "2.0", "id": id, "result": map[string]any{
"protocolVersion": mcpProtocolVersion, "capabilities": map[string]any{},
}})
case "notifications/initialized":
return nil
default:
c.push(map[string]any{"jsonrpc": "2.0", "id": id, "result": map[string]any{
"isError": false,
"content": []map[string]any{{"type": "text", "text": "ok"}},
}})
}
return nil
}
func (c *lockedScriptedConn) Recv() (agentruntime.ProcessFrame, error) {
frame, ok := <-c.frames
if !ok {
return agentruntime.ProcessFrame{}, context.Canceled
}
return frame, nil
}
func (c *lockedScriptedConn) Close() error { close(c.frames); return nil }
func TestMCPClientConcurrentSendUsesWriteLock(t *testing.T) {
conn := newLockedScriptedConn()
client := newMCPClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
var wg sync.WaitGroup
for i := 0; i < 8; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_, _ = client.call(ctx, "tools/call", map[string]any{"name": "list_pages"})
}()
}
wg.Wait()
for _, frame := range conn.sent {
if len(frame) == 0 || frame[len(frame)-1] != '\n' {
t.Fatalf("corrupt frame without trailing newline: %q", frame)
}
var msg map[string]any
if err := json.Unmarshal(frame[:len(frame)-1], &msg); err != nil {
t.Fatalf("invalid json frame: %v (%q)", err, frame)
}
}
}