From 6df02f8efec9e3749133177eda3373cfbdaac2ae Mon Sep 17 00:00:00 2001 From: Neo Date: Tue, 5 May 2026 19:17:19 +0800 Subject: [PATCH] =?UTF-8?q?docs(audit):=20F1-5b=20BE-3=20+=20T3=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=9B=9E=E5=BD=92=E8=A6=86=E7=9B=96=20(W1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补强 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(commit af02446)的 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) --- ...e1_f1_5b_be3_run_log_runtime_regression.md | 71 +++++++++++++++++++ ...5-05__wave1_f1_5b_t3_dispatcher_runtime.md | 61 ++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 docs/audit/changes/2026-05-05__wave1_f1_5b_be3_run_log_runtime_regression.md create mode 100644 docs/audit/changes/2026-05-05__wave1_f1_5b_t3_dispatcher_runtime.md diff --git a/docs/audit/changes/2026-05-05__wave1_f1_5b_be3_run_log_runtime_regression.md b/docs/audit/changes/2026-05-05__wave1_f1_5b_be3_run_log_runtime_regression.md new file mode 100644 index 0000000..b020abd --- /dev/null +++ b/docs/audit/changes/2026-05-05__wave1_f1_5b_be3_run_log_runtime_regression.md @@ -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) diff --git a/docs/audit/changes/2026-05-05__wave1_f1_5b_t3_dispatcher_runtime.md b/docs/audit/changes/2026-05-05__wave1_f1_5b_t3_dispatcher_runtime.md new file mode 100644 index 0000000..95920f7 --- /dev/null +++ b/docs/audit/changes/2026-05-05__wave1_f1_5b_t3_dispatcher_runtime.md @@ -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)