Add unit tests for models and services

- Implemented unit tests for Object model including folder and file creation, properties, and path retrieval.
- Added unit tests for Setting model covering creation, unique constraints, and type enumeration.
- Created unit tests for User model focusing on user creation, uniqueness, and group relationships.
- Developed unit tests for Login service to validate login functionality, including 2FA and token generation.
- Added utility tests for JWT creation and verification, ensuring token integrity and expiration handling.
- Implemented password utility tests for password generation, hashing, and TOTP verification.
This commit is contained in:
2025-12-19 19:48:05 +08:00
parent 51b6de921b
commit f93cb3eedb
60 changed files with 8189 additions and 117 deletions

246
tests/README.md Normal file
View File

@@ -0,0 +1,246 @@
# DiskNext Server 单元测试文档
## 测试结构
```
tests/
├── conftest.py # Pytest 配置和 fixtures
├── unit/ # 单元测试
│ ├── models/ # 模型层测试
│ │ ├── test_base.py # TableBase/UUIDTableBase 测试
│ │ ├── test_user.py # User 模型测试
│ │ ├── test_group.py # Group/GroupOptions 测试
│ │ ├── test_object.py # Object 模型测试
│ │ └── test_setting.py # Setting 模型测试
│ ├── utils/ # 工具层测试
│ │ ├── test_password.py # Password 工具测试
│ │ └── test_jwt.py # JWT 工具测试
│ └── service/ # 服务层测试
│ └── test_login.py # Login 服务测试
└── README.md # 本文档
```
## 运行测试
### 安装依赖
```bash
# 使用 uv (推荐)
uv sync
# 或使用 pip
pip install -e .
```
### 运行所有测试
```bash
pytest
```
### 运行特定测试文件
```bash
# 测试模型层
pytest tests/unit/models/test_base.py
# 测试用户模型
pytest tests/unit/models/test_user.py
# 测试工具层
pytest tests/unit/utils/test_password.py
# 测试服务层
pytest tests/unit/service/test_login.py
```
### 运行特定测试函数
```bash
pytest tests/unit/models/test_base.py::test_table_base_add_single
```
### 运行带覆盖率的测试
```bash
# 生成覆盖率报告
pytest --cov
# 生成 HTML 覆盖率报告
pytest --cov --cov-report=html
# 查看 HTML 报告
# 打开 htmlcov/index.html
```
### 并行测试
```bash
# 使用所有 CPU 核心
pytest -n auto
# 使用指定数量的核心
pytest -n 4
```
## Fixtures 说明
### 数据库相关
- `test_engine`: 内存 SQLite 数据库引擎
- `initialized_db`: 已初始化表结构的数据库
- `db_session`: 数据库会话(每个测试函数独立)
### 用户相关(在 conftest.py 中已提供)
- `test_user`: 创建测试用户,返回 {id, username, password, token, group_id, policy_id}
- `admin_user`: 创建管理员用户
- `auth_headers`: 测试用户的认证请求头
- `admin_headers`: 管理员的认证请求头
### 数据相关
- `test_directory`: 为测试用户创建目录结构
## 测试覆盖范围
### 模型层 (tests/unit/models/)
#### test_base.py - TableBase/UUIDTableBase
- ✅ 单条记录创建
- ✅ 批量创建
- ✅ save() 方法
- ✅ update() 方法
- ✅ delete() 方法
- ✅ get() 三种 fetch_mode
- ✅ offset/limit 分页
- ✅ get_exist_one() 存在/不存在场景
- ✅ UUID 自动生成
- ✅ 时间戳自动维护
#### test_user.py - User 模型
- ✅ 创建用户
- ✅ 用户名唯一约束
- ✅ to_public() DTO 转换
- ✅ 用户与用户组关系
- ✅ status 默认值
- ✅ storage 默认值
- ✅ ThemeType 枚举
#### test_group.py - Group/GroupOptions 模型
- ✅ 创建用户组
- ✅ 用户组与选项一对一关系
- ✅ to_response() DTO 转换
- ✅ 多对多关系policies
#### test_object.py - Object 模型
- ✅ 创建目录
- ✅ 创建文件
- ✅ is_file 属性
- ✅ is_folder 属性
- ✅ get_root() 方法
- ✅ get_by_path() 根目录
- ✅ get_by_path() 嵌套路径
- ✅ get_by_path() 路径不存在
- ✅ get_children() 方法
- ✅ 父子关系
- ✅ 同目录名称唯一约束
#### test_setting.py - Setting 模型
- ✅ 创建设置
- ✅ type+name 唯一约束
- ✅ SettingsType 枚举
- ✅ 更新设置值
### 工具层 (tests/unit/utils/)
#### test_password.py - Password 工具
- ✅ 默认长度生成密码
- ✅ 自定义长度生成密码
- ✅ 密码哈希
- ✅ 正确密码验证
- ✅ 错误密码验证
- ✅ TOTP 密钥生成
- ✅ TOTP 验证正确
- ✅ TOTP 验证错误
#### test_jwt.py - JWT 工具
- ✅ 访问令牌创建
- ✅ 自定义过期时间
- ✅ 刷新令牌创建
- ✅ 令牌解码
- ✅ 令牌过期
- ✅ 无效签名
### 服务层 (tests/unit/service/)
#### test_login.py - Login 服务
- ✅ 正常登录
- ✅ 用户不存在
- ✅ 密码错误
- ✅ 用户被封禁
- ✅ 需要 2FA
- ✅ 2FA 错误
- ✅ 2FA 成功
## 常见问题
### 1. 数据库连接错误
所有测试使用内存 SQLite 数据库,不需要外部数据库服务。
### 2. 导入错误
确保从项目根目录运行测试:
```bash
cd c:\Users\Administrator\Documents\Code\Server
pytest
```
### 3. 异步测试错误
项目已配置 `pytest-asyncio`,使用 `@pytest.mark.asyncio` 装饰器即可。
### 4. Fixture 依赖错误
检查 conftest.py 中是否定义了所需的 fixture确保使用正确的参数名。
## 编写新测试
### 模板
```python
"""
模块名称的单元测试
"""
import pytest
from sqlmodel.ext.asyncio.session import AsyncSession
from models.xxx import YourModel
@pytest.mark.asyncio
async def test_your_feature(db_session: AsyncSession):
"""测试功能描述"""
# 准备数据
instance = YourModel(field="value")
instance = await instance.save(db_session)
# 执行操作
result = await YourModel.get(db_session, YourModel.id == instance.id)
# 断言验证
assert result is not None
assert result.field == "value"
```
## 持续集成
项目配置了覆盖率要求80%),确保新代码有足够的测试覆盖。
```bash
# 检查覆盖率是否达标
pytest --cov --cov-fail-under=80
```