tynder
Version:
TypeScript friendly Data validator for JavaScript.
387 lines (344 loc) • 14.2 kB
text/typescript
// tslint:disable-next-line: no-implicit-dependencies no-var-requires
const Ajv = require('ajv');
import { ValidationContext } from '../types';
import { validate,
getType } from '../validator';
import { pick,
patch } from '../picker';
import { compile } from '../compiler';
import { generateJsonSchemaObject } from '../codegen';
import { serialize, deserialize } from '../serializer';
import * as op from '../operators';
describe("json-schema-1", function() {
it("json-schema-1-1", function() {
const z = compile(`
type Foo = string;
interface A {
a: Foo;
@range(3, 5)
b: number;
@maxLength(3)
c: Foo;
[propNames: /^[e-f]$/]: number;
i?: string;
}
`);
const schema = generateJsonSchemaObject(z);
const ajv = new Ajv({allErrors: true});
// tslint:disable-next-line: no-implicit-dependencies
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
const ajvValidate = ajv.addSchema(schema).getSchema('#/definitions/A');
expect(ajvValidate({a: 'z', b: 5, c: '123'})).toEqual(true);
expect(ajvValidate({b: 5, c: '123'})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'a' },
message: 'should have required property \'a\'',
}]);
expect(ajvValidate({a: 1, b: 5, c: '123'})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'type',
dataPath: '.a',
schemaPath: '#/definitions/Foo/type',
params: { type: 'string' },
message: 'should be string',
}]);
expect(ajvValidate({a: 'z', b: 6, c: '123'})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'maximum',
dataPath: '.b',
schemaPath: '#/properties/b/maximum',
params: { comparison: '<=', limit: 5, exclusive: false },
message: 'should be <= 5',
}]);
expect(ajvValidate({a: 'z', b: 5, c: '1234'})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'maxLength',
dataPath: '.c',
schemaPath: '#/properties/c/maxLength',
params: { limit: 3 },
message: 'should NOT be longer than 3 characters',
}]);
expect(ajvValidate({b: 6, c: '1234'})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'a' },
message: 'should have required property \'a\'',
}, {
keyword: 'maximum',
dataPath: '.b',
schemaPath: '#/properties/b/maximum',
params: { comparison: '<=', limit: 5, exclusive: false },
message: 'should be <= 5',
}, {
keyword: 'maxLength',
dataPath: '.c',
schemaPath: '#/properties/c/maxLength',
params: { limit: 3 },
message: 'should NOT be longer than 3 characters',
}]);
expect(ajvValidate({a: 'z', b: 5, c: '123', d: 11})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'additionalProperties',
dataPath: '',
schemaPath: '#/additionalProperties',
params: { additionalProperty: 'd' },
message: 'should NOT have additional properties',
}]);
expect(ajvValidate({a: 'z', b: 5, c: '123', d: '11'})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'additionalProperties',
dataPath: '',
schemaPath: '#/additionalProperties',
params: { additionalProperty: 'd' },
message: 'should NOT have additional properties',
}]);
expect(ajvValidate({a: 'z', b: 5, c: '123', e: 11})).toEqual(true);
expect(ajvValidate({a: 'z', b: 5, c: '123', e: '11'})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'type',
dataPath: '[\'e\']',
schemaPath: '#/patternProperties/%5E%5Be-f%5D%24/type',
params: { type: 'number' },
message: 'should be number',
}]);
expect(ajvValidate({a: 'z', b: 5, c: '123', i: '11'})).toEqual(true);
expect(ajvValidate({a: 'z', b: 5, c: '123', i: 11})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'type',
dataPath: '.i',
schemaPath: '#/properties/i/type',
params: { type: 'string' },
message: 'should be string',
}]);
});
it("json-schema-1-2", function() {
const z = compile(`
type Foo = string;
interface A {
a: Foo;
@range(3, 5)
b: number;
@maxLength(3)
c: Foo;
[propNames: /^[e-f]$/]: number;
i?: string;
}
interface B {
x: A;
}
`);
const schema = generateJsonSchemaObject(z);
const ajv = new Ajv({allErrors: true});
// tslint:disable-next-line: no-implicit-dependencies
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
const ajvValidate = ajv.addSchema(schema).getSchema('#/definitions/B');
expect(ajvValidate({x: {a: 'z', b: 5, c: '123'}})).toEqual(true);
expect(ajvValidate({x: {b: 5, c: '123'}})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'required',
dataPath: '.x',
schemaPath: '#/required',
params: { missingProperty: 'a' },
message: 'should have required property \'a\'',
}]);
expect(ajvValidate({x: {a: 1, b: 5, c: '123'}})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'type',
dataPath: '.x.a',
schemaPath: '#/definitions/Foo/type',
params: { type: 'string' },
message: 'should be string',
}]);
});
it("json-schema-1-3", function() {
const z = compile(`
type Foo = string;
type A = {
a: Foo,
@range(3, 5)
b: number,
@maxLength(3)
c: Foo,
[propNames: /^[e-f]$/]: number,
i?: string,
};
type B = {
x: A,
};
`);
const schema = generateJsonSchemaObject(z);
const ajv = new Ajv({allErrors: true});
// tslint:disable-next-line: no-implicit-dependencies
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
const ajvValidate = ajv.addSchema(schema).getSchema('#/definitions/B');
expect(ajvValidate({x: {a: 'z', b: 5, c: '123'}})).toEqual(true);
expect(ajvValidate({x: {b: 5, c: '123'}})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'required',
dataPath: '.x',
schemaPath: '#/required',
params: { missingProperty: 'a' },
message: 'should have required property \'a\'',
}]);
expect(ajvValidate({x: {a: 1, b: 5, c: '123'}})).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'type',
dataPath: '.x.a',
schemaPath: '#/definitions/Foo/type',
params: { type: 'string' },
message: 'should be string',
}]);
});
it("json-schema-1-4", function() {
const z = compile(`
type Foo = string|number;
`);
const schema = generateJsonSchemaObject(z);
const ajv = new Ajv({allErrors: true});
// tslint:disable-next-line: no-implicit-dependencies
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
const ajvValidate = ajv.addSchema(schema).getSchema('#/definitions/Foo');
expect(ajvValidate(11)).toEqual(true);
expect(ajvValidate('11')).toEqual(true);
expect(ajvValidate(true)).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'type',
dataPath: '',
schemaPath: '#/anyOf/0/type',
params: { type: 'string' },
message: 'should be string',
}, {
keyword: 'type',
dataPath: '',
schemaPath: '#/anyOf/1/type',
params: { type: 'number' },
message: 'should be number',
}, {
keyword: 'anyOf',
dataPath: '',
schemaPath: '#/anyOf',
params: {},
message: 'should match some schema in anyOf',
}]);
});
it("json-schema-1-5", function() {
const z = compile(`
type Foo = number[];
`);
const schema = generateJsonSchemaObject(z);
const ajv = new Ajv({allErrors: true});
// tslint:disable-next-line: no-implicit-dependencies
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
const ajvValidate = ajv.addSchema(schema).getSchema('#/definitions/Foo');
expect(ajvValidate([11, 13])).toEqual(true);
expect(ajvValidate([11, 13, '17'])).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'type',
dataPath: '[2]',
schemaPath: '#/items/type',
params: { type: 'number' },
message: 'should be number',
}]);
});
it("json-schema-1-6", function() {
const z = compile(`
type Foo = (number|boolean)[];
`);
const schema = generateJsonSchemaObject(z);
const ajv = new Ajv({allErrors: true});
// tslint:disable-next-line: no-implicit-dependencies
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
const ajvValidate = ajv.addSchema(schema).getSchema('#/definitions/Foo');
expect(ajvValidate([11, 13, false])).toEqual(true);
expect(ajvValidate([11, 13, '17'])).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'type',
dataPath: '[2]',
schemaPath: '#/items/anyOf/0/type',
params: { type: 'number' },
message: 'should be number',
}, {
keyword: 'type',
dataPath: '[2]',
schemaPath: '#/items/anyOf/1/type',
params: { type: 'boolean' },
message: 'should be boolean',
}, {
keyword: 'anyOf',
dataPath: '[2]',
schemaPath: '#/items/anyOf',
params: {},
message: 'should match some schema in anyOf',
}]);
});
it("json-schema-1-6", function() {
const z = compile(`
type Foo = [number, boolean];
`);
const schema = generateJsonSchemaObject(z);
const ajv = new Ajv({allErrors: true});
// tslint:disable-next-line: no-implicit-dependencies
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
const ajvValidate = ajv.addSchema(schema).getSchema('#/definitions/Foo');
expect(ajvValidate([11, false])).toEqual(true); // NOTE: In JSON Schema, compile to `(number | boolean)[]`
expect(ajvValidate([false, 11, 13])).toEqual(true);
expect(ajvValidate([11, 13, '17'])).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'type',
dataPath: '[2]',
schemaPath: '#/items/anyOf/0/type',
params: { type: 'number' },
message: 'should be number',
}, {
keyword: 'type',
dataPath: '[2]',
schemaPath: '#/items/anyOf/1/type',
params: { type: 'boolean' },
message: 'should be boolean',
}, {
keyword: 'anyOf',
dataPath: '[2]',
schemaPath: '#/items/anyOf',
params: {},
message: 'should match some schema in anyOf',
}]);
});
it("json-schema-1-7", function() {
const z = compile(`
enum Foo {
A = 'a',
B = 10,
C = 'b',
}
`);
const schema = generateJsonSchemaObject(z);
const ajv = new Ajv({allErrors: true});
// tslint:disable-next-line: no-implicit-dependencies
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
const ajvValidate = ajv.addSchema(schema).getSchema('#/definitions/Foo');
expect(ajvValidate('a')).toEqual(true);
expect(ajvValidate(10)).toEqual(true);
expect(ajvValidate('b')).toEqual(true);
expect(ajvValidate(11)).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'enum',
dataPath: '',
schemaPath: '#/enum',
params: { allowedValues: ['a', 10, 'b'] },
message: 'should be equal to one of the allowed values',
}]);
expect(ajvValidate('c')).toEqual(false);
expect(ajvValidate.errors).toEqual([{
keyword: 'enum',
dataPath: '',
schemaPath: '#/enum',
params: { allowedValues: ['a', 10, 'b'] },
message: 'should be equal to one of the allowed values',
}]);
});
});