ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenWork 测试体系:不阻塞无关工作的 PR 门槛设计与 CI 演进

OpenWork 测试体系:不阻塞无关工作的 PR 门槛设计与 CI 演进 OpenWork 测试体系不阻塞无关工作的 PR 门槛设计与 CI 演进【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork导读本文以 OpenWork基于 opencode 的开源 Claude Cowork 替代品的官方测试策略文档 docs/testing.md 为核心深入剖析其两层测试体系——合并门槛只跑pnpm test别名test:core精选核心套件更广泛的回归交给每日定时 CI同时结合仓库内 .github/workflows/ci-tests.yml、各包package.json与 evals/vitest.config.ts 等源码证据还原变更分类lane机制、失败案例审计与失败必须诊断修复、不重试的工程纪律。读完你将掌握如何在本仓库正确搭建测试环境、核心套件与扩展套件分别覆盖什么、PR 合并到底被哪些检查卡住以及 CI 运行时间与覆盖范围之间如何取舍。一、为什么需要不阻塞无关工作的测试策略大型 monorepo 的典型困境是一次无关紧要的改动比如改文档、更新模型快照触发整套全量回归PR 排队、CI 变慢而真正有价值的覆盖反而被淹没在噪音里。OpenWork 通过一份审计数据做出了改变对 2026 年 8 月 28 日至 9 月 3 日UTC期间 OpenWork Tests 运行记录进行审计632 次运行中有 159 次失败其中 17 次等待审批在 615 个成功/失败结果中失败率高达25.9%。这是运行次数统计含同一分支的重复推送不是经过归因的 flake 率但足以说明默认门槛过宽让大量无关改动为脆弱测试买单。抽样失败日志显示不同的问题需要不同的修法详见本文第五节于是仓库把合并门槛收敛为两条独立的 Linux job把广覆盖挪到夜间任务让大部分改动只跑最小必需验证成为默认路径。这套策略沉淀为 docs/testing.md 这份文档并落地在 .github/workflows/ci-tests.yml 中。二、本地环境准备与 CI 完全一致的运行时固定在提交 PR 前先运行pnpm test别名pnpm test:core。为了让本地结果与 CI 一致仓库通过 .github/actions/setup-tests/action.yml 固定了全部运行时本地应与之对齐运行时固定版本作用Node.js24运行 pnpm、测试脚本Bun1.3.14执行 app / server / den-api 的bun testpnpm11.4.0包管理器根 package.json 声明packageManager: pnpm11.4.0OpenCode见 constants.jsonv1.18.30真实引擎需在 PATH 上安装两个工作区仓库是双工作区结构两个工作区都要安装依赖# 根工作区 pnpm install --frozen-lockfile # evals 工作区独立 lockfile pnpm --dir evals install --frozen-lockfileCI 中的 setup-tests 会做同样的事并额外从 OpenCode 的 GitHub Releases 下载与constants.json中版本匹配的二进制放入 PATHLinux 用opencode-linux-x64-baseline.tar.gzmacOS 用opencode-darwin-arm64.zip等随后用opencode --version校验。核心套件无需任何外部依赖这是核心套件与扩展套件的关键差异核心套件使用本地 fixtures 和脚本化 providers不需要云账号、provider key、Docker daemon 或正在运行的 Den 数据库。也就是说克隆仓库、装好依赖、放好 opencode 二进制pnpm test:core就能在纯本地确定性执行——这正是它能作为每个 PR 默认门槛的前提。三、合并门槛openwork-tests-required检查的两个独立 Linux jobopenwork-tests-required检查保留原有名称并采用fail-closed失败即封锁合并策略。对于普通代码改动它要求两条独立的 Linux job 全部通过Job 1核心回归Core regressions由pnpm test:core驱动。从根 package.json 的test:core定义可以看到它由五个子套件串联而成test:core: pnpm --filter openwork/app test:core pnpm --filter openwork-server test:core pnpm --filter openwork-ee/den-api test pnpm --filter openwork/desktop test:core pnpm --dir evals run test:core五个子套件的覆盖面与文档描述一一对应客户端app会话准入、流式与重连、权限状态、附件、provider 凭据、Connect 对账。见 apps/app/package.json 的test:core包含tests/session-admission-terminal-invariant.test.ts、tests/session-sync-lifecycle.test.ts、tests/session-sync-permissions.test.ts、tests/attachment-file-part.test.ts、tests/cloud-provider-credentials.test.ts、tests/connect-policy-reconciler.test.ts等 18 个文件通过bun test --isolate运行。服务端真实路由server线程headless threads、群组session groups、代理opencode proxy、文件夹权限、上传审批、artifact I/O、云配置、引擎驱逐与热重载、token 作用域、导出安全。见 apps/server/package.json 的test:core运行headless-threads.e2e.test.ts、session-groups.e2e.test.ts、opencode-proxy.e2e.test.ts、authorized-folders.e2e.test.ts、inbox-upload-approval.e2e.test.ts、artifact-files.e2e.test.ts、cloud-mcp-reconcile.e2e.test.ts、runtime-config-patch-reload.e2e.test.ts、engine-instance-eviction.e2e.test.ts、tokens.test.ts、workspace-export-safety.test.ts等 14 个文件。Den 认证den-api见 ee/apps/den-api/package.json 的test脚本覆盖api-keys、bearer-session、cloud-provider-materialization-read-retry、remote-session-capability-wire、organization-capabilities、auth-organization-metadata以及 gateway-deployment 路由。桌面端desktop工作区持久化、归档、链接、凭据密钥、自动化执行、进程韧性、TLS。见 apps/desktop/package.json 的test:core包含workspace-store.test.mjs、workspace-archive.test.mjs、connect-link.test.mjs、secure-vault-key.test.mjs、automation-runner.test.mjs、process-resilience.test.mjs、runtime-ca.test.mjs、runtime-chain-repair.test.mjs等 12 个文件pretest:core会先构建 headless-threads。真实引擎旅程evals三个既有真实引擎旅程额外检查线程审批记忆thread approvals replay、**有效权限归属effective permissions attribution**与PDF 模型路由。见 evals/package.json 的test:corevitest run --config vitest.config.ts --project pr \ specs/thread-approvals-replay.test.ts \ specs/effective-permissions-attribution.test.ts \ specs/pdf-attachments-model-routing.test.ts \ specs/route-session-list.test.ts其中prproject 定义在 evals/vitest.config.tsinclude为specs/**/*.test.ts与../scenarios/**/*.test.ts排除e2e与live规格live 规格被视为挂接系统的故障信号除非显式点名否则不纳入。以 route-session-list.test.ts 为例它直接 import 客户端源码apps/app/src/react-app/shell/route-workspaces.ts、sidebar/utils.ts用合成会话数据验证路由会话列表与归档分区逻辑体现了规格直连实现、不依赖运行环境的写法。CI 中核心 job.github/workflows/ci-tests.yml 的openwork-tests-core还会追加运行测试框架自身的检查node --test evals/scripts/journey-ci.test.mjs evals/scripts/flake-report.test.mjsJob 2打包Packaging打包 jobopenwork-tests-build验证测试源码解析与打包后的 Node 插件解析不同这一风险点包含四步# 1. outbound-access 声明校验 pnpm check:outbound-access # 2. 按打包方式构建服务端 Node 目标插件模拟打包解析路径 pnpm --filter openwork-server build # 3. 校验打包后的 Fast 模块消费 node --test packages/types/tests/packaged-cloud-model-fast.test.mjs # 4. Electron 主进程对 IPC 契约的类型检查 pnpm --filter openwork/desktop typecheck:electron随后还会运行虚拟显示引导xvfb与安装包 smoke 测试packaged-smoke.mjs --server-built并上传 evidence 与耗时数据。一条测试失败不能阻止这条独立 job 报告打包回归——两条 job 相互独立正是为了让打包坏没坏不被核心测试的噪音掩盖。变更分类snapshot-only 与 docs-only 走专门验证openwork-tests-required不是无脑要求全绿而是依据**变更分类lane**放行。ci-tests.yml 的classify-changesjob 用 GitHub Script 检查变更文件snapshot唯一变更文件是ee/apps/gateway/src/models/base.json模型快照只跑validate-models-snapshot校验快照结构provider 有 id/name/models模型有 modalities.input/output 与 limitdocs全部变更都在packages/docs/下只跑validate-docs用 Mintlify 校验站点构建、断链、锚点与图片full其余所有情况要求核心与打包两条 job 通过。openwork-tests-required聚合 job 在if: always()下运行根据 lane 断言对应 job 的成败矩阵例如 full lane 要求CORE_RESULTsuccess BUILD_RESULTsuccess SNAPSHOT_RESULTskipped DOCS_RESULTskipped为分支规则集提供单一稳定的检查名始终上报、按分类判定。同 PR 新推送会取消过时的运行concurrency.cancel-in-progressdev 分支推送仍跑核心与打包检查。模型快照与文档专用验证保持既有通道不变。核心套件的选择纪律文档明确指出test:core脚本就是选择机制——没有第二套清单、没有选择生成器、没有覆盖率棘轮、也没有断言清单与自身一致的测试。要扩大覆盖正确做法是为某个核心失败模式扩展既有测试而不是新增簿记类检查。只有满足以下条件才应往门槛里加测试能捕获关键旅程中的可观测回归在声明了前置条件下确定性运行值得付出运行成本。禁止向门槛加入源码文本/布局断言、测试运行器包装器、簿记检查。核心测试一旦失败必须被诊断并修复而不是重试到绿或静默忽略。四、更广覆盖夜间全量回归与pnpm test:extended核心门槛刻意保持窄那么其余套件去哪了同一个 OpenWork Tests 工作流通过openwork-tests-extendedjob 在Linux 与 macOS 双平台上运行全量套件触发方式每日 UTC 07:37 定时cron37 7 * * *或对选定分支通过Run workflow手动触发fail-fast: false即使前面某个套件失败后续套件照常运行并各自上报失败仍会把该次运行标红覆盖清单app 全量、server 全量、den-api 全量、desktop 全量、release 脚本测试node --test scripts/release/*.test.mjs、测试框架检查pnpm test:eval-runner、PR 规格pnpm sdk:build pnpm evals:pr、引擎 smokepnpm test:e2e、服务端插件构建与 Electron typecheck。本地复现这套全量回归用pnpm test:extended从根 package.json 可见其定义pnpm --filter openwork/app test pnpm --filter openwork-server test \ pnpm --filter openwork-ee/den-api test pnpm --filter openwork/desktop test \ node --test scripts/release/*.test.mjs pnpm test:eval-runner pnpm evals:pr pnpm test:e2e打包部分可以单独复现# 服务端真实 Node-target 插件构建 pnpm --filter openwork-server build # Electron IPC 契约类型检查 pnpm --filter openwork/desktop typecheck:electron权责转移与注意事项把广泛套件移出统一门槛意味着门槛之外的回归可能先由定向验证或夜间运行发现。因此文档强调改动某个包内部逻辑时应运行该包的完整测试命令而非只跑核心子集改动测试框架本身时运行pnpm test:eval-runner发布前检查 macOS 夜间运行结果——macOS 不再是 PR 前置条件发布风险由夜间任务兜底现有的 Daytona E2E 与夜间 flake-report 工作流保持不变。跳过即不完整原则一个被跳过的旅程就是不完整的覆盖即使 runner 以成功退出。文档明确禁止把包含跳过的运行描述为完整证明——这条原则保证了夜间全量任务不会用静默 skip 粉饰太平。五、为什么改变审计数据与四个典型失败案例回到开头的审计632 次运行、159 次失败、25.9% 失败率。抽样失败日志揭示了四种不同性质的问题需要四种不同的修法案例失败现象根因与处置9 月 3 日spec-impact与spec-quarantine清单断言在双 OS 失败而同批其余 115/116 个 spec 通过特定规格已移除把测试框架簿记移出默认门槛防止同类壁垒在其他地方重建8 月 28 日一个兼容性规格又派生了一个测试 runner底层失败被包装断言遮蔽属于 runner 包装器问题印证不给门槛加测试运行器包装器的禁令PR #4442共享套件在引擎退役时序断言上失败与 dev 运行同因竞态已由 PR #4439 独立修复核心覆盖仍包含真实引擎驱逐与重载行为广泛测试予以保留PR #4442 的 SDK 检查schema 生成在无数据库的情况下连接127.0.0.1:3306的 MySQL这是 SDK 改动自身的setup 依赖应在 SDK 变更中修复而不是以此为由压制 schema-drift 校验文档特别说明本次 CI 清理不修复该分支——即它只负责调整门槛结构不替失败的改动补丁。另外强调本次改动不增加测试的测试也不新增任何测试文件运行时间收益必须在上线后实测把四个广覆盖 PR job 收敛为两个聚焦 job本身并不构成特定墙钟时间必然缩短的证据。这种克制表述本身就是工程严谨性的体现。六、从源码看这套体系的落地细节1. 运行时固定是可审计的setup-tests/action.yml 不仅装运行时还用node -e从constants.json读出opencodeVersion动态拼接下载 URL——引擎版本只在一个地方声明CI 与文档引用同一来源。缓存键同样引用constants.json、pnpm-lock.yaml与 sidecar 准备脚本确保引擎变化时缓存自动失效。2. 核心 job 的 15 分钟超时是聚焦的硬约束openwork-tests-core与openwork-tests-build都设置了timeout-minutes: 15扩展 job 为 30 分钟。这从 CI 层面强制核心套件保持窄而快任何把门槛拖慢的回归都会直接撞上超时。3. 规格层与实现层的边界evals/vitest.config.ts 将prproject 的 alias 指向../apps/app/src/使规格可以直接引用客户端源码如 route-session-list.test.ts 引入route-workspaces.ts的listRouteSessions、readRouteSessionsWithRetry同时把*.e2e.test.ts、*.live.test.ts排除在 PR 门槛之外——e2e 需要真实堆栈runner/prepare-stack.ts全局 setup、testTimeout: 600_000live 规格则是挂接生产系统时才用的信号。这种规格spec/端到端e2e/在线live三级划分正是门槛窄、覆盖广的落地形态。4. 失败必须修复的 CI 兑现ci-tests.yml 中没有任何continue-on-error或重试包装扩展 job 用if: (success() || failure())让后续套件继续跑、各自上报但结果仍计入红标openwork-tests-required对 lane 矩阵做严格断言任何非预期组合直接exit 1。这从流水线层面杜绝了重试到绿的侥幸文化。七、实践总结如何正确使用这套测试体系提交前pnpm test即test:core确保本地运行时与 setup-tests 固定版本一致Node 24、Bun 1.3.14、pnpm 11.4.0、opencode 版本见 constants.json并先执行两个工作区的pnpm install --frozen-lockfile。核心失败时诊断并修复不要重试、不要跳过、不要静默忽略。改动包内部时运行该包的完整测试命令如pnpm --filter openwork-server test。改动测试框架时运行pnpm test:eval-runner。发布前检查 macOS 夜间运行结果如需本地全量复现运行pnpm test:extended。扩覆盖时为既有核心失败模式扩展测试而不是新增簿记/包装器/文本断言更不要建立第二套测试清单。这套体系的本质是用变更分类把验证成本精确匹配到改动风险上用两条独立 Linux job 守住核心回归 打包契约两个最关键的失败面用夜间双平台全量回归承接广覆盖再用失败必须修复的纪律保住每个检查信号的可信度——最终让 CI 既快、又准、还不撒谎。参考路径速查测试策略文档docs/testing.mdCI 工作流lane 分类、核心/打包/扩展 job、required 聚合.github/workflows/ci-tests.yml运行时安装与固定.github/actions/setup-tests/action.yml引擎版本声明constants.json根脚本test/test:core/test:extended/test:eval-runnerpackage.json各子套件定义apps/app/package.json、apps/server/package.json、apps/desktop/package.json、ee/apps/den-api/package.json真实引擎旅程与规格配置evals/package.json、evals/vitest.config.ts核心规格示例evals/specs/route-session-list.test.ts【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表