Files
DevFlow/docs/02-架构设计/专项设计/测试落地实施方案-2026-07-01.md
绝尘 f4fc0feff5 新增: 测试落地实施方案(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)
2026-07-01 21:37:07 +08:00

8.9 KiB

测试落地实施方案 — 2026-07-01

关联:后续批次测试用例设计-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

执行命令:

# 全量
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):

// 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。

引入步骤(纳入数据层批次实施):

bun add -d vitest @vue/test-utils jsdom

vitest.config.ts:

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 拆分后行为不变

执行命令:

bun run test              # watch 模式
bun run test -- --run     # 单次跑
bun run test -- PlanProgress  # 按文件名过滤

2.4 端到端手动验证(测试脚本 + 日志)

适用用例:E2E / EDGE

落地方式:测试用 DB + 日志分析脚本,非自动化框架。

手动验证检查清单格式(每个批次完成时填写):

## 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 数据库执行):

-- 检查 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 残留检查:

# 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:

# .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 数据正确性)
  ↓
提交(代码+测试同一提交)