"""OpenAPI 统一 ErrorResponse 组件与 router 引用。""" from __future__ import annotations from fastapi import FastAPI from app.core.error_codes import AUTH_ERROR_CODES, ERROR_CODE_ENUM from app.core.openapi import COMMON_ERROR_RESPONSES, custom_openapi, error_responses from app.features.auth.router import router as auth_router from app.features.memoir.router import router as memoir_router from app.features.payment.router import router as payment_router def _openapi_app(*routers) -> FastAPI: app = FastAPI() for router in routers: app.include_router(router) app.openapi = lambda: custom_openapi(app) # type: ignore[assignment] return app def test_openapi_includes_error_response_schema() -> None: app = _openapi_app(payment_router) app.openapi_schema = None schema = custom_openapi(app) error_schema = schema["components"]["schemas"]["ErrorResponse"] assert set(error_schema["required"]) == {"error_code", "message", "request_id"} error_code_prop = error_schema["properties"]["error_code"] assert error_code_prop["allOf"][0]["$ref"].endswith("ErrorCode") error_code_enum = schema["components"]["schemas"]["ErrorCode"]["enum"] assert "NOT_FOUND" in error_code_enum assert "PHONE_EXISTS" in error_code_enum assert set(error_code_enum) == set(ERROR_CODE_ENUM) def test_openapi_description_includes_error_code_table() -> None: app = _openapi_app(auth_router) app.openapi_schema = None schema = custom_openapi(app) description = schema["info"]["description"] assert "PHONE_EXISTS" in description assert "INVALID_SMS_CODE" in description assert AUTH_ERROR_CODES[0]["code"] in description def test_openapi_domain_error_code_schema() -> None: app = _openapi_app(auth_router) app.openapi_schema = None schema = custom_openapi(app) domain_enum = schema["components"]["schemas"]["DomainErrorCode"]["enum"] assert "PHONE_EXISTS" in domain_enum assert "NOT_FOUND" not in domain_enum def test_error_responses_reference_error_response_component() -> None: responses = error_responses(401, 404) assert responses[401]["content"]["application/json"]["schema"]["$ref"].endswith( "ErrorResponse" ) assert 404 in responses assert responses[404] == COMMON_ERROR_RESPONSES[404] def test_memoir_router_openapi_uses_error_response_ref() -> None: app = _openapi_app(memoir_router) app.openapi_schema = None schema = custom_openapi(app) get_chapters = schema["paths"]["/api/chapters"]["get"] assert "401" in get_chapters["responses"] ref = get_chapters["responses"]["401"]["content"]["application/json"]["schema"]["$ref"] assert ref.endswith("ErrorResponse") def test_auth_register_openapi_uses_error_response_ref() -> None: app = _openapi_app(auth_router) app.openapi_schema = None schema = custom_openapi(app) register = schema["paths"]["/api/auth/register"]["post"] assert "400" in register["responses"] ref = register["responses"]["400"]["content"]["application/json"]["schema"]["$ref"] assert ref.endswith("ErrorResponse") def test_error_responses_includes_502() -> None: responses = error_responses(502) assert responses[502] == COMMON_ERROR_RESPONSES[502] assert responses[502]["content"]["application/json"]["schema"]["$ref"].endswith( "ErrorResponse" ) def test_payment_router_openapi_includes_502() -> None: app = _openapi_app(payment_router) app.openapi_schema = None schema = custom_openapi(app) create_order = schema["paths"]["/api/payment/create-order"]["post"] assert "502" in create_order["responses"] ref = create_order["responses"]["502"]["content"]["application/json"]["schema"]["$ref"] assert ref.endswith("ErrorResponse") def test_payment_router_openapi_includes_504_and_500() -> None: app = _openapi_app(payment_router) app.openapi_schema = None schema = custom_openapi(app) create_order = schema["paths"]["/api/payment/create-order"]["post"] for status in ("500", "504"): assert status in create_order["responses"] ref = create_order["responses"][status]["content"]["application/json"]["schema"]["$ref"] assert ref.endswith("ErrorResponse")