PayrollManagementSystem/backend/tests/unit/test_auth_schemas.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

72 lines
2.6 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.

"""认证请求模型单元测试。
守的是:**密码按字节数校验**。
bcrypt 5.0 起对超过 72 字节的密码直接抛 ValueError(此前是静默截断),
而中文一个字符占 3 字节 —— 只按字符数校验(如 max_length=20)根本拦不住,
24 个汉字就会让哈希函数抛异常、登录接口返回 500。
"""
import pytest
from pydantic import ValidationError
from app.core.constants import CAPTCHA_LENGTH, PASSWORD_MAX_BYTES
from app.schemas.auth import LoginIn
def _payload(**overrides: object) -> dict[str, object]:
base: dict[str, object] = {
"username": "admin",
"password": "Pwd@12345",
"captcha_id": "a" * 32,
"captcha_code": "A" * CAPTCHA_LENGTH,
}
base.update(overrides)
return base
class TestPasswordByteLimit:
"""72 字节边界 —— 与 bcrypt 算法上限严格对齐。"""
def test_合法密码通过(self) -> None:
assert LoginIn(**_payload()).password == "Pwd@12345"
def test_恰好72字节ASCII通过(self) -> None:
password = "a" * PASSWORD_MAX_BYTES
assert LoginIn(**_payload(password=password)).password == password
def test_超过72字节ASCII被拒(self) -> None:
with pytest.raises(ValidationError):
LoginIn(**_payload(password="a" * (PASSWORD_MAX_BYTES + 1)))
def test_24个汉字恰好72字节通过(self) -> None:
"""一个汉字 3 字节:24 个正好等于上限。"""
password = "中" * 24
assert len(password.encode("utf-8")) == PASSWORD_MAX_BYTES
LoginIn(**_payload(password=password))
def test_25个汉字被拒(self) -> None:
"""若只按字符数校验,25 个字符远低于常见的 max_length=128,
会一路放行到哈希函数才炸成 500。"""
with pytest.raises(ValidationError):
LoginIn(**_payload(password="中" * 25))
class TestFieldConstraints:
"""其余字段的边界。"""
def test_验证码位数不符被拒(self) -> None:
with pytest.raises(ValidationError):
LoginIn(**_payload(captcha_code="AB"))
def test_用户名为空被拒(self) -> None:
with pytest.raises(ValidationError):
LoginIn(**_payload(username=""))
def test_用户名超长被拒(self) -> None:
with pytest.raises(ValidationError):
LoginIn(**_payload(username="a" * 65))
def test_首尾空白被自动去除(self) -> None:
"""用户复制粘贴账号时常带空格,自动去除比报错友好。"""
assert LoginIn(**_payload(username=" admin ")).username == "admin"