默认原则

issue #81 起,仓库测试体系默认以离线可复现为第一层保障。普通 pnpm test 不连接 Supabase、LLM、GitLab、外部 CLI 或真实服务器;这些真实环境验证只放进后续 opt-in smoke 套件,避免本地开发被环境状态阻断。 测试分层如下:
层级命令用途
旧有基础测试pnpm test:legacy继续运行现有 node:test 用例,覆盖 core、server 和 App 纯函数脚本。
单元/组件测试pnpm test:unit使用 Vitest 跑 app、server/web、shared run-workspace-input、server/cli 的离线测试。
UI 测试pnpm test:ui只跑 App、默认 server/web 与 shared run-workspace-input 的 Vitest/jsdom 测试。
Workspace workflow 契约pnpm test:workspace检查 workspace/workflow/* 示例的专属 fixture、结构快照与离线 runtime smoke。
浏览器 smokepnpm test:e2e使用 Playwright 跑不依赖 dev server 的首轮 smoke;后续可扩展到真实页面。
聚合入口pnpm test顺序运行默认离线测试层。
覆盖率pnpm coverage基于 Vitest 输出覆盖率报告。
真实环境 smokepnpm smoke:real第六轮 opt-in 套件;默认 skip,只有设置 WORKFLOW_CODE_REAL_SMOKE=1 且提供对应变量时才连接真实服务。

给新代码补测试

  • 修改 core/** 或 root CLI 时,优先补 core/*.test.ts 或保持现有 node:test 入口可运行。
  • 修改 server/src/** 时,优先扩展 server/src/*.test.tsserver/src/routes/*.test.ts,用 stub storage / mock repository 保持离线。
  • 修改 server/cli/** 时,优先把参数解析、路径处理、输出格式和错误码做成可 import 的 helper,再用 Vitest 测试;真实 server 调用留到 smoke。
  • 修改默认管理端 server/web/** 时,优先补 server/web/src/**/*.test.ts(x),覆盖 API client、路由、状态转换和关键组件 loading / empty / error / disabled 状态。
  • 修改 shared/run-workspace-input/** 时,优先补 shared/run-workspace-input/**/*.test.ts(x),覆盖 Run workspace 输入、conversation 列表、附件、Markdown/output item、图片预览、Enter 发送、右键/更多菜单、标题锁定和窄屏/宽屏响应式状态。
  • 修改 app/** 时,优先补 app/src/renderer/src/**/*.test.ts(x),覆盖参数转换、运行状态合并、Diagram 工具、主题/路由和关键组件;完整 Electron 流程后续放到 Playwright/Electron smoke。
  • 修改或新增 workspace/workflow/<name> 时,至少确保 pnpm test:workspace 通过;外部服务依赖必须 mock、skip 或放入 opt-in smoke。

第二/三轮覆盖重点

issue #81 第二轮开始,后端与 CLI 覆盖不只检查 happy path,还要覆盖 public routes、storage/persistence、运行队列、取消/超时、错误码和打包后 CLI smoke:
  • server/src/routes/*.test.ts 覆盖管理端 API、公开 Embed、Webhook 和 Dify 风格 external run。External run 默认验证 blocking / streaming 两种模式,SSE 使用 data: {...} 行格式,external.source 会标记为 external;公开 Embed conversation streaming 默认验证 NDJSON runtime event、run_resultresponseMode: "streaming"
  • server/src/storage.test.ts 覆盖 run file 附件存储、Webhook 映射与 header 脱敏、Embed token 拒绝、Embed streaming 元数据、run queue 配置、PostgreSQL 持久化 SQL 和版本生命周期边界。
  • root CLI 与 server/cli 的 dist smoke 测试在构建产物存在时执行;focused test 中没有 dist 会跳过,首轮/二轮验收仍应先跑 pnpm build 再跑聚合命令确认 packaged CLI。
issue #81 第三轮开始,前端测试要覆盖真实交互状态,而不是只渲染静态快照:
  • App jsdom 测试覆盖主题选择、手动诊断抽屉、连接缺 token 禁用态、显式 Run/Send 后保持 Run 主视图、runtime event 合并、conversation 图片附件、assistant 输出状态、重复新建空白会话防重、会话重命名/删除、标题锁定以及锁定后忽略 runtime 标题更新。
  • 默认 server/web jsdom 测试覆盖项目详情里的 run queue 控件、日志抽屉、版本上下线、Webhook 映射/过滤摘要、Embed 表单保存禁用态,以及公开 Embed conversation 的 NDJSON 分片解析、runtime chunk 追加、最终结果去重、会话列表新到旧排序、分页加载、Enter 发送、右键/更多菜单、会话重命名/删除和标题锁定。
  • shared run-workspace-input jsdom 测试覆盖 App、server web 日志回放和公开 Embed 共同复用的 conversation shell:会话 rail、紧凑选择器、新旧排序、分页加载、拖拽/键盘调宽、运行中会话切换、工具调用状态、Advanced options、图片预览、Enter 发送、右键/更多菜单、会话重命名/删除和标题锁定。
  • dark/light、窄屏、抽屉与图片预览后续可以继续加 Playwright 视觉 smoke;默认仍保持离线,真实 Electron 或真实 server 只进入 opt-in smoke。

第四轮 Workspace Workflow 专属套件

issue #81 第四轮开始,每个 workspace/workflow/<name> 示例都要把自己的测试约定放在示例目录内:
  • __fixtures__/smoke.json:记录离线 runtime smoke 的参数、conversation id、期望输出和必要节点。需要外部进程或真实 SDK 的样例可以设置 run: false,但必须写清 skipReason
  • __snapshots__/structure.json:记录精简结构快照,包括源码文件、节点 factory、参数、conversation、registry、分支和 loop 摘要。
  • 外部依赖默认 mock:LLM 由测试中的 MockLLMProvider 注入,文件输入使用本地 fixture,GitLab webhook 样例只解析本地 payload,不访问 GitLab。
  • 如果示例 workflow 自身包含专属工具或解析逻辑,可以在示例目录内补充 *.test.tspnpm test:workspace 会同时执行 workspace/**/*.test.tsworkspace/workflow/**/*.test.ts,外部 HTTP 仍需用 fixture fetch 或同等方式离线化。
当前重点回归:
  • game:覆盖多层 runIf、LLM 节点、classifier 和多个输出节点。
  • loop:覆盖 runForrunWhile、嵌套 runIf 和最终达标输出。
  • details-output:覆盖 details output payload 的 title、icon 和 markdown content。
  • runtime-timer:覆盖 createTimerNode / endTimer 的显式结束计时,并断言输入毫秒数会体现在最终 durationMs 中。
  • claude-agent:覆盖 fixture、structure、conversation 与 tokenUsage 元数据;真实 Claude Agent SDK 运行需要 ANTHROPIC_API_KEY 和外部 Claude Code 进程,因此默认 runtime smoke 设置 run: false 并保留 opt-in README 命令。
  • conversation:覆盖 conversation、图片附件、本地 file store、KV trace 与 mock LLM 消息输入。
  • gpt-image:覆盖图片 file param、离线 mock image runner、conversation 上下文、generate/edit 模式和不支持的透明背景参数校验。
新增或修改示例 workflow 时,至少同步更新 __fixtures__/smoke.json__snapshots__/structure.json,并运行:
pnpm test:workspace

第五轮本地质量门禁

当前仓库不再把 GitLab CI 作为默认质量门禁或发布入口;提交前在当前电脑本地顺序运行默认阻断命令。建议先确认 pnpm --version 与根 packageManager 一致,再执行:
分层命令
类型检查pnpm typecheckpnpm -C app typecheckpnpm -C server/web typecheck
单元测试pnpm test:legacypnpm test:unit
覆盖率pnpm coverage
构建pnpm buildpnpm -C app buildpnpm -C server/web build
workspace smokepnpm test:workspacepnpm smoke:runtime
浏览器 smokepnpm test:e2e
真实环境 smoke手动 opt-in pnpm smoke:real
如果本地环境暂时无法执行完整阻断链,必须先修复环境;仍无法修复时,在 issue 与最终说明中写清阻塞原因、影响范围和替代验证。真实环境 smoke 仍保持 opt-in,不进入默认 pnpm test

第六轮真实环境 Smoke

真实环境 smoke 只在显式 opt-in 时执行:
WORKFLOW_CODE_REAL_SMOKE=1 pnpm smoke:real
缺少变量的子项会 skip,不会阻断其它子项。支持的变量如下:
子项变量
Supabase storageWORKFLOW_SUPABASE_URLWORKFLOW_SUPABASE_SERVICE_ROLE_KEY、可选 WORKFLOW_STORAGE_BUCKET
LLM providerLLM_TYPELLM_API_KEY、可选 LLM_BASE_URLLLM_MODEL
GitLab webhookWORKFLOW_CODE_GITLAB_WEBHOOK_URL、可选 WORKFLOW_CODE_GITLAB_WEBHOOK_TOKEN
本地 serverWORKFLOW_SERVER_URLWORKFLOW_SERVER_ADMIN_KEY
远程 serverWORKFLOW_CODE_REMOTE_SERVER_URLWORKFLOW_CODE_REMOTE_SERVER_ADMIN_KEY
Electron appWORKFLOW_CODE_ELECTRON_SMOKE=1,并先完成 pnpm -C app build
真实环境 smoke 只有 WORKFLOW_CODE_REAL_SMOKE=1 时才连接真实 Supabase、LLM、GitLab webhook、Electron 或远程 server;普通本地默认测试不会触发这些外部依赖。

Smoke 边界

真实环境 smoke 可以继续增加这些套件,但不能进入默认 pnpm test
  • Supabase / PostgreSQL 真实连接与 migration smoke。
  • LLM provider、Codex SDK、GitLab webhook、Lark CLI 等外部集成。
  • Electron App 真实启动、主进程 IPC、文件系统和本地 CLI 调用。
  • server/web 连接真实 Workflow Server 的登录、项目详情、日志、KV、Webhook 页面回归。
这些测试必须显式检查环境变量;缺少配置时应 skip,并在输出中说明跳过原因。