Mastra 实验 Worker E2E 测试套件解析:发布安装契约、隔离沙箱与协议验证
【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra
导读
Mastra 的experiment-workerE2E 套件(位于 e2e-tests/experiment-worker/README.md)从“隔离的客户项目”视角,验证已发布、已安装的mastra experiment build契约:构建独立产物、将产物复制到远离源码的位置、启动全新 worker 进程,并记录协议、产物、进程、服务与清理证据。读完本文,你将掌握该套件的本地命令与分层(pr/full/gated)测试策略、临时 Verdaccio 与 CI 发布型两种注册表模式、NDJSON 运行协议与 manifest 结构、隔离与所有权断言的设计思路,以及如何用它守护 Mastra 实验 Worker 的交付质量。
背景:为什么要做“已安装契约”E2E
普通单元测试在 monorepo 内运行,import解析、依赖提升、路径重写都会掩盖“真实客户安装后”才会暴露的问题。该套件的核心主张是:只在隔离的客户项目中,验证发布并安装后的产物。README 的第一段明确了整条验证链路:
- 从发布包安装依赖(而非 workspace 链接);
- 运行
mastra experiment build生成独立 artifact; - 把 artifact 从源码项目复制走,甚至删除源项目;
- 以全新 worker 进程启动 artifact,通过标准输入输出交互;
- 记录协议事件、产物清单、进程、服务与清理证据。
套件自身以 Vitest 驱动,工程入口在 e2e-tests/experiment-worker/package.json,运行环境为 Node,testTimeout为 90 秒、hookTimeout为 15 分钟(见 vitest.config.ts)。
本地命令与测试分层
README 给出的本地命令如下:
pnpm install --frozen-lockfile pnpm test:experiment pnpm test:full pnpm test:full:strict pnpm test:scenario -- minimal-agent pnpm test:workflow-routing各命令的实际含义可在 package.json 中对应到具体实现:
pnpm test:experiment:设置MASTRA_EXPERIMENT_E2E_TIER=pr后运行vitest run,是确定性、无需凭据的 PR 门禁;pnpm test:full:设置MASTRA_EXPERIMENT_E2E_TIER=full,追加包管理器、workspace、浏览器、LSP、原生依赖、Docker/Postgres、可移植性与负向边界覆盖;pnpm test:full:strict:在full基础上再加MASTRA_EXPERIMENT_E2E_REQUIRE_PUBLISHED_REGISTRY=1,强制使用 CI 发布的注册表快照;pnpm test:scenario -- <id>:经由 scripts/run-scenario.mjs 只跑一个 owning scenario——它会把MASTRA_EXPERIMENT_E2E_TIER默认置为full并注入MASTRA_EXPERIMENT_E2E_SCENARIO,但仍会执行注册表发布与全局 setup;pnpm test:workflow-routing:使用独立的vitest.routing.config.ts跑工作流路由测试。
分层选择在 vitest.config.ts 中直接体现:prTests只含setup/harness/minimal-agent/target-process/runtime-resources/native-duckdb六个文件;fullTests再追加full-workspace-lifecycle、postgres-lifecycle、project-shapes、portability-isolation、installed-boundary-negative五个文件。测试文件清单见 tests/。
注册表模式:本地临时 Verdaccio 与 CI 发布快照
默认情况下,套件在 global setup(setup.ts)中完成四件事:构建 CLI(pnpm build:cli)、把 snapshot 包发布到临时 Verdaccio 注册表、用npm view断言mastra与@mastra/core两个必发包存在、结束时清理整个运行根目录与注册表。这里借用了仓库通用的本地注册表基建 e2e-tests/_local-registry-setup/,其 Verdaccio 配置为 verdaccio.yaml。
CI 则只发布一次,并把发布产物以不可变快照形式传给各 job,通过以下环境变量交接(README 列出的完整清单):
| 环境变量 | 作用 |
|---|---|
MASTRA_E2E_REGISTRY_STORAGE | 已发布注册表的存储目录 |
MASTRA_E2E_REGISTRY_CONFIG | Verdaccio 配置文件路径 |
MASTRA_E2E_REGISTRY_TAG | snapshot 发布的 tag(CI 中为experiment-worker-ci-<run_id>-<run_attempt>) |
MASTRA_E2E_REGISTRY_ARTIFACT_PATH | 注册表快照 tar 包路径 |
MASTRA_E2E_REGISTRY_ARTIFACT_DIGEST | 快照的规范摘要(发布方计算、消费方校验) |
MASTRA_E2E_REGISTRY_PORT | 本地起注册表时使用的端口 |
MASTRA_EXPERIMENT_E2E_REQUIRE_PUBLISHED_REGISTRY=1 | 强制使用发布型注册表,禁止本地发布 |
发布型模式的防篡改设计(见 setup.ts):消费方先用 scripts/registry-artifact-digest.cjs 对下载的注册表快照计算摘要,与MASTRA_E2E_REGISTRY_ARTIFACT_DIGEST比对;不一致直接抛错拒绝。被复制或下载、与发布方规范摘要不一致的存储一律拒绝使用。摘要计算与校验逻辑在 helpers/registry-digest.ts。
MASTRA_EXPERIMENT_E2E_REPORT_DIR用于把 JSON、Markdown、构建日志和 NDJSON 协议转录保存到临时运行根之外,便于 CI 上传取证。PR/full 层级要求每个必跑 scenario 各产出一份 JSON 与一份 Markdown 报告,外加summary.json;只要有必跑 scenario 缺失、跳过或失败即整体失败。
CI 分层:pr / full / gated
CI 编排在 .github/workflows/e2e-experiment-worker.yml:
- 调度:每周二 07:00 UTC(
cron: '0 7 * * 2')自动运行;也支持手动workflow_dispatch,可选pr、full、gated分层和单个 scenario 选择器; - 共享不可变产物:
publish-experiment-worker-registryjob 先pnpm build、发布注册表、打成registry-snapshot.tar、用 registry-artifact-digest.cjs 计算摘要并上传为experiment-worker-registryartifact;full、browser、gated 各 job 都从它下载并解包校验; - PR 复用同一发布型契约:PR 走 .github/workflows/e2e-tests.yml,由
prebuild.yml的精确路径路由(path routing)决定是否触发,从而避免无关改动白白消耗 E2E 资源; - gated 接口独立运行:
remote-sandbox、object-store、real-model、hosted-proxy四个 gated job 运行在experiment-worker-e2e-gatedGitHub Environment 中,通过 scripts/gated-interface.mjs 调用。缺少凭据时产出status: skipped、skip code 为GATED_CREDENTIALS_MISSING,永远不会阻塞开源必须通过的验收;而必跑的 PR/full job 不注入任何 provider 凭据,保证 OSS 验收的确定性。
隔离与所有权:不信任源码、不共享状态
README 的“Isolation and ownership”段落总结了三条硬性约束,均有源码佐证:
- 依赖布局敏感的 fixture 使用独立安装根,绝不共享
node_modules:materialize/install 逻辑在 helpers/materialize-project.ts 与 helpers/install-project.ts,fixtures 分别覆盖 npm、yarn(berry)、pnpm workspace 单仓、native 原生依赖、postgres 等安装形态(见 fixtures/)。 - 复制后的产物要检查是否残留源码 workspace 引用:helpers/copy-artifact.ts 在
cp后遍历产物目录,对每个文件内容、每个符号链接目标做 POSIX/Windows/file:///URL 编码多形态的“针搜索”,发现任一源路径即抛错;测试还会删除源项目再运行协议,证明 worker 与源码完全无关。 - 清理断言只覆盖 harness 自己创建的资源:临时路径、进程组、端口、注册表、数据库、容器。进程组与资源追踪在 helpers/process-cleanup.ts,
minimal-agent测试用expect(cleanup.remainingPaths).toEqual([])收尾(见 tests/minimal-agent.test.ts)。
边界说明:README 明确指出 Platform 的 Linux sandbox 执行不属于本 OSS 套件;托管调度与权威的 experiment/score 持久化由 Platform 负责,worker 只在不持有 Platform 凭据的前提下验证独立产物与协议边界。
协议契约:NDJSON 帧与最小环境
worker 通过 stdin/stdout 通信,协议为NDJSON,每行一个事件,事件带type与自增sequence。请求构建与响应解析全部集中在 helpers/run-protocol.ts:
createRunRequest()构造type: 'run'帧,含protocolVersion: '1'、experimentId/jobId/idempotencyKey、deadlineAt、基于 items 规范序列化(key 排序)的 SHA-256datasetAttestation.digest、以及包含 target/dataset/scorers/limits/policies/secretReferences 的packet;parseProtocolOutput()要求 stdout 以换行结尾、逐行 JSON 可解析,且event.sequence必须从 0 连续递增,否则报“非协议 stdout/非连续序列”;assertProtocolResult()断言事件边界:成功路径必须是accepted → run-started → (item-completed)* → terminal,失败路径首事件为accepted、末事件为terminal;runCancelledProtocol()用spawn(非 Windows 上detached)启动 worker,在目标事件(默认run-started,可用readyWhen谓词等待在途 item 到达特定状态)后发送type: 'cancel'帧,随后期望退出码 30,并校验取消帧确已送达;minimalWorkerEnvironment()只保留PATH/HOME/TMPDIR/TMP/TEMP/SystemRoot/WINDIR/NODE_OPTIONS七个白名单变量,模拟近乎裸机的最小运行环境。
对应到场景:truncated-input(截断输入应得到协议退出码、stdout 为空、stderr 有诊断)、process-cancellation与sandbox-cancellation(终止一致性、后代进程被杀、取消后能再次成功)等场景定义在 scenarios/index.ts。
Manifest:产物自描述的契约载体
构建产物根目录必须包含experiment-worker-manifest.json,其结构与校验逻辑在 helpers/inspect-manifest.ts:
{ artifactVersion: 1, kind: 'mastra-experiment-worker', build: { buildId, cliVersion, createdAt }, protocol: { versions: ['1'], framing: 'ndjson', datasetCanonicalizationVersion: '1' }, launch: { executable: 'node', arguments: [...'index.mjs'], workingDirectory: '.' }, dependencies: { manifest, lockfile? }, artifact: { digestAlgorithm: 'sha256', contentDigest, excludes: [...] }, files: [{ path, sha256, type?: 'file'|'symlink', target? }] }校验器会强制artifactVersion === 1、kind === 'mastra-experiment-worker'、启动方式必须是node index.mjs且工作目录为.,并要求excludes至少声明experiment-worker-manifest.json与node_modules(产物不自带依赖目录)。文件清单必须按路径排序,每个文件的 SHA-256 逐一重算比对,符号链接则对链接目标求摘要,任何逃逸出产物根目录的路径都会被拒绝;最后用path\0sha256\n拼接重算contentDigest与 artifact 层摘要比对,形成文件级 + 内容级双重完整性校验。
场景矩阵:从最小 Agent 到负向边界
scenarios/index.ts 以ScenarioDefinition(id/fixture/isolationKey/tier/services/credentials/timeoutMs/assertions)声明 27 个场景,可按需归类:
- PR 层运行时场景(runtime fixture,180s):
minimal-agent(发布安装→构建→清单合法→产物搬移→源码无关→协议成功→stdout 仅协议→清理完成)、copied-artifact、mocked-tool-agent(mock 工具成功/拒绝未 mock/副作用缺席/先失败后成功)、resumable-workflow(挂起恢复 + 同步/异步 scorer)、process-cancellation、truncated-input; - PR 层资源场景(resources fixture,180s):
workspace-skill-agent(workspace 继承、skill 发现与提示注入)、workspace-sandbox(文件写入、沙箱命令、skill 列表)、sandbox-cancellation、persistence-isolation(配置初始化隔离、应用存储写入、vector adapter 执行、experiment/score 记录缺席); - full 层资源场景:
workspace-owned-override、workspace-dynamic(并发 item、同 key 一致、异 key 隔离、清理)、workspace-search(BM25/向量/混合检索)、workspace-mounts(多挂载路由、只读挂载)、workspace-lsp(语言服务器启动/hover/关闭)、workspace-browser(惰性启动、线程触发、CLI 执行、关闭)、workspace-failures(初始化/关闭失败上报、非法配置拒绝、干净退出); - 服务型场景:
postgres(300s,需 docker + postgres,验证应用状态持久化、experiment 持久化缺席、有界关闭、连接复用、docker 清理); - 项目形态场景(240s):
npm-minimal、yarn-minimal(验证 berry 的 node_modules 布局)、pnpm-monorepo(workspace 包导入)、portability-isolation(重复构建契约稳定、易变构建元数据、并发 worker、突然终止恢复、产物不可变)、kitchen-sink(重 import 构建、选中 agent/workflow 执行、studio 不启动)、native-duckdb(240s,原生依赖声明、可移植提升布局、原生 vector 执行); - 负向边界场景:
negative-malformed-approvals(非法 pnpm 审批诊断)、negative-missing-mastra、negative-import-failure(客户 import 诊断)。
报告与证据链
每个场景结束时,helpers/report.ts 按schemaVersion: 1写出<scenarioId>.json与<scenarioId>.md,内容包括 tier、fixture、isolationKey、包管理器版本、注册表 tag 与 artifact 摘要、断言级passed/failed/skipped状态与证据、起止时间与耗时、诊断信息;断言证据的落盘在 helpers/assertion-evidence.ts。minimal-agent测试还会把协议 NDJSON 转录与构建日志(含 stderr)一并写入报告目录,供失败时离线排查(tests/minimal-agent.test.ts)。报告结构与断言 schema 的一致性由 tests/scenario-reporter.test.ts 守护。
实际 fixture 长什么样
以 fixtures/runtime/src/mastra/index.ts 为例,一个典型 fixture 会在不联网的前提下构造确定性模型(doGenerate/doStream返回固定 token 统计与文本)、最小 Agent、带工具的 mock 模型 Agent(奇偶调用先返回tool-calls再返回文本)、25 秒慢模型 Agent(配合取消场景)、挂起/恢复的 approval step 工作流、同步与异步 scorer,最后export const mastra = new Mastra({ agents, workflows, scorers }),并在console.error输出minimal experiment fixture initialized作为初始化标记(测试断言 stderr 包含该标记)。这也是“无需真实模型、可复现、可离线跑 PR 门禁”的关键设计。
小结
experiment-workerE2E 套件把 Mastra 实验 Worker 的交付质量拆解为四层防线:发布型注册表摘要校验保证安装源可信;隔离安装 + 产物搬移 + 源引用扫描保证 artifact 与源码解耦;NDJSON 协议 + 最小环境保证 worker 进程边界确定;pr/full/gated 分层 + 断言级报告保证 OSS 验收确定性且不阻塞凭据敏感场景。理解这套结构,无论是要扩展新的实验场景、接入新的包管理器形态,还是复现 CI 失败,都能直接定位到 scenarios/index.ts、setup.ts 与对应 helpers 完成闭环。
【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考