DevWizard-Vandan
fix(api): mount Next.js frontend as default app and prefix all API routes under /api
cede2ad | """Tests for the telemetry and Governor status endpoints in FastAPI.""" | |
| from typing import Generator | |
| from unittest.mock import AsyncMock, MagicMock, patch | |
| import pytest | |
| from fastapi.testclient import TestClient | |
| from synthra.governor.events import WorkerIdle | |
| def _make_mock_state_with_governor() -> MagicMock: | |
| """Return a fully-configured mock ServiceState with a Governor.""" | |
| state = MagicMock() | |
| state.is_ready = True | |
| state.campaign_count.return_value = 5 | |
| state.simulations_executed.return_value = 10 | |
| state.initialize = AsyncMock() | |
| state.shutdown = AsyncMock() | |
| # Mock Governor | |
| gov = MagicMock() | |
| # Telemetry | |
| gov.telemetry.get_metrics.return_value = { | |
| "running_workers": 2, | |
| "queued_jobs": 1, | |
| "completed_jobs": 3, | |
| "failed_jobs": 0, | |
| "campaigns_running": 1, | |
| "campaigns_completed": 3, | |
| "simulations_running": 1, | |
| "simulations_completed": 10, | |
| "accepted_candidates": 2, | |
| "rejected_candidates": 4, | |
| "mutations_generated": 15, | |
| "average_simulation_time": 4.5, | |
| "uptime": 120.5, | |
| } | |
| # Event History | |
| gov.telemetry.get_events.return_value = [WorkerIdle(worker_name="Worker-1")] | |
| # Workers | |
| w1 = MagicMock() | |
| w1.worker_id = 1 | |
| w1.is_alive.return_value = True | |
| w2 = MagicMock() | |
| w2.worker_id = 2 | |
| w2.is_alive.return_value = False | |
| gov.scheduler._workers = [w1, w2] | |
| gov.scheduler.num_workers = 2 | |
| gov.scheduler.max_retries = 3 | |
| gov.scheduler.initial_backoff = 2.0 | |
| gov.scheduler._running = True | |
| # Queue | |
| campaign = MagicMock() | |
| campaign.id = "CMP-0001" | |
| campaign.name = "Test Queue Campaign" | |
| gov.queue.size.return_value = 1 | |
| gov.queue._campaigns = {"CMP-0001": (campaign, 5)} | |
| # Submission Queue (Candidates) | |
| cand = MagicMock() | |
| cand.candidate_id = "CAND-0001" | |
| cand.campaign_id = "CMP-0001" | |
| cand.hypothesis_id = "HYP-0001" | |
| cand.expression = "close/open" | |
| cand.metrics = {"sharpe": 1.5} | |
| cand.generation = 1 | |
| gov.submission_queue.size.return_value = 1 | |
| gov.submission_queue.get_all.return_value = [cand] | |
| # DB connection for campaigns query | |
| conn = MagicMock() | |
| cursor = MagicMock() | |
| cursor.fetchall.return_value = [ | |
| ( | |
| "CMP-0001", | |
| "Test Queue Campaign", | |
| "US", | |
| "TOP2000", | |
| 100.0, | |
| 10.0, | |
| 5, | |
| 20, | |
| "ACTIVE", | |
| "RUNNING", | |
| ) | |
| ] | |
| conn.execute.return_value = cursor | |
| gov.db_manager.connection.return_value.__enter__.return_value = conn | |
| state.governor = gov | |
| return state | |
| def client() -> Generator[TestClient, None, None]: | |
| """Provide a TestClient with mocked ServiceState and Governor.""" | |
| mock_state = _make_mock_state_with_governor() | |
| with patch("synthra.api.app.ServiceState.get_instance", return_value=mock_state): | |
| from synthra.api.app import create_app | |
| application = create_app() | |
| with TestClient(application, raise_server_exceptions=True) as tc: | |
| yield tc | |
| def test_get_metrics(client: TestClient) -> None: | |
| """GET /api/metrics returns the telemetry metrics.""" | |
| response = client.get("/api/metrics") | |
| assert response.status_code == 200 | |
| metrics = response.json() | |
| assert metrics["running_workers"] == 2 | |
| assert metrics["queued_jobs"] == 1 | |
| assert metrics["average_simulation_time"] == 4.5 | |
| def test_get_workers(client: TestClient) -> None: | |
| """GET /api/workers returns the status of active worker threads.""" | |
| response = client.get("/api/workers") | |
| assert response.status_code == 200 | |
| workers = response.json() | |
| assert len(workers) == 2 | |
| assert workers[0]["worker_id"] == 1 | |
| assert workers[0]["is_alive"] is True | |
| assert workers[1]["worker_id"] == 2 | |
| assert workers[1]["is_alive"] is False | |
| def test_get_campaigns(client: TestClient) -> None: | |
| """GET /api/campaigns returns the list of all campaigns with state.""" | |
| response = client.get("/api/campaigns") | |
| assert response.status_code == 200 | |
| body = response.json() | |
| assert body["total"] == 1 | |
| campaigns = body["campaigns"] | |
| assert campaigns[0]["id"] == "CMP-0001" | |
| assert campaigns[0]["state"] == "RUNNING" | |
| def test_get_queue(client: TestClient) -> None: | |
| """GET /api/queue returns campaigns currently in the queue.""" | |
| response = client.get("/api/queue") | |
| assert response.status_code == 200 | |
| queue = response.json() | |
| assert len(queue) == 1 | |
| assert queue[0]["id"] == "CMP-0001" | |
| assert queue[0]["priority"] == 5 | |
| def test_get_candidates(client: TestClient) -> None: | |
| """GET /api/candidates returns alpha candidates enqueued for submission.""" | |
| response = client.get("/api/candidates") | |
| assert response.status_code == 200 | |
| candidates = response.json() | |
| assert len(candidates) == 1 | |
| assert candidates[0]["candidate_id"] == "CAND-0001" | |
| assert candidates[0]["metrics"]["sharpe"] == 1.5 | |
| def test_get_events(client: TestClient) -> None: | |
| """GET /api/events returns recent events.""" | |
| response = client.get("/api/events") | |
| assert response.status_code == 200 | |
| events = response.json() | |
| assert len(events) == 1 | |
| assert events[0]["event_type"] == "WorkerIdle" | |
| assert events[0]["worker_name"] == "Worker-1" | |
| def test_get_system(client: TestClient) -> None: | |
| """GET /api/system returns system stats.""" | |
| response = client.get("/api/system") | |
| assert response.status_code == 200 | |
| system = response.json() | |
| assert system["database_backend"] == "sqlite" | |
| assert system["campaign_count"] == 5 | |
| assert system["simulations_executed"] == 10 | |
| def test_get_governor(client: TestClient) -> None: | |
| """GET /api/governor returns governor stats and configuration.""" | |
| response = client.get("/api/governor") | |
| assert response.status_code == 200 | |
| governor = response.json() | |
| assert governor["status"] == "running" | |
| assert governor["worker_count"] == 2 | |
| assert governor["max_retries"] == 3 | |
| def test_events_stream(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None: | |
| """GET /api/events/stream returns event-stream media type.""" | |
| from typing import Any, AsyncGenerator | |
| from fastapi import Request | |
| async def mock_sse_generator( | |
| request: Request, event_bus: Any | |
| ) -> AsyncGenerator[str, None]: | |
| yield ": connection established\n\n" | |
| monkeypatch.setattr("synthra.api.routers.status.sse_generator", mock_sse_generator) | |
| with client.stream("GET", "/api/events/stream") as response: | |
| assert response.status_code == 200 | |
| assert "text/event-stream" in response.headers["content-type"] | |