后端 FastAPI 六层结构(api/services/repositories/models/schemas/core), 统一响应信封、全局异常、日志脱敏;6 张表经 Alembic 迁移建库,SQL 留存 db/migration;登录鉴权含图形验证码(Redis GETDEL 防重放)、JWT、登录限流、 登出黑名单(TTL=令牌剩余有效期)、登录日志。 前端 Vue3 + TS + Element Plus 骨架,含登录页、路由守卫、请求封装与 Vite 代理。 工程配置:uv 锁定 Python 3.12、ruff 检查与格式化、editorconfig、gitattributes。 测试 184 项(单元 155 / 集成 29)全绿,浏览器端到端 8 项通过。 Co-Authored-By: Claude Code <noreply@anthropic.com>
56 lines
2.1 KiB
Python
56 lines
2.1 KiB
Python
"""应用级 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"]
|