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
+288
View File
@@ -0,0 +1,288 @@
"""Integration tests for the Query API — all 17 frontend-facing endpoints.
Validates every GET endpoint the frontend calls against the live sandbox
with deterministic seed data. Uses the ``query_client`` and ``seed_ids``
fixtures from conftest.py.
"""
import pytest
pytestmark = pytest.mark.asyncio
# ---------------------------------------------------------------------------
# 13 Companies
# ---------------------------------------------------------------------------
class TestQueryAPICompanies:
"""Endpoints: /api/companies, /api/companies/{id}, /api/companies/{id}/sources."""
async def test_list_companies(self, query_client, seed_ids):
"""GET /api/companies — expect at least 5 seeded companies."""
resp = await query_client.get("/api/companies")
assert resp.status_code == 200
data = resp.json()
assert len(data) >= 5
tickers = {c["ticker"] for c in data}
assert {"AAPL", "MSFT", "JPM", "JNJ", "XOM"} <= tickers
# Every company row must have core fields
for c in data:
assert "id" in c
assert "legal_name" in c
assert "sector" in c
async def test_get_company(self, query_client, seed_ids):
"""GET /api/companies/{id} — detail for AAPL."""
company_id = seed_ids["companies"]["AAPL"]
resp = await query_client.get(f"/api/companies/{company_id}")
assert resp.status_code == 200
data = resp.json()
assert data["ticker"] == "AAPL"
assert data["legal_name"] == "Apple Inc"
assert "aliases" in data
assert "active_source_count" in data
async def test_list_company_sources(self, query_client, seed_ids):
"""GET /api/companies/{id}/sources — AAPL has at least 1 source."""
company_id = seed_ids["companies"]["AAPL"]
resp = await query_client.get(f"/api/companies/{company_id}/sources")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
assert len(data) >= 1
assert "source_type" in data[0]
# ---------------------------------------------------------------------------
# 45 Documents
# ---------------------------------------------------------------------------
class TestQueryAPIDocuments:
"""Endpoints: /api/documents, /api/documents/{id}."""
async def test_list_documents(self, query_client, seed_ids):
"""GET /api/documents — expect at least 10 seeded documents."""
resp = await query_client.get("/api/documents")
assert resp.status_code == 200
data = resp.json()
assert len(data) >= 10
for doc in data:
assert "id" in doc
assert "document_type" in doc
assert "title" in doc
async def test_get_document(self, query_client, seed_ids):
"""GET /api/documents/{id} — detail with intelligence."""
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
assert "document_type" in data
# Intelligence extraction should be present for seeded docs
assert "intelligence" in data
assert "company_mentions" in data
# ---------------------------------------------------------------------------
# 67 Trends
# ---------------------------------------------------------------------------
class TestQueryAPITrends:
"""Endpoints: /api/trends, /api/trends/{id}."""
async def test_list_trends(self, query_client, seed_ids):
"""GET /api/trends — expect at least 5 seeded trend windows."""
resp = await query_client.get("/api/trends")
assert resp.status_code == 200
data = resp.json()
assert len(data) >= 5
for t in data:
assert "id" in t
assert "trend_direction" in t
assert "confidence" in t
async def test_get_trend(self, query_client, seed_ids):
"""GET /api/trends/{id} — detail for first seeded trend."""
trend_id = seed_ids["trends"]["TREND_01"]
resp = await query_client.get(f"/api/trends/{trend_id}")
assert resp.status_code == 200
data = resp.json()
assert data["id"] == trend_id
assert data["trend_direction"] in ("bullish", "bearish", "mixed")
assert 0 <= data["confidence"] <= 1
# ---------------------------------------------------------------------------
# 89 Recommendations
# ---------------------------------------------------------------------------
class TestQueryAPIRecommendations:
"""Endpoints: /api/recommendations, /api/recommendations/{id}."""
async def test_list_recommendations(self, query_client, seed_ids):
"""GET /api/recommendations — expect at least 5 seeded recs."""
resp = await query_client.get("/api/recommendations", params={"latest": "false"})
assert resp.status_code == 200
data = resp.json()
assert len(data) >= 5
for r in data:
assert "id" in r
assert "ticker" in r
assert "action" in r
assert "confidence" in r
async def test_get_recommendation(self, query_client, seed_ids):
"""GET /api/recommendations/{id} — detail with evidence."""
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 "thesis" in data
assert "evidence" in data
assert "risk_evaluation" in data
# ---------------------------------------------------------------------------
# 1011 Orders
# ---------------------------------------------------------------------------
class TestQueryAPIOrders:
"""Endpoints: /api/orders, /api/orders/{id}."""
async def test_list_orders(self, query_client, seed_ids):
"""GET /api/orders — expect at least 3 seeded orders."""
resp = await query_client.get("/api/orders")
assert resp.status_code == 200
data = resp.json()
assert len(data) >= 3
statuses = {o["status"] for o in data}
# Seed has filled, pending, cancelled
assert len(statuses) >= 2
for o in data:
assert "id" in o
assert "ticker" in o
assert "side" in o
async def test_get_order(self, query_client, seed_ids):
"""GET /api/orders/{id} — detail with events and audit trail."""
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 "ticker" in data
assert "events" in data
assert isinstance(data["events"], list)
assert "audit_trail" in data
# ---------------------------------------------------------------------------
# 12 Positions
# ---------------------------------------------------------------------------
class TestQueryAPIPositions:
"""Endpoint: /api/positions."""
async def test_list_positions(self, query_client, seed_ids):
"""GET /api/positions — expect at least 2 seeded positions."""
resp = await query_client.get("/api/positions")
assert resp.status_code == 200
data = resp.json()
assert len(data) >= 2
for p in data:
assert "id" in p
assert "ticker" in p
assert "quantity" in p
assert "unrealized_pnl" in p
# ---------------------------------------------------------------------------
# 13 Pipeline Health
# ---------------------------------------------------------------------------
class TestQueryAPIOps:
"""Endpoints: /api/ops/pipeline/health, /api/ops/ingestion/summary, /api/ops/sources/coverage-gaps."""
async def test_pipeline_health(self, query_client, seed_ids):
"""GET /api/ops/pipeline/health — returns structured health data."""
resp = await query_client.get("/api/ops/pipeline/health")
assert resp.status_code == 200
data = resp.json()
assert "hours" in data
assert "document_stages" in data
assert "parsing" in data
assert "extraction" in data
assert "aggregation" in data
assert "queue_depths" in data
# -----------------------------------------------------------------------
# 14 Ingestion Summary
# -----------------------------------------------------------------------
async def test_ingestion_summary(self, query_client, seed_ids):
"""GET /api/ops/ingestion/summary — returns ingestion stats."""
resp = await query_client.get("/api/ops/ingestion/summary")
assert resp.status_code == 200
data = resp.json()
assert "hours" in data
assert "total_runs" in data
assert "by_source_type" in data
assert isinstance(data["by_source_type"], list)
# -----------------------------------------------------------------------
# 15 Coverage Gaps
# -----------------------------------------------------------------------
async def test_coverage_gaps(self, query_client, seed_ids):
"""GET /api/ops/sources/coverage-gaps — returns gap analysis."""
resp = await query_client.get("/api/ops/sources/coverage-gaps")
assert resp.status_code == 200
data = resp.json()
assert "missing_source_types" in data
assert "stale_sources" in data
assert isinstance(data["missing_source_types"], list)
assert isinstance(data["stale_sources"], list)
# ---------------------------------------------------------------------------
# 1617 Agents & Variants
# ---------------------------------------------------------------------------
class TestQueryAPIAgents:
"""Endpoints: /api/agents, /api/agents/{id}/variants."""
async def test_list_agents(self, query_client, seed_ids):
"""GET /api/agents — expect at least 3 seeded agents."""
resp = await query_client.get("/api/agents")
assert resp.status_code == 200
data = resp.json()
assert len(data) >= 3
for a in data:
assert "id" in a
assert "name" in a
assert "slug" in a
async def test_list_agent_variants(self, query_client, seed_ids):
"""GET /api/agents/{id}/variants — variants for the extractor agent."""
agent_id = seed_ids["agents"]["extractor"]
resp = await query_client.get(f"/api/agents/{agent_id}/variants")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
assert len(data) >= 1
for v in data:
assert "id" in v
assert "variant_name" in v
assert v["agent_id"] == agent_id