import { describe, expect, it } from "vitest"; import { parseConnectorToolArgs } from "./lazy-tool-catalog.js"; import { jsonSchemaParameters, parametersFor } from "./pi-runtime.js"; describe("jsonSchemaParameters", () => { it("keeps model-facing nullable parameters compatible with connector validation", () => { const tool = { name: "catalog_lookup", description: "Look up catalog entries", inputSchema: { type: "object", properties: { enabled: { type: ["boolean", "null"] } }, required: ["enabled"], }, }; const wire = JSON.parse(JSON.stringify(parametersFor(tool))); for (const enabled of [true, false, null]) { expect(parseConnectorToolArgs(wire, { enabled })).toEqual( parseConnectorToolArgs(tool.inputSchema, { enabled }), ); } expect(() => parseConnectorToolArgs(wire, { enabled: "false" })).toThrow(); expect(() => parseConnectorToolArgs(wire, {})).toThrow(); }); it("preserves nullable object fields instead of advertising strings", () => { const schema = jsonSchemaParameters({ type: "object", properties: { filter: { type: ["object", "null"], properties: { enabled: { type: "boolean" } }, required: ["enabled"], additionalProperties: false, }, }, required: ["filter"], }); expect(JSON.parse(JSON.stringify(schema))).toEqual({ type: "object", required: ["filter"], properties: { filter: { anyOf: [ { type: "object", properties: { enabled: { type: "boolean" } }, required: ["enabled"], additionalProperties: false, }, { type: "null" }, ], }, }, }); }); it("preserves null branches in anyOf", () => { const schema = jsonSchemaParameters({ type: "object", properties: { cursor: { anyOf: [{ type: "string" }, { type: "null" }] } }, }); expect(JSON.parse(JSON.stringify(schema)).properties.cursor).toEqual({ anyOf: [{ type: "string" }, { type: "null" }], }); }); it("preserves nullable array items and array constraints", () => { const schema = jsonSchemaParameters({ type: "object", properties: { values: { type: "array", items: { type: ["boolean", "null"] }, minItems: 1 }, }, }); expect(JSON.parse(JSON.stringify(schema)).properties.values).toEqual({ type: "array", minItems: 1, items: { anyOf: [{ type: "boolean" }, { type: "null" }] }, }); }); it("keeps primitive enums as literal unions", () => { const schema = jsonSchemaParameters({ type: "object", properties: { mode: { type: "string", enum: ["fast", "slow"] } }, required: ["mode"], }) as unknown as { properties: { mode: { anyOf: { const: unknown }[] } } }; expect(schema.properties.mode.anyOf.map((member) => member.const)).toEqual(["fast", "slow"]); }); it("accepts a nullable enum without throwing", () => { expect(() => jsonSchemaParameters({ type: "object", properties: { cursor: { type: ["string", "null"], enum: ["a", "b", null] } }, }), ).not.toThrow(); }); it("accepts enums whose members are objects or arrays", () => { expect(() => jsonSchemaParameters({ type: "object", properties: { filter: { type: "object", enum: [{ kind: "all" }, ["x"]] } }, }), ).not.toThrow(); }); });