mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
relax JSON schema validation (#9332)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
This commit is contained in:
@@ -0,0 +1,125 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { SchemaValidator } from './schemaValidator.js';
|
||||||
|
|
||||||
|
describe('SchemaValidator', () => {
|
||||||
|
it('should allow any params if schema is undefined', () => {
|
||||||
|
const params = {
|
||||||
|
foo: 'bar',
|
||||||
|
};
|
||||||
|
expect(SchemaValidator.validate(undefined, params)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects null params', () => {
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
foo: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(SchemaValidator.validate(schema, null)).toBe(
|
||||||
|
'Value of params must be an object',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects params that are not objects', () => {
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
foo: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(SchemaValidator.validate(schema, 'not an object')).toBe(
|
||||||
|
'Value of params must be an object',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows schema with extra properties', () => {
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
example_enum: {
|
||||||
|
type: 'string',
|
||||||
|
enum: ['FOO', 'BAR'],
|
||||||
|
// enum-descriptions is not part of the JSON schema spec.
|
||||||
|
// This test verifies that the SchemaValidator allows the
|
||||||
|
// use of extra keywords, like this one, in the schema.
|
||||||
|
'enum-descriptions': ['a foo', 'a bar'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const params = {
|
||||||
|
example_enum: 'BAR',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(SchemaValidator.validate(schema, params)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows custom format values', () => {
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
duration: {
|
||||||
|
type: 'string',
|
||||||
|
// See: https://cloud.google.com/docs/discovery/type-format
|
||||||
|
format: 'google-duration',
|
||||||
|
},
|
||||||
|
mask: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'google-fieldmask',
|
||||||
|
},
|
||||||
|
foo: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'something-totally-custom',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const params = {
|
||||||
|
duration: '10s',
|
||||||
|
mask: 'foo.bar,biz.baz',
|
||||||
|
foo: 'some value',
|
||||||
|
};
|
||||||
|
expect(SchemaValidator.validate(schema, params)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows valid values for known formats', () => {
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
today: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'date',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const params = {
|
||||||
|
today: '2025-04-08',
|
||||||
|
};
|
||||||
|
expect(SchemaValidator.validate(schema, params)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects invalid values for known formats', () => {
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
today: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'date',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const params = {
|
||||||
|
today: 'this is not a date',
|
||||||
|
};
|
||||||
|
expect(SchemaValidator.validate(schema, params)).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -9,7 +9,18 @@ import * as addFormats from 'ajv-formats';
|
|||||||
// Ajv's ESM/CJS interop: use 'any' for compatibility as recommended by Ajv docs
|
// Ajv's ESM/CJS interop: use 'any' for compatibility as recommended by Ajv docs
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const AjvClass = (AjvPkg as any).default || AjvPkg;
|
const AjvClass = (AjvPkg as any).default || AjvPkg;
|
||||||
const ajValidator = new AjvClass();
|
const ajValidator = new AjvClass(
|
||||||
|
// See: https://ajv.js.org/options.html#strict-mode-options
|
||||||
|
{
|
||||||
|
// strictSchema defaults to true and prevents use of JSON schemas that
|
||||||
|
// include unrecognized keywords. The JSON schema spec specifically allows
|
||||||
|
// for the use of non-standard keywords and the spec-compliant behavior
|
||||||
|
// is to ignore those keywords. Note that setting this to false also
|
||||||
|
// allows use of non-standard or custom formats (the unknown format value
|
||||||
|
// will be logged but the schema will still be considered valid).
|
||||||
|
strictSchema: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const addFormatsFunc = (addFormats as any).default || addFormats;
|
const addFormatsFunc = (addFormats as any).default || addFormats;
|
||||||
addFormatsFunc(ajValidator);
|
addFormatsFunc(ajValidator);
|
||||||
|
|||||||
Reference in New Issue
Block a user