Files
stonks-oracle/tests/integration/test_risk_approval_lifecycle.py
Celes Renata 898f89926d feat: beta API integration test suite — 85 new tests across 6 modules
Extends integration test coverage from 108 to 193 tests for the beta gate.

New test modules:
- test_query_api_extended.py (33 tests): documents, evidence, macro/competitive, ops/admin, agents, analytics
- test_registry_write_paths.py (16 tests): write paths, validation, duplicates, competitor/exposure CRUD
- test_risk_approval_lifecycle.py (8 tests): evaluation edge cases, full approval lifecycle
- test_trading_extended.py (12 tests): config round-trips, decision filtering, override validation
- test_cross_service_roundtrip.py (4 tests): cross-service data consistency
- test_error_handling.py (12 tests): 404s, 422s, empty states, health checks

Seed script extended with watchlists, approvals, lockouts, notifications,
ingestion runs, saved queries, and daily risk snapshots.
2026-04-20 02:34:19 +00:00

137 lines
5.0 KiB
Python

"""Integration tests for Risk Engine — evaluation edge cases and approval lifecycle.
Validates evaluation with minimal/extreme orders, custom config overrides,
and the full approval lifecycle (list → detail → review → expire) against
the live sandbox with deterministic seed data.
Uses the ``risk_client`` and ``seed_ids`` fixtures from conftest.py.
"""
import pytest
pytestmark = pytest.mark.asyncio
# ---------------------------------------------------------------------------
# 1 Evaluation Edge Cases
# ---------------------------------------------------------------------------
class TestRiskEvaluationEdgeCases:
"""Edge-case scenarios for POST /evaluate."""
async def test_minimal_order_evaluation(self, risk_client):
"""POST /evaluate — minimal order with only ticker."""
payload = {"order": {"ticker": "AAPL"}}
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
async def test_order_exceeding_position_cap(self, risk_client):
"""POST /evaluate — order exceeding position cap returns eligible: false."""
payload = {
"order": {
"ticker": "AAPL",
"action": "buy",
"quantity": 100000,
"estimated_value": 18550000.00,
"confidence": 0.5,
"sector": "Technology",
},
}
resp = await risk_client.post("/evaluate", json=payload)
assert resp.status_code == 200
data = resp.json()
assert data["eligible"] is False
assert len(data["rejection_reasons"]) >= 1
async def test_evaluation_with_custom_config(self, risk_client):
"""POST /evaluate — custom config override."""
payload = {
"order": {
"ticker": "MSFT",
"action": "buy",
"quantity": 5,
"estimated_value": 2050.00,
"confidence": 0.8,
},
"config": {
"max_portfolio_heat": 0.50,
"max_single_position_pct": 0.10,
"max_sector_concentration": 0.50,
"daily_loss_limit_pct": 0.05,
},
}
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
# ---------------------------------------------------------------------------
# 2 Approval Lifecycle
# ---------------------------------------------------------------------------
class TestRiskApprovalLifecycle:
"""Full approval lifecycle: list → detail → review → expire."""
async def test_pending_approvals_list(self, risk_client, seed_ids):
"""GET /approvals/pending — list pending approvals from seed."""
resp = await risk_client.get("/approvals/pending")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
assert len(data) >= 1
async def test_approval_detail(self, risk_client, seed_ids):
"""GET /approvals/{id} — seeded pending approval detail."""
approval_id = seed_ids["approvals"]["PENDING"]
resp = await risk_client.get(f"/approvals/{approval_id}")
assert resp.status_code == 200
data = resp.json()
assert data["ticker"] == "AAPL"
assert data["side"] == "buy"
assert "status" in data
assert "expires_at" in data
async def test_approval_review(self, risk_client, seed_ids):
"""POST /approvals/{id}/review — approve the seeded pending approval."""
approval_id = seed_ids["approvals"]["PENDING"]
payload = {
"approved": True,
"reviewed_by": "test-operator",
"review_note": "Integration test approval",
}
resp = await risk_client.post(
f"/approvals/{approval_id}/review", json=payload,
)
assert resp.status_code == 200
data = resp.json()
assert data["status"] == "approved"
async def test_review_nonexistent_approval(self, risk_client):
"""POST /approvals/{id}/review — 404 for non-existent approval."""
fake_id = "00000000-0000-4000-ffff-000000000099"
payload = {
"approved": True,
"reviewed_by": "test-operator",
"review_note": "Should fail",
}
resp = await risk_client.post(
f"/approvals/{fake_id}/review", json=payload,
)
assert resp.status_code == 404
async def test_approval_expiry(self, risk_client):
"""POST /approvals/expire — expire stale approvals."""
resp = await risk_client.post("/approvals/expire")
assert resp.status_code == 200
data = resp.json()
assert "expired" in data
assert isinstance(data["expired"], int)