5.8 KiB
5.8 KiB
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 技术设计(关键决策)
- 执行环境 = 生产构建 +
max preview,不用 dev server。 umi dev 按需 bundling 会让页面长时间停在 "Bundling..." 中间页、CSS 未注入,测试不稳定;playwright.config.ts的webServer自动托管npx max preview --port 8000(跑前需npm run build)。 - API 一律
page.route('**/api/**')mock,响应信封{code,data,message}与后端ApiResponseMiddleware对齐;登录/未登录由 mock 内authed状态机控制(未登录一律 401)。 无需真实账号、PG、Redis,CI 可跑。 - antd v6 confirm 弹窗断言陷阱:
Modal.success的经典.ant-modal-header是display:none,可见标题在.ant-modal-confirm-title;getByText('标题')会命中隐藏副本, 必须用.ant-modal-confirm-title或正文内容断言。(本次实测踩坑后固化为此条规范。) - 定位策略:业务组件加
data-testid(obsidian-sync-open/obsidian-vault-input/obsidian-sync-submit),登录表单用 placeholder(antd Form.Item 的 name 不映射到 input name 属性)。 - antd v6 Select 下拉项无 role:下拉选项渲染为
generic(无option角色),getByRole('option')/.ant-select-item-option类名定位均不可靠;正确做法是getByRole('combobox', { name: /字段标签/ })展开后getByText('选项文案', { exact: true })点击。 (create-forms.spec.ts 实测踩坑后固化。) - 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 覆盖了前端全链路行为;如需打到真实后端:
- 配置允许根目录(环境变量注入,不改入库配置):
export Rag__Obsidian__AllowedRoots__0=/path/to/test-vault - 准备测试 vault(若干 .md 文件,含一个子目录与一个
.obsidian/目录); cd build-flow.api && ./run-dev.sh(连远程中间件,端口 5667);cd build-flow.admin && npm run start:no-mock(代理/api→ 5667);- 浏览器手动执行 BDD「React 管理端」4 场景,或以真实账号改造 spec 去掉 mock 后执行。
全链路自动化需要可用的测试账号凭据与稳定的远程中间件,当前未纳入自动套件,列入 SOP 检查项。
6. 后续补测清单
SyncObsidianVaultCommandHandler直测:added/updated/skipped 三分支 + 知识库归属校验(NotFound)- 端点分块模式
Enum.IsDefined校验的参数化测试(0-5 接受、6/-1 拒绝→null) - 带 Docker 基础设施环境复跑
RagPgVectorIntegrationTests/RedisChatMessageCacheTests