# 后续批次测试用例设计 — 2026-07-01

> 覆盖:多Agent并行执行 / Git worktree 隔离 / 仲裁合并 / 模板系统 / Git-CI / 审批政策
> 关联:[多Agent并行执行与仲裁合并设计-2026-07-01.md](02-架构设计/专项设计/多Agent并行执行与仲裁合并设计-2026-07-01.md)

---

## 一、测试分层

| 层级 | 范围 | 工具 |
|------|------|------|
| **单元测试** | 纯函数/数据结构/repo CRUD | `#[test]` Rust / vitest TS |
| **集成测试** | 跨模块协作(dispatch+merge+worktree) | `#[tokio::test]` |
| **端到端** | 用户发消息→Plan→并行→合并→展示 | 手动 + 日志验证 |

---

## 二、数据层与并行执行 测试用例

### 2.1 V36 迁移

| ID | 场景 | 预期 |
|----|------|------|
| MIG-01 | 新库空跑 V36 | 3 新表 + 2 列 ALTER 成功,ai_plans 为空 |
| MIG-02 | 老库(有数据)跑 V36 | ai_messages/ai_tool_executions 加 subtask_id 列(NULL),原有数据不动 |
| MIG-03 | V36 幂等(重跑) | 不崩溃,版本号已写入直接跳过 |
| MIG-04 | 老库 ai_messages 有 100 条数据 | subtask_id 全部为 NULL,查询不报错 |

### 2.2 Repo CRUD

| ID | 场景 | 预期 |
|----|------|------|
| REPO-01 | PlanRepo.insert + get | 写入后能按 id 读回,字段完整 |
| REPO-02 | PlanRepo.update_status | planning→executing→done 状态流转正确 |
| REPO-03 | SubTaskRepo.insert + list_by_plan | 按 plan_id 查子任务,layer 排序正确 |
| REPO-04 | SubTaskRepo.update_status | pending→running→done 流转 |
| REPO-05 | ConflictRepo.insert + list_pending | 按 plan_id 查 resolution=pending 的冲突 |
| REPO-06 | ConflictRepo.resolve | 更新 resolution + resolved_by + resolved_at |
| REPO-07 | SubTaskRepo 查 branch=NULL | 非 Git 工程的 SubTask branch 为 NULL,查询正常 |

### 2.3 Coordinator 拆解

| ID | 场景 | 输入 | 预期 |
|----|------|------|------|
| DEC-01 | 读+写意图 | "先 read 文件再修改" | 2 SubTask(read→write),write deps=[read] |
| DEC-02 | 纯读意图 | "分析现有代码结构" | 1 SubTask(read),无 deps |
| DEC-03 | 纯写意图 | "implement new feature" | 1 SubTask(write),无 deps |
| DEC-04 | 兜底意图 | "你好" | 1 SubTask(default) |
| DEC-05 | 人设分配 | "review this code" | SubTask persona=reviewer |
| DEC-06 | Plan 非空 | 任意非空输入 | plan.tasks.len() >= 1 |

### 2.4 Git worktree 隔离

| ID | 场景 | 预期 |
|----|------|------|
| WT-01 | worktree 创建 | `.devflow/wt/{plan_id}/A` 目录存在,git branch `subtask/{plan_id}/A` 存在 |
| WT-02 | worktree 内 write_file | 写入落到 worktree 目录,主目录文件不变 |
| WT-03 | 同层并行隔离 | SubTask A 写 main.rs,SubTask B 读 main.rs → B 读到的是 fork 基点内容,非 A 的改动 |
| WT-04 | worktree commit | SubTask 完成后 git add+commit,分支有新 commit |
| WT-05 | worktree 清理 | Plan 完成后 `.devflow/wt/{plan_id}/` 目录被移除 |
| WT-06 | 非 Git 工程降级 | 无 .git 的工程 → branch=NULL,走串行,不崩溃 |
| WT-07 | worktree 创建失败 | 磁盘满/权限不足 → 降级串行,日志告警 |

### 2.5 并行调度

| ID | 场景 | 预期 |
|----|------|------|
| PAR-01 | 线性链(2层×1任务) | Layer0 done → Layer1 启动,总耗时≈串行 |
| PAR-02 | 并行层(1层×3任务) | 3 SubTask 并发执行,总耗时≈最慢的一个 |
| PAR-03 | 钻石依赖 | A→{B,C}→D,B/C 并行,D 等 B+C 都 done |
| PAR-04 | 层间隔离 | Layer1 的 SubTask 能读到 Layer0 merge 后的改动 |
| PAR-05 | 单 SubTask 失败 | 标记 error,不中断同层其他 SubTask |
| PAR-06 | 全部 SubTask 失败 | Plan.status=error |
| PAR-07 | SubTask 超时(120s) | 标记 error,其他 SubTask 不受影响 |

### 2.6 Token 预算池

| ID | 场景 | 预期 |
|----|------|------|
| TOK-01 | 预算充足 | try_reserve(10k) → true,consumed+=10k |
| TOK-02 | 预算超限 | consumed=98k,total=100k,try_reserve(5k) → false |
| TOK-03 | 并发 reserve | 2 个 SubTask 同时 reserve(CAS 竞争) | 只有一个成功 |
| TOK-04 | 超限降级串行 | 预算用完 → 剩余 SubTask 串行执行(不拒绝) |

### 2.7 事件协议

| ID | 场景 | 预期 |
|----|------|------|
| EVT-01 | AiPlanCreated | 前端 PlanProgress 立即展示 DAG 层结构 |
| EVT-02 | AiSubTaskStatusChanged | 节点状态从 pending→running→done 更新 |
| EVT-03 | AiMergeCompleted(无冲突) | 合并输出展示,无冲突徽章 |
| EVT-04 | AiMergeCompleted(有冲突) | 冲突徽章出现 |
| EVT-05 | AiConflictResolved | 冲突徽章消失 |
| EVT-06 | 事件双写 | emit 和 publish_event 都触发 |

---

## 三、仲裁合并与冲突 测试用例

### 3.1 Git merge 冲突检测

| ID | 场景 | 预期 |
|----|------|------|
| MRG-01 | 无冲突合并 | A 改 file1,B 改 file2 → merge-tree 无冲突 → git merge 成功 |
| MRG-02 | 同文件冲突 | A 改 main.rs 第10行,B 改 main.rs 第10行 → conflict markers 出现 |
| MRG-03 | 同文件不冲突区 | A 改 main.rs 第10行,B 改 main.rs 第50行 → git 自动合并成功 |
| MRG-04 | 跨层同文件 | Layer0 改 main.rs,merge 到 plan;Layer1 改 main.rs → 基于已含 Layer0 的版本,无冲突 |
| MRG-05 | 三方合并预检 | git merge-tree 返回非空冲突列表 → 不执行 merge,标记 conflict |

### 3.2 编译检查(semantic 冲突)

| ID | 场景 | 预期 |
|----|------|------|
| CMP-01 | 合并后编译通过 | cargo check exit 0 → 无 semantic 冲突 |
| CMP-02 | 合并后编译失败 | A 删函数 foo(),B 在调 foo() → cargo check 失败 → conflict_type=semantic |
| CMP-03 | 前端项目 | tsc --noEmit 失败 → semantic 冲突 |
| CMP-04 | 无构建工具的工程 | 跳过编译检查(无 cargo/tsc) → 不阻塞 merge |

### 3.3 Reviewer 仲裁

| ID | 场景 | 预期 |
|----|------|------|
| ARB-01 | Reviewer 推荐 A | diff_a 更完整 → resolution=a,resolved_by=reviewer |
| ARB-02 | Reviewer 推荐 merged | 两者各有价值 → resolution=merged,给出合并方案 |
| ARB-03 | Reviewer 无法判断 | 两者等价 → resolution=pending,留给用户 |
| ARB-04 | Reviewer 超时 | LLM 调用超时 → 自动降级为 pending |

### 3.4 冲突解决闭环

| ID | 场景 | 预期 |
|----|------|------|
| RSV-01 | 用户选接受 A | resolution=a,文件应用 A 的改动,B 的改动丢弃 |
| RSV-02 | 用户选手动 | resolution=manual,用户在 worktree 手动编辑后 commit |
| RSV-03 | 全部冲突解决 | Plan.status 从 merging→done |
| RSV-04 | 前端徽章更新 | AiConflictResolved 事件 → 冲突徽章消失 |

### 3.5 合并产出落回主对话

| ID | 场景 | 预期 |
|----|------|------|
| OUT-01 | 无冲突合并 | 主对话新增 1 条 assistant 消息(merged_output) |
| OUT-02 | 有冲突合并 | 主对话新增 1 条 + 冲突徽章 |
| OUT-03 | 展开查看子 Agent 轨迹 | 点击展开 → 从 ai_messages WHERE subtask_id=? 加载,按 SubTask 分组 |
| OUT-04 | 折叠状态默认 | 新消息默认折叠,只显示摘要 |

### 3.6 命令互斥锁

| ID | 场景 | 预期 |
|----|------|------|
| LCK-01 | 同目录 npm install 并发 | 第二个等第一个完成才执行(不并行) |
| LCK-02 | 不同目录 npm install | 并行执行(锁 key 含目录) |
| LCK-03 | cargo + npm 同目录 | 不互斥(锁 key 含命令名) |

---

## 四、模板系统 测试用例

### 4.1 YAML 模板加载

| ID | 场景 | 预期 |
|----|------|------|
| TPL-01 | 合法 YAML 加载 | DagDef 结构完整,nodes/edges/conditions 正确解析 |
| TPL-02 | 缺必填字段 | nodes 为空 → 报错"模板节点不能为空" |
| TPL-03 | 依赖闭环 | A→B→A → 报错"依赖存在环" |
| TPL-04 | 未知节点类型 | node.type="UnknownNode" → 报错"未知节点类型" |
| TPL-05 | 内置模板加载 | 3 个内置模板(代码审查/bug修复/功能开发)全部加载成功 |
| TPL-06 | 自定义模板 CRUD | 创建→读取→更新→删除,持久化到 DB |

### 4.2 SubflowNode

| ID | 场景 | 预期 |
|----|------|------|
| SUB-01 | 嵌套子工作流执行 | SubflowNode 加载子模板 → 递归执行 → 返回子工作流结果 |
| SUB-02 | 子工作流审批 | 子工作流内的 HumanNode 审批 → 挂起 → 恢复 |
| SUB-03 | 嵌套深度限制 | 超过 MAX_PLAN_DEPTH → 报错 |

### 4.3 新节点

| ID | 场景 | 预期 |
|----|------|------|
| NODE-01 | GitNode 创建分支 | branch_name 存在,git branch 列表可见 |
| NODE-02 | GitNode commit | 工作区改动已提交,git log 有新记录 |
| NODE-03 | HTTPNode GET | 请求成功,返回 body 存入 NodeOutput |
| NODE-04 | HTTPNode 超时 | 30s 超时 → 返回错误,不阻塞 |
| NODE-05 | NotifyNode 桌面通知 | 系统通知弹出 |
| NODE-06 | NotifyNode Webhook | POST 到飞书/钉钉,HTTP 200 |

---

## 五、Git/CI 集成 测试用例

| ID | 场景 | 预期 |
|----|------|------|
| CI-01 | DockerNode 构建成功 | docker run cargo build → exit 0 → 构建产物存在 |
| CI-02 | DockerNode 构建失败 | docker run cargo build → exit !=0 → 返回 stderr |
| CI-03 | PR 创建 | GitNode push → API 创建 PR → PR URL 返回 |
| CI-04 | CI 状态读取 | 调 API → 返回 check 结果(passed/failed/pending) |
| CI-05 | 全链路 | 模板→分支→代码→commit→push→PR→CI→结果回显,全流程不中断 |
| CI-06 | CI 失败时 | 前端 CI 面板显示红色,日志可查看 |

---

## 六、审批政策 测试用例

| ID | 场景 | 预期 |
|----|------|------|
| POL-01 | coder+Low → auto | 匹配规则 → 自动执行 |
| POL-02 | coder+High → approve | 匹配规则 → 走审批 |
| POL-03 | reviewer+任意 → auto | reviewer 只读工具,全部自动 |
| POL-04 | 无匹配规则 | 降级到 auto_exec_mode 三档(向后兼容) |
| POL-05 | 策略热加载 | 修改策略 → 下次工具调用立即生效(不重启) |
| POL-06 | 策略冲突检测 | 同一 Persona+Node+Risk 有多条规则 → 优先级最高的生效 |

---

## 七、God 文件拆分 测试用例

| ID | 场景 | 预期 |
|----|------|------|
| REF-01 | agentic/mod.rs 拆分 | 拆出 compress/title/knowledge_inject 后,原文件<800 行,cargo check 通过 |
| REF-02 | context.rs 拆分 | 拆出 sanitize/budget/compress 后,原文件<800 行,所有 context 测试通过 |
| REF-03 | MessageList.vue 拆分 | 拆出 StreamRenderer/ToolCardGroup/EmptyState,vue-tsc 通过 |
| REF-04 | ChatInput.vue 拆分 | 拆出 MentionPopover/EnrichmentPanel,vue-tsc 通过 |
| REF-05 | 拆分后行为不变 | 原有所有单元测试/集成测试全绿 |

---

## 八、端到端手动测试

| ID | 场景 | 步骤 | 预期 |
|----|------|------|------|
| E2E-01 | 简单并行任务 | 发"先读取 main.rs 再修改 utils.rs" | PlanProgress 展示 2 层,Layer0 读,Layer1 写,最终合并输出 |
| E2E-02 | 并行同文件冲突 | 发"同时重构 auth.rs 的两个函数" | 2 SubTask 并行改同文件 → 冲突徽章 → 用户选择 → 最终输出 |
| E2E-03 | 取消并行任务 | 发消息→Plan 执行中→点停止 | 所有 SubTask 停止,worktree 清理,Plan.status=error |
| E2E-04 | 切换会话后台执行 | 发消息→切换到另一个会话 | 原 Plan 后台继续,PlanProgress 在原会话保留 |
| E2E-05 | 非 Git 工程 | 在无 .git 的项目中发并行任务 | 降级串行执行,正常完成 |
| E2E-06 | 模板驱动全链路 | 选"功能开发"模板→AI 执行 | 从模板加载工作流→创建分支→写代码→测试→PR |
| E2E-07 | 长对话不崩溃 | 50+ 轮对话后发并行任务 | token 预算池正常,不 OOM,PlanProgress 正常展示 |

---

## 九、边界与异常

| ID | 场景 | 预期 |
|----|------|------|
| EDGE-01 | 空 Plan | decompose 返回空 Plan → 不启动 dispatch,主对话正常回复(退单 Agent) |
| EDGE-02 | 全部 SubTask 超时 | Plan.status=error,前端展示超时提示,worktree 清理 |
| EDGE-03 | Git 不可用 | git 命令失败 → 降级串行,日志告警,不崩溃 |
| EDGE-04 | DB 写入失败 | PlanRepo.insert 失败 → 降级不落库,内存态执行,日志告警 |
| EDGE-05 | worktree 磁盘满 | 创建失败 → 降级串行 |
| EDGE-06 | token 预算=0 | 设置项 df-ai-plan-token-budget=0 → 不限制(等价无限) |
| EDGE-07 | 冲突解决时会话删除 | resolve_conflict IPC 到达时会话已删 → 忽略,清理 conflict 记录 |
