Skip to content

Commit b8297f5

Browse files
committed
More tests
1 parent 0e185e5 commit b8297f5

File tree

2 files changed

+1077
-0
lines changed

2 files changed

+1077
-0
lines changed

client/client_edge_cases_test.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/mark3labs/mcp-go/client/transport"
9+
"github.com/mark3labs/mcp-go/mcp"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
// TestClient_UnsupportedProtocolVersionResponse tests that client rejects unsupported protocol versions
15+
func TestClient_UnsupportedProtocolVersionResponse(t *testing.T) {
16+
// Create mock transport
17+
mockTrans := newMockTransport()
18+
19+
// Create client
20+
client := &Client{
21+
transport: mockTrans,
22+
}
23+
24+
ctx := context.Background()
25+
err := client.Start(ctx)
26+
require.NoError(t, err)
27+
28+
// Server responds with an unsupported/invalid protocol version
29+
initResponse := transport.NewJSONRPCResultResponse(
30+
mcp.NewRequestId(1),
31+
[]byte(`{"protocolVersion":"9999-99-99","capabilities":{},"serverInfo":{"name":"test-server","version":"1.0.0"}}`),
32+
)
33+
34+
go func() {
35+
mockTrans.responseChan <- initResponse
36+
}()
37+
38+
initRequest := mcp.InitializeRequest{
39+
Params: mcp.InitializeParams{
40+
ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION,
41+
ClientInfo: mcp.Implementation{
42+
Name: "test-client",
43+
Version: "1.0.0",
44+
},
45+
},
46+
}
47+
48+
_, err = client.Initialize(ctx, initRequest)
49+
require.Error(t, err)
50+
51+
// Should be an UnsupportedProtocolVersionError
52+
var unsupportedErr mcp.UnsupportedProtocolVersionError
53+
assert.ErrorAs(t, err, &unsupportedErr)
54+
assert.Equal(t, "9999-99-99", unsupportedErr.Version)
55+
}
56+
57+
// TestClient_OperationsBeforeInitialize tests operations fail before initialization
58+
func TestClient_OperationsBeforeInitialize(t *testing.T) {
59+
mockTrans := newMockTransport()
60+
client := &Client{
61+
transport: mockTrans,
62+
}
63+
64+
ctx := context.Background()
65+
err := client.Start(ctx)
66+
require.NoError(t, err)
67+
68+
// Try to send request before initialization
69+
err = client.Ping(ctx)
70+
require.Error(t, err)
71+
assert.Contains(t, err.Error(), "not initialized")
72+
73+
// List tools should also fail
74+
_, err = client.ListTools(ctx, mcp.ListToolsRequest{})
75+
require.Error(t, err)
76+
assert.Contains(t, err.Error(), "not initialized")
77+
78+
// List resources should also fail
79+
_, err = client.ListResources(ctx, mcp.ListResourcesRequest{})
80+
require.Error(t, err)
81+
assert.Contains(t, err.Error(), "not initialized")
82+
}
83+
84+
// TestClient_NotificationHandlers tests notification handler behavior
85+
func TestClient_NotificationHandlers(t *testing.T) {
86+
t.Run("multiple handlers called in order", func(t *testing.T) {
87+
mockTrans := newMockTransport()
88+
client := &Client{
89+
transport: mockTrans,
90+
}
91+
92+
ctx := context.Background()
93+
err := client.Start(ctx)
94+
require.NoError(t, err)
95+
96+
var callOrder []int
97+
var handlerCalls int
98+
99+
// Register multiple handlers
100+
for i := 0; i < 3; i++ {
101+
handlerID := i
102+
client.OnNotification(func(notification mcp.JSONRPCNotification) {
103+
callOrder = append(callOrder, handlerID)
104+
handlerCalls++
105+
})
106+
}
107+
108+
// Simulate notification via the handler
109+
notif := mcp.JSONRPCNotification{
110+
JSONRPC: mcp.JSONRPC_VERSION,
111+
Notification: mcp.Notification{
112+
Method: "test-method",
113+
},
114+
}
115+
116+
// Manually trigger the handlers we registered on the client
117+
// Access them through the read lock
118+
client.notifyMu.RLock()
119+
handlers := make([]func(mcp.JSONRPCNotification), len(client.notifications))
120+
copy(handlers, client.notifications)
121+
client.notifyMu.RUnlock()
122+
123+
for _, h := range handlers {
124+
h(notif)
125+
}
126+
127+
// Wait a bit for handlers to execute
128+
time.Sleep(50 * time.Millisecond)
129+
130+
// All handlers should have been called in order
131+
assert.Equal(t, []int{0, 1, 2}, callOrder)
132+
assert.Equal(t, 3, handlerCalls)
133+
})
134+
}
135+
136+
// TestClient_GetSessionId tests session ID retrieval
137+
func TestClient_GetSessionId(t *testing.T) {
138+
mockTrans := newMockTransport()
139+
client := &Client{
140+
transport: mockTrans,
141+
}
142+
143+
// Should return the transport's session ID
144+
sessionID := client.GetSessionId()
145+
assert.Equal(t, "mock-session-id", sessionID)
146+
}
147+
148+
// TestClient_IsInitialized tests initialization state tracking
149+
func TestClient_IsInitialized(t *testing.T) {
150+
mockTrans := newMockTransport()
151+
client := &Client{
152+
transport: mockTrans,
153+
}
154+
155+
// Should not be initialized initially
156+
assert.False(t, client.IsInitialized())
157+
158+
ctx := context.Background()
159+
err := client.Start(ctx)
160+
require.NoError(t, err)
161+
162+
// Still not initialized after Start
163+
assert.False(t, client.IsInitialized())
164+
165+
// Initialize
166+
initResponse := transport.NewJSONRPCResultResponse(
167+
mcp.NewRequestId(1),
168+
[]byte(`{"protocolVersion":"2025-03-26","capabilities":{},"serverInfo":{"name":"test-server","version":"1.0.0"}}`),
169+
)
170+
go func() {
171+
mockTrans.responseChan <- initResponse
172+
mockTrans.responseChan <- transport.NewJSONRPCResultResponse(mcp.NewRequestId(2), []byte(`{}`))
173+
}()
174+
175+
_, err = client.Initialize(ctx, mcp.InitializeRequest{
176+
Params: mcp.InitializeParams{
177+
ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION,
178+
ClientInfo: mcp.Implementation{
179+
Name: "test-client",
180+
Version: "1.0.0",
181+
},
182+
},
183+
})
184+
require.NoError(t, err)
185+
186+
// Should be initialized now
187+
assert.True(t, client.IsInitialized())
188+
}

0 commit comments

Comments
 (0)