@@ -2,6 +2,7 @@ package server
2
2
3
3
import (
4
4
"context"
5
+ "errors"
5
6
"testing"
6
7
"time"
7
8
@@ -32,41 +33,41 @@ func (f sessionTestClient) Initialized() bool {
32
33
return f .initialized
33
34
}
34
35
35
- // fakeSessionWithTools implements the SessionWithTools interface for testing
36
- type fakeSessionWithTools struct {
36
+ // sessionTestClientWithTools implements the SessionWithTools interface for testing
37
+ type sessionTestClientWithTools struct {
37
38
sessionID string
38
39
notificationChannel chan mcp.JSONRPCNotification
39
40
initialized bool
40
41
sessionTools map [string ]ServerTool
41
42
}
42
43
43
- func (f * fakeSessionWithTools ) SessionID () string {
44
+ func (f * sessionTestClientWithTools ) SessionID () string {
44
45
return f .sessionID
45
46
}
46
47
47
- func (f * fakeSessionWithTools ) NotificationChannel () chan <- mcp.JSONRPCNotification {
48
+ func (f * sessionTestClientWithTools ) NotificationChannel () chan <- mcp.JSONRPCNotification {
48
49
return f .notificationChannel
49
50
}
50
51
51
- func (f * fakeSessionWithTools ) Initialize () {
52
+ func (f * sessionTestClientWithTools ) Initialize () {
52
53
f .initialized = true
53
54
}
54
55
55
- func (f * fakeSessionWithTools ) Initialized () bool {
56
+ func (f * sessionTestClientWithTools ) Initialized () bool {
56
57
return f .initialized
57
58
}
58
59
59
- func (f * fakeSessionWithTools ) GetSessionTools () map [string ]ServerTool {
60
+ func (f * sessionTestClientWithTools ) GetSessionTools () map [string ]ServerTool {
60
61
return f .sessionTools
61
62
}
62
63
63
- func (f * fakeSessionWithTools ) SetSessionTools (tools map [string ]ServerTool ) {
64
+ func (f * sessionTestClientWithTools ) SetSessionTools (tools map [string ]ServerTool ) {
64
65
f .sessionTools = tools
65
66
}
66
67
67
68
// Verify that both implementations satisfy their respective interfaces
68
69
var _ ClientSession = sessionTestClient {}
69
- var _ SessionWithTools = & fakeSessionWithTools {}
70
+ var _ SessionWithTools = & sessionTestClientWithTools {}
70
71
71
72
func TestSessionWithTools_Integration (t * testing.T ) {
72
73
server := NewMCPServer ("test-server" , "1.0.0" , WithToolCapabilities (true ))
@@ -80,7 +81,7 @@ func TestSessionWithTools_Integration(t *testing.T) {
80
81
}
81
82
82
83
// Create a session with tools
83
- session := & fakeSessionWithTools {
84
+ session := & sessionTestClientWithTools {
84
85
sessionID : "session-1" ,
85
86
notificationChannel : make (chan mcp.JSONRPCNotification , 10 ),
86
87
initialized : true ,
@@ -145,7 +146,7 @@ func TestMCPServer_ToolsWithSessionTools(t *testing.T) {
145
146
)
146
147
147
148
// Create a session with tools
148
- session := & fakeSessionWithTools {
149
+ session := & sessionTestClientWithTools {
149
150
sessionID : "session-1" ,
150
151
notificationChannel : make (chan mcp.JSONRPCNotification , 10 ),
151
152
initialized : true ,
@@ -194,7 +195,7 @@ func TestMCPServer_AddSessionTools(t *testing.T) {
194
195
195
196
// Create a session
196
197
sessionChan := make (chan mcp.JSONRPCNotification , 10 )
197
- session := & fakeSessionWithTools {
198
+ session := & sessionTestClientWithTools {
198
199
sessionID : "session-1" ,
199
200
notificationChannel : sessionChan ,
200
201
initialized : true ,
@@ -229,7 +230,7 @@ func TestMCPServer_DeleteSessionTools(t *testing.T) {
229
230
230
231
// Create a session with tools
231
232
sessionChan := make (chan mcp.JSONRPCNotification , 10 )
232
- session := & fakeSessionWithTools {
233
+ session := & sessionTestClientWithTools {
233
234
sessionID : "session-1" ,
234
235
notificationChannel : sessionChan ,
235
236
initialized : true ,
@@ -294,7 +295,7 @@ func TestMCPServer_ToolFiltering(t *testing.T) {
294
295
)
295
296
296
297
// Create a session with tools
297
- session := & fakeSessionWithTools {
298
+ session := & sessionTestClientWithTools {
298
299
sessionID : "session-1" ,
299
300
notificationChannel : make (chan mcp.JSONRPCNotification , 10 ),
300
301
initialized : true ,
@@ -339,20 +340,20 @@ func TestMCPServer_SendNotificationToSpecificClient(t *testing.T) {
339
340
server := NewMCPServer ("test-server" , "1.0.0" )
340
341
341
342
session1Chan := make (chan mcp.JSONRPCNotification , 10 )
342
- session1 := & fakeSession {
343
+ session1 := & sessionTestClient {
343
344
sessionID : "session-1" ,
344
345
notificationChannel : session1Chan ,
345
346
initialized : true ,
346
347
}
347
348
348
349
session2Chan := make (chan mcp.JSONRPCNotification , 10 )
349
- session2 := & fakeSession {
350
+ session2 := & sessionTestClient {
350
351
sessionID : "session-2" ,
351
352
notificationChannel : session2Chan ,
352
353
initialized : true ,
353
354
}
354
355
355
- session3 := & fakeSession {
356
+ session3 := & sessionTestClient {
356
357
sessionID : "session-3" ,
357
358
notificationChannel : make (chan mcp.JSONRPCNotification , 10 ),
358
359
initialized : false , // Not initialized
@@ -399,3 +400,74 @@ func TestMCPServer_SendNotificationToSpecificClient(t *testing.T) {
399
400
assert .Error (t , err )
400
401
assert .Contains (t , err .Error (), "not properly initialized" )
401
402
}
403
+
404
+ func TestMCPServer_NotificationChannelBlocked (t * testing.T ) {
405
+ // Set up a hooks object to capture error notifications
406
+ errorCaptured := false
407
+ errorSessionID := ""
408
+ errorMethod := ""
409
+
410
+ hooks := & Hooks {}
411
+ hooks .AddOnError (func (ctx context.Context , id any , method mcp.MCPMethod , message any , err error ) {
412
+ errorCaptured = true
413
+ // Extract session ID and method from the error message metadata
414
+ if msgMap , ok := message .(map [string ]interface {}); ok {
415
+ if sid , ok := msgMap ["sessionID" ].(string ); ok {
416
+ errorSessionID = sid
417
+ }
418
+ if m , ok := msgMap ["method" ].(string ); ok {
419
+ errorMethod = m
420
+ }
421
+ }
422
+ // Verify the error is a notification channel blocked error
423
+ assert .True (t , errors .Is (err , ErrNotificationChannelBlocked ))
424
+ })
425
+
426
+ // Create a server with hooks
427
+ server := NewMCPServer ("test-server" , "1.0.0" , WithHooks (hooks ))
428
+
429
+ // Create a session with a very small buffer that will get blocked
430
+ smallBufferChan := make (chan mcp.JSONRPCNotification , 1 )
431
+ session := & sessionTestClient {
432
+ sessionID : "blocked-session" ,
433
+ notificationChannel : smallBufferChan ,
434
+ initialized : true ,
435
+ }
436
+
437
+ // Register the session
438
+ err := server .RegisterSession (context .Background (), session )
439
+ require .NoError (t , err )
440
+
441
+ // Fill the buffer first to ensure it gets blocked
442
+ server .SendNotificationToSpecificClient (session .SessionID (), "first-message" , nil )
443
+
444
+ // This will cause the buffer to block
445
+ err = server .SendNotificationToSpecificClient (session .SessionID (), "blocked-message" , nil )
446
+ assert .Error (t , err )
447
+ assert .Equal (t , ErrNotificationChannelBlocked , err )
448
+
449
+ // Wait a bit for the goroutine to execute
450
+ time .Sleep (10 * time .Millisecond )
451
+
452
+ // Verify the error was logged via hooks
453
+ assert .True (t , errorCaptured , "Error hook should have been called" )
454
+ assert .Equal (t , "blocked-session" , errorSessionID , "Session ID should be captured in the error hook" )
455
+ assert .Equal (t , "blocked-message" , errorMethod , "Method should be captured in the error hook" )
456
+
457
+ // Also test SendNotificationToAllClients with a blocked channel
458
+ // Reset the captured data
459
+ errorCaptured = false
460
+ errorSessionID = ""
461
+ errorMethod = ""
462
+
463
+ // Send to all clients (which includes our blocked one)
464
+ server .SendNotificationToAllClients ("broadcast-message" , nil )
465
+
466
+ // Wait a bit for the goroutine to execute
467
+ time .Sleep (10 * time .Millisecond )
468
+
469
+ // Verify the error was logged via hooks
470
+ assert .True (t , errorCaptured , "Error hook should have been called for broadcast" )
471
+ assert .Equal (t , "blocked-session" , errorSessionID , "Session ID should be captured in the error hook" )
472
+ assert .Equal (t , "broadcast-message" , errorMethod , "Method should be captured in the error hook" )
473
+ }
0 commit comments