"""Pytest configuration and shared fixtures for CleverClaude tests.""" import asyncio import tempfile from collections.abc import AsyncGenerator, Generator from pathlib import Path from unittest.mock import AsyncMock, MagicMock import pytest import pytest_asyncio from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.pool import StaticPool from cleverclaude.config.settings import Settings from cleverclaude.database.models import Base @pytest.fixture(scope="session") def event_loop(): """Create an instance of the default event loop for the test session.""" loop = asyncio.new_event_loop() yield loop loop.close() @pytest.fixture def temp_dir() -> Generator[Path]: """Create a temporary directory for test files.""" with tempfile.TemporaryDirectory(prefix="cleverclaude_test_") as temp_path: yield Path(temp_path) @pytest.fixture def test_settings(temp_dir: Path) -> Settings: """Create test settings with temporary directories.""" config_dir = temp_dir / ".cleverclaude" config_dir.mkdir(exist_ok=True) return Settings( app=Settings.AppConfig(name="CleverClaude Test", version="2.0.0-test", environment="testing", debug=True), database=Settings.DatabaseConfig(url=f"sqlite+aiosqlite:///{config_dir}/test.db", echo=False), redis=Settings.RedisConfig( url="redis://localhost:6379/15" # Test database ), agents=Settings.AgentsConfig(max_agents=10, default_timeout=30, health_check_interval=5), swarm=Settings.SwarmConfig(default_topology="mesh", max_swarm_size=5, coordination_timeout=30), api=Settings.APIConfig( host="127.0.0.1", port=8080, # Different port for testing docs_enabled=False, ), monitoring=Settings.MonitoringConfig(metrics_enabled=False, log_level="DEBUG", log_format="json"), ) @pytest_asyncio.fixture async def async_engine(test_settings: Settings): """Create async SQLAlchemy engine for testing.""" engine = create_async_engine( test_settings.database.url, echo=test_settings.database.echo, connect_args={"check_same_thread": False}, poolclass=StaticPool, ) # Create all tables async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) yield engine # Clean up await engine.dispose() @pytest_asyncio.fixture async def async_session(async_engine) -> AsyncGenerator[AsyncSession]: """Create async database session for testing.""" async with AsyncSession(async_engine, expire_on_commit=False) as session: yield session @pytest.fixture def mock_redis(): """Create a mock Redis client.""" mock = MagicMock() mock.get = AsyncMock() mock.set = AsyncMock() mock.delete = AsyncMock() mock.exists = AsyncMock() mock.keys = AsyncMock() mock.expire = AsyncMock() mock.ping = AsyncMock(return_value=True) return mock @pytest.fixture def mock_agent(): """Create a mock agent for testing.""" mock = AsyncMock() mock.agent_id = "test_agent_123" mock.name = "Test Agent" mock.agent_type = "researcher" mock.status = "active" mock.capabilities = {"research", "analysis"} mock.created_at = "2024-01-01T12:00:00Z" mock.health_status = {"cpu_usage": 25.5, "memory_usage": 150.2, "task_count": 3, "status": "healthy"} return mock @pytest.fixture def mock_swarm(): """Create a mock swarm for testing.""" mock = AsyncMock() mock.swarm_id = "test_swarm_456" mock.name = "Test Swarm" mock.topology = "mesh" mock.state = "active" mock.agents = [] mock.tasks = [] mock.created_at = "2024-01-01T12:00:00Z" mock.metrics = {"total_agents": 3, "active_agents": 2, "completed_tasks": 15, "efficiency_score": 85.5} return mock @pytest.fixture def mock_mcp_client(): """Create a mock MCP client for testing.""" mock = AsyncMock() mock.is_connected = True mock.available_tools = [ "swarm_init", "agent_spawn", "task_orchestrate", "memory_usage", "neural_train", "performance_report", ] mock.execute_tool = AsyncMock() mock.get_tool_info = AsyncMock() mock.connect = AsyncMock() mock.disconnect = AsyncMock() return mock @pytest.fixture def mock_task(): """Create a mock task for testing.""" return { "task_id": "test_task_789", "type": "research_query", "status": "pending", "priority": "medium", "data": {"query": "Test research query", "scope": "general", "depth": "standard"}, "created_at": "2024-01-01T12:00:00Z", "assigned_agent": None, "result": None, } @pytest.fixture(autouse=True) def setup_test_environment(test_settings: Settings, monkeypatch): """Set up test environment variables.""" monkeypatch.setenv("CLEVERCLAUDE_ENVIRONMENT", "testing") monkeypatch.setenv("CLEVERCLAUDE_DEBUG", "true") monkeypatch.setenv("CLEVERCLAUDE_CONFIG_DIR", str(test_settings.database.url.split("///")[1].rsplit("/", 1)[0])) # Override settings monkeypatch.setattr("cleverclaude.config.settings.get_settings", lambda: test_settings) @pytest.fixture def cleanup_tasks(): """Cleanup any remaining asyncio tasks after tests.""" yield # Cancel any remaining tasks tasks = [task for task in asyncio.all_tasks() if not task.done()] for task in tasks: task.cancel() if tasks: asyncio.gather(*tasks, return_exceptions=True) # Marker definitions pytest.mark.unit = pytest.mark.unit pytest.mark.integration = pytest.mark.integration pytest.mark.async_test = pytest.mark.asyncio pytest.mark.slow = pytest.mark.slow