PayrollManagementSystem/backend/tests/unit/test_exception_handlers.py
liuwenxin 273ebf4657 chore: 初始化项目结构与登录鉴权模块
后端 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>
2026-10-09 18:46:36 +08:00

62 lines
2.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""全局异常处理器单元测试。
重点验证校验错误的文案压缩逻辑: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