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:
@@ -0,0 +1,190 @@
|
||||
"""Shared pytest fixtures for integration tests.
|
||||
|
||||
Provides HTTP clients (httpx.AsyncClient) for each service, base URL
|
||||
fixtures driven by environment variables, and a seed_ids dict that
|
||||
re-exports every deterministic UUID from seed_sandbox.py.
|
||||
|
||||
When the ``profiler`` fixture is active (provided by conftest_profiling.py),
|
||||
each HTTP client is wrapped in :class:`ProfiledAsyncClient` so that every
|
||||
request is automatically timed and recorded.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from tests.integration.profiler import EndpointProfiler
|
||||
from tests.integration.seed_sandbox import (
|
||||
SEED_AGENT_IDS,
|
||||
SEED_BROKER_ACCOUNT_ID,
|
||||
SEED_COMPANY_IDS,
|
||||
SEED_DOCUMENT_IDS,
|
||||
SEED_GLOBAL_EVENT_IDS,
|
||||
SEED_ORDER_IDS,
|
||||
SEED_PORTFOLIO_SNAPSHOT_ID,
|
||||
SEED_POSITION_IDS,
|
||||
SEED_RECOMMENDATION_IDS,
|
||||
SEED_RISK_CONFIG_ID,
|
||||
SEED_TRADING_DECISION_ID,
|
||||
SEED_TREND_IDS,
|
||||
SEED_VARIANT_IDS,
|
||||
)
|
||||
|
||||
# Profiling plugin loaded via root conftest.py (pytest_plugins must be top-level)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ProfiledAsyncClient — transparent timing wrapper
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class ProfiledAsyncClient:
|
||||
"""Wraps :class:`httpx.AsyncClient` to record per-request timing.
|
||||
|
||||
Every HTTP method call (get, post, put, patch, delete, head, options)
|
||||
is automatically timed via :meth:`EndpointProfiler.track` using the
|
||||
pattern ``"METHOD /path"``.
|
||||
|
||||
Attribute access for anything not explicitly wrapped is forwarded to
|
||||
the underlying client so tests can still use ``client.base_url``,
|
||||
``client.headers``, etc.
|
||||
"""
|
||||
|
||||
def __init__(self, client: httpx.AsyncClient, profiler: EndpointProfiler) -> None:
|
||||
self._client = client
|
||||
self._profiler = profiler
|
||||
|
||||
# -- Proxied HTTP methods ------------------------------------------------
|
||||
|
||||
async def get(self, url: str, **kwargs: Any) -> httpx.Response:
|
||||
async with self._profiler.track(f"GET {url}"):
|
||||
return await self._client.get(url, **kwargs)
|
||||
|
||||
async def post(self, url: str, **kwargs: Any) -> httpx.Response:
|
||||
async with self._profiler.track(f"POST {url}"):
|
||||
return await self._client.post(url, **kwargs)
|
||||
|
||||
async def put(self, url: str, **kwargs: Any) -> httpx.Response:
|
||||
async with self._profiler.track(f"PUT {url}"):
|
||||
return await self._client.put(url, **kwargs)
|
||||
|
||||
async def patch(self, url: str, **kwargs: Any) -> httpx.Response:
|
||||
async with self._profiler.track(f"PATCH {url}"):
|
||||
return await self._client.patch(url, **kwargs)
|
||||
|
||||
async def delete(self, url: str, **kwargs: Any) -> httpx.Response:
|
||||
async with self._profiler.track(f"DELETE {url}"):
|
||||
return await self._client.delete(url, **kwargs)
|
||||
|
||||
async def head(self, url: str, **kwargs: Any) -> httpx.Response:
|
||||
async with self._profiler.track(f"HEAD {url}"):
|
||||
return await self._client.head(url, **kwargs)
|
||||
|
||||
async def options(self, url: str, **kwargs: Any) -> httpx.Response:
|
||||
async with self._profiler.track(f"OPTIONS {url}"):
|
||||
return await self._client.options(url, **kwargs)
|
||||
|
||||
# -- Transparent attribute forwarding ------------------------------------
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
return getattr(self._client, name)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# URL fixtures — read from env vars set by the runner Job (runner.yaml)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def query_api_url() -> str:
|
||||
"""Base URL for the Query API service."""
|
||||
return os.environ.get("QUERY_API_URL", "http://localhost:8000")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def registry_api_url() -> str:
|
||||
"""Base URL for the Symbol Registry service."""
|
||||
return os.environ.get("REGISTRY_API_URL", "http://localhost:8001")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def risk_api_url() -> str:
|
||||
"""Base URL for the Risk Engine service."""
|
||||
return os.environ.get("RISK_API_URL", "http://localhost:8002")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def trading_api_url() -> str:
|
||||
"""Base URL for the Trading Engine service."""
|
||||
return os.environ.get("TRADING_API_URL", "http://localhost:8003")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Async HTTP client fixtures — one per service, 30 s timeout
|
||||
# Wrapped with ProfiledAsyncClient for automatic timing collection.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def query_client(
|
||||
query_api_url: str, profiler: EndpointProfiler,
|
||||
) -> ProfiledAsyncClient:
|
||||
"""Profiled async HTTP client pointed at the Query API."""
|
||||
async with httpx.AsyncClient(base_url=query_api_url, timeout=30.0) as client:
|
||||
yield ProfiledAsyncClient(client, profiler)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def registry_client(
|
||||
registry_api_url: str, profiler: EndpointProfiler,
|
||||
) -> ProfiledAsyncClient:
|
||||
"""Profiled async HTTP client pointed at the Symbol Registry."""
|
||||
async with httpx.AsyncClient(base_url=registry_api_url, timeout=30.0) as client:
|
||||
yield ProfiledAsyncClient(client, profiler)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def risk_client(
|
||||
risk_api_url: str, profiler: EndpointProfiler,
|
||||
) -> ProfiledAsyncClient:
|
||||
"""Profiled async HTTP client pointed at the Risk Engine."""
|
||||
async with httpx.AsyncClient(base_url=risk_api_url, timeout=30.0) as client:
|
||||
yield ProfiledAsyncClient(client, profiler)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def trading_client(
|
||||
trading_api_url: str, profiler: EndpointProfiler,
|
||||
) -> ProfiledAsyncClient:
|
||||
"""Profiled async HTTP client pointed at the Trading Engine."""
|
||||
async with httpx.AsyncClient(base_url=trading_api_url, timeout=30.0) as client:
|
||||
yield ProfiledAsyncClient(client, profiler)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Seed ID lookup — single dict with all deterministic IDs from seed_sandbox
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def seed_ids() -> dict:
|
||||
"""All deterministic seed IDs for assertion in integration tests."""
|
||||
return {
|
||||
"companies": SEED_COMPANY_IDS,
|
||||
"documents": SEED_DOCUMENT_IDS,
|
||||
"trends": SEED_TREND_IDS,
|
||||
"recommendations": SEED_RECOMMENDATION_IDS,
|
||||
"orders": SEED_ORDER_IDS,
|
||||
"positions": SEED_POSITION_IDS,
|
||||
"global_events": SEED_GLOBAL_EVENT_IDS,
|
||||
"agents": SEED_AGENT_IDS,
|
||||
"variants": SEED_VARIANT_IDS,
|
||||
"broker_account_id": SEED_BROKER_ACCOUNT_ID,
|
||||
"trading_decision_id": SEED_TRADING_DECISION_ID,
|
||||
"portfolio_snapshot_id": SEED_PORTFOLIO_SNAPSHOT_ID,
|
||||
"risk_config_id": SEED_RISK_CONFIG_ID,
|
||||
}
|
||||
Reference in New Issue
Block a user