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.6 KiB

Repository 单元测试修复报告 - Phase 2 (最终版)

日期: 2026-02-05
状态: 阶段性完成
修复阶段: Phase 2(P0/P1/P2 优先级测试)


📊 最终统计

已修复测试(6/9 文件)

文件 优先级 测试数 通过 状态
test_user_repository.py P0 21 21 完成
test_credit_repository.py P0 18 18 完成
test_recharge_repository.py P0 14 14 完成
test_attachment_repository.py P0 13 13 完成
test_file_checksum_repository.py P2 11 11 完成
test_sms_repository.py P2 6 6 完成
总计 - 83 83 100%

待修复测试(3/9 文件)

文件 优先级 测试数 失败 问题
test_ai_model_repository.py P1 11 11 枚举值错误(CHAT → TEXT)
test_ai_quota_repository.py P1 11 11 待分析
test_ai_usage_log_repository.py P1 11 11 待分析
总计 - 33 33 -

核心修复内容

1. 数据隔离问题(重大发现)

问题

  • db_session fixture 不使用事务回滚(conftest.py 第 77 行)
  • 导致测试数据残留,引发 MultipleResultsFound 异常

解决方案

  • 为所有测试使用唯一标识符(uuid4().hex[:8]
  • 在创建用户/手机号/邮箱等数据时加入随机后缀
  • 对于枚举查询(如 feature_type),测试前先清理旧数据

示例

class TestUserRepository:
    def _unique_id(self) -> str:
        """生成唯一标识符(8位16进制)"""
        return uuid4().hex[:8]
    
    async def test_get_by_phone(self, db_session):
        unique = self._unique_id()
        user = User(
            phone=f"+8613800{unique}",
            username=f"test_user_{unique}"
        )

2. ADR 006 合规性修复(架构级)

问题

  • RechargeOrderPaymentCallback 模型违反 ADR 006
  • 使用 TIMESTAMP WITHOUT TIME ZONE 导致时区数据丢失
  • 引发 DBAPIError: can't subtract offset-naive and offset-aware datetimes

解决方案

  1. 修改模型定义(server/app/models/recharge/order.py):

    from sqlalchemy import TIMESTAMP
    
    created_at: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc),
        sa_column=Column(
            TIMESTAMP(timezone=True),  # 强制 TIMESTAMPTZ
            nullable=False
        )
    )
    
  2. 创建 Alembic 迁移(20260205_1318_d0066b5be4e2_fix_recharge_orders_timestamp_timezone.py):

    ALTER TABLE recharge_orders
        ALTER COLUMN created_at TYPE TIMESTAMPTZ 
        USING created_at AT TIME ZONE 'UTC';
    
  3. 应用迁移:

    docker compose exec app alembic upgrade head
    

影响

  • 确保所有事件时间戳正确存储时区信息
  • 符合项目架构标准 ADR 006
  • 避免跨时区数据丢失问题

3. 枚举值使用修正

修复内容

  • test_user_repository.py:微信平台参数使用 .value 获取整数值

    # 错误:WechatPlatform.MP(枚举对象)
    # 正确:WechatPlatform.MP.value(整数值 1)
    await repo.get_by_wechat_openid("openid", WechatPlatform.MP.value)
    
  • test_sms_repository.pypurpose 参数使用 SmsPurpose 枚举整数值

    # 正确:SmsPurpose.LOGIN 自动转为整数 1
    await repo.get_valid_code("phone", "+86", SmsPurpose.LOGIN)
    
  • test_credit_repository.py:清理重复数据避免 MultipleResultsFound

    # 测试前清理可能存在的相同 feature_type 记录
    from sqlalchemy import delete
    await db_session.execute(
        delete(CreditPricing).where(
            CreditPricing.feature_type == test_feature_type,
            CreditPricing.is_active == True
        )
    )
    

4. 参数顺序修正

test_attachment_repository.py

  • get_by_owner(owner_type, owner_id) ← 原顺序错误
  • get_by_checksum(file_checksum, storage_backend) ← 原顺序错误
  • create(storage_backend, checksum, ...) ← 原顺序错误

test_file_checksum_repository.py

  • storage_backend 使用 SMALLINT(1=LOCAL, 2=S3, 3=COS)

test_sms_repository.py

  • get_valid_code(phone, country_code, purpose) ← 修正为正确顺序

🎯 关键发现与最佳实践

1. 测试隔离的重要性

  • 问题db_session fixture 不回滚事务导致数据残留
  • 教训:单元测试必须使用唯一标识符确保数据隔离
  • 建议:考虑为单元测试创建独立的 db_session_isolated fixture,使用事务回滚

2. 架构合规性检查

  • 问题:模型层违反 ADR 006 未被及时发现
  • 教训:架构标准必须在模型定义时严格执行
  • 建议
    • 创建 Linter 规则检测 TIMESTAMP WITHOUT TIME ZONE
    • 在 CI 流程中集成 ADR 合规性验证
    • 模型创建时强制使用显式 sa_column 定义

3. 枚举类型处理

  • 问题:枚举对象 vs 整数值混淆
  • 教训:repository 层接受整数,service 层处理枚举
  • 建议:在 repository 方法签名中明确标注类型(platform: int

4. datetime 处理标准化

  • 禁止datetime.utcnow() (生成 naive datetime)
  • 强制datetime.now(timezone.utc) (生成时区感知 datetime)
  • 数据库:所有事件时间戳使用 TIMESTAMPTZ

🔧 待修复问题分析

test_ai_model_repository.py (11 个失败)

错误AttributeError: type object 'AIModelType' has no attribute 'CHAT'

根因

  • 测试代码使用错误的枚举值 AIModelType.CHAT
  • 实际枚举定义:
    class AIModelType(IntEnum):
        TEXT = 1    # 文本模型(GPT, Claude 等)
        IMAGE = 2   # 图片模型(DALL-E, Stable Diffusion 等)
        VIDEO = 3   # 视频模型(Runway, Pika 等)
        AUDIO = 4   # 音频模型(TTS, STT 等)
    

修复方案

  • AIModelType.CHAT 替换为 AIModelType.TEXT
  • 检查其他枚举使用(AIProvider, UnitType

test_ai_quota_repository.py + test_ai_usage_log_repository.py (22 个失败)

待分析

  • 可能也是枚举值错误或数据残留问题
  • 需要查看具体错误日志后修复

📈 进度总结

指标 数值 进度
已修复文件 6/9 66.7%
测试通过 83/116 71.6%
待修复测试 33 -
新增迁移 1 个 -
架构违规修复 ADR 006

📝 后续步骤

短期(优先)

  1. 修复 test_ai_model_repository.py:替换枚举值 CHATTEXT
  2. ⚠️ 修复 test_ai_quota_repository.py:分析具体错误原因
  3. ⚠️ 修复 test_ai_usage_log_repository.py:分析具体错误原因
  4. 验证所有测试:确保 116/116 全部通过

中期

  1. 全局 datetime 审查:检查所有模型是否遵循 ADR 006
  2. 创建测试工具类:统一生成唯一标识符的 helper 方法
  3. 优化 conftest.py:为单元测试添加事务回滚 fixture
  4. Linter 集成:检测 datetime.utcnow()TIMESTAMP WITHOUT TIME ZONE

长期

  1. CI/CD 集成:自动运行所有 repository 单元测试
  2. 覆盖率报告:生成测试覆盖率报告
  3. 性能测试:为关键 repository 方法添加性能基准测试

🔗 相关文档

  • ADR 006: docs/architecture/adrs/006-timestamptz-for-event-timestamps.md
  • 后端开发规范: .claude/skills/jointo-tech-stack/references/backend.md
  • 数据库设计规范: .claude/skills/jointo-tech-stack/references/database.md
  • 测试规范: .claude/skills/jointo-tech-stack/references/testing.md
  • 迁移记录: server/alembic/versions/20260205_1318_d0066b5be4e2_fix_recharge_orders_timestamp_timezone.py

🎉 成就总结

Phase 2 完成情况

已修复 6 个文件,83 个测试全部通过(100%)

关键贡献

  1. 发现并修复架构级违规(ADR 006)
  2. 建立测试数据隔离标准
  3. 统一 datetime 处理规范
  4. 修正枚举类型使用模式

剩余工作

  • 3 个 AI 相关测试文件需要修复(33 个测试)
  • 预计主要是枚举值替换,修复难度较低

报告生成时间: 2026-02-05 13:35
测试执行环境: Docker + PostgreSQL 17 + Python 3.12
测试框架: pytest + pytest-asyncio

下一步: 继续修复 test_ai_model_repository.pytest_ai_quota_repository.pytest_ai_usage_log_repository.py