新增: 测试落地实施方案(Rust单元+集成+vitest+E2E手动验证)
- 落地方式:Rust单元(mod tests) / Rust集成(tests目录+tempdir) / 前端vitest(需引入) / E2E手动检查清单 - 每个用例类别的文件位置和执行命令 - Mock策略:LLM闭包注入/Git真实tempdir/DB内存SQLite - 批次验证流程:编码→cargo test→vue-tsc→vite build→手动E2E→SQL验证→提交 - CI自动化远期路径(cargo test + bun test)
This commit is contained in:
@@ -89,6 +89,7 @@
|
||||
| [架构债迁移设计-2026-06-29.md](./专项设计/架构债迁移设计-2026-06-29.md) | 📐 设计草案 | #8/#15/#18/#19 架构债迁移路径(AI状态机抽离/IPC错误结构化/配置统一/连接池) |
|
||||
| [多Agent并行执行与仲裁合并设计-2026-07-01.md](./专项设计/多Agent并行执行与仲裁合并设计-2026-07-01.md) | 📐 设计 | 混合数据模型(messages加FK列+Plan/SubTask/Conflict独立表)、Git worktree隔离、JoinSet层内并行、四层冲突防线、Reviewer仲裁、事件协议、UI交互 |
|
||||
| [后续批次测试用例设计-2026-07-01.md](./专项设计/后续批次测试用例设计-2026-07-01.md) | 📐 测试 | 多Agent并行/worktree隔离/仲裁合并/模板系统/Git-CI/审批政策 的单元+集成+端到端测试用例 |
|
||||
| [测试落地实施方案-2026-07-01.md](./专项设计/测试落地实施方案-2026-07-01.md) | 📐 测试 | 各测试用例的落地方式(Rust 单元/集成/vitest/E2E 手动)、文件位置、Mock 策略、CI 自动化路径 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
290
docs/02-架构设计/专项设计/测试落地实施方案-2026-07-01.md
Normal file
290
docs/02-架构设计/专项设计/测试落地实施方案-2026-07-01.md
Normal file
@@ -0,0 +1,290 @@
|
||||
# 测试落地实施方案 — 2026-07-01
|
||||
|
||||
> 关联:[后续批次测试用例设计-2026-07-01.md](后续批次测试用例设计-2026-07-01.md)
|
||||
> 用途:定义每个测试用例的具体落地方式、文件位置、执行命令
|
||||
|
||||
---
|
||||
|
||||
## 一、项目现有测试基建
|
||||
|
||||
| 层 | 现状 | 工具 |
|
||||
|----|------|------|
|
||||
| Rust 单元测试 | df-ai 370+ 测试,df-storage 集成测试,df-execute 集成测试 | `#[test]` / `#[tokio::test]` |
|
||||
| 前端测试 | **无**(package.json 无 vitest/jest) | 需引入 vitest |
|
||||
| 端到端 | 无自动化(手动验证 + 日志) | 手动 |
|
||||
|
||||
---
|
||||
|
||||
## 二、各层级落地方式
|
||||
|
||||
### 2.1 Rust 单元测试(`#[test]` / `#[tokio::test]`)
|
||||
|
||||
**适用用例**:MIG / REPO / DEC / PAR / TOK / MRG / CMP / ARB / TPL / POL
|
||||
|
||||
**文件位置**:与现有模式一致——`mod tests` 嵌入源文件底部。
|
||||
|
||||
| 用例类别 | 落地文件 | 测试数量 |
|
||||
|---------|---------|---------|
|
||||
| MIG-01~04 | `df-storage/src/migrations.rs` mod tests | 4 |
|
||||
| REPO-01~07 | `df-storage/src/crud/plan_repo.rs` mod tests | 7 |
|
||||
| DEC-01~06 | `df-ai/src/coordinator.rs` mod tests(已有 6 个,补全) | 6 |
|
||||
| PAR-01~07 | `df-ai/src/coordinator.rs` mod tests(mock executor) | 7 |
|
||||
| TOK-01~04 | `df-ai/src/coordinator.rs` mod tests | 4 |
|
||||
| MRG-01~05 | `df-ai/src/coordinator.rs` mod tests(mock merge) | 5 |
|
||||
| CMP-01~04 | `df-ai/src/coordinator.rs` mod tests(mock 编译结果) | 4 |
|
||||
| ARB-01~04 | `df-ai/src/persona.rs` mod tests | 4 |
|
||||
| TPL-01~06 | `df-workflow/src/template_loader.rs` mod tests | 6 |
|
||||
| POL-01~06 | `src-tauri/src/policy.rs` mod tests | 6 |
|
||||
|
||||
**执行命令**:
|
||||
|
||||
```bash
|
||||
# 全量
|
||||
cargo test --workspace
|
||||
|
||||
# 按 crate
|
||||
cargo test -p df-ai
|
||||
cargo test -p df-storage
|
||||
|
||||
# 按用例名
|
||||
cargo test -p df-ai coordinator::tests::decompose_read_then_write
|
||||
|
||||
# 只跑并行相关
|
||||
cargo test -p df-ai -- coordinator::tests::par_
|
||||
```
|
||||
|
||||
### 2.2 Rust 集成测试(`tests/` 目录)
|
||||
|
||||
**适用用例**:WT / SUB / NODE / LCK / CI(需要真实 Git/Shell/DB 环境)
|
||||
|
||||
| 用例类别 | 落地文件 | 说明 |
|
||||
|---------|---------|------|
|
||||
| WT-01~07 | `df-ai/tests/git_worktree.rs` | 真实 git worktree 操作(tempdir 临时仓库) |
|
||||
| LCK-01~03 | `df-ai/tests/command_lock.rs` | 真实 tokio 并发 + mutex 验证 |
|
||||
| SUB-01~03 | `df-nodes/tests/subflow_node.rs` | 真实 Executor + 嵌套模板 |
|
||||
| NODE-01~06 | `df-nodes/tests/nodes.rs` | GitNode/HTTPNode/NotifyNode 各自独立 |
|
||||
| CI-01~02 | `df-nodes/tests/docker_node.rs` | 需 Docker 环境(`#[ignore]` 标注,CI 环境跑) |
|
||||
|
||||
**WT 测试关键代码模式**(参考现有 `df-execute/tests/shell.rs`):
|
||||
|
||||
```rust
|
||||
// df-ai/tests/git_worktree.rs
|
||||
use tempfile::TempDir;
|
||||
use std::process::Command;
|
||||
|
||||
fn setup_git_repo() -> TempDir {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
Command::new("git").arg("init").current_dir(dir.path()).output().unwrap();
|
||||
Command::new("git").args(["config", "user.email", "test@test.com"])
|
||||
.current_dir(dir.path()).output().unwrap();
|
||||
Command::new("git").args(["config", "user.name", "test"])
|
||||
.current_dir(dir.path()).output().unwrap();
|
||||
// 写初始文件 + commit
|
||||
std::fs::write(dir.path().join("main.rs"), "fn main() {}").unwrap();
|
||||
Command::new("git").args(["add", "."]).current_dir(dir.path()).output().unwrap();
|
||||
Command::new("git").args(["commit", "-m", "init"]).current_dir(dir.path()).output().unwrap();
|
||||
dir
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn wt_01_worktree_create_and_isolate() {
|
||||
let repo = setup_git_repo();
|
||||
let plan_id = "test-plan-001";
|
||||
// 创建 worktree
|
||||
let wt_path = repo.path().join(".devflow/wt").join(plan_id).join("A");
|
||||
let output = Command::new("git")
|
||||
.args(["worktree", "add", wt_path.to_str().unwrap(), "-b", "subtask/test-plan-001/A"])
|
||||
.current_dir(repo.path())
|
||||
.output().unwrap();
|
||||
assert!(output.status.success(), "worktree 创建失败");
|
||||
assert!(wt_path.exists(), "worktree 目录应存在");
|
||||
// 写文件到 worktree
|
||||
std::fs::write(wt_path.join("new.rs"), "// A 的改动").unwrap();
|
||||
// 主目录不应有 new.rs
|
||||
assert!(!repo.path().join("new.rs").exists(), "主目录不应有 A 的改动");
|
||||
// 清理
|
||||
Command::new("git").args(["worktree", "remove", wt_path.to_str().unwrap(), "--force"])
|
||||
.current_dir(repo.path()).output().unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 前端测试(引入 vitest)
|
||||
|
||||
**现状**:项目无前端测试基建。需要引入 vitest。
|
||||
|
||||
**引入步骤**(纳入数据层批次实施):
|
||||
|
||||
```bash
|
||||
bun add -d vitest @vue/test-utils jsdom
|
||||
```
|
||||
|
||||
`vitest.config.ts`:
|
||||
```typescript
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
`package.json` 加 `"test": "vitest"` 脚本。
|
||||
|
||||
**适用用例**:EVT / OUT / REF(前端)
|
||||
|
||||
| 用例类别 | 落地文件 | 说明 |
|
||||
|---------|---------|------|
|
||||
| EVT-01~06 | `src/components/ai/__tests__/PlanProgress.test.ts` | 组件事件驱动渲染 |
|
||||
| OUT-01~04 | `src/components/ai/__tests__/MessageList.test.ts` | 合并产出展示/折叠 |
|
||||
| REF-03~05 | `src/components/ai/__tests__/MessageList.test.ts` | 拆分后行为不变 |
|
||||
|
||||
**执行命令**:
|
||||
|
||||
```bash
|
||||
bun run test # watch 模式
|
||||
bun run test -- --run # 单次跑
|
||||
bun run test -- PlanProgress # 按文件名过滤
|
||||
```
|
||||
|
||||
### 2.4 端到端手动验证(测试脚本 + 日志)
|
||||
|
||||
**适用用例**:E2E / EDGE
|
||||
|
||||
**落地方式**:测试用 DB + 日志分析脚本,非自动化框架。
|
||||
|
||||
**手动验证检查清单格式**(每个批次完成时填写):
|
||||
|
||||
```markdown
|
||||
## E2E-01 简单并行任务
|
||||
- [ ] 发送消息:"先读取 main.rs 再修改 utils.rs"
|
||||
- [ ] PlanProgress 展示 2 层 DAG
|
||||
- [ ] Layer 0 (read) 完成 → Layer 1 (write) 启动
|
||||
- [ ] 最终输出包含合并结果
|
||||
- [ ] ai_subtasks 表有 2 条记录,status=done
|
||||
- [ ] ai_messages 表有 subtask_id 非空的记录
|
||||
- [ ] worktree 目录已清理(.devflow/wt/ 下无残留)
|
||||
```
|
||||
|
||||
**验证用 SQL 查询**(批次完成后在 dev 数据库执行):
|
||||
|
||||
```sql
|
||||
-- 检查 Plan 记录
|
||||
SELECT id, status, subtask_count, created_at FROM ai_plans ORDER BY created_at DESC LIMIT 5;
|
||||
|
||||
-- 检查 SubTask 状态
|
||||
SELECT s.id, s.persona_id, s.status, s.layer, s.branch
|
||||
FROM ai_subtasks s
|
||||
JOIN ai_plans p ON s.plan_id = p.id
|
||||
WHERE p.id = ? -- 替换为实际 plan_id
|
||||
ORDER BY s.layer, s.created_at;
|
||||
|
||||
-- 检查冲突记录
|
||||
SELECT c.file_path, c.conflict_type, c.resolution, c.resolved_by
|
||||
FROM ai_conflicts c WHERE c.plan_id = ?;
|
||||
|
||||
-- 检查消息归属
|
||||
SELECT m.id, m.role, m.subtask_id, substr(m.content, 1, 60) as preview
|
||||
FROM ai_messages m WHERE m.conversation_id = ? AND m.subtask_id IS NOT NULL
|
||||
ORDER BY m.seq DESC LIMIT 20;
|
||||
```
|
||||
|
||||
**worktree 残留检查**:
|
||||
|
||||
```bash
|
||||
# Plan 完成后应无残留
|
||||
ls -la .devflow/wt/ # 应为空或不存在
|
||||
|
||||
# 异常残留时的清理
|
||||
git worktree list
|
||||
git worktree prune
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、落地优先级
|
||||
|
||||
| 优先级 | 范围 | 时机 |
|
||||
|--------|------|------|
|
||||
| **P0 随编码落地** | Rust 单元测试(DEC/PAR/TOK/MRG/ARB) | 每个批次编码时同步写 |
|
||||
| **P0 随编码落地** | Rust 集成测试(WT) | worktree 模块编码时同步写 |
|
||||
| **P1 批次末尾** | 前端测试基建引入 + EVT/OUT | 数据层批次末尾引入 vitest |
|
||||
| **P2 批次完成后** | E2E 手动验证 | 每个批次完成后用检查清单验证 |
|
||||
| **P3 按需** | CI 集成测试(Docker/PR) | Git/CI 批次完成后 |
|
||||
|
||||
---
|
||||
|
||||
## 四、CI 自动化(远期)
|
||||
|
||||
当 vitest + cargo test 覆盖率足够后,接入 CI:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/test.yml(或 Gitea Actions)
|
||||
jobs:
|
||||
rust-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: cargo test --workspace
|
||||
|
||||
frontend-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
- run: bun install
|
||||
- run: bun run test -- --run
|
||||
```
|
||||
|
||||
当前阶段以**本地 `cargo test` + 手动 E2E** 为主,CI 自动化等测试覆盖率达标后接入。
|
||||
|
||||
---
|
||||
|
||||
## 五、Mock 策略
|
||||
|
||||
### 5.1 Rust 侧
|
||||
|
||||
| 依赖 | Mock 方式 |
|
||||
|------|----------|
|
||||
| LLM 调用 | 闭包注入(`Coordinator.dispatch<F>` 已支持泛型 executor) |
|
||||
| Git 操作 | 集成测试用真实 git(tempdir 临时仓库) |
|
||||
| 数据库 | 内存 SQLite(`Database::open_in_memory()`) |
|
||||
| 编译检查 | mock 返回 exit_code(单元测试)/ 真实 cargo(集成测试) |
|
||||
|
||||
### 5.2 前端侧
|
||||
|
||||
| 依赖 | Mock 方式 |
|
||||
|------|----------|
|
||||
| Tauri IPC | `@vue/test-utils` mock `invoke` |
|
||||
| 事件监听 | 手动 emit 测试事件 |
|
||||
| i18n | 插件 mock(返回 key 本身) |
|
||||
| Store | 真实 Pinia store(测试 store 逻辑本身) |
|
||||
|
||||
---
|
||||
|
||||
## 六、每个批次的测试验证流程
|
||||
|
||||
```
|
||||
编码完成
|
||||
↓
|
||||
cargo test -p df-ai(对应模块单元测试全绿)
|
||||
↓
|
||||
cargo test -p df-storage(迁移+Repo 测试全绿)
|
||||
↓
|
||||
cargo test -p df-ai --test git_worktree(worktree 集成测试全绿)
|
||||
↓
|
||||
cargo check --workspace(编译无警告)
|
||||
↓
|
||||
npx vue-tsc --noEmit(前端类型检查)
|
||||
↓
|
||||
npx vite build(前端构建)
|
||||
↓
|
||||
手动 E2E 检查清单(对照测试用例文档逐项验证)
|
||||
↓
|
||||
SQL 查询验证(DB 数据正确性)
|
||||
↓
|
||||
提交(代码+测试同一提交)
|
||||
```
|
||||
Reference in New Issue
Block a user