"""Integration tests for the Risk Engine API — all 4 frontend-facing endpoints. Validates every endpoint the frontend calls against the live sandbox with deterministic seed data. Uses the ``risk_client`` and ``seed_ids`` fixtures from conftest.py. Routes are at the root level (no prefix): /health, /evaluate, /approvals/pending, /approvals/{id}/review """ import pytest pytestmark = pytest.mark.asyncio # --------------------------------------------------------------------------- # 1 Health Check # --------------------------------------------------------------------------- class TestRiskHealth: """Endpoint: GET /health.""" async def test_health(self, risk_client): """GET /health — returns {"status": "ok"}.""" resp = await risk_client.get("/health") assert resp.status_code == 200 data = resp.json() assert data["status"] == "ok" # --------------------------------------------------------------------------- # 2 Evaluate Order # --------------------------------------------------------------------------- class TestRiskEvaluate: """Endpoint: POST /evaluate.""" async def test_evaluate_order(self, risk_client): """POST /evaluate — evaluate a proposed order and verify response structure.""" payload = { "order": { "ticker": "AAPL", "action": "buy", "quantity": 10, "estimated_value": 1855.00, "confidence": 0.85, "recommendation_id": None, "sector": "Technology", }, "config": None, "state": None, } resp = await risk_client.post("/evaluate", json=payload) assert resp.status_code == 200 data = resp.json() # Core RiskEvaluation fields assert "evaluation_id" in data assert "ticker" in data assert data["ticker"] == "AAPL" assert "eligible" in data assert isinstance(data["eligible"], bool) assert "rejection_reasons" in data assert isinstance(data["rejection_reasons"], list) assert "checks" in data assert isinstance(data["checks"], list) assert "evaluated_at" in data async def test_evaluate_order_minimal(self, risk_client): """POST /evaluate — minimal order with only required fields.""" payload = { "order": { "ticker": "MSFT", }, } resp = await risk_client.post("/evaluate", json=payload) assert resp.status_code == 200 data = resp.json() assert "evaluation_id" in data assert "eligible" in data assert "rejection_reasons" in data # --------------------------------------------------------------------------- # 3 Pending Approvals # --------------------------------------------------------------------------- class TestRiskApprovalsPending: """Endpoint: GET /approvals/pending.""" async def test_list_pending_approvals(self, risk_client): """GET /approvals/pending — returns 200 with a list (may be empty in sandbox).""" resp = await risk_client.get("/approvals/pending") assert resp.status_code == 200 data = resp.json() assert isinstance(data, list) # --------------------------------------------------------------------------- # 4 Review Approval # --------------------------------------------------------------------------- class TestRiskApprovalReview: """Endpoint: POST /approvals/{id}/review.""" async def test_review_nonexistent_approval(self, risk_client): """POST /approvals/{id}/review — 404 for a non-existent approval ID.""" fake_id = "00000000-0000-4000-ffff-000000000099" payload = { "approved": True, "reviewed_by": "test-operator", "review_note": "integration test", } resp = await risk_client.post( f"/approvals/{fake_id}/review", json=payload, ) assert resp.status_code == 404