"""Integration tests for MCP (Model Context Protocol) system.""" import asyncio from unittest.mock import AsyncMock, patch import pytest from cleverclaude.agents.manager import AgentManager from cleverclaude.config.settings import Settings from cleverclaude.coordination.swarm import SwarmCoordinator from cleverclaude.core.app import CleverClaudeApp from cleverclaude.mcp.client import MCPClient from cleverclaude.mcp.types import MCPToolExecutionResult @pytest.mark.integration @pytest.mark.async_test class TestMCPIntegration: """Integration tests for MCP system with other components.""" async def test_mcp_with_agent_manager(self, test_settings: Settings, async_session, mock_redis): """Test MCP integration with AgentManager.""" # Initialize MCP client and agent manager mcp_client = MCPClient(test_settings) agent_manager = AgentManager(test_settings.agents, async_session, mock_redis) await mcp_client.initialize() await agent_manager.initialize() # Mock MCP tool for agent creation with patch.object(mcp_client, "execute_tool") as mock_execute: mock_execute.return_value = MCPToolExecutionResult( success=True, result={"agent_id": "mcp_agent_123", "type": "researcher", "status": "created"} ) # Execute MCP tool to create agent result = await mcp_client.execute_tool( "agent_spawn", {"type": "researcher", "name": "MCP Test Agent", "capabilities": ["research", "analysis"]}, ) assert result.success is True assert result.result["agent_id"] == "mcp_agent_123" await mcp_client.disconnect() await agent_manager.shutdown() async def test_mcp_with_swarm_coordinator(self, test_settings: Settings, async_session, mock_redis): """Test MCP integration with SwarmCoordinator.""" mcp_client = MCPClient(test_settings) agent_manager = AgentManager(test_settings.agents, async_session, mock_redis) swarm_coordinator = SwarmCoordinator(test_settings.swarm, async_session, agent_manager, mock_redis) await mcp_client.initialize() await agent_manager.initialize() await swarm_coordinator.initialize() # Test swarm creation via MCP with patch.object(mcp_client, "execute_tool") as mock_execute: mock_execute.return_value = MCPToolExecutionResult( success=True, result={"swarm_id": "mcp_swarm_456", "topology": "mesh", "status": "created"} ) result = await mcp_client.execute_tool( "swarm_init", {"topology": "mesh", "maxAgents": 10, "strategy": "balanced"} ) assert result.success is True assert result.result["swarm_id"] == "mcp_swarm_456" await swarm_coordinator.shutdown() await agent_manager.shutdown() await mcp_client.disconnect() async def test_mcp_end_to_end_workflow(self, test_settings: Settings, async_session, mock_redis): """Test complete end-to-end workflow using MCP tools.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Mock a complete workflow: swarm -> agents -> tasks -> results workflow_steps = [ ("swarm_init", {"topology": "hierarchical"}, {"swarm_id": "workflow_swarm"}), ("agent_spawn", {"type": "researcher"}, {"agent_id": "workflow_agent_1"}), ("agent_spawn", {"type": "coder"}, {"agent_id": "workflow_agent_2"}), ("task_orchestrate", {"task": "Complex analysis task"}, {"task_id": "workflow_task_1"}), ("task_status", {"taskId": "workflow_task_1"}, {"status": "completed"}), ("performance_report", {"format": "detailed"}, {"metrics": {"efficiency": 92.5}}), ] results = [] with patch.object(mcp_client, "execute_tool") as mock_execute: mock_execute.side_effect = [ MCPToolExecutionResult(success=True, result=expected_result) for _, _, expected_result in workflow_steps ] for tool_name, params, expected_result in workflow_steps: result = await mcp_client.execute_tool(tool_name, params) results.append(result) assert result.success is True assert result.result == expected_result # Verify workflow completion assert len(results) == 6 assert all(r.success for r in results) await mcp_client.disconnect() async def test_mcp_error_recovery(self, test_settings: Settings): """Test MCP error recovery and resilience.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Test recovery from tool execution errors with patch.object(mcp_client, "execute_tool") as mock_execute: # First call fails, second succeeds mock_execute.side_effect = [ MCPToolExecutionResult(success=False, result=None, error="Temporary network error"), MCPToolExecutionResult(success=True, result={"swarm_id": "recovered_swarm"}), ] # First attempt should fail result1 = await mcp_client.execute_tool("swarm_init", {"topology": "mesh"}) assert result1.success is False assert "network error" in result1.error.lower() # Second attempt should succeed (simulating retry) result2 = await mcp_client.execute_tool("swarm_init", {"topology": "mesh"}) assert result2.success is True assert result2.result["swarm_id"] == "recovered_swarm" await mcp_client.disconnect() async def test_mcp_concurrent_operations(self, test_settings: Settings): """Test concurrent MCP operations.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Define concurrent operations concurrent_ops = [ ("swarm_status", {}, {"active_swarms": 2}), ("agent_metrics", {"agentId": "agent_1"}, {"performance": 85.5}), ("memory_usage", {"action": "list"}, {"total_keys": 42}), ("neural_status", {"modelId": "model_1"}, {"status": "trained"}), ("performance_report", {"format": "summary"}, {"uptime": "24h"}), ] async def execute_operation(tool_name, params, expected_result): with patch.object(mcp_client, "execute_tool") as mock_execute: mock_execute.return_value = MCPToolExecutionResult(success=True, result=expected_result) return await mcp_client.execute_tool(tool_name, params) # Execute operations concurrently tasks = [execute_operation(tool_name, params, expected) for tool_name, params, expected in concurrent_ops] results = await asyncio.gather(*tasks) # Verify all operations completed successfully assert len(results) == 5 assert all(r.success for r in results) await mcp_client.disconnect() async def test_mcp_with_full_application(self, test_settings: Settings, temp_dir): """Test MCP integration with full CleverClaude application.""" # Create config directory config_dir = temp_dir / ".cleverclaude" config_dir.mkdir(exist_ok=True) with patch("cleverclaude.core.app.CleverClaudeApp._initialize_mcp") as mock_init_mcp: mock_mcp_client = AsyncMock() mock_mcp_client.get_available_tools.return_value = { "swarm_init": {"description": "Initialize swarm"}, "agent_spawn": {"description": "Spawn agent"}, "task_orchestrate": {"description": "Orchestrate task"}, } mock_init_mcp.return_value = mock_mcp_client # Initialize application app = CleverClaudeApp(config_dir) with ( patch.object(app, "_initialize_database"), patch.object(app, "_initialize_redis"), patch.object(app, "_initialize_agents"), patch.object(app, "_initialize_swarm"), ): await app.initialize() # Verify MCP client was initialized assert app.mcp_client is not None mock_init_mcp.assert_called_once() await app.shutdown() @pytest.mark.integration @pytest.mark.async_test class TestMCPTools: """Integration tests for specific MCP tools.""" async def test_neural_tools_integration(self, test_settings: Settings): """Test neural network tools integration.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Test neural training workflow training_workflow = [ ( "neural_train", {"pattern_type": "coordination", "training_data": "sample_coordination_data", "epochs": 10}, ), ("neural_status", {"modelId": "coordination_model"}), ("neural_predict", {"modelId": "coordination_model", "input": "test_coordination_scenario"}), ] with patch.object(mcp_client, "execute_tool") as mock_execute: mock_execute.side_effect = [ MCPToolExecutionResult(success=True, result={"training_id": "train_123", "status": "started"}), MCPToolExecutionResult(success=True, result={"status": "trained", "accuracy": 0.92}), MCPToolExecutionResult(success=True, result={"prediction": "optimal_coordination", "confidence": 0.88}), ] results = [] for tool_name, params in training_workflow: result = await mcp_client.execute_tool(tool_name, params) results.append(result) assert len(results) == 3 assert all(r.success for r in results) assert results[0].result["status"] == "started" assert results[1].result["accuracy"] == 0.92 assert results[2].result["confidence"] == 0.88 await mcp_client.disconnect() async def test_memory_tools_integration(self, test_settings: Settings): """Test memory management tools integration.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Test memory operations workflow memory_ops = [ ( "memory_usage", {"action": "store", "key": "test_key", "value": "test_value", "namespace": "integration_test"}, ), ("memory_usage", {"action": "retrieve", "key": "test_key", "namespace": "integration_test"}), ("memory_search", {"pattern": "test_*", "namespace": "integration_test"}), ("memory_usage", {"action": "delete", "key": "test_key", "namespace": "integration_test"}), ] with patch.object(mcp_client, "execute_tool") as mock_execute: mock_execute.side_effect = [ MCPToolExecutionResult(success=True, result={"action": "store", "status": "success"}), MCPToolExecutionResult(success=True, result={"value": "test_value", "found": True}), MCPToolExecutionResult(success=True, result={"matches": ["test_key"], "count": 1}), MCPToolExecutionResult(success=True, result={"action": "delete", "status": "success"}), ] results = [] for tool_name, params in memory_ops: result = await mcp_client.execute_tool(tool_name, params) results.append(result) assert len(results) == 4 assert all(r.success for r in results) assert results[1].result["value"] == "test_value" assert results[2].result["count"] == 1 await mcp_client.disconnect() async def test_workflow_tools_integration(self, test_settings: Settings): """Test workflow automation tools integration.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Test workflow creation and execution workflow_definition = { "name": "Integration Test Workflow", "steps": [ {"action": "create_agents", "count": 3}, {"action": "create_swarm", "topology": "mesh"}, {"action": "assign_tasks", "task_count": 5}, {"action": "monitor_execution"}, {"action": "collect_results"}, ], "triggers": ["on_demand"], } workflow_ops = [ ("workflow_create", workflow_definition), ("workflow_execute", {"workflowId": "workflow_123"}), ("workflow_status", {"workflowId": "workflow_123"}), ("workflow_results", {"workflowId": "workflow_123"}), ] with patch.object(mcp_client, "execute_tool") as mock_execute: mock_execute.side_effect = [ MCPToolExecutionResult(success=True, result={"workflow_id": "workflow_123", "status": "created"}), MCPToolExecutionResult(success=True, result={"execution_id": "exec_456", "status": "running"}), MCPToolExecutionResult(success=True, result={"status": "completed", "progress": 100}), MCPToolExecutionResult(success=True, result={"results": {"tasks_completed": 5, "success_rate": 100}}), ] results = [] for tool_name, params in workflow_ops: result = await mcp_client.execute_tool(tool_name, params) results.append(result) assert len(results) == 4 assert all(r.success for r in results) assert results[0].result["workflow_id"] == "workflow_123" assert results[2].result["progress"] == 100 assert results[3].result["results"]["success_rate"] == 100 await mcp_client.disconnect() @pytest.mark.integration @pytest.mark.slow class TestMCPPerformance: """Performance and stress tests for MCP system.""" @pytest.mark.async_test async def test_mcp_high_throughput(self, test_settings: Settings): """Test MCP system under high throughput.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Execute many operations rapidly num_operations = 100 operations = [] for _i in range(num_operations): operations.append(("swarm_status", {"detailed": False})) with patch.object(mcp_client, "execute_tool") as mock_execute: mock_execute.return_value = MCPToolExecutionResult(success=True, result={"status": "running", "swarms": 2}) start_time = asyncio.get_event_loop().time() # Execute operations in batches to avoid overwhelming batch_size = 20 results = [] for i in range(0, num_operations, batch_size): batch = operations[i : i + batch_size] batch_tasks = [mcp_client.execute_tool(tool_name, params) for tool_name, params in batch] batch_results = await asyncio.gather(*batch_tasks) results.extend(batch_results) end_time = asyncio.get_event_loop().time() execution_time = end_time - start_time # Verify performance assert len(results) == num_operations assert all(r.success for r in results) assert execution_time < 10.0 # Should complete within 10 seconds throughput = num_operations / execution_time assert throughput > 10 # Should handle more than 10 ops/second await mcp_client.disconnect() @pytest.mark.async_test async def test_mcp_connection_resilience(self, test_settings: Settings): """Test MCP connection resilience under stress.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Simulate connection failures and recoveries failure_count = 0 success_count = 0 def mock_execute_with_failures(tool_name, params): nonlocal failure_count, success_count # Simulate intermittent failures (20% failure rate) if (success_count + failure_count) % 5 == 0: failure_count += 1 return MCPToolExecutionResult(success=False, result=None, error="Connection temporarily unavailable") else: success_count += 1 return MCPToolExecutionResult(success=True, result={"status": "success", "operation": tool_name}) with patch.object(mcp_client, "execute_tool", side_effect=mock_execute_with_failures): # Execute operations with expected failures num_operations = 50 results = [] for _i in range(num_operations): result = await mcp_client.execute_tool("health_check", {}) results.append(result) # Verify resilience total_results = len(results) successful_results = len([r for r in results if r.success]) failed_results = len([r for r in results if not r.success]) assert total_results == num_operations assert successful_results > 0 # Should have some successes assert failed_results > 0 # Should have some expected failures assert successful_results >= failed_results # More successes than failures await mcp_client.disconnect() @pytest.mark.async_test async def test_mcp_memory_efficiency(self, test_settings: Settings): """Test MCP memory efficiency during extended operations.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Execute long-running sequence of operations import gc initial_objects = len(gc.get_objects()) with patch.object(mcp_client, "execute_tool") as mock_execute: mock_execute.return_value = MCPToolExecutionResult( success=True, result={"data": "test" * 100}, # Some data payload ) # Execute many operations for i in range(200): result = await mcp_client.execute_tool("memory_usage", {"action": "list"}) assert result.success # Force garbage collection periodically if i % 50 == 0: gc.collect() # Final garbage collection gc.collect() final_objects = len(gc.get_objects()) # Verify no significant memory growth object_growth = final_objects - initial_objects assert object_growth < 1000 # Should not have excessive object growth await mcp_client.disconnect() @pytest.mark.integration class TestMCPToolValidation: """Test MCP tool parameter validation and error handling.""" @pytest.mark.async_test async def test_tool_parameter_validation(self, test_settings: Settings): """Test comprehensive tool parameter validation.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Test various invalid parameter scenarios invalid_scenarios = [ ("swarm_init", {"topology": "invalid_topology"}, "Invalid topology"), ("agent_spawn", {"type": "invalid_type"}, "Invalid agent type"), ("task_orchestrate", {"priority": "invalid_priority"}, "Invalid priority"), ("memory_usage", {"action": "invalid_action"}, "Invalid action"), ("neural_train", {"epochs": -1}, "Invalid epochs"), ] with patch.object(mcp_client, "execute_tool") as mock_execute: for tool_name, invalid_params, expected_error in invalid_scenarios: mock_execute.return_value = MCPToolExecutionResult(success=False, result=None, error=expected_error) result = await mcp_client.execute_tool(tool_name, invalid_params) assert result.success is False assert expected_error.lower() in result.error.lower() await mcp_client.disconnect() @pytest.mark.async_test async def test_tool_response_validation(self, test_settings: Settings): """Test MCP tool response validation.""" mcp_client = MCPClient(test_settings) await mcp_client.initialize() # Test various response formats response_scenarios = [ ("swarm_init", {"swarm_id": "test", "topology": "mesh", "status": "created"}), ("agent_spawn", {"agent_id": "test", "type": "researcher", "status": "active"}), ("task_status", {"task_id": "test", "status": "completed", "progress": 100}), ("performance_report", {"metrics": {"cpu": 50, "memory": 200}, "timestamp": "2024-01-01T12:00:00Z"}), ] with patch.object(mcp_client, "execute_tool") as mock_execute: for tool_name, expected_response in response_scenarios: mock_execute.return_value = MCPToolExecutionResult(success=True, result=expected_response) result = await mcp_client.execute_tool(tool_name, {}) assert result.success is True assert result.result == expected_response # Verify required fields are present if tool_name == "swarm_init": assert "swarm_id" in result.result assert "topology" in result.result elif tool_name == "agent_spawn": assert "agent_id" in result.result assert "type" in result.result await mcp_client.disconnect()