test(cliproxy): add edge case coverage for Gemini schema sanitizer

Add tests for: exclusiveMinimum/exclusiveMaximum/multipleOf stripping,
uniqueItems/contains/additionalItems stripping, writeOnly/definitions
stripping, example with array values, default with complex nested
objects, empty properties/anyOf edge cases.

Fix proxy log message to say "Gemini-unsupported" instead of
"non-standard".
This commit is contained in:
kaitranntt
2026-02-07 06:25:29 -05:00
parent 505d6d0f11
commit 917f0bbef7
2 changed files with 67 additions and 1 deletions
+1 -1
View File
@@ -218,7 +218,7 @@ export class ToolSanitizationProxy {
for (const entry of schemaResult.removedByTool) {
this.writeLog(
'warn',
`[tool-sanitization-proxy] Schema sanitized for "${entry.name}": removed ${entry.removed.length} non-standard properties`
`[tool-sanitization-proxy] Schema sanitized for "${entry.name}": removed ${entry.removed.length} Gemini-unsupported properties`
);
}
this.log(
@@ -305,6 +305,72 @@ describe('sanitizeInputSchema', () => {
expect(result.schema).toEqual({ type: 'object' });
});
test('strips number/array/object validation fields unsupported by Gemini', () => {
const schema = {
type: 'object',
properties: {
score: {
type: 'number',
exclusiveMinimum: 0,
exclusiveMaximum: 100,
multipleOf: 5,
},
tags: {
type: 'array',
items: { type: 'string' },
uniqueItems: true,
contains: { type: 'string' },
additionalItems: false,
},
meta: {
type: 'object',
writeOnly: true,
definitions: { addr: { type: 'string' } },
},
},
};
const result = sanitizeInputSchema(schema);
expect(result.removedPaths).toContain('properties.score.exclusiveMinimum');
expect(result.removedPaths).toContain('properties.score.exclusiveMaximum');
expect(result.removedPaths).toContain('properties.score.multipleOf');
expect(result.removedPaths).toContain('properties.tags.uniqueItems');
expect(result.removedPaths).toContain('properties.tags.contains');
expect(result.removedPaths).toContain('properties.tags.additionalItems');
expect(result.removedPaths).toContain('properties.meta.writeOnly');
expect(result.removedPaths).toContain('properties.meta.definitions');
expect(result.removedCount).toBe(8);
});
test('preserves example/default with complex values without recursion', () => {
const schema = {
type: 'object',
example: ['value1', 'value2'],
default: { nested: { deep: { unsupportedKey: true } } },
};
const result = sanitizeInputSchema(schema);
expect(result.removedCount).toBe(0);
expect(result.schema.example).toEqual(['value1', 'value2']);
expect(result.schema.default).toEqual({ nested: { deep: { unsupportedKey: true } } });
});
test('handles empty properties and anyOf edge cases', () => {
const schema = {
type: 'object',
properties: {},
anyOf: [],
};
const result = sanitizeInputSchema(schema);
expect(result.removedCount).toBe(0);
expect(result.schema.properties).toEqual({});
expect(result.schema.anyOf).toEqual([]);
});
test('preserves all Gemini-supported fields', () => {
const schema = {
type: 'object',