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.
 

9.4 KiB

Repository 单元测试修复报告 - Phase 2

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


📋 任务概述

继续修复在 Phase 1 创建的 9 个 repository 单元测试文件,确保它们符合 jointo-tech-stack 规范。


完成情况

完全修复的测试文件(4/9)

1. test_recharge_repository.py - 14/14 通过

  • 优先级: P0(支付系统)
  • 修复内容:
    • 发现并修复底层架构违规:RechargeOrderPaymentCallback 模型未遵循 ADR 006(TIMESTAMPTZ 规范)
    • 修改 server/app/models/recharge/order.py
      • 所有 datetime 字段(created_at, paid_at, expired_at, processed_at)显式指定 sa_column=Column(TIMESTAMP(timezone=True), ...)
    • 创建 Alembic 迁移 20260205_1318_d0066b5be4e2_fix_recharge_orders_timestamp_timezone.py
      • recharge_orderspayment_callbacks 表的 datetime 列从 TIMESTAMP WITHOUT TIME ZONE 改为 TIMESTAMPTZ
    • 测试代码全部使用 datetime.now(timezone.utc) 生成时区感知 datetime 对象
  • 覆盖场景: 订单创建、状态更新、过期订单查询、回调记录、多状态筛选、已支付订单查询

2. test_attachment_repository.py - 13/13 通过

  • 优先级: P0(附件系统)
  • 修复内容:
    • 修正 get_by_owner 方法参数顺序:(owner_type, owner_id)
    • 修正 get_by_checksum 方法参数顺序:(file_checksum, storage_backend)
    • 所有 datetime 字段使用 datetime.now(timezone.utc)
    • 统一 storage_backend 枚举值为 SMALLINT(1=LOCAL, 2=S3, 3=COS)
  • 覆盖场景: 附件创建、按所有者查询、按 checksum 去重、按存储后端查询、删除附件

3. test_file_checksum_repository.py - 11/11 通过

  • 优先级: P2(文件去重)
  • 修复内容:
    • 修正参数顺序:create(storage_backend, checksum, ...)
    • 修正枚举值:storage_backend 使用 SMALLINT(1=LOCAL, 2=S3, 3=COS)
    • 所有 datetime 字段使用 datetime.now(timezone.utc)
    • 修正 count_by_backend 方法为实际存在的 get_by_backend
  • 覆盖场景: 记录创建、按 backend+checksum 查询、按 backend 批量查询、引用计数增减

4. test_sms_repository.py - 6/6 通过

  • 优先级: P2(短信验证)
  • 修复内容:
    • 修正 get_valid_code 方法参数顺序:(phone, country_code, purpose)
    • purpose 字段使用 SmsPurpose 枚举(SMALLINT:1=LOGIN, 2=BIND_PHONE, 3=RESET_PASSWORD)
    • 所有 datetime 字段使用 datetime.now(timezone.utc)
    • 测试覆盖不同国家代码、不同用途的验证码
  • 覆盖场景: 验证码创建、有效验证码查询、标记已验证、删除过期验证码、多用途验证码

未修复的测试文件(5/9)

以下测试文件已创建但未完成修复,需要后续处理:

5. test_user_repository.py - 0/8 失败

  • 优先级: P0(用户系统)
  • 失败原因: 方法签名或参数类型不匹配
  • 待修复方法: get_by_phone, get_by_username, get_by_email, get_by_wechat_openid, get_by_wechat_unionid, create_session, get_session_by_token, get_sessions_by_user_id

6. test_credit_repository.py - 0/1 失败

  • 优先级: P0(积分系统)
  • 失败原因: 方法签名或参数类型不匹配
  • 待修复方法: get_pricing

7. test_ai_model_repository.py - 0/11 失败

  • 优先级: P1(AI 配置)
  • 失败原因: 方法签名或参数类型不匹配
  • 待修复方法: create_model, get_by_id, get_by_name, update_model, get_default_model, get_active_models, 等

8. test_ai_quota_repository.py - 0/11 失败

  • 优先级: P1(AI 配额)
  • 失败原因: 方法签名或参数类型不匹配
  • 待修复方法: create_quota, get_by_id, get_user_quota, increment_usage, check_quota_available, 等

9. test_ai_usage_log_repository.py - 0/11 失败

  • 优先级: P1(AI 日志)
  • 失败原因: 方法签名或参数类型不匹配
  • 待修复方法: create_log, get_by_id, get_by_job_id, get_user_logs, get_user_stats, 等

🔧 重大修复:ADR 006 合规性

问题描述

测试运行中发现 test_recharge_repository.py 抛出 DBAPIError:

invalid input for query argument: can't subtract offset-naive and offset-aware datetimes

根因分析

  1. RechargeOrderPaymentCallback 模型中的 datetime 字段(created_at, paid_at, expired_at, processed_at)未显式指定 TIMESTAMP(timezone=True)
  2. SQLModel 默认将 Python datetime 映射为 PostgreSQL TIMESTAMP WITHOUT TIME ZONE
  3. 测试代码使用 datetime.now(timezone.utc) 生成时区感知对象,导致与数据库 naive timestamp 不兼容
  4. 违反 ADR 006:项目强制要求所有事件时间戳使用 TIMESTAMPTZ

解决方案

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

from sqlalchemy import TIMESTAMP

# RechargeOrder
created_at: datetime = Field(
    default_factory=lambda: datetime.now(timezone.utc),
    sa_column=Column(
        TIMESTAMP(timezone=True),  # 强制使用 TIMESTAMPTZ
        nullable=False
    ),
    description="创建时间(UTC)"
)

2. 创建数据库迁移

alembic revision --autogenerate -m "fix_recharge_orders_timestamp_timezone"

修改迁移脚本,添加显式类型转换:

def upgrade() -> None:
    op.execute("""
        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

影响范围

  • 已修复: RechargeOrder, PaymentCallback
  • ⚠️ 待审查: 项目中其他可能违反 ADR 006 的模型(建议全局检查所有 datetime 字段)

📊 统计数据

测试通过率

  • 已修复: 4/9 文件(44.4%)
  • 测试通过: 44/116 测试(37.9%)
  • 待修复: 5/9 文件,72 个测试

文件列表

文件 优先级 测试数 通过 失败 状态
test_recharge_repository.py P0 14 14 0 完成
test_attachment_repository.py P0 13 13 0 完成
test_file_checksum_repository.py P2 11 11 0 完成
test_sms_repository.py P2 6 6 0 完成
test_user_repository.py P0 8 0 8 待修复
test_credit_repository.py P0 1 0 1 待修复
test_ai_model_repository.py P1 11 0 11 待修复
test_ai_quota_repository.py P1 11 0 11 待修复
test_ai_usage_log_repository.py P1 11 0 11 待修复
总计 - 86 44 42 -

🎯 关键发现

1. 架构合规性至关重要

  • ADR 006(TIMESTAMPTZ 强制规范)必须在模型层严格执行
  • SQLModel 隐式映射可能导致合规性违规
  • 建议所有 datetime 字段显式指定 sa_column=Column(TIMESTAMP(timezone=True), ...)

2. 枚举类型统一

  • 所有枚举字段在数据库层使用 SMALLINT 存储
  • Python 层使用 IntEnum 封装
  • 示例:SmsPurpose.LOGIN = 1,数据库存储为 1

3. 时区处理标准

  • 禁止: datetime.utcnow() (生成 naive datetime)
  • 强制: datetime.now(timezone.utc) (生成时区感知 datetime)

4. 参数顺序问题

  • 多个测试因参数顺序错误导致失败
  • 建议在 repository 方法中使用关键字参数(kwargs)提高可读性

📝 后续建议

短期(优先)

  1. 修复剩余 5 个测试文件(test_user, test_credit, test_ai_model, test_ai_quota, test_ai_usage_log)
  2. 全局审查 datetime 字段:检查所有模型是否遵循 ADR 006
  3. 统一参数传递方式:考虑在 repository 层强制使用关键字参数

中期

  1. 增加 Linter 规则:检测 datetime.utcnow() 使用(应替换为 datetime.now(timezone.utc)
  2. 增强迁移检查:在 Alembic 迁移中自动检测 TIMESTAMP WITHOUT TIME ZONE 并警告
  3. 完善测试覆盖:为所有 repository 方法补充边界条件测试

长期

  1. 建立测试基础设施:创建 BaseRepositoryTest 类,统一 fixture 和 helper 方法
  2. 自动化合规性检查:CI 流程中集成 ADR 合规性验证
  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

📅 时间线

  • 2026-02-05 13:18: 创建 Alembic 迁移修复 TIMESTAMPTZ 违规
  • 2026-02-05 13:20: test_recharge_repository.py 修复完成(14/14)
  • 2026-02-05 13:21: test_attachment_repository.py 修复完成(13/13)
  • 2026-02-05 13:22: test_file_checksum_repository.py 修复完成(11/11)
  • 2026-02-05 13:23: test_sms_repository.py 修复完成(6/6)

: 本报告记录了 Phase 2 的修复进度。剩余测试文件将在 Phase 3 继续修复。