# 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(支付系统) - **修复内容**: - 发现并修复底层架构违规:`RechargeOrder` 和 `PaymentCallback` 模型未遵循 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_orders` 和 `payment_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. `RechargeOrder` 和 `PaymentCallback` 模型中的 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`) ```python 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. 创建数据库迁移 ```bash alembic revision --autogenerate -m "fix_recharge_orders_timestamp_timezone" ``` 修改迁移脚本,添加显式类型转换: ```python def upgrade() -> None: op.execute(""" ALTER TABLE recharge_orders ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'UTC'; -- ... 其他字段 """) ``` #### 3. 应用迁移 ```bash 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 继续修复。