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
+130 -24
View File
@@ -120,48 +120,119 @@ Wraps each test with timing:
- Outputs a summary table at the end
- Flags any endpoint > 500ms as "slow"
### 6. Runner Script (`tests/integration/run_pipeline.sh`)
### 6. Runner Script (`infra/inttest/run_pipeline.sh`)
Orchestrates the full pipeline:
Standalone orchestration script with a well-defined CLI contract so any CI/CD system (or a human) can invoke it. The future CI/CD pipeline spec will call this script as a stage.
**CLI interface:**
```
Usage: bash infra/inttest/run_pipeline.sh [OPTIONS]
Options:
--image-tag TAG Docker image tag to deploy (default: latest)
--namespace NAME Override namespace name (default: stonks-inttest-<timestamp>)
--skip-teardown Leave namespace running after tests (for debugging)
--results-file PATH Path for JSON results output (default: inttest-results.json)
Exit codes:
0 All tests passed
1 One or more test failures
2 Infrastructure setup failure (postgres/redis/minio/services didn't start)
```
**JSON result contract** (`inttest-results.json`):
```json
{
"run_id": "stonks-inttest-1705312800",
"image_tag": "abc123",
"started_at": "2025-01-15T12:00:00Z",
"completed_at": "2025-01-15T12:07:30Z",
"exit_code": 0,
"stages": {
"infra_deploy": {"duration_s": 45.2, "status": "ok"},
"seed_data": {"duration_s": 8.1, "status": "ok"},
"service_deploy": {"duration_s": 32.5, "status": "ok"},
"integration_tests": {"duration_s": 28.3, "status": "ok"},
"teardown": {"duration_s": 5.0, "status": "ok"}
},
"tests": {
"total": 41,
"passed": 41,
"failed": 0,
"errors": 0
},
"profiling": {
"endpoints": {
"/api/companies": {"p50_ms": 12, "p95_ms": 25, "p99_ms": 45},
...
},
"slow_endpoints": []
}
}
```
This contract is designed so the future CI/CD pipeline can:
1. Parse `exit_code` to decide whether to promote to the next stage
2. Parse `profiling.slow_endpoints` to flag performance regressions
3. Archive the full JSON as a build artifact
4. Display `tests.passed`/`tests.failed` in a dashboard
```bash
#!/bin/bash
set -euo pipefail
# Parse CLI args
IMAGE_TAG="latest"
NAMESPACE="stonks-inttest-$(date +%s)"
PROFILING_OUTPUT="inttest-results-${NAMESPACE}.json"
SKIP_TEARDOWN=false
RESULTS_FILE="inttest-results.json"
while [[ $# -gt 0 ]]; do
case $1 in
--image-tag) IMAGE_TAG="$2"; shift 2 ;;
--namespace) NAMESPACE="$2"; shift 2 ;;
--skip-teardown) SKIP_TEARDOWN=true; shift ;;
--results-file) RESULTS_FILE="$2"; shift 2 ;;
*) echo "Unknown option: $1"; exit 2 ;;
esac
done
# Cleanup function (always runs, even on failure)
cleanup() {
if [ "$SKIP_TEARDOWN" = false ]; then
kubectl delete namespace "$NAMESPACE" --wait=false 2>/dev/null || true
fi
}
trap cleanup EXIT
# Stage 1: Create namespace
kubectl create namespace $NAMESPACE
kubectl create namespace "$NAMESPACE"
# Stage 2: Deploy infra
envsubst < infra/inttest/postgres.yaml | kubectl apply -n $NAMESPACE -f -
envsubst < infra/inttest/redis.yaml | kubectl apply -n $NAMESPACE -f -
envsubst < infra/inttest/minio.yaml | kubectl apply -n $NAMESPACE -f -
kubectl wait --for=condition=ready pod -l app=postgres -n $NAMESPACE --timeout=120s
kubectl wait --for=condition=ready pod -l app=redis -n $NAMESPACE --timeout=60s
kubectl wait --for=condition=ready pod -l app=minio -n $NAMESPACE --timeout=60s
kubectl create configmap postgres-migrations --from-file=infra/migrations/ -n "$NAMESPACE"
export NAMESPACE
envsubst < infra/inttest/postgres.yaml | kubectl apply -n "$NAMESPACE" -f -
envsubst < infra/inttest/redis.yaml | kubectl apply -n "$NAMESPACE" -f -
envsubst < infra/inttest/minio.yaml | kubectl apply -n "$NAMESPACE" -f -
kubectl wait --for=condition=ready pod -l app=postgres -n "$NAMESPACE" --timeout=120s
kubectl wait --for=condition=ready pod -l app=redis -n "$NAMESPACE" --timeout=60s
kubectl wait --for=condition=ready pod -l app=minio -n "$NAMESPACE" --timeout=60s
# Stage 3: Run migrations + seed
kubectl run seed-runner --image=ghcr.io/celesrenata/stonks-oracle/query-api:latest \
-n $NAMESPACE --restart=Never --env="POSTGRES_HOST=postgres" ... \
-- python -c "import asyncio; from tests.integration.seed_sandbox import seed; asyncio.run(seed())"
kubectl wait --for=condition=complete job/seed-runner -n $NAMESPACE --timeout=120s
# Stage 3: Seed data (run from a pod with DB access)
# ... seed runner pod ...
# Stage 4: Deploy services
envsubst < infra/inttest/services.yaml | kubectl apply -n $NAMESPACE -f -
kubectl wait --for=condition=ready pod -l tier=api -n $NAMESPACE --timeout=120s
# Stage 4: Deploy services (using specified image tag)
envsubst < infra/inttest/services.yaml | sed "s/:latest/:${IMAGE_TAG}/g" | kubectl apply -n "$NAMESPACE" -f -
kubectl wait --for=condition=ready pod -l tier=api -n "$NAMESPACE" --timeout=120s
# Stage 5: Run integration tests
kubectl run test-runner --image=ghcr.io/celesrenata/stonks-oracle/query-api:latest \
-n $NAMESPACE --restart=Never \
-- python -m pytest tests/integration/ -v --tb=short
envsubst < infra/inttest/runner.yaml | sed "s/:latest/:${IMAGE_TAG}/g" | kubectl apply -n "$NAMESPACE" -f -
kubectl wait --for=condition=complete job/inttest-runner -n "$NAMESPACE" --timeout=600s
# Stage 6: Collect results
kubectl logs job/test-runner -n $NAMESPACE > $PROFILING_OUTPUT
kubectl logs job/inttest-runner -n "$NAMESPACE" > "$RESULTS_FILE"
# Stage 7: Teardown
kubectl delete namespace $NAMESPACE --wait=false
# Stage 7: Teardown (handled by trap)
```
## Profiling Strategy
@@ -217,3 +288,38 @@ CREATE namespace
→ Collect results
→ DELETE namespace (always, even on failure)
```
## Integration Contract for Future CI/CD Pipeline
This spec produces a standalone runner (`infra/inttest/run_pipeline.sh`) with a well-defined contract. A future spec ("CI/CD Deployment Pipeline") will consume it as one stage in a larger pipeline:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Future CI/CD Pipeline (separate spec) │
│ │
│ 1. Git push → webhook to self-hosted runner on gremlin nodes │
│ 2. Lint + Unit Tests (ruff, pytest, vitest) │
│ 3. Docker Build → push to GHCR (self-hosted, no GH Actions compute) │
│ 4. ┌──────────────────────────────────────────────────────────┐ │
│ │ Integration Tests (THIS SPEC) │ │
│ │ bash infra/inttest/run_pipeline.sh --image-tag $SHA │ │
│ │ → reads inttest-results.json │ │
│ │ → exit code 0 = promote, 1 = block │ │
│ └──────────────────────────────────────────────────────────┘ │
│ 5. Promote to beta namespace (if tests pass) │
│ 6. Promote to paper namespace (manual gate or auto) │
│ 7. Promote to live namespace (market-hours blocker + break-glass) │
│ │
│ Each stage has enable/disable toggle. │
│ Promotions blocked during market hours (9:3016:00 ET) unless │
│ break-glass is activated. │
└─────────────────────────────────────────────────────────────────────────┘
```
**What this spec provides to the future pipeline:**
- `infra/inttest/run_pipeline.sh` — callable with `--image-tag` to test any build
- `inttest-results.json` — machine-readable results for promotion decisions
- Exit codes for pass/fail gating
- `--skip-teardown` for debugging failed runs
- All K8s manifests in `infra/inttest/` for sandbox lifecycle
- Deterministic seed data and comprehensive API test coverage
@@ -5,16 +5,23 @@ End-to-end integration test pipeline that runs in Kubernetes, spinning up isolat
## Functional Requirements
### FR-1: Pipeline Stages
1. **Lint** — ruff check on Python, eslint on frontend
2. **Unit Tests** — pytest + vitest against local mocks
3. **Build** — Docker images for all services + dashboard
4. **Deploy Sandbox** — ephemeral namespace with own PostgreSQL, Redis, MinIO (no Ollama — too heavy for CI)
5. **Seed Data** — populate DB and S3 with enough data to exercise every frontend component
6. **Integration Tests** — HTTP-level validation of every API endpoint the frontend depends on
7. **Frontend E2E**render every page against the live sandbox APIs, assert no errors and expected data
8. **Profiling** — measure and report timing for each pipeline stage and each API endpoint
9. **Teardown** — delete the ephemeral namespace and all resources
### FR-1: Integration Test Stages
This spec covers the **integration test foundation** — sandbox infra, seed data, test suites, profiling, and a standalone runner script. A separate CI/CD pipeline spec will consume this foundation to provide build, staged promotion (beta → paper → live), market-hours gating, and break-glass deployment.
Stages owned by this spec:
1. **Deploy Sandbox** — ephemeral namespace with own PostgreSQL, Redis, MinIO (no Ollama — too heavy for CI)
2. **Seed Data** — populate DB and S3 with enough data to exercise every frontend component
3. **Integration Tests** — HTTP-level validation of every API endpoint the frontend depends on
4. **Frontend Data Deps**verify every page's API dependencies return valid data
5. **Profiling** — measure and report timing for each stage and each API endpoint
6. **Teardown** — delete the ephemeral namespace and all resources
Stages deferred to the CI/CD pipeline spec:
- Lint, unit tests, Docker image builds (self-hosted on gremlin nodes)
- Staged promotion: beta → paper → live namespaces
- Market-hours promotion blockers (no deploys during 9:3016:00 ET unless break-glass)
- Break-glass emergency production deploy
- Per-stage enable/disable toggles
### FR-2: Sandbox Infrastructure
- PostgreSQL 16 (ephemeral, no persistent volume)
@@ -72,5 +79,15 @@ Target: full pipeline completes in under 10 minutes. Seed data insertion under 3
### NFR-3: Reproducibility
Seed data is deterministic (fixed UUIDs, timestamps). No external API calls (Polygon, Alpaca). All data is synthetic.
### NFR-4: CI Integration
Pipeline can be triggered from GitHub Actions as a separate workflow, or manually via `kubectl apply`.
### NFR-4: Pipeline Integration Contract
The runner script is a standalone tool that can be invoked by any CI/CD system. It exposes:
- **CLI interface**: `bash infra/inttest/run_pipeline.sh [--image-tag TAG] [--namespace NAME] [--skip-teardown]`
- **Exit codes**: 0 = all tests passed, 1 = test failures, 2 = infra setup failure
- **JSON result file**: `inttest-results.json` with test counts, pass/fail, per-endpoint latency, stage timings
- **stdout/stderr**: human-readable progress and summary
A future CI/CD pipeline spec will invoke this script as a stage, passing in the image tag from a self-hosted build step. That spec will handle:
- Self-hosted build runners on gremlin nodes (no GitHub Actions compute)
- Staged promotion (beta → paper → live) with per-stage enable/disable
- Market-hours promotion blockers (9:3016:00 ET)
- Break-glass emergency deploy to production
+17 -18
View File
@@ -1,31 +1,30 @@
# Integration Test Pipeline — Tasks
## Phase 1: Sandbox Infrastructure Manifests
- [ ] 1. Create `infra/inttest/postgres.yaml` — PostgreSQL 16 Deployment with migrations as init container, no PV
- [ ] 2. Create `infra/inttest/redis.yaml` — Redis 7 Deployment, no persistence
- [ ] 3. Create `infra/inttest/minio.yaml` — MinIO Deployment + bucket init Job
- [ ] 4. Create `infra/inttest/services.yaml` — query-api, symbol-registry, risk, trading-engine Deployments pointing at sandbox infra
- [ ] 5. Create `infra/inttest/runner.yaml` — test runner Job template
- [x] 1. Create `infra/inttest/postgres.yaml` — PostgreSQL 16 Deployment with migrations as init container, no PV
- [x] 2. Create `infra/inttest/redis.yaml` — Redis 7 Deployment, no persistence
- [x] 3. Create `infra/inttest/minio.yaml` — MinIO Deployment + bucket init Job
- [x] 4. Create `infra/inttest/services.yaml` — query-api, symbol-registry, risk, trading-engine Deployments pointing at sandbox infra
- [x] 5. Create `infra/inttest/runner.yaml` — test runner Job template
## Phase 2: Seed Data
- [ ] 6. Create `tests/integration/seed_sandbox.py` — deterministic seed script with fixed UUIDs for 5 companies, 10 documents, 5 trends, 5 recommendations, 3 orders, 2 positions, 2 global events, 2 competitive signals, 3 agents, trading config, portfolio snapshot
- [ ] 7. Create `tests/integration/seed_minio.py` — seed MinIO buckets with sample normalized text files
- [x] 6. Create `tests/integration/seed_sandbox.py` — deterministic seed script with fixed UUIDs for 5 companies, 10 documents, 5 trends, 5 recommendations, 3 orders, 2 positions, 2 global events, 2 competitive signals, 3 agents, trading config, portfolio snapshot
- [x] 7. Create `tests/integration/seed_minio.py` — seed MinIO buckets with sample normalized text files
## Phase 3: API Integration Tests
- [ ] 8. Create `tests/integration/conftest.py` — pytest fixtures for HTTP client, base URLs, seed IDs
- [ ] 9. Create `tests/integration/test_query_api.py` — tests for all 17 query API endpoints
- [ ] 10. Create `tests/integration/test_registry_api.py` — tests for all 8 symbol registry endpoints
- [ ] 11. Create `tests/integration/test_risk_api.py` — tests for all 4 risk engine endpoints
- [ ] 12. Create `tests/integration/test_trading_api.py` — tests for all 12 trading engine endpoints
- [ ] 13. Create `tests/integration/test_frontend_data_deps.py` — tests verifying every frontend page's API dependencies return valid data
- [x] 8. Create `tests/integration/conftest.py` — pytest fixtures for HTTP client, base URLs, seed IDs
- [x] 9. Create `tests/integration/test_query_api.py` — tests for all 17 query API endpoints
- [x] 10. Create `tests/integration/test_registry_api.py` — tests for all 8 symbol registry endpoints
- [x] 11. Create `tests/integration/test_risk_api.py` — tests for all 4 risk engine endpoints
- [x] 12. Create `tests/integration/test_trading_api.py` — tests for all 12 trading engine endpoints
- [x] 13. Create `tests/integration/test_frontend_data_deps.py` — tests verifying every frontend page's API dependencies return valid data
## Phase 4: Profiling
- [ ] 14. Create `tests/integration/profiler.py` — timing wrapper that records per-endpoint latency and produces a summary report
- [ ] 15. Add profiling output to test runner (JSON report with P50/P95/P99 per endpoint, stage timings)
- [x] 14. Create `tests/integration/profiler.py` — timing wrapper that records per-endpoint latency and produces a summary report
- [x] 15. Add profiling output to test runner (JSON report with P50/P95/P99 per endpoint, stage timings)
## Phase 5: Pipeline Runner
- [ ] 16. Create `infra/inttest/run_pipeline.sh` — orchestration script that creates namespace, deploys infra, seeds, deploys services, runs tests, collects results, tears down
- [ ] 17. Create `.github/workflows/integration.yml` — GitHub Actions workflow that triggers the pipeline on demand or on PR
- [x] 16. Create `infra/inttest/run_pipeline.sh` standalone orchestration script with CLI args (`--image-tag`, `--namespace`, `--skip-teardown`, `--results-file`), exit codes (0=pass, 1=fail, 2=infra error), JSON result output; creates namespace, deploys infra, seeds, deploys services, runs tests, collects results, tears down
## Phase 6: Documentation
- [ ] 18. Add integration test section to `docs/LOCAL_DEV_SETUP.md` with instructions for running locally
- [x] 17. Add integration test section to `docs/LOCAL_DEV_SETUP.md` with instructions for running locally, CLI usage, JSON result contract, and a note that a future CI/CD pipeline spec will consume this runner