import httpx2 import pytest from fastapi import FastAPI from fastapi.testclient import TestClient from prometheus_client import CONTENT_TYPE_LATEST from core.api.observability.metrics import ApiMetrics from core.api.observability.routes import create_observability_router from core.api.observability.stats import ( ApiRuntimeStats, HttpWorkerStatsClient, WorkerStatsUnavailable, ) @pytest.mark.anyio async def test_http_worker_stats_client_success() -> None: async def fake_handler(request: httpx2.Request) -> httpx2.Response: assert request.url == "http://test-worker/internal/stats" return httpx2.Response( status_code=200, json={ "pipeline": { "total_processed": 5, "total_dlq": 1, "in_flight_buffer": 2, "last_flush_at": "2026-09-22T12:00:00Z", }, "breakdown": { "by_service_type": {"4G_DATA": 5}, "by_radio_access_type": {"LTE_4G": 5}, "dlq_by_reason": {"timeout": 1}, }, }, ) transport = httpx2.MockTransport(fake_handler) client = HttpWorkerStatsClient( url="http://test-worker/internal/stats", timeout_seconds=2.0, ) # Inject mock transport client._client = httpx2.AsyncClient(transport=transport) stats = await client.get_stats() assert stats["pipeline"]["total_processed"] == 5 await client.close() @pytest.mark.anyio async def test_http_worker_stats_client_error() -> None: async def failing_handler(request: httpx2.Request) -> httpx2.Response: return httpx2.Response(status_code=500) transport = httpx2.MockTransport(failing_handler) client = HttpWorkerStatsClient( url="http://test-worker/internal/stats", timeout_seconds=2.0, ) client._client = httpx2.AsyncClient(transport=transport) with pytest.raises(WorkerStatsUnavailable) as exc_info: await client.get_stats() assert "Unable to retrieve worker stats" in str(exc_info.value) await client.close() def test_api_observability_router_metrics() -> None: metrics = ApiMetrics() stats = ApiRuntimeStats() class DummyWorkerStatsProvider: async def get_stats(self) -> dict: return {} async def close(self) -> None: pass app = FastAPI() router = create_observability_router( metrics=metrics, stats=stats, worker_stats_provider=DummyWorkerStatsProvider(), ) app.include_router(router) client = TestClient(app) response = client.get("/metrics") assert response.status_code == 200 assert CONTENT_TYPE_LATEST in response.headers["content-type"] assert "cdr_api_received_total" in response.text def test_api_observability_router_stats_aggregation() -> None: metrics = ApiMetrics() stats = ApiRuntimeStats() stats.record_received() stats.record_received() class FakeProvider: async def get_stats(self) -> dict: return { "pipeline": { "total_processed": 15, "total_dlq": 3, "in_flight_buffer": 0, "last_flush_at": "2026-09-22T10:00:00Z", }, "breakdown": { "by_service_type": {"4G_DATA": 15}, "by_radio_access_type": {"LTE_4G": 15}, "dlq_by_reason": {}, }, } async def close(self) -> None: pass app = FastAPI() router = create_observability_router( metrics=metrics, stats=stats, worker_stats_provider=FakeProvider(), ) app.include_router(router) client = TestClient(app) response = client.get("/stats") assert response.status_code == 200 body = response.json() assert body["pipeline"]["total_received"] == 2 assert body["pipeline"]["total_processed"] == 15 assert body["pipeline"]["total_dlq"] == 3