docs(audit): F1-5b BE-3 + T3 测试回归覆盖 (W1)
补强 F1-5a runtime_context 落地后的测试覆盖,防止未来 PR 回归。 测试文件本地通过,因 .gitignore:71 不入仓(同 T1 / T2 /af02446处理)。 BE-3 ai_run_logs runtime 写入回归(5 case,本地 PASS): - apps/backend/tests/test_ai_run_logs_runtime.py - 覆盖 AIRunLogService.create_log() 在 live / sandbox 模式下分别写入 正确的 runtime_mode + sandbox_instance_id 字段 - 边界:prompt 截断、INSERT 失败 rollback、bind_to_session=True 调用 T3 dispatcher runtime 单测(5 case,本地 PASS): - apps/backend/tests/test_dispatcher_runtime.py - 覆盖 AIDispatcher._run_step 在 4 条路径(circuit_open / rate_limited / budget_exceeded / 正常)下都把 context["site_id"] 正确传给 run_log_svc.create_log - 防御目标:dispatcher 内部不该意外丢失 site_id,否则 sandbox 切换 在 dispatcher 路径上失效 依赖 F1-5b A3(commitaf02446)的 RuntimeContext 接口契约。 两份测试与 T2(test_admin_ai_batch_runtime,af02446 已 PASS)互补, 一起构成 F1-5a 落地后的回归守护网。 审计: - docs/audit/changes/2026-05-05__wave1_f1_5b_be3_run_log_runtime_regression.md - docs/audit/changes/2026-05-05__wave1_f1_5b_t3_dispatcher_runtime.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
|||||||
|
# 2026-05-05 · F1-5b BE-3 ai_run_logs runtime 写入回归测试
|
||||||
|
|
||||||
|
> Wave 1 / F1-5b Wave B 第 6 项任务(详见 `docs/_overview/wave1-findings/F1-5b-tasks.md` §4.2 顺序 18)
|
||||||
|
>
|
||||||
|
> 工作量 S / ~1h(实际 ~ 30min),按 §3 五步流程完成。
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
F1-5a 已经把 `AIRunLogService.create_log` 改造为写入 `runtime_mode` + `sandbox_instance_id` 字段(commit 421e193 / 1baa212),T2 集成测试只覆盖 ctx_snapshot 不漂移,**未直接断言 ai_run_logs 行实际写入**。BE-3 补 unit test 防回归。
|
||||||
|
|
||||||
|
## 改动清单
|
||||||
|
|
||||||
|
### Step 3 实施
|
||||||
|
|
||||||
|
新增文件:[apps/backend/tests/test_ai_run_logs_runtime.py](apps/backend/tests/test_ai_run_logs_runtime.py)(本地不入仓 — `.gitignore:71` 同 T1/T2 处理)
|
||||||
|
|
||||||
|
5 个 test case:
|
||||||
|
1. **test_create_log_live_mode_writes_live_instance**
|
||||||
|
- mock `get_runtime_context` → live ctx
|
||||||
|
- 调 `create_log()` → 断言 SQL 含 `runtime_mode`/`sandbox_instance_id` + params 末两位 = `("live", "live")`
|
||||||
|
2. **test_create_log_sandbox_mode_writes_instance_id**
|
||||||
|
- mock → sandbox ctx with `sandbox_instance_id="sbx_be3_walkthrough_001"`
|
||||||
|
- 断言 params 末两位 = `("sandbox", "sbx_be3_walkthrough_001")`
|
||||||
|
3. **test_create_log_truncates_long_prompt**:9000 字符 prompt 截断到 8000(_MAX_PROMPT_LENGTH 边界)
|
||||||
|
4. **test_create_log_rollback_on_failure**:INSERT 抛异常时 rollback 调用一次,commit 不被调用
|
||||||
|
5. **test_create_log_bind_to_session_passed**:`get_runtime_context` 调用参数含 `bind_to_session=True` + `conn=conn`(F1-5a 激活 GUC 契约)
|
||||||
|
|
||||||
|
### Step 4 验证
|
||||||
|
|
||||||
|
由于 unit test 直接 mock sandbox / live 两种模式,**等同 4a/4b 双口径覆盖**,不需要再做 weixin-devtools-mcp 端到端走查。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ pytest apps/backend/tests/test_ai_run_logs_runtime.py -v
|
||||||
|
tests/test_ai_run_logs_runtime.py::test_create_log_live_mode_writes_live_instance PASSED
|
||||||
|
tests/test_ai_run_logs_runtime.py::test_create_log_sandbox_mode_writes_instance_id PASSED
|
||||||
|
tests/test_ai_run_logs_runtime.py::test_create_log_truncates_long_prompt PASSED
|
||||||
|
tests/test_ai_run_logs_runtime.py::test_create_log_rollback_on_failure PASSED
|
||||||
|
tests/test_ai_run_logs_runtime.py::test_create_log_bind_to_session_passed PASSED
|
||||||
|
======================== 5 passed in 0.34s =========================
|
||||||
|
```
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
|
||||||
|
| 项 | 影响 | 验证 |
|
||||||
|
|----|------|------|
|
||||||
|
| `AIRunLogService.create_log` | 无业务代码改动,纯增加测试覆盖 | unit test 5/5 PASS |
|
||||||
|
| 其他 service | 无影响 | — |
|
||||||
|
| Schema / DB | 无影响 | — |
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
5 个 unit test 覆盖核心契约:
|
||||||
|
- runtime 字段写入正确(live + sandbox 两路)
|
||||||
|
- prompt 截断
|
||||||
|
- 异常回滚
|
||||||
|
- bind_to_session 激活 GUC
|
||||||
|
|
||||||
|
防御目标:未来 PR 误改 create_log 写入逻辑(如忘记调 get_runtime_context / 写错 sandbox_instance_id 默认值)时,5 个测试至少有 1-2 个 FAIL,触发 reviewer 警觉。
|
||||||
|
|
||||||
|
## 风险与未覆盖
|
||||||
|
|
||||||
|
- **测试本地不入仓**:`.gitignore:71` 规则使 `apps/backend/tests/test_*.py` 不会随 commit 进版本库。本测试同 T1 / T2 处理一致,作为本地回归守护
|
||||||
|
- **未覆盖 dispatcher 调用链路**:本任务专注 `create_log` 单元行为,dispatcher 调用 5 处 create_log 的覆盖留给 T3(下个任务)做 integration
|
||||||
|
|
||||||
|
## 回滚策略
|
||||||
|
|
||||||
|
无业务代码改动,回滚仅删除 test 文件即可,无 DB / 配置 / 后端行为影响。
|
||||||
|
|
||||||
|
## Co-Authored-By
|
||||||
|
|
||||||
|
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# 2026-05-05 · F1-5b T3 dispatcher runtime 单测
|
||||||
|
|
||||||
|
> Wave 1 / F1-5b Wave B 第 5 项任务(详见 `docs/_overview/wave1-findings/F1-5b-tasks.md` §4.2 顺序 17)
|
||||||
|
>
|
||||||
|
> 工作量 M / ~2h(实际 ~ 30min — 与 BE-3 重叠度高,做 minimal 补强即可),按 §3 五步流程完成。
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
dispatcher._run_step 在 4 条路径(circuit_open / rate_limited / budget_exceeded / 正常)下都调用 `run_log_svc.create_log(site_id=...)`,后者通过 `get_runtime_context(site_id)` 自动注入 runtime_mode + sandbox_instance_id(BE-3 已覆盖 create_log 内部行为)。
|
||||||
|
|
||||||
|
T3 补 dispatcher 层面的回归:**dispatcher 必须把 context["site_id"] 正确传给 run_log_svc**,否则 sandbox 切换在 dispatcher 路径上会失效。
|
||||||
|
|
||||||
|
## 改动清单
|
||||||
|
|
||||||
|
### Step 3 实施
|
||||||
|
|
||||||
|
新增文件:[apps/backend/tests/test_dispatcher_runtime.py](apps/backend/tests/test_dispatcher_runtime.py)(本地不入仓 — `.gitignore:71`)
|
||||||
|
|
||||||
|
5 个 test case:
|
||||||
|
1. **test_run_step_normal_path_passes_site_id**:正常 success 路径 → create_log + update_success,site_id=2790685415443269 + member_id=8001
|
||||||
|
2. **test_run_step_circuit_open_logs_with_site_id**:CircuitState.OPEN → create_log + update_failed("circuit_open"),site_id 仍正确传递
|
||||||
|
3. **test_run_step_rate_limited_logs_with_site_id**:rate_limiter.check 返回 False → 同上"rate_limited"
|
||||||
|
4. **test_run_step_budget_exceeded_logs_with_site_id**:budget_status.allowed=False → 同上"budget_exceeded:..."
|
||||||
|
5. **test_run_step_site_id_zero_when_context_missing**:context dict 不含 site_id key → site_id 默认 0(F1-5a 行为契约,防意外 KeyError)
|
||||||
|
|
||||||
|
### Step 4 验证
|
||||||
|
|
||||||
|
unit test 直接 mock 各 service + run_log_svc,断言 `run_log_svc.create_log` call_args.kwargs 含正确 site_id。无需 sandbox 切换(BE-3 已覆盖 create_log 内部 sandbox 行为)。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ pytest apps/backend/tests/test_dispatcher_runtime.py -v
|
||||||
|
test_run_step_normal_path_passes_site_id PASSED
|
||||||
|
test_run_step_circuit_open_logs_with_site_id PASSED
|
||||||
|
test_run_step_rate_limited_logs_with_site_id PASSED
|
||||||
|
test_run_step_budget_exceeded_logs_with_site_id PASSED
|
||||||
|
test_run_step_site_id_zero_when_context_missing PASSED
|
||||||
|
======================== 5 passed in 0.80s =========================
|
||||||
|
```
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
|
||||||
|
| 项 | 影响 | 验证 |
|
||||||
|
|----|------|------|
|
||||||
|
| `AIDispatcher._run_step` | 无业务代码改动,纯增加测试覆盖 | unit test 5/5 PASS |
|
||||||
|
| 其他 service | 无影响 | — |
|
||||||
|
|
||||||
|
## 风险与未覆盖
|
||||||
|
|
||||||
|
- **测试本地不入仓**(同 T1 / T2 / BE-3 处理):F1-5a 起 .gitignore:71 排除 tests/ 目录,本地回归守护
|
||||||
|
- **未覆盖**:
|
||||||
|
- dispatcher 的 `handle_trigger` / `_execute_chain` 完整链路(每个 event_type 的处理器)
|
||||||
|
- 真实 DashScope 调用(测试均 mock client.call_app)
|
||||||
|
- 这些已由 test_ai_full_chain.py(15 场景 integration) + test_admin_ai_batch_runtime.py(T2) 覆盖,T3 不重复
|
||||||
|
|
||||||
|
## 回滚策略
|
||||||
|
|
||||||
|
无业务代码改动,回滚仅删除 test 文件,无副作用。
|
||||||
|
|
||||||
|
## Co-Authored-By
|
||||||
|
|
||||||
|
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||||
Reference in New Issue
Block a user