"""Tests for the Query API app structure and helper functions.""" import json from datetime import datetime, timezone import pytest from services.api.app import _parse_jsonb, _row_to_dict, app # --- _parse_jsonb --- def test_parse_jsonb_dict(): assert _parse_jsonb({"a": 1}) == {"a": 1} def test_parse_jsonb_list(): assert _parse_jsonb([1, 2]) == [1, 2] def test_parse_jsonb_string(): assert _parse_jsonb('{"x": 1}') == {"x": 1} def test_parse_jsonb_list_string(): assert _parse_jsonb('["a", "b"]') == ["a", "b"] def test_parse_jsonb_none(): assert _parse_jsonb(None) is None def test_parse_jsonb_invalid_string(): assert _parse_jsonb("not json") == "not json" # --- _row_to_dict --- class FakeRecord(dict): """Mimics asyncpg.Record enough for _row_to_dict.""" def items(self): return super().items() def test_row_to_dict_converts_datetime(): dt = datetime(2026, 4, 11, 12, 0, 0, tzinfo=timezone.utc) row = FakeRecord({"created_at": dt, "name": "test"}) result = _row_to_dict(row) assert result["created_at"] == dt.isoformat() assert result["name"] == "test" def test_row_to_dict_passes_primitives(): row = FakeRecord({"count": 42, "active": True, "label": "ok", "val": None}) result = _row_to_dict(row) assert result == {"count": 42, "active": True, "label": "ok", "val": None} # --- App structure --- def test_app_has_expected_routes(): paths = [route.path for route in app.routes] assert "/health" in paths assert "/api/companies" in paths assert "/api/companies/{company_id}" in paths assert "/api/documents" in paths assert "/api/documents/{document_id}" in paths assert "/api/trends" in paths assert "/api/trends/{trend_id}" in paths assert "/api/trends/{trend_id}/evidence" in paths assert "/api/recommendations" in paths assert "/api/recommendations/{recommendation_id}" in paths assert "/api/recommendations/{recommendation_id}/evidence" in paths assert "/api/orders" in paths assert "/api/orders/{order_id}" in paths assert "/api/positions" in paths assert "/api/audit/{entity_type}/{entity_id}" in paths def test_app_has_admin_routes(): paths = [route.path for route in app.routes] # Source health assert "/api/admin/sources/health" in paths assert "/api/admin/sources/{source_id}/runs" in paths assert "/api/admin/sources/{source_id}/toggle" in paths assert "/api/admin/sources/{source_id}/credibility" in paths # Symbol configs assert "/api/admin/companies/{company_id}/toggle" in paths assert "/api/admin/companies/{company_id}/sector" in paths assert "/api/admin/companies/coverage" in paths # Trading mode assert "/api/admin/trading/config" in paths assert "/api/admin/trading/mode" in paths assert "/api/admin/trading/approvals" in paths assert "/api/admin/trading/approvals/{approval_id}" in paths assert "/api/admin/trading/lockouts" in paths # Macro toggle assert "/api/admin/macro/status" in paths assert "/api/admin/macro/toggle" in paths def test_app_has_macro_routes(): paths = [route.path for route in app.routes] assert "/api/macro/events" in paths assert "/api/macro/events/{event_id}" in paths assert "/api/macro/impacts/{ticker}" in paths assert "/api/trends/{trend_id}/projection" in paths def test_app_has_ops_dashboard_routes(): paths = [route.path for route in app.routes] assert "/api/ops/ingestion/throughput" in paths assert "/api/ops/ingestion/summary" in paths assert "/api/ops/model/failures" in paths assert "/api/ops/model/performance" in paths assert "/api/ops/pipeline/health" in paths assert "/api/ops/sources/coverage-gaps" in paths