返回 DeepSeek-Reasonix
mcpproxy_test.go
根目录 / internal / extension / mcpproxy_test.go
1 package extension
2
3 import (
4 "context"
5 "sync/atomic"
6 "testing"
7 )
8
9 type fakeMCP struct {
10 id string
11 closed atomic.Bool
12 calls atomic.Int32
13 }
14
15 func (m *fakeMCP) ID() string { return m.id }
16 func (m *fakeMCP) Close(context.Context) error {
17 m.closed.Store(true)
18 return nil
19 }
20 func (m *fakeMCP) Call(context.Context, string, []byte) ([]byte, error) {
21 m.calls.Add(1)
22 return []byte(`{"ok":true}`), nil
23 }
24
25 func TestMCPProxyRollingReplace(t *testing.T) {
26 p := NewMCPProxy("fs")
27 a := &fakeMCP{id: "a"}
28 b := &fakeMCP{id: "b"}
29 if err := p.Replace(context.Background(), a, 1); err != nil {
30 t.Fatal(err)
31 }
32 if _, err := p.Call(context.Background(), "read", nil); err != nil {
33 t.Fatal(err)
34 }
35 if a.calls.Load() != 1 {
36 t.Fatalf("calls = %d", a.calls.Load())
37 }
38 if err := p.Replace(context.Background(), b, 2); err != nil {
39 t.Fatal(err)
40 }
41 if !a.closed.Load() {
42 t.Fatal("previous MCP backend not drained")
43 }
44 if _, err := p.Call(context.Background(), "read", nil); err != nil {
45 t.Fatal(err)
46 }
47 if b.calls.Load() != 1 || p.Generation() != 2 {
48 t.Fatalf("backend b not active: calls=%d gen=%d", b.calls.Load(), p.Generation())
49 }
50 }
51
52 func TestMCPProxyFailFastWithoutBackend(t *testing.T) {
53 p := NewMCPProxy("empty")
54 if _, err := p.Call(context.Background(), "x", nil); err == nil {
55 t.Fatal("expected fail-fast")
56 }
57 }
58
58 lines GO