后端 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>
62 lines
2.2 KiB
Python
62 lines
2.2 KiB
Python
"""全局异常处理器单元测试。
|
||
|
||
重点验证校验错误的文案压缩逻辑:FastAPI 默认返回 422 与 detail 数组,
|
||
本项目需映射为 400 与单行中文提示。
|
||
"""
|
||
|
||
from fastapi.exceptions import RequestValidationError
|
||
|
||
from app.core.constants import CODE_BAD_REQUEST
|
||
from app.core.exception_handlers import _build_response, _format_validation_errors
|
||
|
||
|
||
class TestFormatValidationErrors:
|
||
"""校验错误文案压缩。"""
|
||
|
||
def test_包含字段名与原始说明(self) -> None:
|
||
exc = RequestValidationError(
|
||
[{"loc": ("body", "username"), "msg": "Field required", "type": "missing"}]
|
||
)
|
||
assert _format_validation_errors(exc) == "参数错误:username Field required"
|
||
|
||
def test_去掉来源前缀只保留字段路径(self) -> None:
|
||
exc = RequestValidationError(
|
||
[
|
||
{
|
||
"loc": ("query", "pageNum"),
|
||
"msg": "Input should be greater than 0",
|
||
"type": "greater_than",
|
||
}
|
||
]
|
||
)
|
||
assert "query" not in _format_validation_errors(exc)
|
||
assert "pageNum" in _format_validation_errors(exc)
|
||
|
||
def test_多个错误时只取第一条(self) -> None:
|
||
exc = RequestValidationError(
|
||
[
|
||
{"loc": ("body", "a"), "msg": "错误甲", "type": "missing"},
|
||
{"loc": ("body", "b"), "msg": "错误乙", "type": "missing"},
|
||
]
|
||
)
|
||
message = _format_validation_errors(exc)
|
||
assert "错误甲" in message
|
||
assert "错误乙" not in message
|
||
|
||
def test_错误列表为空时兜底(self) -> None:
|
||
assert _format_validation_errors(RequestValidationError([])) == "参数错误"
|
||
|
||
|
||
class TestBuildResponse:
|
||
"""统一响应构造:HTTP 状态码与响应体 code 必须一致。"""
|
||
|
||
def test_状态码与响应体code一致(self) -> None:
|
||
response = _build_response(CODE_BAD_REQUEST, "参数错误")
|
||
assert response.status_code == CODE_BAD_REQUEST
|
||
import json
|
||
|
||
body = json.loads(response.body)
|
||
assert body["code"] == CODE_BAD_REQUEST
|
||
assert body["message"] == "参数错误"
|
||
assert body["data"] is None
|