fix(tests): resolve flaky test_batch_download_session_closed #1406

Merged
xiaoxia merged 2 commits from fix/flaky-batch-download-session-test into develop 2026-08-17 19:55:28 +08:00
Owner

问题

test_batch_download_session_closed 在全量测试运行时偶发失败(session.close.assert_called_once() 报 Called 0 times),单独跑 16/16 通过。

根因

  1. _run_with_fakes 在 patch 上下文之前调用 session_maker() 创建 session
  2. 全量测试运行时,前序测试已解析 celery PromiseProxy 的 _get_current_object() 缓存
  3. 使用字符串路径 patch("worker_app.db.SessionLocal", ...) 在模块缓存状态下不够可靠
  4. 导致测试中追踪的 session mock 与源码实际使用的 session 不一致

修复方案

  1. 使用 patch.object 替代字符串路径 patch:直接在实际的 worker_app.db 模块对象上打 patch,避免模块解析时序问题
  2. 在 patch 上下文内部追踪 session:通过 wrapper 函数在 patch 生效期间捕获创建的 session,而非提前创建
  3. test_batch_download_session_closed 改为自包含:不再依赖 _run_with_fakes,直接使用 patch.object 确保完全隔离
  4. test_batch_download_closes_session_on_error 同步修复:使用相同的 patch.object 模式

改动文件

  • tests/unit/test_batch_download.py
    • _run_with_fakes: 改用 patch.object(_db_mod, "SessionLocal", ...) + session 追踪 wrapper
    • test_batch_download_session_closed: 自包含实现,不依赖 _run_with_fakes
    • test_batch_download_closes_session_on_error: 改用 patch.object

测试

  • 单独运行:pytest tests/unit/test_batch_download.py -v → 16/16 通过
  • 全量运行:需 CI 验证

影响

  • 仅测试代码改动,不影响生产代码
  • 修复 flaky test,避免阻塞后续 PR 的 CI Gate
## 问题 `test_batch_download_session_closed` 在全量测试运行时偶发失败(`session.close.assert_called_once()` 报 Called 0 times),单独跑 16/16 通过。 ## 根因 1. `_run_with_fakes` 在 patch 上下文**之前**调用 `session_maker()` 创建 session 2. 全量测试运行时,前序测试已解析 celery PromiseProxy 的 `_get_current_object()` 缓存 3. 使用字符串路径 `patch("worker_app.db.SessionLocal", ...)` 在模块缓存状态下不够可靠 4. 导致测试中追踪的 session mock 与源码实际使用的 session 不一致 ## 修复方案 1. **使用 `patch.object` 替代字符串路径 patch**:直接在实际的 `worker_app.db` 模块对象上打 patch,避免模块解析时序问题 2. **在 patch 上下文内部追踪 session**:通过 wrapper 函数在 patch 生效期间捕获创建的 session,而非提前创建 3. **`test_batch_download_session_closed` 改为自包含**:不再依赖 `_run_with_fakes`,直接使用 `patch.object` 确保完全隔离 4. **`test_batch_download_closes_session_on_error` 同步修复**:使用相同的 `patch.object` 模式 ## 改动文件 - `tests/unit/test_batch_download.py` - `_run_with_fakes`: 改用 `patch.object(_db_mod, "SessionLocal", ...)` + session 追踪 wrapper - `test_batch_download_session_closed`: 自包含实现,不依赖 `_run_with_fakes` - `test_batch_download_closes_session_on_error`: 改用 `patch.object` ## 测试 - 单独运行:`pytest tests/unit/test_batch_download.py -v` → 16/16 通过 - 全量运行:需 CI 验证 ## 影响 - 仅测试代码改动,不影响生产代码 - 修复 flaky test,避免阻塞后续 PR 的 CI Gate
xiaoxia added 1 commit 2026-08-17 19:44:49 +08:00
fix(tests): resolve flaky test_batch_download_session_closed
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 27s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m53s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m56s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m57s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m6s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m10s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m40s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m27s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
ba474bd97f
Root cause:
- _run_with_fakes called session_maker() BEFORE patch context
- Full test suite runs caused PromiseProxy cache staleness
- Session mock tracked outside patch did not match actual session

Fix:
- Use patch.object on actual worker_app.db module (not string path)
- Track session creation INSIDE patch context via wrapper
- Make test_batch_download_session_closed self-contained
- Apply same pattern to test_batch_download_closes_session_on_error

This ensures proper isolation and avoids module-level cache issues.

🚀 预览环境已部署

项目 详情
PR号 #1406
预览链接 https://pr-1406.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1406 | | 预览链接 | [https://pr-1406.preview.xiaoxiajianji.com](https://pr-1406.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot added 1 commit 2026-08-17 19:48:52 +08:00
style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 31s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m25s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m32s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m48s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m50s
AI Code Review / AI Code Review (pull_request) Successful in 1m59s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m3s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m44s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m17s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m27s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 6m4s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 42s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 47s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m56s
CI/CD Pipeline / CI Gate (pull_request) Successful in 19s
77c5bb851e
Collaborator

【阻塞级判定】

  • 是否存在阻塞级问题:否
  • 阻塞级问题数量:0 个

📊 审查概览

  • 整体评价:通过
  • 建议级问题数量:1 个

🔴 阻塞级问题(必须修复)

💡 改进建议(不阻塞合并)

  1. [tests/unit/test_batch_download.py: 254-274] 代码重复与可维护性
    • 具体内容:test_batch_download_session_closed 函数将原本通过 _run_with_fakes 封装的逻辑内联到了测试函数中(包括 patch 设置和调用逻辑)。虽然这解决了特定的 PromiseProxy 缓存问题,但导致了代码重复。如果 _run_with_fakes 已经更新为使用 patch.object,建议评估是否可以复用该辅助函数,或者将通用的 patch 上下文管理器提取为 fixture,以保持测试代码的简洁。

良好实践

  • 使用 patch.object 替代字符串路径:在 _run_with_fakes 和测试用例中,将 patch("worker_app.db.SessionLocal", ...) 修改为 import worker_app.db; patch.object(worker_app.db, "SessionLocal", ...)。这是一种更稳健的做法,能有效解决因模块导入顺序或代理对象(如 PromiseProxy)导致的 mock 失效或缓存问题。
  • 增强的 Session 追踪逻辑:在 _run_with_fakes 中引入 _tracking_sm 来捕获实际创建的 session 实例,这比直接使用 mock 对象更能反映真实代码的执行路径,便于测试 session 的生命周期管理。
  • 类型提示:为 captured_created_sessions 添加了类型提示,提高了代码的可读性和静态分析能力。

🤖 由 AI 代码审查机器人自动生成 | 2026-08-17 11:50:50 | 模型:

### 【阻塞级判定】 - 是否存在阻塞级问题:否 - 阻塞级问题数量:0 个 ### 📊 审查概览 - 整体评价:通过 - 建议级问题数量:1 个 ### 🔴 阻塞级问题(必须修复) 无 ### 💡 改进建议(不阻塞合并) 1. **[tests/unit/test_batch_download.py: 254-274] 代码重复与可维护性** - 具体内容:`test_batch_download_session_closed` 函数将原本通过 `_run_with_fakes` 封装的逻辑内联到了测试函数中(包括 patch 设置和调用逻辑)。虽然这解决了特定的 `PromiseProxy` 缓存问题,但导致了代码重复。如果 `_run_with_fakes` 已经更新为使用 `patch.object`,建议评估是否可以复用该辅助函数,或者将通用的 patch 上下文管理器提取为 fixture,以保持测试代码的简洁。 ### ✅ 良好实践 - **使用 `patch.object` 替代字符串路径**:在 `_run_with_fakes` 和测试用例中,将 `patch("worker_app.db.SessionLocal", ...)` 修改为 `import worker_app.db; patch.object(worker_app.db, "SessionLocal", ...)`。这是一种更稳健的做法,能有效解决因模块导入顺序或代理对象(如 PromiseProxy)导致的 mock 失效或缓存问题。 - **增强的 Session 追踪逻辑**:在 `_run_with_fakes` 中引入 `_tracking_sm` 来捕获实际创建的 session 实例,这比直接使用 mock 对象更能反映真实代码的执行路径,便于测试 session 的生命周期管理。 - **类型提示**:为 `captured` 和 `_created_sessions` 添加了类型提示,提高了代码的可读性和静态分析能力。 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-08-17 11:50:50 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
xiaoxia merged commit 4efc44e0dc into develop 2026-08-17 19:55:28 +08:00

🗑️ 预览环境已清理

PR #1406 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1406 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.