后端 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>
68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
"""统一响应模型单元测试。"""
|
||
|
||
import pytest
|
||
from pydantic import ValidationError
|
||
|
||
from app.core.response import ApiResponse, PageQuery, PageResult, ok
|
||
|
||
|
||
class TestApiResponse:
|
||
"""响应信封结构。"""
|
||
|
||
def test_默认值符合规范(self) -> None:
|
||
resp = ApiResponse[None]()
|
||
assert resp.code == 200
|
||
assert resp.message == "success"
|
||
assert resp.data is None
|
||
|
||
def test_序列化字段固定为code_message_data(self) -> None:
|
||
keys = set(ApiResponse[None]().model_dump().keys())
|
||
assert keys == {"code", "message", "data"}
|
||
|
||
|
||
class TestOkHelper:
|
||
"""成功响应构造助手。"""
|
||
|
||
def test_携带业务数据(self) -> None:
|
||
resp = ok({"id": 1})
|
||
assert resp.code == 200
|
||
assert resp.data == {"id": 1}
|
||
|
||
def test_可自定义提示语(self) -> None:
|
||
assert ok(message="登录成功").message == "登录成功"
|
||
|
||
|
||
class TestPageResult:
|
||
"""分页结果:Python 蛇形属性名,序列化为规范要求的驼峰字段。"""
|
||
|
||
def test_序列化输出驼峰字段(self) -> None:
|
||
dumped = PageResult[int](records=[1, 2], total=2, page_num=1, page_size=10).model_dump(
|
||
by_alias=True
|
||
)
|
||
assert dumped == {"list": [1, 2], "total": 2, "pageNum": 1, "pageSize": 10}
|
||
|
||
def test_空结果时list为空数组而非null(self) -> None:
|
||
dumped = PageResult[int]().model_dump(by_alias=True)
|
||
assert dumped["list"] == []
|
||
|
||
|
||
class TestPageQuery:
|
||
"""分页入参。"""
|
||
|
||
def test_offset按页码换算(self) -> None:
|
||
assert PageQuery(page_num=1, page_size=10).offset == 0
|
||
assert PageQuery(page_num=3, page_size=20).offset == 40
|
||
|
||
@pytest.mark.parametrize("bad_page", [0, -1])
|
||
def test_页码小于1时拒绝(self, bad_page: int) -> None:
|
||
with pytest.raises(ValidationError):
|
||
PageQuery(page_num=bad_page)
|
||
|
||
def test_每页条数超上限时拒绝(self) -> None:
|
||
with pytest.raises(ValidationError):
|
||
PageQuery(page_size=201)
|
||
|
||
def test_接受驼峰输入(self) -> None:
|
||
query = PageQuery.model_validate({"pageNum": 2, "pageSize": 5})
|
||
assert (query.page_num, query.page_size) == (2, 5)
|