fix: clean up utcnow deprecation warnings, fix 12 failing tests, add CI/CD pipeline manifests

- Replace all datetime.utcnow() with datetime.now(tz=timezone.utc) across 8 files
- Fix 12 failing tests to match current implementation behavior
- Fix pytest_plugins in non-top-level conftest (moved to root conftest.py)
- Auto-fix 189 lint issues (import sorting, unused imports)
- Add CI/CD pipeline infrastructure (ARC, ArgoCD, Kargo manifests)
- Add values-beta.yaml and values-paper.yaml for staged deployments
- Update GitHub Actions workflow to use self-hosted-gremlin runners
- Add integration-test job to CI pipeline

Result: 1596 passed, 0 failed, 0 warnings
This commit is contained in:
Celes Renata
2026-04-18 03:59:28 +00:00
parent 40227a4eb2
commit c85c0068a2
123 changed files with 7221 additions and 405 deletions
@@ -0,0 +1,430 @@
"""Frontend data dependency tests — verify every page's API calls return valid data.
Each test function represents one frontend page and calls all the API
endpoints that page depends on. For each endpoint we assert:
• HTTP 200
• Response is non-empty (list has items, or dict has expected keys)
Uses ``query_client``, ``registry_client``, ``trading_client``, and
``seed_ids`` fixtures from conftest.py.
"""
import pytest
pytestmark = pytest.mark.asyncio
# ---------------------------------------------------------------------------
# 1 Home
# ---------------------------------------------------------------------------
class TestHomePage:
"""Home page depends on: companies, pipeline health, ingestion summary, recommendations."""
async def test_home_page_deps(self, query_client, seed_ids):
# /api/companies
resp = await query_client.get("/api/companies")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list) and len(data) >= 1
# /api/ops/pipeline/health
resp = await query_client.get("/api/ops/pipeline/health")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict) and "document_stages" in data
# /api/ops/ingestion/summary
resp = await query_client.get("/api/ops/ingestion/summary")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict) and "total_runs" in data
# /api/recommendations
resp = await query_client.get("/api/recommendations")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list) and len(data) >= 1
# ---------------------------------------------------------------------------
# 2 Companies
# ---------------------------------------------------------------------------
class TestCompaniesPage:
"""Companies list page depends on: companies (query API)."""
async def test_companies_page_deps(self, query_client, seed_ids):
resp = await query_client.get("/api/companies")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list) and len(data) >= 5
# ---------------------------------------------------------------------------
# 3 CompanyDetail
# ---------------------------------------------------------------------------
class TestCompanyDetailPage:
"""CompanyDetail depends on: company, sources, trends, recommendations,
competitors (registry), exposure (registry), macro-impacts."""
async def test_company_detail_page_deps(
self, query_client, registry_client, seed_ids,
):
company_id = seed_ids["companies"]["AAPL"]
# /api/companies/{id}
resp = await query_client.get(f"/api/companies/{company_id}")
assert resp.status_code == 200
data = resp.json()
assert data["ticker"] == "AAPL"
# /api/companies/{id}/sources
resp = await query_client.get(f"/api/companies/{company_id}/sources")
assert resp.status_code == 200
assert isinstance(resp.json(), list)
# /api/trends?ticker=AAPL
resp = await query_client.get("/api/trends", params={"ticker": "AAPL"})
assert resp.status_code == 200
assert isinstance(resp.json(), list)
# /api/recommendations?ticker=AAPL
resp = await query_client.get("/api/recommendations", params={"ticker": "AAPL"})
assert resp.status_code == 200
assert isinstance(resp.json(), list)
# /companies/{id}/competitors (registry — no /api/ prefix)
resp = await registry_client.get(f"/companies/{company_id}/competitors")
assert resp.status_code == 200
assert isinstance(resp.json(), list)
# /companies/{id}/exposure (registry — no /api/ prefix)
resp = await registry_client.get(f"/companies/{company_id}/exposure")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict) and "company_id" in data
# /api/companies/AAPL/macro-impacts (query API via /api/macro/impacts/AAPL)
resp = await query_client.get("/api/macro/impacts/AAPL")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict)
# ---------------------------------------------------------------------------
# 4 Documents
# ---------------------------------------------------------------------------
class TestDocumentsPage:
"""Documents list page depends on: documents."""
async def test_documents_page_deps(self, query_client, seed_ids):
resp = await query_client.get("/api/documents")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list) and len(data) >= 1
# ---------------------------------------------------------------------------
# 5 DocumentDetail
# ---------------------------------------------------------------------------
class TestDocumentDetailPage:
"""DocumentDetail depends on: documents/{id}."""
async def test_document_detail_page_deps(self, query_client, seed_ids):
doc_id = seed_ids["documents"]["DOC_01"]
resp = await query_client.get(f"/api/documents/{doc_id}")
assert resp.status_code == 200
data = resp.json()
assert data["id"] == doc_id
assert "title" in data
# ---------------------------------------------------------------------------
# 6 Trends
# ---------------------------------------------------------------------------
class TestTrendsPage:
"""Trends list page depends on: trends."""
async def test_trends_page_deps(self, query_client, seed_ids):
resp = await query_client.get("/api/trends")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list) and len(data) >= 1
# ---------------------------------------------------------------------------
# 7 TrendDetail
# ---------------------------------------------------------------------------
class TestTrendDetailPage:
"""TrendDetail depends on: trends/{id}, trends/{id}/projection."""
async def test_trend_detail_page_deps(self, query_client, seed_ids):
trend_id = seed_ids["trends"]["TREND_01"]
# /api/trends/{id}
resp = await query_client.get(f"/api/trends/{trend_id}")
assert resp.status_code == 200
data = resp.json()
assert data["id"] == trend_id
assert "trend_direction" in data
# /api/trends/{id}/projection
resp = await query_client.get(f"/api/trends/{trend_id}/projection")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict)
assert "projected_direction" in data
# ---------------------------------------------------------------------------
# 8 Recommendations
# ---------------------------------------------------------------------------
class TestRecommendationsPage:
"""Recommendations list page depends on: recommendations."""
async def test_recommendations_page_deps(self, query_client, seed_ids):
resp = await query_client.get("/api/recommendations")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list) and len(data) >= 1
# ---------------------------------------------------------------------------
# 9 RecommendationDetail
# ---------------------------------------------------------------------------
class TestRecommendationDetailPage:
"""RecommendationDetail depends on: recommendations/{id}."""
async def test_recommendation_detail_page_deps(self, query_client, seed_ids):
rec_id = seed_ids["recommendations"]["REC_01"]
resp = await query_client.get(f"/api/recommendations/{rec_id}")
assert resp.status_code == 200
data = resp.json()
assert data["id"] == rec_id
assert "ticker" in data
assert "evidence" in data
# ---------------------------------------------------------------------------
# 10 Orders
# ---------------------------------------------------------------------------
class TestOrdersPage:
"""Orders list page depends on: orders."""
async def test_orders_page_deps(self, query_client, seed_ids):
resp = await query_client.get("/api/orders")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list) and len(data) >= 1
# ---------------------------------------------------------------------------
# 11 OrderDetail
# ---------------------------------------------------------------------------
class TestOrderDetailPage:
"""OrderDetail depends on: orders/{id}."""
async def test_order_detail_page_deps(self, query_client, seed_ids):
order_id = seed_ids["orders"]["ORDER_01"]
resp = await query_client.get(f"/api/orders/{order_id}")
assert resp.status_code == 200
data = resp.json()
assert data["id"] == order_id
assert "events" in data
# ---------------------------------------------------------------------------
# 12 Positions
# ---------------------------------------------------------------------------
class TestPositionsPage:
"""Positions page depends on: positions."""
async def test_positions_page_deps(self, query_client, seed_ids):
resp = await query_client.get("/api/positions")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list) and len(data) >= 1
# ---------------------------------------------------------------------------
# 13 GlobalEvents
# ---------------------------------------------------------------------------
class TestGlobalEventsPage:
"""GlobalEvents page depends on: macro/events."""
async def test_global_events_page_deps(self, query_client, seed_ids):
resp = await query_client.get("/api/macro/events")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list) and len(data) >= 1
# ---------------------------------------------------------------------------
# 14 GlobalEventDetail
# ---------------------------------------------------------------------------
class TestGlobalEventDetailPage:
"""GlobalEventDetail depends on: macro/events/{id}."""
async def test_global_event_detail_page_deps(self, query_client, seed_ids):
event_id = seed_ids["global_events"]["EVT_01"]
resp = await query_client.get(f"/api/macro/events/{event_id}")
assert resp.status_code == 200
data = resp.json()
assert data["id"] == event_id
assert "summary" in data
assert "impacts" in data
# ---------------------------------------------------------------------------
# 15 OpsPipeline
# ---------------------------------------------------------------------------
class TestOpsPipelinePage:
"""OpsPipeline page depends on: ops/pipeline/health."""
async def test_ops_pipeline_page_deps(self, query_client, seed_ids):
resp = await query_client.get("/api/ops/pipeline/health")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict) and "document_stages" in data
# ---------------------------------------------------------------------------
# 16 OpsIngestion
# ---------------------------------------------------------------------------
class TestOpsIngestionPage:
"""OpsIngestion page depends on: ingestion summary + throughput."""
async def test_ops_ingestion_page_deps(self, query_client, seed_ids):
# /api/ops/ingestion/summary
resp = await query_client.get("/api/ops/ingestion/summary")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict) and "total_runs" in data
# /api/ops/ingestion/throughput
resp = await query_client.get("/api/ops/ingestion/throughput")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
# ---------------------------------------------------------------------------
# 17 OpsCoverage
# ---------------------------------------------------------------------------
class TestOpsCoveragePage:
"""OpsCoverage page depends on: ops/sources/coverage-gaps."""
async def test_ops_coverage_page_deps(self, query_client, seed_ids):
resp = await query_client.get("/api/ops/sources/coverage-gaps")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict)
assert "missing_source_types" in data
assert "stale_sources" in data
# ---------------------------------------------------------------------------
# 18 Agents
# ---------------------------------------------------------------------------
class TestAgentsPage:
"""Agents page depends on: agents."""
async def test_agents_page_deps(self, query_client, seed_ids):
resp = await query_client.get("/api/agents")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list) and len(data) >= 1
# ---------------------------------------------------------------------------
# 19 TradingEngine
# ---------------------------------------------------------------------------
class TestTradingEnginePage:
"""TradingEngine page depends on: trading status, metrics, decisions."""
async def test_trading_engine_page_deps(self, trading_client, seed_ids):
# /api/trading/status
resp = await trading_client.get("/api/trading/status")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict) and "enabled" in data
# /api/trading/metrics
resp = await trading_client.get("/api/trading/metrics")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict) and "total_portfolio_value" in data
# /api/trading/decisions
resp = await trading_client.get("/api/trading/decisions")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
# ---------------------------------------------------------------------------
# 20 Trading
# ---------------------------------------------------------------------------
class TestTradingPage:
"""Trading page depends on: trading status."""
async def test_trading_page_deps(self, trading_client, seed_ids):
resp = await trading_client.get("/api/trading/status")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict) and "enabled" in data
# ---------------------------------------------------------------------------
# 21 Watchlists
# ---------------------------------------------------------------------------
class TestWatchlistsPage:
"""Watchlists page depends on: /watchlists (registry client)."""
async def test_watchlists_page_deps(self, registry_client, seed_ids):
resp = await registry_client.get("/watchlists")
assert resp.status_code == 200
data = resp.json()
# Watchlists may be empty — just verify 200 + list type
assert isinstance(data, list)