"""Integration tests for metrics and alerts API.""" from datetime import timedelta import pytest from fastapi.testclient import TestClient from sqlmodel import Session, SQLModel, create_engine, select from unittest.mock import patch, MagicMock from agenteval.intelligent_eval.models import IntelligentEvalStatus from agenteval.storage.db import ( IntelligentEvalDB, IntelligentEvalTaskQueueDB, 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 ( # noqa: F401 IntelligentEvalDB, IntelligentEvalTaskQueueDB, OpenClawCronPoolDB, ) from agenteval.intelligent_eval.alerts import AlertHistoryDB 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]()) def test_get_metrics_api(client: TestClient, db_session: Session): """Test getting metrics via API.""" # Create some test data for i in range(5): cron = OpenClawCronPoolDB( openclaw_cron_id=f"cron-{i}", status="busy" if i < 3 else "idle", last_active_at=utc_now(), ) db_session.add(cron) db_session.commit() response = client.get("/api/openclaw/cron-pool/metrics") assert response.status_code == 200 data = response.json() assert "metrics" in data assert "pool_utilization" in data["metrics"] assert "task_backlog" in data["metrics"] assert "stuck_rate" in data["metrics"] assert "avg_processing_time_seconds" in data["metrics"] assert "eval_completion_rate" in data["metrics"] assert data["metrics"]["pool_utilization"] == 0.6 def test_check_alerts_api_no_alerts(client: TestClient, db_session: Session): """Test checking alerts when no rules are triggered.""" # Create healthy state for i in range(5): cron = OpenClawCronPoolDB( openclaw_cron_id=f"cron-{i}", status="idle", last_active_at=utc_now(), ) db_session.add(cron) db_session.commit() response = client.post("/api/openclaw/cron-pool/check-alerts") assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["alerts_triggered"] == 0 def test_check_alerts_api_with_alerts(client: TestClient, db_session: Session): """Test checking alerts when rules are triggered.""" # Create high backlog for i in range(60): task = IntelligentEvalTaskQueueDB( eval_id=f"eval-{i}", status="pending", priority=1, reason="slot_due", ) db_session.add(task) db_session.commit() response = client.post("/api/openclaw/cron-pool/check-alerts") assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["alerts_triggered"] > 0 # Should have task_backlog alert backlog_alerts = [a for a in data["alerts"] if a["alert_type"] == "task_backlog"] assert len(backlog_alerts) == 1 assert backlog_alerts[0]["metric_value"] == 60 def test_get_alert_history_api(client: TestClient, db_session: Session): """Test getting alert history via API.""" # Create some alerts from agenteval.intelligent_eval.alerts import AlertHistoryDB for i in range(3): alert = AlertHistoryDB( id=f"alert-{i}", alert_type="task_backlog", severity="warning", message=f"Test alert {i}", metric_value=50 + i, threshold=50, ) db_session.add(alert) db_session.commit() response = client.get("/api/openclaw/cron-pool/alerts") assert response.status_code == 200 data = response.json() assert "alerts" in data assert len(data["alerts"]) == 3 def test_get_alert_history_unresolved_only(client: TestClient, db_session: Session): """Test getting unresolved alerts only.""" from agenteval.intelligent_eval.alerts import AlertHistoryDB # Create resolved and unresolved alerts alert1 = AlertHistoryDB( id="alert-1", alert_type="task_backlog", severity="warning", message="Test alert 1", metric_value=60, threshold=50, ) alert2 = AlertHistoryDB( id="alert-2", alert_type="stuck_rate", severity="critical", message="Test alert 2", metric_value=0.2, threshold=0.1, resolved_at=utc_now(), ) db_session.add_all([alert1, alert2]) db_session.commit() response = client.get("/api/openclaw/cron-pool/alerts?unresolved_only=true") assert response.status_code == 200 data = response.json() assert len(data["alerts"]) == 1 assert data["alerts"][0]["id"] == "alert-1" def test_resolve_alert_api(client: TestClient, db_session: Session): """Test resolving an alert via API.""" from agenteval.intelligent_eval.alerts import AlertHistoryDB alert = AlertHistoryDB( id="alert-1", alert_type="task_backlog", severity="warning", message="Test alert", metric_value=60, threshold=50, ) db_session.add(alert) db_session.commit() response = client.post("/api/openclaw/cron-pool/alerts/alert-1/resolve") assert response.status_code == 200 data = response.json() assert data["success"] is True # Verify alert is resolved db_session.refresh(alert) assert alert.resolved_at is not None def test_resolve_nonexistent_alert_api(client: TestClient): """Test resolving a non-existent alert.""" response = client.post("/api/openclaw/cron-pool/alerts/nonexistent/resolve") assert response.status_code == 404 def test_end_to_end_metrics_and_alerts(client: TestClient, db_session: Session): """Test end-to-end metrics and alerts flow.""" # Create high utilization state for i in range(19): cron = OpenClawCronPoolDB( openclaw_cron_id=f"busy-{i}", status="busy", last_active_at=utc_now(), ) db_session.add(cron) cron = OpenClawCronPoolDB( openclaw_cron_id="idle-0", status="idle", last_active_at=utc_now(), ) db_session.add(cron) # Create high backlog for i in range(60): task = IntelligentEvalTaskQueueDB( eval_id=f"eval-{i}", status="pending", priority=1, reason="slot_due", ) db_session.add(task) db_session.commit() # Get metrics response = client.get("/api/openclaw/cron-pool/metrics") assert response.status_code == 200 metrics_data = response.json()["metrics"] assert metrics_data["pool_utilization"] == 0.95 assert metrics_data["task_backlog"] == 60 # Check alerts response = client.post("/api/openclaw/cron-pool/check-alerts") assert response.status_code == 200 alerts_data = response.json() assert alerts_data["alerts_triggered"] > 0 # Get alert history response = client.get("/api/openclaw/cron-pool/alerts") assert response.status_code == 200 history_data = response.json() assert len(history_data["alerts"]) > 0 # Resolve first alert if history_data["alerts"]: alert_id = history_data["alerts"][0]["id"] response = client.post(f"/api/openclaw/cron-pool/alerts/{alert_id}/resolve") assert response.status_code == 200