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.
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
"""Integration tests for cross-service data consistency.
|
||||
|
||||
These tests validate round-trip behavior: writing data via one service
|
||||
and reading it back via another (or the same service on a different path).
|
||||
They catch data propagation issues and schema drift between services.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cross-Service: Company creation via Registry → read via Query API
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestCrossServiceCompanyRoundTrip:
|
||||
"""Write company via registry, verify visibility in query API."""
|
||||
|
||||
async def test_create_company_via_registry_read_via_query(
|
||||
self, registry_client, query_client,
|
||||
):
|
||||
"""Create company via registry, read via query API."""
|
||||
payload = {
|
||||
"ticker": "XRND",
|
||||
"legal_name": "Cross Round Trip Corp",
|
||||
"exchange": "NYSE",
|
||||
"sector": "Technology",
|
||||
"industry": "Software",
|
||||
"market_cap_bucket": "small",
|
||||
}
|
||||
create_resp = await registry_client.post("/companies", json=payload)
|
||||
assert create_resp.status_code == 201
|
||||
|
||||
# Read via query API
|
||||
query_resp = await query_client.get("/api/companies")
|
||||
assert query_resp.status_code == 200
|
||||
tickers = {c["ticker"] for c in query_resp.json()}
|
||||
assert "XRND" in tickers
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cross-Service: Exposure profile round-trip via Registry
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestCrossServiceExposureRoundTrip:
|
||||
"""PUT exposure via registry, GET via registry on a different path."""
|
||||
|
||||
async def test_exposure_round_trip(self, registry_client, seed_ids):
|
||||
"""PUT exposure via registry → GET via registry."""
|
||||
company_id = seed_ids["companies"]["MSFT"]
|
||||
payload = {
|
||||
"geographic_revenue_mix": {
|
||||
"North America": 0.55,
|
||||
"Europe": 0.25,
|
||||
"Asia": 0.20,
|
||||
},
|
||||
"supply_chain_regions": ["North America", "Europe"],
|
||||
"key_input_commodities": [],
|
||||
"regulatory_jurisdictions": ["US", "EU"],
|
||||
"market_position_tier": "global_leader",
|
||||
"export_dependency_pct": 0.35,
|
||||
"source": "manual",
|
||||
"confidence": 0.85,
|
||||
}
|
||||
put_resp = await registry_client.put(
|
||||
f"/companies/{company_id}/exposure", json=payload,
|
||||
)
|
||||
assert put_resp.status_code == 200
|
||||
|
||||
# Read back
|
||||
get_resp = await registry_client.get(
|
||||
f"/companies/{company_id}/exposure",
|
||||
)
|
||||
assert get_resp.status_code == 200
|
||||
data = get_resp.json()
|
||||
assert data["geographic_revenue_mix"]["North America"] == 0.55
|
||||
assert data["market_position_tier"] == "global_leader"
|
||||
assert "supply_chain_regions" in data
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cross-Service: Competitor relationship bidirectional visibility
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestCrossServiceCompetitorBidirectional:
|
||||
"""Competitor relationships visible from both sides."""
|
||||
|
||||
async def test_competitor_bidirectional_visibility(
|
||||
self, registry_client, seed_ids,
|
||||
):
|
||||
"""Competitor visible from both sides."""
|
||||
aapl_id = seed_ids["companies"]["AAPL"]
|
||||
msft_id = seed_ids["companies"]["MSFT"]
|
||||
|
||||
# Check from AAPL side
|
||||
resp_a = await registry_client.get(
|
||||
f"/companies/{aapl_id}/competitors",
|
||||
)
|
||||
assert resp_a.status_code == 200
|
||||
a_partners = set()
|
||||
for rel in resp_a.json():
|
||||
if rel["company_a_id"] == aapl_id:
|
||||
a_partners.add(rel["company_b_id"])
|
||||
else:
|
||||
a_partners.add(rel["company_a_id"])
|
||||
assert msft_id in a_partners
|
||||
|
||||
# Check from MSFT side
|
||||
resp_b = await registry_client.get(
|
||||
f"/companies/{msft_id}/competitors",
|
||||
)
|
||||
assert resp_b.status_code == 200
|
||||
b_partners = set()
|
||||
for rel in resp_b.json():
|
||||
if rel["company_a_id"] == msft_id:
|
||||
b_partners.add(rel["company_b_id"])
|
||||
else:
|
||||
b_partners.add(rel["company_a_id"])
|
||||
assert aapl_id in b_partners
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cross-Service: Risk evaluation schema matches query API recommendations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestCrossServiceRiskEvaluationSchema:
|
||||
"""Risk evaluation schema matches what query API returns for recommendations."""
|
||||
|
||||
async def test_risk_evaluation_matches_recommendation(
|
||||
self, risk_client, query_client, seed_ids,
|
||||
):
|
||||
"""Risk evaluation schema matches what query API returns for recommendations."""
|
||||
# Evaluate via risk engine
|
||||
eval_resp = await risk_client.post("/evaluate", json={
|
||||
"order": {
|
||||
"ticker": "AAPL",
|
||||
"action": "buy",
|
||||
"quantity": 10,
|
||||
"estimated_value": 1855.00,
|
||||
"confidence": 0.85,
|
||||
},
|
||||
})
|
||||
assert eval_resp.status_code == 200
|
||||
eval_data = eval_resp.json()
|
||||
assert "evaluation_id" in eval_data
|
||||
assert "eligible" in eval_data
|
||||
assert "checks" in eval_data
|
||||
|
||||
# Query recommendation with risk evaluation
|
||||
rec_id = seed_ids["recommendations"]["REC_01"]
|
||||
rec_resp = await query_client.get(f"/api/recommendations/{rec_id}")
|
||||
assert rec_resp.status_code == 200
|
||||
rec_data = rec_resp.json()
|
||||
assert "risk_evaluation" in rec_data
|
||||
Reference in New Issue
Block a user