"""Integration tests for cron pool API.""" from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi.testclient import TestClient from sqlmodel import Session, SQLModel, create_engine from agenteval.intelligent_eval.openclaw_client import OpenClawClient from agenteval.storage.db import OpenClawCronPoolDB, utc_now from agenteval.web.app import app from agenteval.web.deps import get_db @pytest.fixture() def client(tmp_path): """Create a TestClient with a fresh database.""" from agenteval.storage.db import OpenClawCronPoolDB # noqa: F401 engine = create_engine( f"sqlite:///{tmp_path / 'test.db'}", connect_args={"check_same_thread": False}, ) SQLModel.metadata.create_all(engine) session = Session(engine) def override_get_db(): try: yield session finally: pass app.dependency_overrides[get_db] = override_get_db client = TestClient(app) yield client app.dependency_overrides.clear() session.close() engine.dispose() @pytest.fixture() def db_session(client): """Get the database session from the client fixture.""" return next(app.dependency_overrides[get_db]()) @pytest.fixture() def mock_openclaw_client(): """Mock OpenClaw client.""" client = MagicMock(spec=OpenClawClient) counter = {"value": 0} async def create_cron_impl(**kwargs): counter["value"] += 1 return f"cron-{counter['value']}" client.create_cron = AsyncMock(side_effect=create_cron_impl) client.delete_cron = AsyncMock() client.list_crons = AsyncMock(return_value=[]) return client def test_get_cron_pool_status_empty(client: TestClient): """Test getting pool status when pool is empty.""" response = client.get("/api/openclaw/cron-pool") assert response.status_code == 200 data = response.json() assert data["pool"]["total"] == 0 assert data["pool"]["idle"] == 0 assert data["pool"]["busy"] == 0 assert data["pool"]["stuck"] == 0 assert data["pool"]["min_size"] == 5 assert data["pool"]["max_size"] == 20 def test_get_cron_pool_status_with_crons(client: TestClient, db_session: Session): """Test getting pool status with existing crons.""" # Create crons for i in range(10): status = ["idle", "busy", "stuck"][i % 3] cron = OpenClawCronPoolDB( openclaw_cron_id=f"cron-{i}", status=status, last_active_at=utc_now(), ) db_session.add(cron) db_session.commit() response = client.get("/api/openclaw/cron-pool") assert response.status_code == 200 data = response.json() assert data["pool"]["total"] == 10 assert data["pool"]["idle"] == 4 assert data["pool"]["busy"] == 3 assert data["pool"]["stuck"] == 3 @patch("agenteval.web.routers.openclaw_cron_pool.OpenClawClient") def test_scale_up_pool(mock_client_class, client: TestClient, db_session: Session, mock_openclaw_client): """Test manually scaling up the pool.""" mock_client_class.return_value = mock_openclaw_client # Create 5 crons for i in range(5): cron = OpenClawCronPoolDB( openclaw_cron_id=f"existing-cron-{i}", status="idle", last_active_at=utc_now(), ) db_session.add(cron) db_session.commit() # Scale up to 10 response = client.post("/api/openclaw/cron-pool/scale", json={"target_size": 10}) assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["scaled_up"] == 5 assert data["current_size"] == 10 # Verify crons created from sqlmodel import select crons = db_session.exec(select(OpenClawCronPoolDB)).all() assert len(crons) == 10 @patch("agenteval.web.routers.openclaw_cron_pool.OpenClawClient") def test_scale_down_pool(mock_client_class, client: TestClient, db_session: Session, mock_openclaw_client): """Test manually scaling down the pool.""" mock_client_class.return_value = mock_openclaw_client # Create 15 idle crons for i in range(15): cron = OpenClawCronPoolDB( openclaw_cron_id=f"cron-{i}", status="idle", last_active_at=utc_now(), ) db_session.add(cron) db_session.commit() # Scale down to 8 response = client.post("/api/openclaw/cron-pool/scale", json={"target_size": 8}) assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["scaled_down"] == 7 assert data["current_size"] == 8 # Verify crons deleted from sqlmodel import select crons = db_session.exec(select(OpenClawCronPoolDB)).all() assert len(crons) == 8 @patch("agenteval.web.routers.openclaw_cron_pool.OpenClawClient") def test_scale_pool_no_change(mock_client_class, client: TestClient, db_session: Session): """Test scaling pool to same size (no change).""" mock_client_class.return_value = MagicMock() # Create 10 crons for i in range(10): cron = OpenClawCronPoolDB( openclaw_cron_id=f"cron-{i}", status="idle", last_active_at=utc_now(), ) db_session.add(cron) db_session.commit() # Scale to same size response = client.post("/api/openclaw/cron-pool/scale", json={"target_size": 10}) assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["current_size"] == 10 assert "already at target size" in data["message"]