ClaudeFlow ported, needs cleanup
This commit is contained in:
@@ -0,0 +1,506 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user