拓冰建站拓冰建站
首页 / 资讯中心 / 正文

@composio/json-schema-to-zod 的 Zod v3 兼容性端到端测试指南

composio/json-schema-to-zod 的 Zod v3 兼容性端到端测试指南【免费下载链接】composioComposio powers 1000 toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio在 Composio 的 monorepo 中composio/json-schema-to-zod负责把 JSON Schemadraft 4在运行时转换为 Zod 校验对象是工具参数 schema 与各 Agent 框架OpenAI、Claude、LangChain 等之间最重要的桥接层之一。由于生态中既有项目仍大量依赖 Zod v3而新项目已开始迁移到 Zod v4该包必须同时兼容两个大版本。本文以 ts/e2e-tests/runtimes/node/json-schema-to-zod-v3/README.md 为骨架结合测试用例 e2e.test.ts 与转换器源码 parse-object.ts完整讲解这套 Zod v3 兼容性端到端测试套件的目标、运行方式、覆盖场景与底层实现原理。读完本文你将掌握如何在多 Node.js 版本下验证 schema 转换的正确性理解additionalProperties在 JSON Schema → Zod → JSON Schema 往返过程中的语义保持策略并能复现整套测试。为什么需要 Zod v3 兼容性验证composio/json-schema-to-zod包必须同时支持 Zod v3 与 Zod v4 两个大版本。Zod v3 与 v4 在 API 表面、类型系统如深度类型实例化限制和行为细节上存在差异仅做单元测试无法覆盖在真实运行时环境中、与zod-to-json-schema等生态库协作的场景。因此 Composio 在端到端测试目录下单独建立了一个json-schema-to-zod-v3测试套件专门用于验证该包在zod3.25.76下的表现。该套件明确验证四类核心能力引自 README.mdJSON Schema 到 Zod 的转换在 Zod v3 下正常工作所有 schema 类型string、object、array、anyOf 等都能正确转换往返转换JSON Schema → Zod → JSON Schema保持语义不变additionalProperties的处理行为正确。测试套件本身位于 monorepo 的ts/e2e-tests/runtimes/node/目录下属于按运行时 依赖组合组织的端到端测试矩阵。其包名为e2e-tests/node-json-schema-to-zod-v3见 package.json。测试套件结构它覆盖了哪些场景README 中的测试清单表格完整对应 e2e.test.ts 中的两个describe块Basic functionality 与 Round-trip conversion覆盖场景如下测试说明基础 string schema转换{ type: string }并执行校验object schema必填字段、嵌套属性、校验约束array schema带校验的强类型数组元素email 格式对字符串做 email format 校验嵌套 schema复杂嵌套对象与数组anyOf schema联合类型转换往返转换JSON Schema → Zod → JSON Schema 保持additionalProperties语义以上 6 项基础能力测试对应 e2e.test.ts往返转换测试对应 e2e.test.ts。测试环境与运行方式直接在 Bun 中运行无需 Docker fixtures与其他需要 fixture 文件的 e2e 套件不同本套件直接在 Bun 中运行测试文件从 monorepo workspace 中导入两个关键依赖import { jsonSchemaToZod, type JsonSchema } from composio/json-schema-to-zod; import zodToJsonSchema from zod-to-json-schema;见 e2e.test.ts依赖版本在 package.json 中锁定composio/json-schema-to-zod:workspace:*始终使用 monorepo 当前源码zod:3.25.76被验证的目标版本zod-to-json-schema:catalog:由 workspace 目录统一管理版本。测试框架与入口测试使用bun:test断言describe/it/expect并通过e2e-tests/utils提供的e2e()辅助函数接入整套隔离运行基础设施e2e(import.meta.url, { versions: { node: [22.22.3, 24.17.0, 25.9.0] }, defineTests: () { // describe / it / expect 断言块 }, });见 e2e.test.tse2e()的实现位于 ts/e2e-tests/_utils/src/e2e.ts它会校验传入的import.meta.url是合法file://URL然后从调用者所在目录推断出相对于仓库根的 cwd 与 suiteName再交给底层的runE2E()在 Docker 容器内依次按各运行时版本执行。隔离工具Docker 多版本 Node.jsREADME 明确说明隔离工具为Docker覆盖三个 Node.js 版本22.22.3、24.17.0、25.9.0。这三个版本是e2e-tests/utils中预定义的 well-known 版本见 ts/e2e-tests/_utils/README.md 的 Well-Known Node Versions 一节。运行基础设施会为每个 Node 版本构建对应的 Docker 镜像在隔离容器内执行测试命令按版本顺序串行运行对容器卷做 best-effort 清理。运行命令pnpm test:e2e引自 README.md 的 Running 一节在套件目录内package.json 还提供了两个等价脚本test:e2e与test:e2e:node均执行bun test e2e.test.ts另有typecheck脚本tsc --noEmit用于静态类型检查。对应的 tsconfig.json 采用es2022目标与moduleResolution: bundler。运行结果会写入DEBUG.log按 Node 版本分组输出各阶段的 setup 命令、stdout/stderr 与通过/失败汇总该机制定义于 ts/e2e-tests/_utils/README.md。基础转换测试逐项拆解下面逐一展开 e2e.test.ts 中的 6 个基础能力用例每个用例都遵循同一模式定义 JSON Schema → 调用jsonSchemaToZod→ 用.parse()验证合法数据通过、非法数据抛错。1. 基础 string schemaconst schema: JsonSchema { type: string }; const zodSchema jsonSchemaToZod(schema); expect(zodSchema.parse(hello)).toBe(hello); expect(() zodSchema.parse(123)).toThrow();见 e2e.test.ts最简场景字符串通过、数字抛错验证了最基础的type映射。2. 带校验约束的 object schemaconst schema: JsonSchema { type: object, properties: { name: { type: string }, age: { type: number, minimum: 0 }, }, required: [name], }; const zodSchema jsonSchemaToZod(schema); expect(zodSchema.parse({ name: John, age: 30 })).toEqual({ name: John, age: 30 }); expect(zodSchema.parse({ name: John })).toEqual({ name: John }); expect(() zodSchema.parse({ age: 30 })).toThrow();见 e2e.test.ts该用例验证三点必填字段required中的name缺失即抛错、数值约束minimum: 0被转换为 Zod 的.min()校验、非必填字段可省略。3. array schemaconst schema: JsonSchema { type: array, items: { type: string }, }; const zodSchema jsonSchemaToZod(schema); expect(zodSchema.parse([one, two, three])).toEqual([one, two, three]); expect(() zodSchema.parse([one, 2])).toThrow();见 e2e.test.ts验证数组元素类型被强约束items声明为 string 后混入数字即失败。4. email 格式校验const schema: JsonSchema { type: string, format: email, }; const zodSchema jsonSchemaToZod(schema); expect(zodSchema.parse(testexample.com)).toBe(testexample.com); expect(() zodSchema.parse(invalid-email)).toThrow();见 e2e.test.tsformat: email被转换为 Zod 的z.string().email()合法邮箱通过、非法字符串抛错。5. 复杂嵌套 schemaconst schema: JsonSchema { type: object, properties: { user: { type: object, properties: { name: { type: string }, contacts: { type: array, items: { type: object, properties: { type: { type: string }, value: { type: string }, }, required: [type, value], }, }, }, required: [name], }, }, required: [user], };见 e2e.test.ts构造了「对象 → 对象 → 数组 → 对象」的四层嵌套并在每一层标注required。合法的数据形如const validData { user: { name: Jane Doe, contacts: [ { type: email, value: janeexample.com }, { type: phone, value: 555-1234 }, ], }, }; expect(zodSchema.parse(validData)).toEqual(validData);见 e2e.test.ts6. anyOf 联合类型const schema: JsonSchema { anyOf: [{ type: string }, { type: number }], }; const zodSchema jsonSchemaToZod(schema); expect(zodSchema.parse(hello)).toBe(hello); expect(zodSchema.parse(42)).toBe(42); expect(() zodSchema.parse(true)).toThrow();见 e2e.test.tsanyOf被转换为 Zod 联合类型union字符串、数字各自通过布尔值不在联合内因此抛错。往返转换additionalProperties 语义的完整验证往返转换round-trip是这套测试最有价值的部分。它验证的不只是转出来能用而是JSON Schema → Zod → JSON Schemazod-to-json-schema的完整链条上additionalProperties的语义不丢失。这直接关系到工具 schema 的严格/宽松行为例如 OpenAI 等 Provider 对未知参数的处理策略。用例 1空对象 additionalProperties: trueconst schema: JsonSchema { type: object, additionalProperties: true, }; const zodSchema jsonSchemaToZod(schema); const convertedBack zodToJsonSchema(zodSchema, { target: jsonSchema7 }); expect(convertedBack.additionalProperties).toBe(true); expect(convertedBack.type).toBe(object); expect(zodSchema.parse({})).toEqual({}); expect(zodSchema.parse({ any: value, number: 123 })).toEqual({ any: value, number: 123 });见 e2e.test.ts注意测试中的两处ts-expect-error由于zod-to-json-schema返回类型与 Zod v3 深度类型实例化的限制测试需要显式放宽类型这本身就是 Zod v3 环境下真实存在的类型兼容问题。用例 2含命名属性 additionalProperties: trueconst schema: JsonSchema { type: object, properties: { name: { type: string }, age: { type: number } }, required: [name], additionalProperties: true, }; const zodSchema jsonSchemaToZod(schema); const convertedBack zodToJsonSchema(zodSchema, { target: jsonSchema7 }); expect(convertedBack.additionalProperties).toBe(true); expect(convertedBack.properties).toBeDefined(); expect(convertedBack.required).toEqual([name]); expect(zodSchema.parse({ name: John, age: 30, extra: field })).toEqual({ name: John, age: 30, extra: field, });见 e2e.test.tsadditionalProperties: true意味着未知字段应当被允许且原样保留往返后仍为true且命名属性与required均被保留。用例 3空对象 additionalProperties: falseconst schema: JsonSchema { type: object, additionalProperties: false, }; const zodSchema jsonSchemaToZod(schema); const convertedBack zodToJsonSchema(zodSchema, { target: jsonSchema7 }); const additionalPropsValid convertedBack.additionalProperties false || (convertedBack.not typeof convertedBack.not object); expect(additionalPropsValid).toBe(true); expect(convertedBack.type).toBe(object); expect(zodSchema.parse({})).toEqual({}); expect(() zodSchema.parse({ extra: field })).toThrow();见 e2e.test.ts这是一个很能体现语义保持策略的用例测试注释明确说明zod-to-json-schema可能把z.object({}).strict()转换为{ not: {} }——这在语义上等价于additionalProperties: false。因此断言允许两种合法形态之一既验证行为正确又不绑定实现细节。用例 4含命名属性 additionalProperties: falseconst schema: JsonSchema { type: object, properties: { name: { type: string } }, additionalProperties: false, }; const zodSchema jsonSchemaToZod(schema); const convertedBack zodToJsonSchema(zodSchema, { target: jsonSchema7 }); expect(convertedBack.additionalProperties).toBe(false); expect(convertedBack.properties).toBeDefined(); expect(zodSchema.parse({ name: John })).toEqual({ name: John }); expect(() zodSchema.parse({ name: John, extra: field })).toThrow();见 e2e.test.ts命名属性存在时additionalProperties: false精确地往返为false多余字段被拒绝。用例 5additionalProperties带类型 schemaconst schema: JsonSchema { type: object, properties: { name: { type: string } }, additionalProperties: { type: number }, }; const zodSchema jsonSchemaToZod(schema); const convertedBack zodToJsonSchema(zodSchema, { target: jsonSchema7 }); expect(convertedBack.additionalProperties).toEqual({ type: number }); expect(convertedBack.properties).toBeDefined(); expect(zodSchema.parse({ name: John, age: 30 })).toEqual({ name: John, age: 30 }); expect(() zodSchema.parse({ name: John, extra: field })).toThrow();见 e2e.test.tsadditionalProperties不仅可以是布尔值还可以是完整 schema。这里未知键age因符合{ type: number }而通过extra: field因不是数字而被拒绝且往返后additionalProperties精确恢复为{ type: number }。用例 6嵌套对象中不同的 additionalProperties 组合const schema: JsonSchema { type: object, properties: { strictChild: { type: object, properties: { name: { type: string } }, additionalProperties: false, }, flexibleChild: { type: object, properties: { age: { type: number } }, additionalProperties: true, }, }, additionalProperties: { type: string }, }; const zodSchema jsonSchemaToZod(schema); const convertedBack zodToJsonSchema(zodSchema, { target: jsonSchema7 }); expect(convertedBack.additionalProperties).toEqual({ type: string }); const convertedProperties convertedBack.properties as Recordstring, unknown; expect(convertedProperties?.strictChild).toBeDefined(); expect(convertedProperties?.flexibleChild).toBeDefined();见 e2e.test.ts最复杂的场景同一层additionalProperties: { type: string }、子对象 AadditionalProperties: false严格、子对象 BadditionalProperties: true宽松三个层级的策略互不影响。合法数据要求extraString是字符串而extraNumber: 123会被拒绝strictChild内的额外键会被拒绝flexibleChild内的额外键则被允许见 e2e.test.ts。源码级实现additionalProperties 是如何映射到 Zod 的往返测试之所以能成立根源在于转换器 parse-object.ts 对additionalProperties的精细处理。该文件是composio/json-schema-to-zod包的核心实现之一包目录见 ts/packages/json-schema-to-zod。jsonSchemaToZod的入口在 json-schema-to-zod.ts它调用parseSchema递归转换并在需要整 schema 校验时requiresWholeSchemaValidation用withWholeSchemaValidation包裹结果。同文件还导出jsonSchemaToZodShapejson-schema-to-zod.ts用于需要ZodRawShape而非完整ZodType的场景例如 Claude Agent SDK 的tool()函数。在 parse-object.ts 中additionalProperties的决策逻辑可归纳为输入条件Zod 输出有命名属性 additionalProperties: false解析为ZodNeverpropertiesSchema.strict()有命名属性 additionalProperties: true显式propertiesSchema.passthrough()有命名属性 additionalProperties为 schemapropertiesSchema.catchall(schema)有命名属性 省略additionalPropertiespropertiesSchema.strict()对工具输入保持严格无命名属性 additionalProperties: falsez.object({}).strict()无命名属性 additionalProperties: true显式z.object({}).passthrough()无命名属性 additionalProperties为 schemaz.record(schema)无命名属性 省略additionalPropertiesz.object({}).passthrough()开放式对象几个值得注意的实现细节均可从源码注释确认省略时的默认严格带命名属性却省略additionalProperties时转换器故意生成.strict()而不是遵循 JSON Schema 原生默认允许未知键的宽松语义。源码注释parse-object.ts说明这是为了工具输入tool inputs场景而刻意收紧防止模型传入拼写错误的参数被静默吞掉。空对象回退的历史教训parseObjectProperties的注释parse-object.ts记录了此前z.object({})回退导致的缺陷——把{ type: object }错误地塌缩成严格空对象会破坏 OpenAI 等 Provider 的响应处理。因此无命名属性的对象现在采用开放式策略且 OpenAI Provider 测试套件被纳入该包的验证范围。patternProperties的动态键处理当 schema 含patternProperties时走parseDynamicKeyObjectparse-object.ts每个键只路由到匹配的模式或additionalProperties避免键通过满足一个不相关模式而漏过自身模式的问题未匹配且被拒绝的键会以unrecognized_keys错误列出。正是这套决策树保证了 e2e.test.ts 中 6 个往返用例的断言全部成立。版本演进与测试矩阵定位该套件与composio/json-schema-to-zod的发布保持同步。从 CHANGELOG.md 可见0.0.1跟随composio/json-schema-to-zod0.3.10.0.2跟随composio/json-schema-to-zod0.3.2。这意味着每当转换器包升级本套件都会随 monorepo 的 changeset 机制联动更新确保持续在真实 Zod v3 环境回归。在整个 e2e 矩阵中本套件属于按依赖组合划分的兼容性系列——例如 ts/e2e-tests/_utils/README.md 的 DEBUG.log 示例中还展示了同系列的openai-zod4-compat套件。它们共享同一套e2e()基础设施多版本运行时隔离、setup/fixture 两阶段执行、按版本分组的DEBUG.log输出包含各阶段的命令、stdout/stderr、耗时与 PASS/FAIL 汇总。如果你需要为其他依赖组合新增类似的兼容性验证直接在ts/e2e-tests/runtimes/node/下仿照本套件的目录结构e2e.test.tspackage.jsontsconfig.jsonREADME.md新建套件即可e2e()会自动推断工作目录与套件名。总结如何复现与扩展这套验证一句话总结这套测试的价值它把JSON Schema 转换器与 Zod 生态的兼容性从隐式假设变成了显式、可重复、跨 Node 版本的机器验证。本地复现步骤在仓库根目录安装依赖monorepo 使用 pnpm workspace进入套件目录ts/e2e-tests/runtimes/node/json-schema-to-zod-v3执行pnpm test:e2e等价于bun test e2e.test.ts或先执行pnpm typecheck做静态检查测试将在 Docker 中以 Node.js22.22.3、24.17.0、25.9.0三个版本分别运行结果汇总到DEBUG.log。若想扩展覆盖范围可以沿用相同的断言模式补充新的 schema 形态例如oneOf、allOf、patternProperties的往返或在versions配置中加入新的 Node.js 版本参考 ts/e2e-tests/_utils/README.md 的版本解析优先级COMPOSIO_E2E_NODE_VERSION环境变量 config.versions.nodemise.toml默认值。需要深入理解转换语义时建议对照阅读 parse-object.ts 与包内的单元测试ts/packages/json-schema-to-zod/test端到端测试与源码实现互为印证。【免费下载链接】composioComposio powers 1000 toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门