Skip to main content
Glama
mcp_debug_controller_test.go7.72 kB
package tui import ( "fmt" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestMCPDebugController_RaceSafety(t *testing.T) { // Create controller in debug mode controller := NewMCPDebugController(true) require.NotNil(t, controller) assert.True(t, controller.IsDebugMode()) // Test concurrent access to connections and activities var wg sync.WaitGroup numGoroutines := 10 numOperations := 100 // Start multiple goroutines that read and write concurrently for i := 0; i < numGoroutines; i++ { wg.Add(1) go func(id int) { defer wg.Done() sessionId := fmt.Sprintf("session-%d", id) for j := 0; j < numOperations; j++ { // Write operations if j%3 == 0 { // Add connection controller.HandleConnection(mcpConnectionMsg{ sessionId: sessionId, clientInfo: fmt.Sprintf("Test Client %d", id), connected: true, connectedAt: time.Now(), connectionType: "WebSocket", method: "GET", }) } else if j%3 == 1 { // Add activity controller.HandleActivity(mcpActivityMsg{ sessionId: sessionId, activity: MCPActivity{ Method: fmt.Sprintf("method-%d", j), Params: fmt.Sprintf(`{"test": %d}`, j), Response: fmt.Sprintf(`{"result": %d}`, j), Error: "", Timestamp: time.Now(), Duration: time.Duration(j) * time.Millisecond, }, }) } else { // Read operations controller.UpdateConnectionsList() controller.UpdateActivityView() } } }(i) } // Wait for all goroutines to complete wg.Wait() // Verify data integrity controller.mu.RLock() defer controller.mu.RUnlock() assert.GreaterOrEqual(t, len(controller.connections), 1, "Should have at least one connection") assert.GreaterOrEqual(t, len(controller.activities), 1, "Should have at least one activity list") } func TestMCPDebugController_ConnectionHandling(t *testing.T) { controller := NewMCPDebugController(true) // Test connection creation msg := mcpConnectionMsg{ sessionId: "test-session-1", clientInfo: "Claude Code Test Client", connected: true, connectedAt: time.Now(), connectionType: "WebSocket", method: "GET", } controller.HandleConnection(msg) // Verify connection was created controller.mu.RLock() conn, exists := controller.connections["test-session-1"] controller.mu.RUnlock() assert.True(t, exists, "Connection should exist") assert.Equal(t, "Claude Code", conn.clientName, "Should detect Claude Code client") assert.True(t, conn.isConnected, "Connection should be marked as connected") assert.Equal(t, "WebSocket", conn.connectionType) // Test disconnection disconnectMsg := mcpConnectionMsg{ sessionId: "test-session-1", connected: false, } controller.HandleConnection(disconnectMsg) // Verify connection was marked as disconnected controller.mu.RLock() conn, exists = controller.connections["test-session-1"] controller.mu.RUnlock() assert.True(t, exists, "Connection should still exist") assert.False(t, conn.isConnected, "Connection should be marked as disconnected") } func TestMCPDebugController_ActivityTracking(t *testing.T) { controller := NewMCPDebugController(true) sessionId := "test-session-2" // Add some activities for i := 0; i < 5; i++ { controller.HandleActivity(mcpActivityMsg{ sessionId: sessionId, activity: MCPActivity{ Method: fmt.Sprintf("test.method%d", i), Params: fmt.Sprintf(`{"index": %d}`, i), Response: fmt.Sprintf(`{"success": true, "index": %d}`, i), Timestamp: time.Now(), Duration: time.Duration(i*10) * time.Millisecond, }, }) } // Verify activities were tracked controller.mu.RLock() activities, exists := controller.activities[sessionId] controller.mu.RUnlock() assert.True(t, exists, "Activities should exist for session") assert.Len(t, activities, 5, "Should have 5 activities") // Verify connection was auto-created for HTTP-only session controller.mu.RLock() conn, exists := controller.connections[sessionId] controller.mu.RUnlock() assert.True(t, exists, "Connection should be auto-created") assert.Equal(t, "HTTP Client", conn.clientName) assert.False(t, conn.isConnected, "HTTP-only sessions should not be marked as connected") } func TestMCPDebugController_ActivityLimit(t *testing.T) { controller := NewMCPDebugController(true) sessionId := "test-session-3" // Add more than maxActivities (100) for i := 0; i < 110; i++ { controller.HandleActivity(mcpActivityMsg{ sessionId: sessionId, activity: MCPActivity{ Method: fmt.Sprintf("test.method%d", i), Timestamp: time.Now(), Duration: time.Millisecond, }, }) } // Verify activities are limited controller.mu.RLock() activities := controller.activities[sessionId] controller.mu.RUnlock() assert.Len(t, activities, 100, "Activities should be limited to 100") // Verify oldest activities were removed (first activity should be index 10) assert.Equal(t, "test.method10", activities[0].Method, "Oldest activities should be removed") } func TestMCPDebugController_UpdateConnectionsList(t *testing.T) { controller := NewMCPDebugController(true) // Add multiple connections with different timestamps now := time.Now() for i := 0; i < 3; i++ { controller.HandleConnection(mcpConnectionMsg{ sessionId: fmt.Sprintf("session-%d", i), clientInfo: fmt.Sprintf("Client %d", i), connected: true, connectedAt: now.Add(time.Duration(-i) * time.Hour), connectionType: "WebSocket", }) } // Update connections list controller.UpdateConnectionsList() // Verify list was updated and sorted items := controller.connectionsList.Items() assert.Len(t, items, 3, "Should have 3 items in list") // Verify sorting (newest first) for i := 0; i < len(items)-1; i++ { item1 := items[i].(mcpConnectionItem) item2 := items[i+1].(mcpConnectionItem) assert.True(t, item1.connectedAt.After(item2.connectedAt), "Items should be sorted by connection time (newest first)") } } func TestMCPDebugController_NonDebugMode(t *testing.T) { // Create controller in non-debug mode controller := NewMCPDebugController(false) assert.False(t, controller.IsDebugMode()) // All operations should be no-ops controller.HandleConnection(mcpConnectionMsg{ sessionId: "test", connected: true, }) controller.HandleActivity(mcpActivityMsg{ sessionId: "test", activity: MCPActivity{}, }) controller.UpdateConnectionsList() controller.UpdateActivityView() // Verify no data was stored assert.Nil(t, controller.connections) assert.Nil(t, controller.activities) // Render should return empty string result := controller.Render(100, 50, 5, 3) assert.Empty(t, result) } func TestMCPDebugController_ClientNameDetection(t *testing.T) { controller := NewMCPDebugController(true) tests := []struct { clientInfo string expectedName string }{ {"Claude Code/1.0", "Claude Code"}, {"VS Code MCP Extension", "VS Code MCP"}, {"Some Random Client", "Some Random Client"}, {"Very Long Client Name That Should Be Truncated Because It Is Too Long", "Very Long Client ..."}, {"", "Unknown Client"}, } for i, tt := range tests { controller.HandleConnection(mcpConnectionMsg{ sessionId: fmt.Sprintf("session-%d", i), clientInfo: tt.clientInfo, connected: true, connectedAt: time.Now(), }) controller.mu.RLock() conn := controller.connections[fmt.Sprintf("session-%d", i)] controller.mu.RUnlock() assert.Equal(t, tt.expectedName, conn.clientName, "Client name should be detected/formatted correctly for: %s", tt.clientInfo) } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/standardbeagle/brummer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server