evOo-local/packages/adapters/src/pi-runtime-tool-schema.test.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

109 lines
3.4 KiB
TypeScript

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();
});
});