"""OpenAPI Specification and Schema Invariants Contract Test Suite. Validates the full OpenAPI 3.x schema generated by FastAPI, ensuring: 1. Complete path registration across all routers. 2. Valid operation IDs, methods, and tags. 3. Response schemas and JSON content-type definitions. 4. Schema integrity in components (no broken $ref pointers). """ from __future__ import annotations import pytest from app.main import app @pytest.fixture(scope="module") def openapi_schema() -> dict: """Generate OpenAPI specification dictionary from FastAPI app.""" schema = app.openapi() assert isinstance(schema, dict), "OpenAPI schema must be a dictionary" return schema def test_openapi_core_structure(openapi_schema: dict): """Validate core OpenAPI document properties.""" assert "openapi" in openapi_schema assert openapi_schema["openapi"].startswith("3.") assert "info" in openapi_schema assert "title" in openapi_schema["info"] assert "version" in openapi_schema["info"] assert "paths" in openapi_schema assert len(openapi_schema["paths"]) >= 25, "At least 25 core API paths must be registered" def test_openapi_required_routes_presence(openapi_schema: dict): """Verify that all essential platform routes are present in OpenAPI schema.""" paths = openapi_schema["paths"] expected_critical_endpoints = [ "/api/v1/simulations", "/api/v1/simulations/{simulation_id}", "/api/v1/simulations/{simulation_id}/ai-act-report", "/api/v1/banks", "/api/v1/banks/distributions", "/api/v1/training/{simulation_id}/rounds", "/api/v1/alerts", "/api/v1/alerts/{alert_id}", "/api/v1/alerts/{alert_id}/explain", "/api/v1/alerts/{alert_id}/counterfactuals", "/api/v1/alerts/{alert_id}/decision-replay", "/api/v1/alerts/{alert_id}/gnn-explanation", "/api/v1/cases", "/api/v1/cases/{case_id}", "/api/v1/cases/{case_id}/evidence", "/api/v1/cases/audit/logs", "/api/v1/entities", "/api/v1/graph/{entity_id}", "/api/v1/graph/stats/summary", "/api/v1/entities/psi", "/api/v1/entities/fuzzy-resolve", "/api/v1/rules", "/api/v1/rules/test", "/api/v1/security/status", "/api/v1/security/abac/evaluate", "/api/v1/security/audit-chain", "/api/v1/security/audit-chain/verify", "/api/v1/monitoring/drift/analyze", "/api/v1/monitoring/calibration", "/api/v1/monitoring/alerts", "/api/v1/coordinator/clients", "/api/v1/coordinator/negotiate", "/api/v1/privacy-defense/aggregation-methods", "/api/v1/privacy-defense/budget-log", "/api/v1/privacy-defense/audit/mia", "/api/v1/privacy-defense/audit/model-inversion", "/api/v1/registry/{simulation_id}/versions", "/api/v1/registry/{simulation_id}/shadow/metrics", "/api/v1/scenarios", "/health", ] for endpoint in expected_critical_endpoints: assert endpoint in paths, f"Missing critical endpoint in OpenAPI spec: {endpoint}" def test_openapi_operation_contracts(openapi_schema: dict): """Validate HTTP methods, status codes, and response structures for all operations.""" paths = openapi_schema["paths"] for path_str, methods in paths.items(): for method_name, operation in methods.items(): if method_name.startswith("x-") or method_name == "parameters": continue assert isinstance(operation, dict), f"Operation {method_name} {path_str} must be a dict" assert "responses" in operation, f"Operation {method_name} {path_str} must declare responses" # Verify status code contracts responses = operation["responses"] has_success_code = any( code.startswith("2") or code == "default" for code in responses ) assert has_success_code, f"Operation {method_name} {path_str} missing 2xx success response" def test_openapi_component_schema_integrity(openapi_schema: dict): """Verify that all schemas in components have valid property definitions and resolvable refs.""" components = openapi_schema.get("components", {}) schemas = components.get("schemas", {}) assert len(schemas) > 0, "OpenAPI components must contain model schemas" for schema_name, schema_obj in schemas.items(): assert isinstance(schema_obj, dict), f"Schema {schema_name} must be a dictionary" # Verify that schema defines title or type assert "type" in schema_obj or "properties" in schema_obj or "enum" in schema_obj or "allOf" in schema_obj or "anyOf" in schema_obj, ( f"Schema {schema_name} must have a valid structural descriptor" )