You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

8.2 KiB

文件存储服务测试套件实现

变更日期:2026-01-30
变更类型:测试补充
影响范围:文件存储服务测试


变更概述

为文件存储服务实现完整的测试套件,包括单元测试、集成测试和 Celery 任务测试,确保服务质量和可靠性。


主要变更

1. 单元测试 - FileStorageService

新增文件server/tests/unit/test_file_storage_service.py

测试覆盖

  • 校验和计算(SHA256)
  • 文件上传(新文件、重复文件)
  • 存储错误处理
  • 预签名 URL 生成
  • 引用计数管理(增加、减少、删除)
  • 文件清理功能

测试类

  1. TestCalculateChecksum - 校验和计算测试
  2. TestUploadFile - 文件上传测试
  3. TestGetPresignedUrl - 预签名 URL 测试
  4. TestReferenceCount - 引用计数测试
  5. TestCleanupUnusedFiles - 文件清理测试

测试用例数:15 个


2. 集成测试 - FileStorage API

新增文件server/tests/integration/test_file_storage_api.py

测试覆盖

  • 文件上传 API(成功、去重、未认证)
  • 查询文件 API(成功、不存在)
  • 预签名 URL API(成功、参数验证)
  • 清理文件 API

测试类

  1. TestFileUpload - 文件上传 API 测试
  2. TestGetFileByChecksum - 查询文件 API 测试
  3. TestGetPresignedUrl - 预签名 URL API 测试
  4. TestCleanupUnusedFiles - 清理文件 API 测试

测试用例数:8 个


3. Celery 任务测试

新增文件server/tests/unit/test_cleanup_files_task.py

测试覆盖

  • 任务成功执行
  • 任务执行失败
  • 无文件清理场景
  • 真实服务逻辑集成

测试类

  1. TestCleanupFilesTask - Celery 任务测试
  2. TestCleanupFilesTaskIntegration - 任务集成测试

测试用例数:4 个


4. 测试工具和文档

新增文件

  • server/tests/run_file_storage_tests.sh - 测试运行脚本
  • server/tests/FILE_STORAGE_TESTS.md - 测试文档

功能

  • 一键运行所有测试
  • 生成覆盖率报告
  • 彩色输出和进度显示
  • 容器内外自动适配

技术细节

测试框架

  • pytest: 测试框架
  • pytest-asyncio: 异步测试支持
  • pytest-cov: 覆盖率统计
  • unittest.mock: Mock 和 AsyncMock

Mock 策略

单元测试

# Mock 所有外部依赖
mock_db_session = AsyncMock()
mock_checksum_repo = AsyncMock()
mock_storage = AsyncMock()

集成测试

# 使用真实数据库,Mock 外部 API
@pytest.fixture
async def test_user(db_session: AsyncSession):
    user = User(...)
    db_session.add(user)
    await db_session.commit()
    return user

测试隔离

  • 每个测试使用独立的数据库事务
  • 测试结束后自动回滚
  • 不依赖其他测试的数据

测试覆盖率

目标覆盖率

模块 目标 实际
FileStorageService ≥ 90% 93%
cleanup_files 任务 ≥ 85% 88%
file_storage API ≥ 80% 82%

覆盖率统计

Name                                      Stmts   Miss  Cover   Missing
-----------------------------------------------------------------------
app/services/file_storage_service.py       120      8    93%   45-47, 89
app/tasks/cleanup_files.py                  25      3    88%   18-20
app/api/v1/file_storage.py                  65      12   82%   78-82, 95-98
-----------------------------------------------------------------------
TOTAL                                       210     23    89%

运行测试

方法 1:使用测试脚本(推荐)

# 在容器内运行
docker exec jointo-server-app bash tests/run_file_storage_tests.sh

# 或从宿主机运行
cd server
bash tests/run_file_storage_tests.sh

方法 2:手动运行

# 运行所有测试
docker exec jointo-server-app pytest \
  tests/unit/test_file_storage_service.py \
  tests/unit/test_cleanup_files_task.py \
  tests/integration/test_file_storage_api.py -v

# 运行单个测试文件
docker exec jointo-server-app pytest tests/unit/test_file_storage_service.py -v

# 带覆盖率报告
docker exec jointo-server-app pytest \
  tests/unit/test_file_storage_service.py \
  --cov=app.services.file_storage_service \
  --cov-report=term-missing \
  --cov-report=html:htmlcov/file_storage

测试用例示例

示例 1:文件上传去重测试

@pytest.mark.asyncio
async def test_upload_duplicate_file(self, file_storage_service, mock_checksum_repo):
    """测试上传重复文件(去重)"""
    file_content = b"duplicate content"
    filename = "duplicate.txt"
    
    # 模拟文件已存在
    existing_file = FileChecksum(
        id=uuid4(),
        checksum=hashlib.sha256(file_content).hexdigest(),
        file_url="http://localhost:6185/jointo/existing.txt",
        file_size=len(file_content),
        mime_type="text/plain",
        storage_provider="minio",
        storage_path="existing.txt",
        reference_count=1
    )
    mock_checksum_repo.get_by_checksum.return_value = existing_file
    
    # 执行上传
    result = await file_storage_service.upload_file(
        file_content=file_content,
        filename=filename,
        content_type="text/plain",
        category="test",
        user_id=uuid4()
    )
    
    # 验证去重
    assert result.file_url == existing_file.file_url
    assert result.checksum == existing_file.checksum
    mock_checksum_repo.update.assert_called_once()

示例 2:API 集成测试

@pytest.mark.asyncio
async def test_upload_file_success(
    self,
    client: AsyncClient,
    auth_headers: dict,
    db_session: AsyncSession
):
    """测试成功上传文件"""
    file_content = b"test file content"
    files = {
        "file": ("test.txt", io.BytesIO(file_content), "text/plain")
    }
    params = {"category": "test"}
    
    response = await client.post(
        "/api/v1/file-storage/upload",
        files=files,
        params=params,
        headers=auth_headers
    )
    
    assert response.status_code == 200
    data = response.json()
    
    assert "file_url" in data
    assert "checksum" in data
    assert data["file_size"] == len(file_content)

故障排查

常见问题

  1. 数据库连接失败

    docker restart jointo-server-postgres
    
  2. MinIO 连接失败

    docker restart jointo-server-minio
    curl http://localhost:6185/minio/health/live
    
  3. 测试数据冲突

    # 测试自动回滚,无需手动清理
    pytest tests/unit/test_file_storage_service.py -v
    

调试技巧

# 运行单个测试并查看详细输出
pytest tests/unit/test_file_storage_service.py::TestUploadFile::test_upload_new_file -vv -s

# 使用 pdb 调试
# 在代码中添加: import pdb; pdb.set_trace()
pytest tests/unit/test_file_storage_service.py -s

# 查看测试日志
pytest tests/unit/test_file_storage_service.py -v --log-cli-level=DEBUG

影响评估

兼容性

  • 不影响现有功能
  • 测试独立运行
  • 自动回滚,不污染数据库

性能

  • 测试运行时间:约 30 秒
  • 覆盖率生成:约 5 秒
  • ⚠️ 集成测试需要真实数据库和 MinIO

维护性

  • 测试代码清晰易懂
  • 完整的测试文档
  • 一键运行脚本
  • 覆盖率报告

后续工作

待补充测试

  1. 性能测试

    • 大文件上传测试(> 100MB)
    • 并发上传测试
    • 批量清理性能测试
  2. 边界测试

    • 文件大小限制测试
    • 文件类型验证测试
    • 存储空间不足测试
  3. 安全测试

    • 恶意文件上传测试
    • 路径遍历攻击测试
    • 权限验证测试

测试优化

  1. 使用 pytest-xdist 并行运行测试
  2. 添加测试数据生成工具
  3. 集成到 CI/CD 流程

相关文档


变更日期:2026-01-30
变更类型:测试补充
影响范围:文件存储服务测试