OpenHands Agent Canvas 测试矩阵:Install × OS × Agent 的发布验收体系与自动化测试落地
发布时间:2026/9/5 20:34:21 作者:尧图编辑部 阅读量:1,286

OpenHands Agent Canvas 测试矩阵Install × OS × Agent 的发布验收体系与自动化测试落地【免费下载链接】OpenHands OpenHands: AI-Driven Development项目地址: https://gitcode.com/GitHub_Trending/ope/OpenHands本文以仓库中 docs/TESTING_MATRIX.md 定义的测试矩阵为主线解读 OpenHands Agent Canvas 如何在安装方式npm / Docker× 操作系统 × Agent 后端的交叉维度上组织发布前的验收工作P0/P1/P2 优先级如何落到具体检查项、四套自动化测试vitest 单测、mock-LLM E2E、Docker mock-LLM E2E、live E2E分别覆盖矩阵中的哪些格子、以及当前 CI 尚未覆盖的空白区域。读完后你可以直接按矩阵执行手工验收、复现各条 E2E 流水线并理解每一项背后的工程实现。优先级定义P0 / P1 / P2矩阵文档开篇给出全篇的优先级键priority key它决定了哪些格子必须在发布前通过优先级含义P0任何 release 之前必须通过must pass before any releaseP1GA正式发布之前必须通过must pass before GAP2best-effort尽力而为这一约定贯穿后文所有矩阵手工矩阵Install × OS × Agent里每个格子都是一次冒烟测试smoke test而 Automations 与 Auth Modes 矩阵则直接标注了格子对应的优先级。Install × OS × Agent安装冒烟矩阵矩阵文档的第一张表定义了最基础的验收单元每个格子 一次完整冒烟流程安装 → onboarding → 发起对话 → agent 有回复install → onboard → start conversation → agent replies。矩阵的行是安装方式 × Agent 后端的组合列是操作系统macOSLinuxWindowsnpm — OpenHands☐☐☐npm — Claude Code☐☐☐npm — Codex☐☐☐npm — Gemini CLI☐☐☐npm — Custom ACP☐☐☐Docker — OpenHands☐☐☐Docker — Claude Code☐☐☐Docker — Codex☐☐☐Docker — Gemini CLI☐☐☐Docker — Custom ACP☐☐☐行中的 Agent 后端对应两条接入路径OpenHandsAgent Canvas 的原生 agent-server 路径。从 config/defaults.json 可以看到npm 与 Docker 两条安装路径共用同一份版本钉version pin当前仓库锁定agentServer: 1.44.0、agentCanvas: 1.16.0、automation: 1.9.0最低兼容minimumAgentServer: 1.28.0。该文件在注释中明确说明自己是npm 和 Docker 安装路径共享的版本、端口、路径与默认值的唯一事实来源被 scripts/dev-safe.mjs、scripts/dev-with-automation.mjs 和 docker/entrypoint.sh 共同读取——这保证了矩阵中 npm 与 Docker 两行验收的是同一版本语义。Claude Code / Codex / Gemini CLI / Custom ACP通过 Agent Client ProtocolACP接入的第三方 CLI agent。仓库中 src/constants/acp-providers.ts 及其测试tests/constants/acp-providers.test.ts 维护了这些 provider 的定义ACP 冒烟路径还受到config/defaults.json中constraints.agentClientProtocolagent-client-protocol0.11这一临时上界的约束——注释解释了 acp 0.11.0 重排了prompt()参数会破坏 SDK 的 ACP 客户端因此在 openhands-sdk 修复前必须钉住0.11。Custom ACP一行的验证路径在 tests/e2e/live-acp/README.md 中有更具体的说明该 e2e 证明容器化 ACP 凭据路径通过 Canvas 自身代码走通——每个凭据先像 onboarding 一样存进 agent-server 的 secret storebuildStartConversationRequest再以LookupSecret的形式引用它由服务端在 spawn 时解析。这是对tests/api/agent-server-adapter.test.ts 单元测试的真实跑通配套单测断言请求形状live-acp 断言真实 agent 回复。Automations × Install × Agent需要完整栈的验收第二张矩阵要求automation backend 完整运行requires full stack并直接标注了优先级npmDockerOpenHands✅ P0✅ P0Claude Code✅ P1✅ P1Codex✅ P1✅ P1Gemini CLI✅ P2✅ P2每个格子的验收动作是创建 automation → 派发一次 run → run 到达 COMPLETED 状态 → 对话链接可用create automation → dispatch run → run reaches COMPLETED → conversation link works。从源码结构看这条链路的后端组件是 automation backend端口 18001见 config/defaults.json 的ports.automationSQLite 数据库位于automation/automations.dbpaths.automationDb。mock-LLM E2E 配置 playwright.mock-llm.config.ts 对状态目录的清理逻辑也印证了这一点自动化 DB 存放在$parent_of_STATE_DIR/automation/automations.db镜像了 docker/entrypoint.sh 使用$HOME/.openhands/automation/automations.db的布局STATE_DIR与AUTOMATION_DB_DIR在每次运行之间都必须清理以避免脏数据。Auth Modes本地自动密钥与公共模式第三张矩阵覆盖两种会话鉴权模式npmDockerLocalauto-generated key✅ P0✅ P0Public--public user key✅ P1✅ P1Local 模式安装时自动生成会话 API key。mock-LLM 的 Playwright 配置正是这样工作的——playwright.mock-llm.config.ts 在未设置MOCK_LLM_SESSION_API_KEY时用randomBytes(32).toString(hex)生成随机密钥并通过环境变量LOCAL_BACKEND_API_KEY注入给bin/agent-canvas.mjs启动的完整栈。Public 模式以--public对应 static-server 的--auth-required不烘焙 session key启动第二个静态服务器实例复用同一份build/产物与同一后端。在 playwright.mock-llm.config.ts 中这是 webServer 数组的第 3 个进程node scripts/static-server.mjs --dir build --port 18301 --auth-required并把/api/automation、/api、/server_info、/sockets分别路由到 18001/18000 端口。Docker 路径则由容器内 entrypoint 在设置了PUBLIC_MODE_PORT时启动同样的第二实例见 playwright.mock-llm-docker.config.ts 中传入的-e PUBLIC_MODE_PORT…。这两类格子在 mock-LLM 测试集中的具体验证位于 tests/e2e/mock-llm/backends/mock-llm-auth-modes.spec.ts。Feature Checklistnpm 与 Docker 的 12 项功能清单矩阵文档为 npm 与 Docker 各列出一张功能清单两表行项一致这是手工验收时逐格勾选的检查单。以下完整保留原文档的 12 个功能项LLM profiles一行在 Claude Code / Codex / Gemini CLI 列中标注为 —表示该功能不适用于这些 ACP 接入路径因为它们的模型与鉴权由各自的 CLI 管理npmFeatureOpenHandsClaude CodeCodexGemini CLIOnboarding☐☐☐☐Conversation — start, resume, history☐☐☐☐Terminal tool☐☐☐☐File editor tool☐☐☐☐Browser tool☐☐☐☐LLM profiles — create / switch☐———Secrets — add / delete / forwarded☐☐☐☐Automations — create, dispatch, COMPLETED☐☐☐☐Files tab Changes/diff tab☐☐☐☐MCP server install☐☐☐☐Image upload in chat☐☐☐☐Key rotation☐☐☐☐DockerFeatureOpenHandsClaude CodeCodexGemini CLIOnboarding☐☐☐☐Conversation — start, resume, history☐☐☐☐Terminal tool☐☐☐☐File editor tool☐☐☐☐Browser tool☐☐☐☐LLM profiles — create / switch☐———Secrets — add / delete / forwarded☐☐☐☐Automations — create, dispatch, COMPLETED☐☐☐☐Files tab Changes/diff tab☐☐☐☐MCP server install☐☐☐☐Image upload in chat☐☐☐☐Key rotation☐☐☐☐Automated Coverage四套自动化如何支撑矩阵矩阵文档的最后一节把自动化测试与矩阵的四个维度Install / OS / Agents / Automations对齐SuiteInstallOSAgentsAutomationsvitestunit—Linux—partialtest:e2e:mock-llmnpmLinuxOpenHands, ACPmock✅ fulltest:e2e:mock-llm:dockerDockerLinuxOpenHands, ACPmock✅ fulltest:e2e:livenpmLinuxOpenHands❌CI 尚未覆盖的部分真实 ACP 凭据Claude Code / Codex / Gemini、macOS、public 鉴权模式、订阅登录路径、Windows。说明矩阵文档中test:e2e:mock-llm的 Automations ✅ full 与手工矩阵中 Automations 的优先级标注互为印证——自动化已经把 npm 与 Docker 两条路径上的 OpenHands 全栈含 automation跑满剩下的手工格子集中在真实凭据的 ACP agent 与多 OS 组合上。下面结合仓库源码逐个说明四套测试的实际形态。1. vitest 单元测试Linux 上的单元层package.json 中的脚本为npm test→npm run make-i18n vitest run。Vitest 的配置内嵌在 vite.config.ts 的test段jsdom 环境、setup 文件 vitest.setup.ts、并显式exclude: [...configDefaults.exclude, tests]——这解释了为什么 live E2E位于tests/下不属于单测套件。几个对测试行为有实际影响的配置细节testTimeout/hookTimeout提到 30s注释说明大量 DOM 密集型测试并行时userEvent驱动的测试在繁忙机器上可能超过 Vitest 默认 5s 超时提高全局超时让测试保持确定性而不改变任何生产行为coverage.include限定为src/**/*.{ts,tsx}报告输出到coverage/由npm run test:coverage触发vitest.setup.ts 统一桩掉了VITE_SESSION_API_KEYtest-session-key、canvas 的getContext、HTMLElement.scrollTo并为 Node.js 25 提供了内存版localStorage桩否则 zustand 的 persist 中间件无法工作。此外仓库还配置了变异测试stryker.config.mjs 以 vitest 为 testRunner对src/**/*.{ts,tsx}排除测试、类型声明、fixtures/mocks执行变异related: true意味着 Stryker 用vitest related只运行与被变异文件相关的测试入口命令是npm run test:mutation另有test:mutation:diff/test:mutation:incremental走 scripts/stryker-diff.mjs。2. test:e2e:mock-llmnpm 全栈 模拟 LLM对应脚本playwright test --configplaywright.mock-llm.config.ts。playwright.mock-llm.config.ts 的文件头注释把三进程架构写得非常清楚Mock LLM 服务器Pythontests/e2e/mock-llm/scripts/mock-llm-server.py 基于 openhands-sdk 的TestLLM提供 OpenAI 兼容的/v1/chat/completions接口agent-server 的 litellm 层与之对话而无需真实 LLM 凭据。脚本内置一条单轨迹一次 terminal 工具调用标记MOCK_LLM_E2E_BASH_OK加一段文本回复MOCK_LLM_E2E_REPLY_OK并识别 agent-server 的 LLM profile 预检 pingPREFLIGHT_PING_TEXT ping以免污染脚本化轨迹完整 agent-canvas 栈通过bin/agent-canvas.mjs启动agent-server automation backend 静态前端 ingress 代理注释明确说明这镜像的是生产npx openhands/agent-canvas路径配置在启动前会清理.tmp/mock-llm-state与 automation DB 目录并在build/index.html不存在时先npm run build:appPublic 模式静态服务器见上文 Auth Modes 一节。关键端口分配均可用环境变量覆盖mock LLM 在 9999ingress 在 18300public 模式在 18301——刻意与 dev / live E2E 错开避免端口冲突。值得注意的是 webServer 的就绪探测 URL 是http://localhost:18300/api/automation/v1而不是 ingress 根路径注释解释了 automation backend 经 uvx 启动最后才完成、可能耗时 30–60 秒只探测 ingress 根或/server_info会让测试在栈未完全就绪时开始因此要探测最后启动的服务。另外gracefulShutdown: { signal: SIGTERM, timeout: 15_000 }也是刻意为之Playwright 默认的SIGKILL组杀无法被scripts/dev-process-utils.mjs派生的 detached 服务捕获会造成孤儿进程占着端口改用 SIGTERM 才能触发 scripts/dev-with-automation.mjs 中的关停处理器。运行约束workers: 1、fullyParallel: false、单用例超时 60s、CI 下 10 分钟全局硬上限仅 chromium。测试规格按功能域组织在 tests/e2e/mock-llm/onboarding/、conversations/含 image upload、automations/、backends/auth modes、cross-connect、partial stack、files/files git、mcp/、settings/ACP agent、模型切换、profile 管理、skills/、home/folder workspace、canvas-extensions/、regressions/。3. test:e2e:mock-llm:docker同一套规格打 Docker 镜像playwright test --configplaywright.mock-llm-docker.config.ts与上一节复用同一批 test specstestDir: ./tests/e2e/mock-llm只是把宿主上的bin/agent-canvas.mjs uvx换成 all-in-one Docker 镜像默认ghcr.io/openhands/agent-canvas:latest可用MOCK_LLM_DOCKER_IMAGE覆盖与 config/defaults.json 的images.agentCanvas一致。playwright.mock-llm-docker.config.ts 的几个工程细节值得注意网络Linux 上用--network host使容器共享宿主网络栈容器内 agent-server 可以像 npm 路径一样直接访问127.0.0.1:9999的 mock LLMmacOS/Windows 的 Docker Desktopbridge 网络则需设置MOCK_LLM_AGENT_URLhttp://host.docker.internal:port卷挂载把宿主上的 mock ACP 脚本tests/e2e/mock-llm/scripts/mock-acp-server.py→ 容器内/opt/mock-acp-server.py、技能仓库、用户技能目录/home/openhands/.openhands/skills和 folder-workspace 测试目录挂进容器保证与 npm 路径可测同一批场景环境注入容器以-e PORT18300 -e SESSION_API_KEY… -e OH_SESSION_API_KEYS_0… -e PUBLIC_MODE_PORT18301启动容器内还会额外起一个--auth-required的 public 模式 static-server见 docker/entrypoint.sh 对PUBLIC_MODE_PORT的处理清理容器使用唯一随机名 docker rm -f前置清理 --rmPlaywright 退出时 webServer 命令收到 SIGTERMdocker run --rm自动收尾。运行前提已构建的 Docker 镜像和正在运行的 Docker daemon。CI 下全局超时默认 20 分钟MOCK_LLM_DOCKER_GLOBAL_TIMEOUT_MS可覆盖。4. test:e2e:live真实 LLM 的 npm 路径脚本为node --env-file-if-exists.env tests/e2e/live/scripts/run-live-e2e.mjs配合 playwright.live.config.ts。与 mock-LLM 路径不同live 路径需要真实 LLM 凭据tests/e2e/live/scripts/run-live-e2e.mjs 从LIVE_E2E_LLM_API_KEY/OPENAI_API_KEY/ANTHROPIC_API_KEY/LLM_API_KEY中取第一个可用者未配置时回落到内置的 LLM proxy 默认值模型默认openhands/claude-haiku-4-5-20251001。webServer 命令用npm run dev:minimal起最小编译的前端端口 3101加本地 agent-server默认http://127.0.0.1:18100由OH_CANVAS_SAFE_BACKEND_PORT控制并在启动前清理.tmp/live-e2e-state与node_modules/.vite。矩阵表中该套件对 Automations 一栏标 ❌——live 路径不覆盖 automation 全栈这与 mock-LLM 路径✅ full形成互补。受影响测试选择变更驱动的 E2E 收窄在 CI 中跑 E2E 成本高仓库用一个映射文件实现改了哪些源码就只跑哪一组测试。tests/e2e/mock-llm/test-mapping.json 把源码 glob 映射到 mock-LLM 测试子目录例如src/components/features/settings/**、src/routes/llm-settings*.tsx等 →settings测试组src/components/features/chat/**、src/routes/conversation.tsx等 →conversationssrc/components/features/automations/**、src/manifests/**等 →automationsalwaysRun固定包含regressions组runAllSources列出触碰即跑全量的高风险文件如src/api/agent-server-adapter.ts、src/root.tsx、playwright.mock-llm.config.ts、package.json以及 CI workflow 文件本身。tests/e2e/mock-llm/scripts/resolve-affected-tests.mjs 读取该映射根据变更文件集合决定本次运行哪几个测试目录其自身行为由tests/e2e/resolve-affected-tests.test.ts 覆盖。这对维护矩阵很有意义手工矩阵关注发布前各格子绿而这份映射保证日常 PR 阶段的自动化成本与风险成正比。覆盖缺口与矩阵维护建议矩阵文档明确列出当前 CI 未覆盖的区域这也是一份现成的手工验收 TODO真实 ACP 凭据Claude Code / Codex / Geminimock 路径只覆盖 OpenHands 与 mock ACP真实凭据路径可参考 tests/e2e/live-acp/README.md 的手工 e2e——它要求agent-server:1.25.0-python或更新镜像旧镜像会在首轮 ACP 冷启动死锁凭据取自宿主Codex 的~/.codex/auth.json、Claude Code 的 macOS keychain OAuth token、Gemini 的 gcloud ADC凭据缺失的 provider 自动跳过且凭据绝不打印macOS / Windows所有自动化均跑在 Linux跨 OS 只能靠 Install × OS × Agent 手工矩阵public 鉴权模式在 CI 中的覆盖依赖 mock-LLM 的 18301 public 实例矩阵中该模式整体仍是 P1订阅登录路径subscription login paths尚无自动化。维护该矩阵时的实践要点均可从源码直接验证新增功能后同步更新 tests/e2e/mock-llm/test-mapping.json否则变更驱动的测试选择可能漏跑相关 E2E修改config/defaults.json的端口/版本钉时注意它同时被 npm 启动器、Docker entrypoint 与 CI workflow 消费属于矩阵表中 npm 与 Docker 两行共同的地基任何触碰runAllSources中文件的改动CI 会退化为跑全量 mock-LLM 测试组这是设计好的保守策略而非故障。常用命令速查目的命令单元测试npm testvitest runLinuxjsdom单测 覆盖率npm run test:coverage变异测试npm run test:mutation/npm run test:mutation:diffmock-LLM E2Enpm 全栈npm run test:e2e:mock-llmmock-LLM E2EDocker 镜像npm run test:e2e:mock-llm:docker需先构建/拉取镜像Linux 建议 host 网络live E2E真实 LLMnpm run test:e2e:live配置LIVE_E2E_LLM_API_KEY等凭据MSW 驱动的开发服务器基础 e2e 默认 webServernpm run dev:mockplaywright.config.ts 的 webServer 即调用npm run dev:mock -- --port 3001以上命令均可在 package.json 的scripts段与四个 Playwright 配置文件playwright.config.ts、playwright.mock-llm.config.ts、playwright.mock-llm-docker.config.ts、playwright.live.config.ts中逐条对照。【免费下载链接】OpenHands OpenHands: AI-Driven Development项目地址: https://gitcode.com/GitHub_Trending/ope/OpenHands创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考