Files
OmniRoute/tests/unit/gemini-schema-recursive-type.test.ts
Praveen K Palaniswamy 65e81158ab fix(ollama): route models by advertised capability (#11088)
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
2026-08-23 11:45:01 -03:00

143 lines
4.4 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
const { cleanJSONSchemaForAntigravity } = await import(
"../../open-sse/translator/helpers/geminiHelper.ts"
);
test("#9268 injects type:object on nested properties without type", () => {
const input = {
type: "object",
properties: {
name: { type: "string" },
address: {
// nested node with properties but NO type — should get type:object
properties: {
street: { type: "string" },
city: { type: "string" },
},
},
},
required: ["name"],
};
const result = cleanJSONSchemaForAntigravity(input) as Record<string, unknown>;
const props = result.properties as Record<string, unknown>;
const address = props.address as Record<string, unknown>;
assert.equal(address.type, "object", "nested object with properties must get type:object");
});
test("#9268 injects type:object on nested items array schemas", () => {
const input = {
type: "object",
properties: {
items: {
type: "array",
items: {
// array items schema with properties but NO type
properties: {
id: { type: "integer" },
label: { type: "string" },
},
},
},
},
};
const result = cleanJSONSchemaForAntigravity(input) as Record<string, unknown>;
const props = result.properties as Record<string, unknown>;
const items = props.items as Record<string, unknown>;
const inner = items.items as Record<string, unknown>;
assert.equal(inner.type, "object", "array items schema with properties must inject type:object");
});
test("#9268 injects type:object on deeply nested schemas (3+ levels)", () => {
const input = {
type: "object",
properties: {
level1: {
properties: {
level2: {
properties: {
level3: {
properties: {
value: { type: "string" },
},
},
},
},
},
},
},
};
const result = cleanJSONSchemaForAntigravity(input) as Record<string, unknown>;
const l1 = (result.properties as Record<string, unknown>).level1 as Record<string, unknown>;
const l2 = (l1.properties as Record<string, unknown>).level2 as Record<string, unknown>;
const l3 = (l2.properties as Record<string, unknown>).level3 as Record<string, unknown>;
assert.equal(l1.type, "object", "level1 must have type:object");
assert.equal(l2.type, "object", "level2 must have type:object");
assert.equal(l3.type, "object", "level3 must have type:object");
});
test("#9268 schema already typed is not double-injected", () => {
const input = {
type: "object",
properties: {
nested: {
type: "object",
properties: {
x: { type: "string" },
},
},
},
};
const result = cleanJSONSchemaForAntigravity(input) as Record<string, unknown>;
const nested = (result.properties as Record<string, unknown>).nested as Record<string, unknown>;
assert.equal(nested.type, "object", "already-typed nested must keep its type");
// Ensure properties is not clobbered
const nestedProps = nested.properties as Record<string, unknown>;
assert.ok(nestedProps, "nested properties must be preserved");
assert.ok("x" in nestedProps, "nested property 'x' must exist");
});
test("#9268 node with required but no properties still gets type:object", () => {
// Edge case: a node that has `required` but no `type` and no `properties`
// should still get type:object injection (Gemini needs it).
const input = {
type: "object",
properties: {
ref: {
// has required but no type nor properties (e.g. an incomplete $ref stub)
required: ["id"],
},
},
};
const result = cleanJSONSchemaForAntigravity(input) as Record<string, unknown>;
const ref = (result.properties as Record<string, unknown>).ref as Record<string, unknown>;
assert.equal(ref.type, "object", "node with required but no type must get type:object");
});
test("#9268 null/undefined fields do not crash the normalizer", () => {
const input = {
type: "object",
properties: {
a: null,
b: undefined,
// @ts-expect-error - testing runtime resilience
c: { properties: null },
},
};
assert.doesNotThrow(() => {
cleanJSONSchemaForAntigravity(input);
}, "null/undefined fields must not crash the normalizer");
});