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.
21 KiB
Requirements Document
Introduction
Comprehensive API integration test suite for the Stonks Oracle beta gate. The suite validates every HTTP endpoint across all four API services (query-api, symbol-registry, risk-engine, trading-engine) that can be tested without external broker or news API dependencies. Tests run against a seeded PostgreSQL database with deterministic data, exercising read paths, write paths, edge cases, empty-state behavior, error handling, pagination/filtering, and round-trip fidelity. The suite extends the existing ~41 integration tests to provide full endpoint coverage suitable for blocking promotion from beta to paper-trading.
Glossary
- Test_Suite: The collection of pytest-asyncio integration test modules under
tests/integration/that validate API behavior against live sandbox services - Seed_Script: The
tests/integration/seed_sandbox.pymodule that populates the database with deterministic UUIDs, timestamps, and relationships for reproducible assertions - Query_API: The FastAPI service at
services/api/app.pyexposing ~50 read/write endpoints for analytics, evidence drill-down, admin controls, macro/competitive layers, agent management, and operational dashboards - Symbol_Registry: The FastAPI service at
services/symbol_registry/app.pyexposing CRUD endpoints for companies, aliases, watchlists, sources, competitor relationships, and exposure profiles - Risk_Engine: The FastAPI service at
services/risk/app.pyexposing order risk evaluation, approval workflow, and approval expiry endpoints - Trading_Engine: The FastAPI service at
services/trading/app.pyexposing engine control, configuration, decisions, metrics, notifications, backtest, and override order endpoints - Beta_Gate: The promotion pipeline (
infra/inttest/promote.sh) that deploys to beta namespace, seeds data, runs the Test_Suite, and promotes to paper-trading only if all tests pass - Sandbox: The ephemeral Kubernetes namespace with PostgreSQL, Redis, and MinIO where integration tests execute
- Deterministic_UUID: A hardcoded UUID in the Seed_Script that enables exact equality assertions in tests
- Round_Trip: A test pattern where data is written via a POST/PUT endpoint and then read back via a GET endpoint to verify fidelity
Requirements
Requirement 1: Expanded Seed Data for Full Coverage
User Story: As a test engineer, I want the seed script to populate all database tables with enough variety to exercise every API code path, so that tests can validate filtering, pagination, edge cases, and empty-state behavior.
Acceptance Criteria
- THE Seed_Script SHALL insert at least 2 watchlists with at least 3 watchlist members across the watchlists
- THE Seed_Script SHALL insert at least 2 operator approval records: one with status "pending" and one with status "approved"
- THE Seed_Script SHALL insert at least 2 symbol lockout records: one expired and one still active
- THE Seed_Script SHALL insert at least 1 notification record with delivery_status "delivered"
- THE Seed_Script SHALL insert at least 2 ingestion run records with different statuses ("completed" and "failed")
- THE Seed_Script SHALL insert at least 1 saved query record
- THE Seed_Script SHALL insert at least 1 daily risk snapshot record
- THE Seed_Script SHALL export all new Deterministic_UUIDs as named constants and include them in the SEED lookup dictionaries for test assertion use
- WHEN the Seed_Script is run against an already-seeded database, THE Seed_Script SHALL be idempotent and not raise errors due to duplicate key violations
Requirement 2: Query API — Document and Evidence Endpoints
User Story: As a test engineer, I want comprehensive tests for the Query API document timeline, evidence drill-down, and trend projection endpoints, so that schema drift between services is caught before promotion.
Acceptance Criteria
- WHEN a GET request is made to
/api/documentswithtickerfilter parameter, THE Query_API SHALL return only documents mentioning that ticker - WHEN a GET request is made to
/api/documentswithdoc_typefilter parameter, THE Query_API SHALL return only documents of that type - WHEN a GET request is made to
/api/documents/{id}for a seeded document, THE Query_API SHALL return the document withintelligence,company_mentions, andtitlefields populated - WHEN a GET request is made to
/api/documents/{id}with a non-existent UUID, THE Query_API SHALL return HTTP 404 - WHEN a GET request is made to
/api/recommendations/{id}/evidencefor a seeded recommendation, THE Query_API SHALL return an evidence drill-down with document and intelligence references - WHEN a GET request is made to
/api/trends/{id}/evidencefor a seeded trend, THE Query_API SHALL return an evidence drill-down with supporting and opposing document references - WHEN a GET request is made to
/api/trends/{id}/projectionfor a seeded trend with a projection, THE Query_API SHALL return the trend projection withprojected_direction,projected_strength,projected_confidence, andmacro_contribution_pctfields
Requirement 3: Query API — Macro and Competitive Layer Endpoints
User Story: As a test engineer, I want tests for the macro event, competitive signal, and pattern endpoints, so that the three-layer signal aggregation engine is validated end-to-end.
Acceptance Criteria
- WHEN a GET request is made to
/api/macro/status, THE Query_API SHALL return the macro layer toggle status withenabledfield - WHEN a GET request is made to
/api/macro/events, THE Query_API SHALL return at least 2 seeded global events withevent_types,severity,affected_regions, andsummaryfields - WHEN a GET request is made to
/api/macro/events/{id}for a seeded event, THE Query_API SHALL return the event detail withmacro_impactscontaining per-company impact records - WHEN a GET request is made to
/api/macro/impacts/{ticker}for "AAPL", THE Query_API SHALL return at least 1 macro impact record withmacro_impact_score,impact_direction, andcontributing_factors - WHEN a GET request is made to
/api/competitive/status, THE Query_API SHALL return the competitive layer toggle status withenabledfield - WHEN a GET request is made to
/api/competitive/signals/{ticker}for "AAPL", THE Query_API SHALL return competitive signal records withsource_ticker,target_ticker,signal_direction, andsignal_strengthfields - WHEN a GET request is made to
/api/competitive/patterns/{ticker}for "AAPL", THE Query_API SHALL return historical pattern data (may be empty if no patterns computed)
Requirement 4: Query API — Operational and Admin Endpoints
User Story: As a test engineer, I want tests for the operational dashboard, source management, trading config, approval workflow, and lockout endpoints, so that admin functionality is validated before promotion.
Acceptance Criteria
- WHEN a GET request is made to
/api/ops/pipeline/health, THE Query_API SHALL return pipeline health withdocument_stages,parsing,extraction,aggregation, andqueue_depthsfields - WHEN a GET request is made to
/api/ops/ingestion/summary, THE Query_API SHALL return ingestion statistics withtotal_runsandby_source_typefields - WHEN a GET request is made to
/api/ops/sources/coverage-gaps, THE Query_API SHALL return gap analysis withmissing_source_typesandstale_sourceslists - WHEN a PUT request is made to
/api/sources/{id}/toggle?active=falsefor a seeded source, THE Query_API SHALL return HTTP 200 and the source active status SHALL be updated - WHEN a GET request is made to
/api/trading/config, THE Query_API SHALL return the risk configuration withtrading_modeandconfigfields - WHEN a GET request is made to
/api/approvals/pending, THE Query_API SHALL return a list containing the seeded pending approval record - WHEN a GET request is made to
/api/lockouts/active, THE Query_API SHALL return a list containing the seeded active lockout record - WHEN a POST request is made to
/api/lockoutswith a valid ticker and expiry, THE Query_API SHALL create a lockout and return HTTP 200 with the lockout details - WHEN a DELETE request is made to
/api/lockouts/{id}for the created lockout, THE Query_API SHALL remove the lockout and return HTTP 200
Requirement 5: Query API — Agent and Variant Management Endpoints
User Story: As a test engineer, I want tests for the AI agent CRUD, variant lifecycle, and performance endpoints, so that the agent management system is validated.
Acceptance Criteria
- WHEN a GET request is made to
/api/agents, THE Query_API SHALL return at least 3 seeded agents withid,name,slug,model_name, andactivefields - WHEN a GET request is made to
/api/agents/{id}for a seeded agent, THE Query_API SHALL return the agent detail withsystem_prompt,temperature, andmax_tokensfields - WHEN a POST request is made to
/api/agentswith a valid agent body, THE Query_API SHALL create the agent and return HTTP 201 with the new agent including a generatedslug - WHEN a PUT request is made to
/api/agents/{id}with updated fields, THE Query_API SHALL update the agent and return the modified record - WHEN a GET request is made to
/api/agents/{id}/variantsfor a seeded agent, THE Query_API SHALL return at least 1 variant withvariant_name,model_name, andis_activefields - WHEN a POST request is made to
/api/agents/{id}/variantswith a valid variant body, THE Query_API SHALL create the variant and return HTTP 201 - WHEN a POST request is made to
/api/agents/{id}/variants/{vid}/activate, THE Query_API SHALL set the variant as active and deactivate any previously active variant for that agent - WHEN a GET request is made to
/api/agents/{id}/performancefor a seeded agent, THE Query_API SHALL return performance metrics derived from the agent performance log - WHEN a GET request is made to
/api/agents/{id}/variants/{vid}/performancefor a seeded variant, THE Query_API SHALL return variant-level performance metrics
Requirement 6: Symbol Registry — Write Path and Edge Case Endpoints
User Story: As a test engineer, I want tests for the Symbol Registry write paths (create, update, delete) and edge cases (duplicates, not-found, validation), so that data integrity is validated.
Acceptance Criteria
- WHEN a POST request is made to
/companieswith a duplicate ticker and exchange, THE Symbol_Registry SHALL return HTTP 409 - WHEN a GET request is made to
/companies/{id}with a non-existent UUID, THE Symbol_Registry SHALL return HTTP 404 - WHEN a POST request is made to
/companies/{id}/aliaseswith a valid alias, THE Symbol_Registry SHALL create the alias and return HTTP 201 withid,alias, andalias_type - WHEN a POST request is made to
/companies/{id}/sourceswith a valid source body, THE Symbol_Registry SHALL create the source and return HTTP 201 withid,source_type, andsource_name - WHEN a POST request is made to
/companies/{id}/sourceswith an invalidsource_type, THE Symbol_Registry SHALL return HTTP 422 - WHEN a POST request is made to
/watchlistswith a valid name, THE Symbol_Registry SHALL create the watchlist and return HTTP 201 - WHEN a POST request is made to
/watchlists/{id}/members/{company_id}for a seeded company, THE Symbol_Registry SHALL add the member and return HTTP 201 - WHEN a GET request is made to
/watchlists/{id}/membersafter adding members, THE Symbol_Registry SHALL return the member companies withtickerandlegal_namefields - WHEN a POST request is made to
/watchlistswith a duplicate name, THE Symbol_Registry SHALL return HTTP 409
Requirement 7: Symbol Registry — Competitor and Exposure Write Paths
User Story: As a test engineer, I want tests for competitor relationship CRUD and exposure profile upsert, so that the competitive intelligence data layer is validated.
Acceptance Criteria
- WHEN a POST request is made to
/companies/{id}/competitorswith a valid competitor body, THE Symbol_Registry SHALL create the relationship and return HTTP 201 withrelationship_type,strength, andbidirectionalfields - WHEN a POST request is made to
/companies/{id}/competitorswhere company_id equals company_b_id, THE Symbol_Registry SHALL return HTTP 400 with a self-referencing error - WHEN a PUT request is made to
/companies/{id}/competitors/{rel_id}with updated strength, THE Symbol_Registry SHALL update the relationship and return the modified record - WHEN a DELETE request is made to
/companies/{id}/competitors/{rel_id}, THE Symbol_Registry SHALL soft-delete the relationship (set active=false) and return HTTP 200 - WHEN a PUT request is made to
/companies/{id}/exposurewith a valid exposure profile, THE Symbol_Registry SHALL create or update the profile and return the new version with incrementedversionnumber - WHEN a GET request is made to
/companies/{id}/exposure/historyfor a company with multiple profile versions, THE Symbol_Registry SHALL return all versions ordered by version descending - WHEN a GET request is made to
/companies/{id}/exposurefor a company with no exposure profile, THE Symbol_Registry SHALL return HTTP 404
Requirement 8: Risk Engine — Evaluation Edge Cases and Approval Workflow
User Story: As a test engineer, I want tests for risk evaluation edge cases, the full approval lifecycle, and approval expiry, so that the risk gate is validated.
Acceptance Criteria
- WHEN a POST request is made to
/evaluatewith a minimal order (only ticker), THE Risk_Engine SHALL return a valid evaluation withevaluation_id,eligible, andrejection_reasons - WHEN a POST request is made to
/evaluatewith an order exceeding the absolute position cap, THE Risk_Engine SHALL returneligible: falsewith at least one rejection reason - WHEN a POST request is made to
/evaluatewith a customconfigoverriding risk parameters, THE Risk_Engine SHALL use the provided config for evaluation - WHEN a GET request is made to
/approvals/pending, THE Risk_Engine SHALL return a list of pending approval records from the seeded data - WHEN a GET request is made to
/approvals/{id}for a seeded pending approval, THE Risk_Engine SHALL return the approval detail withticker,side,quantity,status, andexpires_atfields - WHEN a POST request is made to
/approvals/{id}/reviewwithapproved: true, THE Risk_Engine SHALL update the approval status to "approved" and return the new status - WHEN a POST request is made to
/approvals/{id}/reviewfor a non-existent approval, THE Risk_Engine SHALL return HTTP 404 - WHEN a POST request is made to
/approvals/expire, THE Risk_Engine SHALL expire stale pending approvals and return the count of expired records
Requirement 9: Trading Engine — Configuration, Metrics, and Notification Endpoints
User Story: As a test engineer, I want tests for trading engine configuration round-trips, metrics consistency, notification config CRUD, and notification history, so that the autonomous trading control plane is validated.
Acceptance Criteria
- WHEN a PUT request is made to
/api/trading/configwithrisk_tier: "aggressive"followed by a GET to/api/trading/status, THE Trading_Engine SHALL reflect the updated risk tier in the status response (Round_Trip) - WHEN a POST request is made to
/api/trading/pausefollowed by a GET to/api/trading/status, THE Trading_Engine SHALL reportpaused: truein the status response - WHEN a POST request is made to
/api/trading/resumefollowed by a GET to/api/trading/status, THE Trading_Engine SHALL reportpaused: falsein the status response - WHEN a GET request is made to
/api/trading/metrics, THE Trading_Engine SHALL return all numeric portfolio metrics wheretotal_portfolio_valueapproximately equalsactive_pool + reserve_pool + unrealized_pnl - WHEN a GET request is made to
/api/trading/metrics/history, THE Trading_Engine SHALL return a list of portfolio snapshots withportfolio_valueandsnapshot_datefields - WHEN a PUT request is made to
/api/trading/notifications/configwithsms_enabled: true, THE Trading_Engine SHALL update the notification config and a subsequent GET SHALL reflect the change (Round_Trip) - WHEN a GET request is made to
/api/trading/notifications/history, THE Trading_Engine SHALL return a list of notification records (at least 1 from seed data) - WHEN a GET request is made to
/api/trading/decisionswithtickerfilter, THE Trading_Engine SHALL return only decisions for that ticker
Requirement 10: Trading Engine — Decision Filtering, Backtest, and Override Validation
User Story: As a test engineer, I want tests for decision audit trail filtering, backtest submission, and override order validation, so that the trading engine's write paths and input validation are verified.
Acceptance Criteria
- WHEN a GET request is made to
/api/trading/decisionswithlimit=1, THE Trading_Engine SHALL return at most 1 decision record - WHEN a GET request is made to
/api/trading/decisionswithdecision=act, THE Trading_Engine SHALL return only decisions withdecision: "act" - WHEN a POST request is made to
/api/trading/override/orderwith an invalid ticker (lowercase or special characters), THE Trading_Engine SHALL return HTTP 422 - WHEN a POST request is made to
/api/trading/override/orderwith quantity of 0 or negative, THE Trading_Engine SHALL return HTTP 422 - WHEN a POST request is made to
/api/trading/override/orderwith a valid market order, THE Trading_Engine SHALL return HTTP 202 withjob_id,status: "queued",ticker,side, andquantityfields, or a structured error if Redis is unavailable - IF the Trading_Engine returns HTTP 503 for an override order due to Redis unavailability, THEN THE Trading_Engine SHALL return a JSON error body (not an unhandled exception)
Requirement 11: Query API — Analytics, Saved Queries, and Schema Introspection
User Story: As a test engineer, I want tests for the analytics query engine, saved query CRUD, and schema introspection endpoints, so that the data exploration features are validated.
Acceptance Criteria
- WHEN a GET request is made to
/api/analytics/schema, THE Query_API SHALL return the Trino schema information (or a structured error if Trino is unavailable) - WHEN a GET request is made to
/api/pg/schema, THE Query_API SHALL return the PostgreSQL schema with table and column information - WHEN a GET request is made to
/api/saved-queries, THE Query_API SHALL return a list of saved queries (at least 1 from seed data or migrations) - WHEN a POST request is made to
/api/saved-querieswith a valid query body, THE Query_API SHALL create the saved query and return the record - WHEN a DELETE request is made to
/api/saved-queries/{id}for the created query, THE Query_API SHALL delete the query and return HTTP 200 - WHEN a POST request is made to
/api/pg/querywith a valid read-only SQL query, THE Query_API SHALL execute the query and return results
Requirement 12: Cross-Service Round-Trip and Contract Validation
User Story: As a test engineer, I want round-trip tests that write data through one service and read it through another, so that cross-service data consistency is validated.
Acceptance Criteria
- WHEN a company is created via POST to Symbol_Registry
/companiesand then queried via GET to Query_API/api/companies, THE company SHALL appear in the Query_API response with matchingtickerandlegal_name - WHEN an exposure profile is created via PUT to Symbol_Registry
/companies/{id}/exposureand then queried via GET to Symbol_Registry/companies/{id}/exposure, THE profile SHALL match the submitted data withgeographic_revenue_mix,supply_chain_regions, andmarket_position_tierfields preserved - WHEN a competitor relationship is created via POST to Symbol_Registry
/companies/{id}/competitorsand then queried from both sides, THE relationship SHALL be visible from both company_a and company_b perspectives - WHEN a risk evaluation is performed via POST to Risk_Engine
/evaluateand then the same ticker's recommendation is queried via Query_API/api/recommendations, THE recommendation SHALL include arisk_evaluationfield with matching evaluation structure
Requirement 13: Error Handling and Empty-State Behavior
User Story: As a test engineer, I want tests that verify all endpoints return structured JSON errors (not HTML or stack traces) and handle empty-state gracefully, so that the frontend never receives unexpected response formats.
Acceptance Criteria
- WHEN a GET request is made to any list endpoint with no matching data, THE service SHALL return HTTP 200 with an empty list (not HTTP 404 or an error)
- WHEN a GET request is made to any detail endpoint with a non-existent UUID, THE service SHALL return HTTP 404 with a JSON body containing an error message
- WHEN a POST request is made with an invalid JSON body (missing required fields), THE service SHALL return HTTP 422 with a JSON body containing validation error details
- WHEN a GET request is made to
/api/trends/{id}/projectionfor a trend with no projection, THE Query_API SHALL return HTTP 404 with a JSON error (not HTTP 500) - WHEN a GET request is made to
/api/macro/impacts/{ticker}for a ticker with no macro impacts, THE Query_API SHALL return HTTP 200 with an empty list - THE Test_Suite SHALL verify that all health endpoints (
/health) across all four services return{"status": "ok"}with HTTP 200