5.8 KiB
Raw Permalink Blame History

TDD — Obsidian Vault 同步 · 测试设计与实现

版本:v1.0 · 日期:2026-08-19 · 对应 PRD/SDD/BDD v1.0 本文是 BDD.md 场景到可执行测试的映射与运行指南。


1. 测试金字塔

层 工具 位置 覆盖目标
单元/集成(后端) xUnit + EF InMemory/临时目录 build-flow.api/tests/BuildFlow.Rag.Tests/ Handler 增量对账、Scanner 安全门禁
架构护栏 xUnit(NetArchTest) tests/BuildFlow.Architecture.Tests/ RAG 模块安全约束(含 Obsidian)
UI 端到端 Playwright(API mock) build-flow.admin/e2e/obsidian-sync.spec.ts 登录→知识库→同步→结果 全链路 UI 行为
全链路端到端(可选) Playwright + 真实后端 同 spec 文件 + §5 手动规程 真实 PG/服务联调

2. BDD 场景 → 测试映射

2.1 后端场景(已存在,本次未新增代码故未新增用例;端点修复由既有测试回归覆盖)

BDD 场景 测试 状态
忽略非笔记内容 / 白名单 / .. / 符号链接逃逸 / 容量超限 / fail-closed ObsidianVaultScannerTests(6 例) ✅ 通过
安全架构约束 ObsidianSyncSecurityTests、RagSecurityArchitectureTests ✅ 通过
首次同步 / 增量跳过 / 修改与新增(Handler 三分支) 由 Scanner 测试 + 架构测试间接覆盖;Handler 直测列入后续补测(全项目 203 功能点补测计划的一部分) ⚠️ 间接

2026-08-19 实跑:dotnet test tests/BuildFlow.Rag.Tests --filter "FullyQualifiedName~Obsidian" → 9/9 通过。 全量 Rag.Tests 另有 17 个失败,全部为需要本地 PostgreSQL(pgvector)/Redis 的集成测试 (RagPgVectorIntegrationTests、RedisChatMessageCacheTests),与本次改动无关,需在带基础设施的环境复跑。

2.2 UI 场景(本次新增,Playwright)

BDD 场景 测试用例 断言要点
从知识库列表发起同步并看到结果 从知识库列表发起同步并展示结果 结果弹窗标题(.ant-modal-confirm-title)、TotalFiles/Added/Updated/Skipped 四计数、后端 message、请求恰好 1 次
必填校验 路径必填校验:不输入路径不发起请求 中文必填提示可见、sync 请求数 = 0
错误透出 后端业务错误以中文 message 透出 400 + invalid_param 时 .ant-message 展示后端中文 message
防重复提交 同步进行中按钮 loading 且防重复提交 慢响应(2.5s)期间确认键 disabled、强制再点不产生第 2 个请求、最终正常出结果

运行结果(2026-08-19):4/4 通过(10.7s,Chromium,生产构建 + max preview)。

3. E2E 技术设计(关键决策)

  1. 执行环境 = 生产构建 + max preview,不用 dev server。 umi dev 按需 bundling 会让页面长时间停在 "Bundling..." 中间页、CSS 未注入,测试不稳定; playwright.config.ts 的 webServer 自动托管 npx max preview --port 8000(跑前需 npm run build)。
  2. API 一律 page.route('**/api/**') mock,响应信封 {code,data,message} 与后端 ApiResponseMiddleware 对齐;登录/未登录由 mock 内 authed 状态机控制(未登录一律 401)。 无需真实账号、PG、Redis,CI 可跑。
  3. antd v6 confirm 弹窗断言陷阱:Modal.success 的经典 .ant-modal-header 是 display:none,可见标题在 .ant-modal-confirm-title;getByText('标题') 会命中隐藏副本, 必须用 .ant-modal-confirm-title 或正文内容断言。(本次实测踩坑后固化为此条规范。)
  4. 定位策略:业务组件加 data-testid(obsidian-sync-open / obsidian-vault-input / obsidian-sync-submit),登录表单用 placeholder(antd Form.Item 的 name 不映射到 input name 属性)。
  5. antd v6 Select 下拉项无 role:下拉选项渲染为 generic(无 option 角色), getByRole('option') / .ant-select-item-option 类名定位均不可靠;正确做法是 getByRole('combobox', { name: /字段标签/ }) 展开后 getByText('选项文案', { exact: true }) 点击。 (create-forms.spec.ts 实测踩坑后固化。)
  6. multipart 断言:page.route 捕获的 req.postData() 对 FormData 提交是 multipart 原始文本, 可直接 toContain('name="ObjectKey"') 等断言表单字段(见文件上传用例)。

4. 运行方式

cd build-flow.admin
npm run build          # 首次或代码变更后
npm run test:e2e       # = playwright test,自动起 max preview
npx playwright test --ui   # 调试模式

后端回归:

cd build-flow.api
dotnet test tests/BuildFlow.Rag.Tests --filter "FullyQualifiedName~Obsidian"

5. 全链路端到端(真实后端,手动可选)

UI 级 E2E 覆盖了前端全链路行为;如需打到真实后端:

  1. 配置允许根目录(环境变量注入,不改入库配置): export Rag__Obsidian__AllowedRoots__0=/path/to/test-vault
  2. 准备测试 vault(若干 .md 文件,含一个子目录与一个 .obsidian/ 目录);
  3. cd build-flow.api && ./run-dev.sh(连远程中间件,端口 5667);
  4. cd build-flow.admin && npm run start:no-mock(代理 /api → 5667);
  5. 浏览器手动执行 BDD「React 管理端」4 场景,或以真实账号改造 spec 去掉 mock 后执行。

全链路自动化需要可用的测试账号凭据与稳定的远程中间件,当前未纳入自动套件,列入 SOP 检查项。

6. 后续补测清单

  • SyncObsidianVaultCommandHandler 直测:added/updated/skipped 三分支 + 知识库归属校验(NotFound)
  • 端点分块模式 Enum.IsDefined 校验的参数化测试(0-5 接受、6/-1 拒绝→null)
  • 带 Docker 基础设施环境复跑 RagPgVectorIntegrationTests / RedisChatMessageCacheTests