"""应用级 HTTP 测试:统一响应格式与全局异常处理。 归在 integration 下是因为需要构造完整 FastAPI 应用; 但用例本身不要求 MySQL/Redis 在跑 —— 健康检查会把依赖状态如实体现在 data 中,接口始终返回 200。 """ from fastapi.testclient import TestClient class TestHealthEndpoint: """健康检查接口。""" def test_返回统一响应格式(self, client: TestClient) -> None: response = client.get("/api/health") assert response.status_code == 200 body = response.json() assert body["code"] == 200 assert body["message"] == "success" assert body["data"]["app"] is True def test_上报数据库与Redis状态为布尔值(self, client: TestClient) -> None: data = client.get("/api/health").json()["data"] assert isinstance(data["db"], bool) assert isinstance(data["redis"], bool) def test_不要求认证(self, client: TestClient) -> None: assert client.get("/api/health").status_code == 200 class TestGlobalExceptionHandling: """全局异常处理器:不得把框架默认的 detail 数组泄漏给前端。""" def test_未知路由返回统一格式(self, client: TestClient) -> None: response = client.get("/api/definitely-not-exist") assert response.status_code == 404 body = response.json() assert body["code"] == 404 assert "detail" not in body def test_方法不允许返回统一格式(self, client: TestClient) -> None: response = client.post("/api/health") assert response.status_code == 405 assert response.json()["code"] == 405 class TestApiDocs: """接口文档(规范第 10 节要求提供)。文档挂在 /api 下,便于经 Vite 代理访问。""" def test_swagger文档可访问(self, client: TestClient) -> None: assert client.get("/api/docs").status_code == 200 def test_openapi结构可访问(self, client: TestClient) -> None: schema = client.get("/api/openapi.json").json() assert "/api/health" in schema["paths"]