From 6ab380ec80b545235a87f0e0d6e89b2a14cd99d3 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Fri, 5 Jun 2026 23:03:50 +0800 Subject: [PATCH] auto-sync: 2026-06-05 23:03:50 --- docs/test-plan-e2e-v27.md | 735 ++++++++++++++++++++++++++++++++------ 1 file changed, 623 insertions(+), 112 deletions(-) diff --git a/docs/test-plan-e2e-v27.md b/docs/test-plan-e2e-v27.md index 3cfb5fa..6e6110d 100644 --- a/docs/test-plan-e2e-v27.md +++ b/docs/test-plan-e2e-v27.md @@ -1,141 +1,652 @@ -# v2.7/v2.8 端到端测试方案 +# v3.0 测试体系设计 -> 日期:2026-05-18(v2.7 初版)→ 2026-06-01 更新(v2.8 #07 系列 + Prompt v3.0) -> 作者:司马懿(初版)→ 庞统(v2.8 更新) -> 环境:真实部署(`~/.sanguo_projects/sanguo_moziplus_v2/`)+ 真实 Agent +> **作者**: 庞统(综合) + 司马懿(v30 草案 + 背靠背审查) +> **日期**: 2026-06-05(v3.0 合并版) +> **状态**: 待主公确认 +> **覆盖设计文档**: #01 四相循环 / #07 Acquire-First / #08 Classify / #09 Rebuttal / #06 PM2 恢复 / #04 黑板协作 / #10 T3 需求探索 / #12 Pipeline / architecture-v3.0 +> **替代**: 本文档替代 `docs/test-plan-e2e-v27.md`(v2.7 初版,2026-05-18) --- -## 一、测试策略 +## 〇、设计原则(主公三原则 + 仲达补充) -- **框架**:pytest + FastAPI TestClient(直接挂载 app,绕过网络层,但走完整业务逻辑) -- **数据隔离**:每个测试 class 用独立项目 ID,测试前创建、测试后清理 -- **Agent 策略**:E1-E8 不启动真实 Agent(只验证 API + DB),E9-E10 启动真实 Agent 执行简单任务 -- **Ticker**:手动调用 `ticker.tick()` 而非等待 30s 循环 -- **测试项目**:统一前缀 `e2e-v27-`,便于识别和清理 +### 原则一:精准测试,最小代价覆盖修改内容 -## 二、场景清单 +- 改了什么模块就跑什么测试,不默认跑全量 +- 全量测试由用户手动触发 +- 测试文件命名要让人一看就知道改了什么该跑什么 -### E1: 项目管理(4 个测试) -- E1.1 创建项目(POST /api/projects) -- E1.2 项目列表(GET /api/projects) -- E1.3 自动发现(含 blackboard.db 的目录) -- E1.4 归档项目(status=archived,目录不移动) +### 原则二:从设计文档正推覆盖 -### E2: Task CRUD + 状态机(5 个测试) -- E2.1 创建 Task(POST /api/projects/{pid}/tasks) -- E2.2 查询 Task(GET,expand=all) -- E2.3 合法状态转换(pending → claimed → working → review → done) -- E2.4 非法转换拒绝(409 + valid_transitions) -- E2.5 列表筛选(status/assignee/parent_task) +- 不是"有什么函数测什么",而是"设计定义了哪些行为就测哪些" +- 每个设计文档对应的测试覆盖要显式标注 -### E3: SubTask 父子关系(4 个测试) -- E3.1 创建父 Task + 3 个子 Task -- E3.2 list_subtasks 验证 -- E3.3 top_level_tasks 排除子 Task -- E3.4 子 Task 的 stage 字段 +### 原则三:真实环境测试 + 测完清理 -### E4: Stage 进度(3 个测试) -- E4.1 带 stages_json 的父 Task → 子 Task 分配到各 stage → progress 端点 -- E4.2 空 stage 的父 Task 进度 -- E4.3 stage 分组统计验证 +- E2E 不搞独立 daemon、独立进程、独立数据 +- 最终汇聚点是真实 Agent,独立环境测不出真实链路 +- 正确做法:**真实环境跑测试 → 测完清掉测试数据** -### E5: 父 Task 状态聚合(6 个测试) -- E5.1 all done → 父 done -- E5.2 has review → 父 review -- E5.3 has working → 父 working -- E5.4 all pending → 父 pending -- E5.5 cancelled 子 Task 排除 -- E5.6 手动状态(cancelled 父 Task)不被覆盖 +### 原则四:串行场景 + 并行压力分离(主公新增) -### E6: 依赖链(3 个测试) -- E6.1 Task B depends_on Task A,A done 后 B 自动 pending(Ticker 推进) -- E6.2 A 未完成时 B 保持 blocked -- E6.3 多层依赖(A → B → C) - -### E7: 超时回收(2 个测试) -- E7.1 claimed 超时 → pending -- E7.2 working 超时 → failed - -### E8: Mail Tab 6 端点(7 个测试) -- E8.1 发送 Mail(inform 类型 → 自动 done) -- E8.2 发送 Mail(task-assign 类型 → pending) -- E8.3 Mail 列表 + 筛选(from/to/unread) -- E8.4 Mail 详情(含 comments) -- E8.5 标记已读 -- E8.6 标记已执行 -- E8.7 Mail summary + agents 列表 - -### E9: 真实 Agent 调度(2 个测试) -- E9.1 创建简单 Task → Ticker 调度 → Agent spawn → Agent 回写 working → 完成任务 -- E9.2 创建 review 类型 Task → 调度到 simayi-challenger → 回写 review 结果 -- **Agent 任务内容**:简单的"echo hello"级别任务,10-30 秒完成 - -### E10: 全链路集成(1 个测试) -- E10.1 创建项目 → 创建父 Task(stages_json)+ 3 个子 Task → Ticker tick → 依赖推进 → 聚合刷新 → Mail 通知 → 验证完整状态链 +- **场景测试**:逐个执行,一个跑完收集结果、分析根因、解决后再跑下一个。用时间换可靠性 +- **压力测试**:单独一轮,多任务并行,验证并发控制、全局上限等 +- 两类测试不混在一起 --- -## 三、v2.8 新增测试场景(#07 Spawner Acquire-First + Prompt v3.0) +## 一、问题诊断 -> 日期:2026-06-01 -> 对应设计:`docs/design/07-spawner-acquire-first.md`、`docs/design/03-prompt-evolution.md` +### 1.1 现有测试的致命缺陷 -### E11: Spawner Acquire-First Phase 0-4(6 个测试) +| 缺陷 | 根因 | 后果 | +|------|------|------| +| E2E 全部写真实 data_root | TestClient 直连生产 DB,无隔离 | 每次跑 `pytest tests/` 残留 e2e-v27 项目,PM2 重启触发广播风暴 | +| E9-E14 cleanup 调不存在的 API | `/projects/{pid}/archive` 未实现 | 异常被吞,数据不清理 | +| 无分层 | 单文件 62K,所有测试混在一起 | 改个小 bug 要跑全量 | +| Agent 无意义响应 | 残留 pending 任务被 daemon 广播 | 浪费 token + 干扰正常工作 | +| 无数据清理能力 | 项目删不掉、测试邮件堆积 | 测试和生产数据混杂 | -- **E11.1 Phase 0 Pre-acquire revive**:任务 timeout/failed 状态 → Phase 0 自动 revive → Phase 1 acquire 成功 -- **E11.2 Phase 0 假死检测**:status=running + lock PID 死 → Phase 0 自动 revive -- **E11.3 Phase 1 Counter acquire 互斥**:同 agent 并发 spawn → 第二个 AgentBusyError(reason=counter_blocked) -- **E11.4 Phase 2 Session check 锁保护**:counter acquire 后 → session check 在锁保护下执行 → session locked → release counter → AgentBusyError(reason=session_locked) -- **E11.5 Phase 2 Blockers 收集**:多 blocker 并列收集(locked + compact)→ 返回全部 blockers -- **E11.6 Phase 3 on_checks_passed 异常回滚**:on_checks_passed 抛异常 → counter 自动 release +### 1.2 Review 发现的额外问题 -### E12: _check_timeouts 统一超时(4 个测试) - -- **E12.1 crash_limit 统一检查(working)**:executor crash 3 次/30min → _check_timeouts 标 failed -- **E12.2 crash_limit 统一检查(review)**:reviewer crash 3 次/30min → _check_timeouts 标 failed(不再走 _dispatch_reviews 的 crash_limit) -- **E12.3 updated_at fallback**:mail auto-working 无 started_at/claimed_at → updated_at fallback 生效 → 超时回收 -- **E12.4 process_dead 对 review 状态**:review agent 进程死 → 保持 review 状态(不推 pending)→ 等 _dispatch_reviews 处理 - -### E13: Compact Hanging 不标 failed(3 个测试) - -- **E13.1 compact_hanging release counter**:compact 等超限 → compact_hanging → release counter → 任务保持 working -- **E13.2 compact_hanging 后 ticker 重新 dispatch**:compact_hanging → ticker _check_timeouts 检测超时 → 推回 pending → 重新 dispatch -- **E13.3 retry 遇 session busy 释放 counter**:_do_retry 遇 AgentBusyError → release counter → 任务保持 working → ticker 重新 dispatch - -### E14: AgentBusyError 分类(3 个测试) - -- **E14.1 counter_blocked**:counter acquire 失败 → AgentBusyError(reason=counter_blocked) -- **E14.2 session_locked/running/compacting**:Phase 2 各种 blocker → AgentBusyError 携带具体 reason + detail.blockers -- **E14.3 Dispatcher 错误区分**:dispatcher 捕获 AgentBusyError → 日志记录具体原因 → 路由决策写入 routing_decisions - -### E15: Prompt v3.0 验证(3 个测试) - -> 这些是集成测试,需要真实 Agent。标记为 P1。 - -- **E15.1 Claim prompt 三级响应**:broadcast → Agent 不匹配 → 写 observation comment(非 NO_REPLY) -- **E15.2 Executor prompt 任务式指挥**:executor 不再收到步骤列表,收到意图+终态+约束 → 自主决策步骤 -- **E15.3 Reviewer prompt 挑战者思维**:reviewer 审查时用挑战者视角 + confidence 自评 +| 问题 | 说明 | +|------|------| +| **E14 编号冲突** | 设计文档 E14="AgentBusyError 分类"(3 个测试),代码 E14="Rollback"(1 个测试),同名不同义 | +| **设计文档严重滞后** | v31 新增 9 个测试类 + test_four_phase.py ~20 个测试未入文档 | +| **E15.2/E15.3 完全缺失** | Executor prompt / Reviewer prompt 无任何层级测试 | +| **E2E gate 不统一** | E1-E8 无 RUN_INTEGRATION gate,E9-E14 有。现已统一加 gate | --- -## 四、通过标准 +## 二、分层架构 -- **P0 必须全部通过**:E1-E8 + E10 + E11-E14 -- **P1 通过率 ≥ 80%**:E9 + E15(依赖真实 Agent,可能受网络/环境干扰) -- **总测试用例**:约 58 个(原 37 + 新增 19 + Prompt 集成 3 减去合并的 1) +### 2.1 三层测试金字塔 -## 五、清理策略 +``` + ┌─────────────────────────────────┐ + │ E2E 测试 │ marker: e2e + │ 场景测试(串行)+ 压力测试 │ gate: RUN_INTEGRATION=1 + │ 真实 daemon + 真实 Agent │ 测完清理 + ├─────────────────────────────────┤ + │ 集成测试 │ marker: integration + │ API 端点 + DB 交互 │ TestClient + tmp_path 隔离 + ├─────────────────────────────────┤ + │ 单元测试 │ marker: unit (default) + │ 纯逻辑·快 │ mock 所有外部依赖 + └─────────────────────────────────┘ +``` -测试完成后: -1. 删除测试项目目录(`data/e2e-v27-*`) -2. 清理 `_mail` 项目中测试数据 -3. 不影响已有项目数据 +**关键规则:只有 E2E 层 spawn 真实 Agent。Unit 和 Integration 层不 spawn。** -## 六、变更记录 +### 2.2 各层定位 + +| 维度 | 单元测试 | 集成测试 | E2E 测试 | +|------|---------|---------|---------| +| **目标** | 验证单个函数/类的逻辑正确性 | 验证 API 端点 + DB 交互 | 验证完整链路(真实 Daemon + Agent) | +| **数据** | mock/fixture | tmp_path + 独立 SQLite | 真实 data_root + 测试前缀隔离 | +| **Agent** | ❌ 不 spawn | ❌ 不 spawn | ✅ 真实 spawn | +| **清理** | N/A(不产生数据) | tmp_path 自动清理 | 清理 API teardown 删除 | +| **速度** | <1s/测试 | 1-5s/测试 | 30-300s/测试 | +| **频率** | 每次提交 | 改了 API 层跑 | 部署前(手动触发) | + +### 2.3 改动 → 测试映射(原则一) + +**改了 X,应该跑哪些测试?** + +| 改动的模块 | 必跑测试 | pytest 命令 | +|-----------|---------|------------| +| `src/blackboard/db.py`(数据模型) | unit/test_models + integration | `pytest tests/unit/test_models.py tests/integration/` | +| `src/blackboard/db.py`(状态机) | unit/test_state_machine + integration | `pytest -m state_machine` | +| `src/daemon/spawner.py`(classify) | unit/test_classify_outcome | `pytest -m classify` | +| `src/daemon/spawner.py`(acquire/release) | unit/test_spawner + integration | `pytest tests/unit/test_spawner.py tests/integration/test_spawner_integration.py` | +| `src/daemon/ticker.py`(广播) | unit/test_broadcast_tracker + integration | `pytest -m broadcast` | +| `src/daemon/ticker.py`(超时) | unit/test_timeout + integration | `pytest tests/integration/test_ticker_integration.py -k timeout` | +| `src/daemon/router.py` | unit/test_router | `pytest tests/unit/test_router.py` | +| `src/daemon/counter.py` | unit/test_counter | `pytest tests/unit/test_counter.py` | +| `src/daemon/dispatcher.py` | unit/test_dispatcher + integration | `pytest tests/unit/test_dispatcher.py tests/integration/test_dispatcher_integration.py` | +| `src/api/mail_routes.py` | integration/test_api_mail | `pytest -m mail` | +| `src/api/blackboard_routes.py` | integration/test_api_tasks + reviews | `pytest tests/integration/ -k task` | +| `src/daemon/review.py` | unit/test_rebuttal + integration | `pytest -m review` | + +**快速验证**:`pytest -m "not e2e"`(~2-5 分钟,不含 E2E) + +--- + +## 三、E2E 执行策略(原则四展开) + +### 3.1 两大类别 + +| 类别 | 目标 | 执行方式 | 文件 | +|------|------|---------|------| +| **场景测试** | 逐个验证每个场景的正确性 | **严格串行**:一个 case 跑完 → 收集结果 → 根因分析(如有问题)→ 修 → 再跑下一个 | `test_e2e_scenarios.py` | +| **压力测试** | 验证并发控制、全局上限、资源竞争 | **并行**:多任务同时 broadcast/dispatch/spawn | `test_e2e_stress.py` | + +### 3.2 场景测试流程(串行) + +``` +┌─────────────────────────────────────────────────────┐ +│ 对每个 E2E 场景: │ +│ │ +│ 1. 创建测试任务(e2e- 前缀) │ +│ 2. 等待任务完成(轮询状态直到终态) │ +│ 3. 收集结果 │ +│ ├── ✅ 通过 → 记录 → 清理 → 执行下一个场景 │ +│ └── ❌ 失败 → 根因分析 → 修复 → 重跑当前场景 │ +│ 4. 清理测试数据(与诊断分离) │ +│ │ +│ 关键:一个场景彻底完成(通过或确认不修)才进入下一个 │ +└─────────────────────────────────────────────────────┘ +``` + +**实现方式**: +- pytest 默认串行执行(不用 xdist) +- 每个测试函数是独立场景 +- `scope="module"` 的 fixture 共享 daemon 连接 +- 测试间无共享状态,各自创建/清理数据 + +### 3.3 压力测试流程(并行) + +``` +┌─────────────────────────────────────────────────────┐ +│ 压力测试(单独执行,场景测试全通过后): │ +│ │ +│ 1. 同时创建 N 个任务(e2e-stress- 前缀) │ +│ 2. 验证并发行为: │ +│ - counter 竞争:同 agent 并发 acquire → 第二个 blocked │ +│ - 全局上限:5/5 满载 → 新任务排队 │ +│ - broadcast 并发:多任务同时广播 → 无死锁 │ +│ - spawn 互斥:同 session 不重复 spawn │ +│ 3. 全部完成后清理 │ +└─────────────────────────────────────────────────────┘ +``` + +### 3.4 数据清理与诊断分离 + +``` +测试执行阶段 → 结果记录 → [可选] 数据保留用于诊断 → 清理阶段 + +- 测试执行时:记录所有创建的资源 ID(manifest) +- 通过时:立即清理 +- 失败时:保留数据供诊断,诊断完成后手动/脚本清理 +- 兜底:session 结束时 atexit 全量清理 e2e- 前缀 +``` + +### 3.5 前置依赖:数据清理 API + +E2E 测试能安全运行的前提: + +| 需求 | API | 状态 | +|------|-----|------| +| 项目物理删除 | `DELETE /api/projects/{pid}?physical=true` | 待实现 | +| 邮件批量删除 | `DELETE /api/mail?prefix=e2e-` | 待实现 | + +--- + +## 四、E2E 场景清单 + +> 编号规则:S = Scenario(场景测试),ST = Stress(压力测试) +> 原文档 E1-E15 重新编号,解决 E14 冲突 + +### 4.1 场景测试(S1-S15,串行执行) + +#### S1: 项目管理(4 个测试)| 对应 design: architecture-v3.0 §5 + +- S1.1 创建项目(POST /api/projects) +- S1.2 项目列表(GET /api/projects) +- S1.3 自动发现(含 blackboard.db 的目录自动注册) +- S1.4 归档项目(status=archived,目录不移动) + +#### S2: Task CRUD + 状态机(5 个测试)| 对应 design: architecture-v3.0 §5.2 + +- S2.1 创建 Task(POST /api/projects/{pid}/tasks) +- S2.2 查询 Task(GET,expand=all) +- S2.3 合法状态转换(pending → claimed → working → review → done) +- S2.4 非法转换拒绝(409 + valid_transitions) +- S2.5 列表筛选(status/assignee/parent_task) + +#### S3: SubTask 父子关系(4 个测试)| 对应 design: architecture-v3.0 §5 + +- S3.1 创建父 Task + 3 个子 Task +- S3.2 list_subtasks 验证 +- S3.3 top_level_tasks 排除子 Task +- S3.4 子 Task 的 stage 字段 + +#### S4: Stage 进度(3 个测试)| 对应 design: architecture-v3.0 §5 + +- S4.1 带 stages_json 的父 Task → 子 Task 分配到各 stage → progress 端点 +- S4.2 空 stage 的父 Task 进度 +- S4.3 stage 分组统计验证 + +#### S5: 父 Task 状态聚合(6 个测试)| 对应 design: architecture-v3.0 §6.1 + +- S5.1 all done → 父 done +- S5.2 has review → 父 review +- S5.3 has working → 父 working +- S5.4 all pending → 父 pending +- S5.5 cancelled 子 Task 排除 +- S5.6 手动状态(cancelled 父 Task)不被覆盖 + +#### S6: 依赖链(3 个测试)| 对应 design: architecture-v3.0 §6.1 + +- S6.1 Task B depends_on Task A,A done 后 B 自动 pending(Ticker 推进) +- S6.2 A 未完成时 B 保持 blocked +- S6.3 多层依赖(A → B → C) + +#### S7: 超时回收(2 个测试)| 对应 design: architecture-v3.0 §6.1 + +- S7.1 claimed 超时 → pending +- S7.2 working 超时 → failed + +#### S8: Mail(7 个测试)| 对应 design: architecture-v3.0 §12 + +- S8.1 发送 Mail(inform 类型 → 自动 done) +- S8.2 发送 Mail(task-assign 类型 → pending) +- S8.3 Mail 列表 + 筛选(from/to/unread) +- S8.4 Mail 详情(含 comments) +- S8.5 标记已读 +- S8.6 标记已执行 +- S8.7 Mail summary + agents 列表 + +#### S9: 真实 Agent 调度(3 个测试)| 对应 design: #01 四相循环 + +- S9.1 创建简单 Task → Ticker 调度 → Agent spawn → Agent 回写 working → 完成任务 +- S9.2 创建 review 类型 Task → 调度到 simayi-challenger → 回写 review 结果 +- S9.3 Guardrail 拦截(不合法任务 → Agent 拒绝执行) + +**Agent 任务内容**:简单的"echo hello"级别任务,10-30 秒完成 + +#### S10: 全链路集成(2 个测试)| 对应 design: #01 四相循环 + +- S10.1 逻辑链(创建项目 → 父 Task + 子 Task → Ticker tick → 依赖推进 → 聚合刷新 → Mail 通知 → 验证完整状态链) +- S10.2 Agent 全链(含真实 Agent spawn + 执行 + 产出 + review) + +#### S11: Spawner Acquire-First(6 个测试)| 对应 design: #07 + +- S11.1 Phase 0 Pre-acquire revive:任务 timeout/failed → Phase 0 自动 revive → Phase 1 acquire 成功 +- S11.2 Phase 0 假死检测:status=running + lock PID 死 → Phase 0 自动 revive +- S11.3 Phase 1 Counter acquire 互斥:同 agent 并发 spawn → 第二个 AgentBusyError(reason=counter_blocked) +- S11.4 Phase 2 Session check 锁保护:counter acquire 后 → session check 在锁保护下执行 → session locked → release counter → AgentBusyError(reason=session_locked) +- S11.5 Phase 2 Blockers 收集:多 blocker 并列收集(locked + compact)→ 返回全部 blockers +- S11.6 Phase 3 on_checks_passed 异常回滚:on_checks_passed 抛异常 → counter 自动 release + +#### S12: 超时统一处理(4 个测试)| 对应 design: #08 §3.7 + +- S12.1 crash_limit 统一检查(working):executor crash 3 次/30min → _check_timeouts 标 failed +- S12.2 crash_limit 统一检查(review):reviewer crash 3 次/30min → _check_timeouts 标 failed +- S12.3 updated_at fallback:mail auto-working 无 started_at/claimed_at → updated_at fallback 生效 → 超时回收 +- S12.4 process_dead 对 review 状态:review agent 进程死 → 保持 review 状态 → 等 _dispatch_reviews 处理 + +#### S13: Compact Hanging(3 个测试)| 对应 design: #07 §4.5 + +- S13.1 compact_hanging release counter:compact 等超限 → compact_hanging → release counter → 任务保持 working +- S13.2 compact_hanging 后 ticker 重新 dispatch:compact_hanging → ticker _check_timeouts 检测超时 → 推回 pending → 重新 dispatch +- S13.3 retry 遇 session busy 释放 counter:_do_retry 遇 AgentBusyError → release counter → 任务保持 working → ticker 重新 dispatch + +#### S14: AgentBusyError 分类(3 个测试)| 对应 design: #07 §2 + +> ⚠️ 原设计文档 E14="AgentBusyError 分类"与代码中 E14="Rollback" 冲突,此处统一修正 + +- S14.1 counter_blocked:counter acquire 失败 → AgentBusyError(reason=counter_blocked) +- S14.2 session_locked/running/compacting:Phase 2 各种 blocker → AgentBusyError 携带具体 reason + detail.blockers +- S14.3 Dispatcher 错误区分:dispatcher 捕获 AgentBusyError → 日志记录具体原因 → 路由决策写入 routing_decisions + +#### S15: Crash Rollback(1 个测试)| 对应 design: #07 §4.5 + +> ⚠️ 原代码中 E14="Rollback",现独立编号为 S15 + +- S15.1 crash 后 current_agent 回退验证:Agent crash → current_agent 指针回退 → 任务可被重新 dispatch + +#### S16: 广播认领扩展(2 个测试)| 对应 design: architecture-v3.0 §8.3 + +- S16.1 不匹配 Agent 写 observation comment(非 NO_REPLY) +- S16.2 匹配 Agent 正常认领 + +#### S17: Pause/Resume(1 个测试)| 对应 design: architecture-v3.0 §5.2 + +- S17.1 paused → resumed_from 状态流转 + +#### S18: Cancelled Restart(1 个测试)| 对应 design: architecture-v3.0 §5.2 + +- S18.1 cancelled → pending 重启 + +#### S19: Retry Chain(1 个测试)| 对应 design: #08 §3.3 + +- S19.1 failed → pending retry 链路 + +#### S20: Full Lifecycle with Review(1 个测试)| 对应 design: #09 + +- S20.1 创建 → 认领 → 执行 → review → rebuttal → done 全链路 + +#### S21: Cache Headers(3 个测试)| 对应 design: architecture-v3.0 §7 + +- S21.1 HTML 不缓存 +- S21.2 JS/CSS immutable 缓存 + +**场景测试合计:58 个测试** + +### 4.2 压力测试(ST1-ST3) + +#### ST1: 并发 Counter 竞争 + +- 同时创建多个任务指定同一 Agent → 验证 counter acquire 互斥 → 第二个 blocked +- 验证 counter release 后下一个 acquire 成功 + +#### ST2: 全局并发上限 + +- 同时创建 5 个任务指定不同 Agent → 全部 acquire 成功 +- 第 6 个 → 被拒绝或排队 + +#### ST3: Broadcast 并发 + +- 同时广播多个任务 → 无死锁 +- 验证广播计数器正确递增 + +**压力测试合计:3 个测试组** + +### 4.3 Prompt 集成测试(P1,延后) + +> 以下场景需要验证 Agent prompt 行为,标 P1 延后 + +- P1.1 Executor prompt 任务式指挥:executor 不再收到步骤列表,收到意图+终态+约束 → 自主决策步骤 +- P1.2 Reviewer prompt 挑战者思维:reviewer 审查时用挑战者视角 + confidence 自评 + +--- + +## 五、测试文件组织 + +### 5.1 目录结构 + +``` +tests/ +├── conftest.py # 全局 fixture + marker 注册 + atexit 清理兜底 +├── unit/ # 单元测试(不 spawn 真实 Agent) +│ ├── test_state_machine.py # 状态机转换(architecture-v3.0 §5.2) +│ ├── test_classify_outcome.py # Classify 场景(#08 §3) +│ ├── test_spawner.py # Spawner + Acquire-First(#07 §2) +│ ├── test_router.py # Router 确定性路由 +│ ├── test_counter.py # Counter 并发控制 +│ ├── test_broadcast_claim.py # BroadcastRound 逻辑 + bug fix 回归 +│ ├── test_mail_validation.py # 邮件 A1-A10 校验(后并入 test_api_mail) +│ ├── test_dispatcher.py # Dispatcher 调度逻辑 +│ ├── test_review.py # Review + Rebuttal 逻辑 +│ ├── test_inbox.py # Inbox JSONL 解析 +│ ├── test_models.py # 数据模型 +│ ├── test_experience.py # 经验蒸馏 +│ ├── test_bootstrap.py # Bootstrap 上下文 +│ ├── test_health.py # 健康检查 +│ ├── test_cli.py # CLI 命令 +│ ├── test_skill_system.py # Skill 匹配/推荐 +│ └── test_guardrails.py # Guardrail 规则 +├── integration/ # 集成测试(不 spawn 真实 Agent) +│ ├── test_api.py # 项目管理 + Daemon API +│ ├── test_api_mail.py # 邮件 API(A1-A10 防御 + 功能) +│ ├── test_main.py # 健康端点 + 配置 +│ ├── test_dispatcher_integration.py # Dispatcher 回滚 +│ ├── test_review_integration.py # Review pipeline + scoring +│ ├── test_spawner_integration.py # Spawner 超时/失败(RUN_INTEGRATION gate) +│ ├── test_ticker_integration.py # Ticker 主循环 + 广播 + 超时 + 依赖推进 +│ ├── test_sse.py # SSE broker + hook manager +│ └── test_v27_subtasks.py # 子任务集成 +└── e2e/ # E2E 测试(spawn 真实 Agent) + ├── conftest.py # E2E fixture:清理 API + atexit 兜底 + manifest + ├── test_four_phase.py # #01 四相循环 + ├── test_e2e_scenarios.py # S1-S21 场景测试(串行) + ├── test_e2e_stress.py # ST1-ST3 压力测试(并行) + └── test_spawner_e2e.py # Spawner 进程管理(timeout kill + spawn failure) +``` + +### 5.2 现有测试迁移映射 + +| 现有文件 | 去向 | 处理 | +|---------|------|------| +| test_e2e_v27.py E1-E8 | integration/ | 重写为 API 集成测试,用 tmp_path 隔离 | +| test_e2e_v27.py E9-E14 | e2e/test_e2e_scenarios.py | 按 S9-S15 重新编号 | +| test_e2e_v31.py E94-E98 | e2e/test_e2e_scenarios.py | 按 S16-S21 编号 | +| test_e2e_v31.py E10c/E10d | e2e/test_e2e_scenarios.py | 按 S19-S20 编号 | +| test_four_phase.py | e2e/test_four_phase.py | 加 RUN_INTEGRATION gate | +| test_spawner_e2e.py | e2e/test_spawner_e2e.py | 已有 gate ✅ | +| test_api.py | integration/ | 保持 | +| test_api_mail.py | integration/ | 保持 | +| test_main.py | integration/ | 保持 | + +### 5.3 pytest marker 定义 + +```python +# conftest.py +def pytest_configure(config): + markers = { + "unit": "单元测试:纯逻辑,mock 外部依赖", + "integration": "集成测试:API 端点 + 隔离数据", + "e2e": "端到端测试:真实环境(RUN_INTEGRATION=1 触发)", + "slow": "慢测试(>5s)", + "broadcast": "广播认领相关", + "mail": "邮件系统相关", + "state_machine": "状态机转换", + "classify": "Classify Outcome 相关", + "review": "审查/Rebuttal 相关", + } + for name, desc in markers.items(): + config.addinivalue_line("markers", f"{name}: {desc}") +``` + +### 5.4 Gate 保护 + +| 层级 | Gate | 说明 | +|------|------|------| +| unit | 无 | 纯 mock,安全 | +| integration | 无 | tmp_path 隔离,安全 | +| e2e | `RUN_INTEGRATION=1` | 所有 e2e 测试统一 gate,`@skip_no_integration` | + +--- + +## 六、设计文档 → 测试覆盖映射(原则二) + +### 6.1 覆盖矩阵 + +| 设计文档 | 关键行为 | 单元测试 | 集成测试 | E2E 测试 | +|---------|---------|---------|---------|---------| +| **architecture-v3.0.md** | 状态机、数据模型、API 契约 | P0 | P0 | — | +| **#01 四相循环** | spawn→execute→classify→retry/advance | P1 | — | P0(S9-S10) | +| **#07 Acquire-First** | Phase 0-4、AgentBusyError 区分 | P0 | P1 | P1(S11-S15) | +| **#08 Classify Outcome** | A0-A14 + B1-B4 场景 | P0 | P1 | P1(S12) | +| **#09 Rebuttal** | 审查流水线、2 轮反驳 | P1 | P0 | P1(S20) | +| **#06 PM2 恢复** | 崩溃后状态恢复、zombie 检测 | P1 | — | P0 | +| **#04 黑板协作** | @mention、comment、产出、review | P1 | P0 | P1 | +| **#10 T3 需求探索** | 苏格拉底式对话、需求规格化 | P1 | — | — | +| **#12 Pipeline** | task_type 路由、状态机约束 | P2 | P2 | — | + +### 6.2 模块 → 测试层级 → 优先级 + +| 模块 | 单元 | 集成 | E2E | 对应设计文档 | +|------|------|------|-----|-------------| +| 状态机转换 | P0 | P0 | — | architecture-v3.0 §5.2 | +| Classify Outcome | P0 | P1 | P1 | #08 §3 | +| 广播认领 | P0 | P1 | P0 | architecture-v3.0 §8.3 | +| 邮件系统 | P1 | P0 | P1 | architecture-v3.0 §12 | +| Acquire-First | P0 | P1 | P1 | #07 §2 | +| 四相循环 | P1 | — | P0 | #01 §2 | +| Spawner 进程管理 | P0 | P1 | P1 | architecture-v3.0 §6.3 | +| Counter 并发控制 | P0 | P1 | — | architecture-v3.0 §8.2 | +| Router 路由 | P0 | P1 | — | architecture-v3.0 §8.1 | +| Review/Rebuttal | P1 | P0 | P1 | #09 §2 | +| Guardrail | P0 | P1 | — | architecture-v3.0 §14 | +| 数据模型 | P0 | P0 | — | architecture-v3.0 §5 | +| 依赖推进 | P0 | P1 | — | architecture-v3.0 §6.1 | +| 超时检测 | P0 | P1 | P1 | architecture-v3.0 §6.1 | +| @mention / 协作 | P1 | P0 | P1 | #04 | +| Inbox JSONL | P0 | P1 | — | architecture-v3.0 §15 | +| SSE 推送 | P1 | P0 | — | architecture-v3.0 §15 | +| Bootstrap 上下文 | P1 | P1 | — | architecture-v3.0 §10 | +| PM2 恢复 | P1 | — | P0 | #06 | + +--- + +## 七、E2E 数据隔离与清理 + +### 7.1 各层隔离策略 + +| 层级 | 策略 | 清理方式 | +|------|------|---------| +| 单元 | mock / 不涉及 IO | 无残留 | +| 集成 | `tmp_path` + 独立 SQLite | fixture teardown 自动清理 | +| E2E | 真实 data_root + `e2e-` 前缀隔离 | 清理 API + atexit 兜底 | + +### 7.2 E2E 清理机制 + +```python +# tests/e2e/conftest.py + +@pytest.fixture(scope="session") +def e2e_session_prefix(): + """Session 级唯一前缀""" + return f"e2e-{uuid.uuid4().hex[:6]}-" + +@pytest.fixture(autouse=True) +def e2e_cleanup(request): + """每个测试后清理,与诊断分离""" + created_pids = [] + + def _track(pid): + created_pids.append(pid) + + yield # 测试执行 + + # 清理阶段 + for pid in created_pids: + requests.delete(f"http://localhost:8083/api/projects/{pid}?physical=true") + +@pytest.fixture(scope="session", autouse=True) +def e2e_session_cleanup(e2e_session_prefix): + """Session 结束时兜底清理""" + yield + # 批量清理所有 e2e- 前缀 +``` + +### 7.3 atexit 兜底 + +```python +# tests/conftest.py(全局) + +import atexit + +def _emergency_cleanup(): + """进程崩溃时的最后防线""" + # 批量删除所有 e2e- 前缀的项目和邮件 + pass + +atexit.register(_emergency_cleanup) +``` + +--- + +## 八、通过标准 + +| 类别 | 标准 | +|------|------| +| **场景测试(S1-S21)** | 58 个全部通过。失败必须分析根因并修复后才继续 | +| **压力测试(ST1-ST3)** | 3 个全部通过。验证并发控制无死锁 | +| **P0 必须全部通过** | S1-S8 + S10 + S11-S14 | +| **P1 通过率 ≥ 80%** | S9 + S15-S21 + ST1-ST3(依赖真实 Agent,可能受网络/环境干扰) | +| **P1 延后** | P1.1/P1.2(Prompt 集成,待 Agent prompt 稳定后补) | +| **速度** | 单元 < 30s,集成 < 5min,E2E 场景 < 60min,E2E 压力 < 30min | +| **零残留** | 测试运行前后,`GET /projects?search=e2e-` 返回 0 条 | + +--- + +## 九、实施计划 + +### Phase 0:数据清理 API(前置依赖) + +| 任务 | API | 优先级 | +|------|-----|--------| +| 项目物理删除 | `DELETE /api/projects/{pid}?physical=true` | P0 | +| 邮件批量删除 | `DELETE /api/mail?prefix=e2e-` | P0 | + +### Phase 1:基础设施 + +| 任务 | 说明 | +|------|------| +| conftest.py 更新 | marker 注册 + atexit 清理兜底 + tmp_path fixture | +| E2E conftest | 清理 API fixture + session 兜底 + manifest | +| Gate 统一 | 所有 E2E 文件加 `@skip_no_integration` | + +### Phase 2:P0 单元测试(已有 394 passed) + +| 任务 | 对应设计文档 | +|------|------------| +| test_state_machine.py | architecture-v3.0 §5.2 | +| test_classify_outcome.py | #08 §3 | +| test_spawner.py(含 Acquire-First) | #07 §2 | +| test_broadcast_claim.py | architecture-v3.0 §8.3 | +| test_counter.py | architecture-v3.0 §8.2 | +| test_guardrails.py | architecture-v3.0 §14 | + +### Phase 3:集成测试(已有 155 passed) + +| 任务 | 说明 | +|------|------| +| test_api_mail.py | A1-A10 + 批量删除(已到位) | +| test_ticker_integration.py | 广播 + 超时 + 依赖推进 | +| test_spawner_integration.py | acquire/release + 监控 | +| test_dispatcher_integration.py | Router + Guardrail 调度 | + +### Phase 4:E2E 场景测试(串行) + +| 任务 | 覆盖设计文档 | +|------|------------| +| S1-S8 API 层场景 | architecture-v3.0 | +| S9-S10 Agent 调度 | #01 四相循环 | +| S11-S15 Spawner/Acquire | #07 + #08 | +| S16-S21 扩展场景 | 各设计文档 | + +### Phase 5:E2E 压力测试(并行) + +| 任务 | 说明 | +|------|------| +| ST1 Counter 竞争 | 同 agent 并发 | +| ST2 全局上限 | 多 agent 并发 | +| ST3 Broadcast 并发 | 多任务广播 | + +--- + +## 十、约束和风险 + +### 10.1 约束 + +1. **只有 E2E spawn 真实 Agent**:Unit 和 Integration 层不 spawn +2. **E2E 用真实环境**:真实 daemon + 真实 Agent,`e2e-` 前缀隔离 +3. **场景测试串行**:一个完成再下一个,用时间换可靠性 +4. **压力测试并行**:单独一轮,场景测试全通过后执行 +5. **测试不依赖执行顺序**:每个测试独立创建/清理数据 +6. **全量 E2E 手动触发**:`RUN_INTEGRATION=1 pytest tests/e2e/` +7. **测试数据前缀规范**:所有 E2E 项目名以 `e2e-` 开头 + +### 10.2 风险 + +| 风险 | 缓解 | +|------|------| +| E2E 场景测试影响生产 Agent | `e2e-` 前缀项目,串行执行减少干扰 | +| 清理 API 失败导致残留 | atexit + session fixture 双保险 | +| E2E 测试不稳定(Agent 响应慢) | 充分 timeout + retry + 失败时保留数据供诊断 | +| 迁移现有测试引入回归 | 先迁移后验证,不删旧文件直到新测试全绿 | + +--- + +## 十一、变更记录 | 日期 | 版本 | 变更 | |------|------|------| -| 2026-05-18 | v2.7 | 初版,E1-E10 共 37 个测试 | -| 2026-06-01 | v2.8 | 新增 E11-E15 共 19 个测试,覆盖 #07 Acquire-First、_check_timeouts 统一、Compact Hanging、AgentBusyError 分类、Prompt v3.0 | -| 2026-06-01 | v2.8.1 | 新增 E11-E14 真实 Agent E2E(6 个集成测试)+ E15 Prompt v3.0 E2E(2 个集成测试),覆盖 Acquire-First 真实调度、crash_limit 真实回收、updated_at fallback、compact_hanging 不标 failed、crash rollback、广播三级响应 | +| 2026-05-18 | v2.7 | 初版 E1-E10(仲达) | +| 2026-06-01 | v2.8 | 新增 E11-E15(庞统) | +| 2026-06-05 | v3.0 | 全面重写:三层分离 + 主公四原则 + 背靠背 review 修正 + E14 编号冲突修复 + 场景/压力分离 + 串行执行策略(庞统综合,基于仲达 v30 草案) | + +### v3.0 vs v2.8 关键变化 + +| 维度 | v2.8 | v3.0 | +|------|------|------| +| E2E 执行策略 | 无明确规定 | 串行场景 + 并行压力分离 | +| 编号体系 | E1-E15(E14 冲突) | S1-S21 + ST1-ST3(无冲突) | +| Agent spawn | E1-E8 无 gate | 统一 RUN_INTEGRATION gate | +| 数据隔离 | 未提及 | 真实 data_root + 清理 API + atexit 兜底 | +| 设计覆盖 | 只管 E2E | 全三层 + 设计文档正推映射 | +| 文件组织 | 两大文件 | 按模块拆分 + 命名规范 |