628 lines
16 KiB
JavaScript
628 lines
16 KiB
JavaScript
'use strict';
|
|
|
|
var Ajv = require('./ajv')
|
|
, should = require('./chai').should();
|
|
|
|
|
|
describe('issue #8: schema with shared references', function() {
|
|
it('should be supported by addSchema', spec('addSchema'));
|
|
|
|
it('should be supported by compile', spec('compile'));
|
|
|
|
function spec(method) {
|
|
return function() {
|
|
var ajv = new Ajv;
|
|
|
|
var propertySchema = {
|
|
type: 'string',
|
|
maxLength: 4
|
|
};
|
|
|
|
var schema = {
|
|
id: 'obj.json#',
|
|
type: 'object',
|
|
properties: {
|
|
foo: propertySchema,
|
|
bar: propertySchema
|
|
}
|
|
};
|
|
|
|
ajv[method](schema);
|
|
|
|
var result = ajv.validate('obj.json#', { foo: 'abc', bar: 'def' });
|
|
result .should.equal(true);
|
|
|
|
result = ajv.validate('obj.json#', { foo: 'abcde', bar: 'fghg' });
|
|
result .should.equal(false);
|
|
ajv.errors .should.have.length(1);
|
|
};
|
|
}
|
|
});
|
|
|
|
describe('issue #50: references with "definitions"', function () {
|
|
it('should be supported by addSchema', spec('addSchema'));
|
|
|
|
it('should be supported by compile', spec('addSchema'));
|
|
|
|
function spec(method) {
|
|
return function() {
|
|
var result;
|
|
|
|
var ajv = new Ajv;
|
|
|
|
ajv[method]({
|
|
id: 'http://example.com/test/person.json#',
|
|
definitions: {
|
|
name: { type: 'string' }
|
|
},
|
|
type: 'object',
|
|
properties: {
|
|
name: { $ref: '#/definitions/name'}
|
|
}
|
|
});
|
|
|
|
ajv[method]({
|
|
id: 'http://example.com/test/employee.json#',
|
|
type: 'object',
|
|
properties: {
|
|
person: { $ref: '/test/person.json#' },
|
|
role: { type: 'string' }
|
|
}
|
|
});
|
|
|
|
result = ajv.validate('http://example.com/test/employee.json#', {
|
|
person: {
|
|
name: 'Alice'
|
|
},
|
|
role: 'Programmer'
|
|
});
|
|
|
|
result. should.equal(true);
|
|
should.equal(ajv.errors, null);
|
|
};
|
|
}
|
|
});
|
|
|
|
|
|
describe('issue #182, NaN validation', function() {
|
|
it('should not pass minimum/maximum validation', function() {
|
|
testNaN({ minimum: 1 }, false);
|
|
testNaN({ maximum: 1 }, false);
|
|
});
|
|
|
|
it('should pass type: number validation', function() {
|
|
testNaN({ type: 'number' }, true);
|
|
});
|
|
|
|
it('should not pass type: integer validation', function() {
|
|
testNaN({ type: 'integer' }, false);
|
|
});
|
|
|
|
function testNaN(schema, NaNisValid) {
|
|
var ajv = new Ajv;
|
|
var validate = ajv.compile(schema);
|
|
validate(NaN) .should.equal(NaNisValid);
|
|
}
|
|
});
|
|
|
|
|
|
describe('issue #204, options schemas and $data used together', function() {
|
|
it('should use v5 metaschemas by default', function() {
|
|
var ajv = new Ajv({
|
|
schemas: [{id: 'str', type: 'string'}],
|
|
$data: true
|
|
});
|
|
|
|
var schema = { const: 42 };
|
|
var validate = ajv.compile(schema);
|
|
|
|
validate(42) .should.equal(true);
|
|
validate(43) .should.equal(false);
|
|
|
|
ajv.validate('str', 'foo') .should.equal(true);
|
|
ajv.validate('str', 42) .should.equal(false);
|
|
});
|
|
});
|
|
|
|
|
|
describe('issue #181, custom keyword is not validated in allErrors mode if there were previous error', function() {
|
|
it('should validate custom keyword that doesn\'t create errors', function() {
|
|
testCustomKeywordErrors({
|
|
type:'object',
|
|
errors: true,
|
|
validate: function v(/* value */) {
|
|
return false;
|
|
}
|
|
});
|
|
});
|
|
|
|
it('should validate custom keyword that creates errors', function() {
|
|
testCustomKeywordErrors({
|
|
type:'object',
|
|
errors: true,
|
|
validate: function v(/* value */) {
|
|
v.errors = v.errors || [];
|
|
v.errors.push({
|
|
keyword: 'alwaysFails',
|
|
message: 'alwaysFails error',
|
|
params: {
|
|
keyword: 'alwaysFails'
|
|
}
|
|
});
|
|
|
|
return false;
|
|
}
|
|
});
|
|
});
|
|
|
|
function testCustomKeywordErrors(def) {
|
|
var ajv = new Ajv({ allErrors: true });
|
|
|
|
ajv.addKeyword('alwaysFails', def);
|
|
|
|
var schema = {
|
|
required: ['foo'],
|
|
alwaysFails: true
|
|
};
|
|
|
|
var validate = ajv.compile(schema);
|
|
|
|
validate({ foo: 1 }) .should.equal(false);
|
|
validate.errors .should.have.length(1);
|
|
validate.errors[0].keyword .should.equal('alwaysFails');
|
|
|
|
validate({}) .should.equal(false);
|
|
validate.errors .should.have.length(2);
|
|
validate.errors[0].keyword .should.equal('required');
|
|
validate.errors[1].keyword .should.equal('alwaysFails');
|
|
}
|
|
});
|
|
|
|
|
|
describe('issue #210, mutual recursive $refs that are schema fragments', function() {
|
|
it('should compile and validate schema when one ref is fragment', function() {
|
|
var ajv = new Ajv;
|
|
|
|
ajv.addSchema({
|
|
"id" : "foo",
|
|
"definitions": {
|
|
"bar": {
|
|
"properties": {
|
|
"baz": {
|
|
"anyOf": [
|
|
{ "enum": [42] },
|
|
{ "$ref": "boo" }
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
ajv.addSchema({
|
|
"id" : "boo",
|
|
"type": "object",
|
|
"required": ["quux"],
|
|
"properties": {
|
|
"quux": { "$ref": "foo#/definitions/bar" }
|
|
}
|
|
});
|
|
|
|
var validate = ajv.compile({ "$ref": "foo#/definitions/bar" });
|
|
|
|
validate({ baz: { quux: { baz: 42 } } }) .should.equal(true);
|
|
validate({ baz: { quux: { baz: "foo" } } }) .should.equal(false);
|
|
});
|
|
|
|
it('should compile and validate schema when both refs are fragments', function() {
|
|
var ajv = new Ajv;
|
|
|
|
ajv.addSchema({
|
|
"id" : "foo",
|
|
"definitions": {
|
|
"bar": {
|
|
"properties": {
|
|
"baz": {
|
|
"anyOf": [
|
|
{ "enum": [42] },
|
|
{ "$ref": "boo#/definitions/buu" }
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
ajv.addSchema({
|
|
"id" : "boo",
|
|
"definitions": {
|
|
"buu": {
|
|
"type": "object",
|
|
"required": ["quux"],
|
|
"properties": {
|
|
"quux": { "$ref": "foo#/definitions/bar" }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
var validate = ajv.compile({ "$ref": "foo#/definitions/bar" });
|
|
|
|
validate({ baz: { quux: { baz: 42 } } }) .should.equal(true);
|
|
validate({ baz: { quux: { baz: "foo" } } }) .should.equal(false);
|
|
});
|
|
});
|
|
|
|
|
|
describe('issue #240, mutually recursive fragment refs reference a common schema', function() {
|
|
var apiSchema = {
|
|
$schema: 'http://json-schema.org/draft-06/schema#',
|
|
id: 'schema://api.schema#',
|
|
resource: {
|
|
id: '#resource',
|
|
properties: {
|
|
id: { type: 'string' }
|
|
}
|
|
},
|
|
resourceIdentifier: {
|
|
id: '#resource_identifier',
|
|
properties: {
|
|
id: { type: 'string' },
|
|
type: { type: 'string' }
|
|
}
|
|
}
|
|
};
|
|
|
|
var domainSchema = {
|
|
$schema: 'http://json-schema.org/draft-06/schema#',
|
|
id: 'schema://domain.schema#',
|
|
properties: {
|
|
data: {
|
|
oneOf: [
|
|
{ $ref: 'schema://library.schema#resource_identifier' },
|
|
{ $ref: 'schema://catalog_item.schema#resource_identifier' },
|
|
]
|
|
}
|
|
}
|
|
};
|
|
|
|
it('should compile and validate schema when one ref is fragment', function() {
|
|
var ajv = new Ajv;
|
|
|
|
var librarySchema = {
|
|
$schema: 'http://json-schema.org/draft-06/schema#',
|
|
id: 'schema://library.schema#',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
links: {
|
|
properties: {
|
|
catalogItems: {
|
|
type: 'array',
|
|
items: { $ref: 'schema://catalog_item_resource_identifier.schema#' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
definitions: {
|
|
resource_identifier: {
|
|
id: '#resource_identifier',
|
|
allOf: [
|
|
{
|
|
properties: {
|
|
type: {
|
|
type: 'string',
|
|
'enum': ['Library']
|
|
}
|
|
}
|
|
},
|
|
{ $ref: 'schema://api.schema#resource_identifier' }
|
|
]
|
|
}
|
|
}
|
|
};
|
|
|
|
var catalogItemSchema = {
|
|
$schema: 'http://json-schema.org/draft-06/schema#',
|
|
id: 'schema://catalog_item.schema#',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
links: {
|
|
properties: {
|
|
library: { $ref: 'schema://library.schema#resource_identifier' }
|
|
}
|
|
}
|
|
},
|
|
definitions: {
|
|
resource_identifier: {
|
|
id: '#resource_identifier',
|
|
allOf: [
|
|
{
|
|
properties: {
|
|
type: {
|
|
type: 'string',
|
|
'enum': ['CatalogItem']
|
|
}
|
|
}
|
|
},
|
|
{ $ref: 'schema://api.schema#resource_identifier' }
|
|
]
|
|
}
|
|
}
|
|
};
|
|
|
|
var catalogItemResourceIdentifierSchema = {
|
|
$schema: 'http://json-schema.org/draft-06/schema#',
|
|
id: 'schema://catalog_item_resource_identifier.schema#',
|
|
allOf: [
|
|
{
|
|
properties: {
|
|
type: {
|
|
type: 'string',
|
|
enum: ['CatalogItem']
|
|
}
|
|
}
|
|
},
|
|
{
|
|
$ref: 'schema://api.schema#resource_identifier'
|
|
}
|
|
]
|
|
};
|
|
|
|
ajv.addSchema(librarySchema);
|
|
ajv.addSchema(catalogItemSchema);
|
|
ajv.addSchema(catalogItemResourceIdentifierSchema);
|
|
ajv.addSchema(apiSchema);
|
|
|
|
var validate = ajv.compile(domainSchema);
|
|
testSchema(validate);
|
|
});
|
|
|
|
it('should compile and validate schema when both refs are fragments', function() {
|
|
var ajv = new Ajv;
|
|
|
|
var librarySchema = {
|
|
$schema: 'http://json-schema.org/draft-06/schema#',
|
|
id: 'schema://library.schema#',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
links: {
|
|
properties: {
|
|
catalogItems: {
|
|
type: 'array',
|
|
items: { $ref: 'schema://catalog_item.schema#resource_identifier' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
definitions: {
|
|
resource_identifier: {
|
|
id: '#resource_identifier',
|
|
allOf: [
|
|
{
|
|
properties: {
|
|
type: {
|
|
type: 'string',
|
|
'enum': ['Library']
|
|
}
|
|
}
|
|
},
|
|
{ $ref: 'schema://api.schema#resource_identifier' }
|
|
]
|
|
}
|
|
}
|
|
};
|
|
|
|
var catalogItemSchema = {
|
|
$schema: 'http://json-schema.org/draft-06/schema#',
|
|
id: 'schema://catalog_item.schema#',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
links: {
|
|
properties: {
|
|
library: { $ref: 'schema://library.schema#resource_identifier' }
|
|
}
|
|
}
|
|
},
|
|
definitions: {
|
|
resource_identifier: {
|
|
id: '#resource_identifier',
|
|
allOf: [
|
|
{
|
|
properties: {
|
|
type: {
|
|
type: 'string',
|
|
'enum': ['CatalogItem']
|
|
}
|
|
}
|
|
},
|
|
{ $ref: 'schema://api.schema#resource_identifier' }
|
|
]
|
|
}
|
|
}
|
|
};
|
|
|
|
ajv.addSchema(librarySchema);
|
|
ajv.addSchema(catalogItemSchema);
|
|
ajv.addSchema(apiSchema);
|
|
|
|
var validate = ajv.compile(domainSchema);
|
|
testSchema(validate);
|
|
});
|
|
|
|
|
|
function testSchema(validate) {
|
|
validate({ data: { type: 'Library', id: '123' } }) .should.equal(true);
|
|
validate({ data: { type: 'Library', id: 123 } }) .should.equal(false);
|
|
validate({ data: { type: 'CatalogItem', id: '123' } }) .should.equal(true);
|
|
validate({ data: { type: 'CatalogItem', id: 123 } }) .should.equal(false);
|
|
validate({ data: { type: 'Foo', id: '123' } }) .should.equal(false);
|
|
}
|
|
});
|
|
|
|
|
|
describe('issue #259, support validating [meta-]schemas against themselves', function() {
|
|
it('should add schema before validation if "id" is the same as "$schema"', function() {
|
|
var ajv = new Ajv;
|
|
ajv.addMetaSchema(require('../lib/refs/json-schema-draft-04.json'));
|
|
var hyperSchema = require('./remotes/hyper-schema.json');
|
|
ajv.addMetaSchema(hyperSchema);
|
|
});
|
|
});
|
|
|
|
|
|
describe.skip('issue #273, schemaPath in error in referenced schema', function() {
|
|
it('should have canonic reference with hash after file name', function() {
|
|
test(new Ajv);
|
|
test(new Ajv({inlineRefs: false}));
|
|
|
|
function test(ajv) {
|
|
var schema = {
|
|
"properties": {
|
|
"a": { "$ref": "int" }
|
|
}
|
|
};
|
|
|
|
var referencedSchema = {
|
|
"id": "int",
|
|
"type": "integer"
|
|
};
|
|
|
|
ajv.addSchema(referencedSchema);
|
|
var validate = ajv.compile(schema);
|
|
|
|
validate({ "a": "foo" }) .should.equal(false);
|
|
validate.errors[0].schemaPath .should.equal('int#/type');
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
describe('issue #342, support uniqueItems with some non-JSON objects', function() {
|
|
var validate;
|
|
|
|
before(function() {
|
|
var ajv = new Ajv;
|
|
validate = ajv.compile({ uniqueItems: true });
|
|
});
|
|
|
|
it('should allow different RegExps', function() {
|
|
validate([/foo/, /bar/]) .should.equal(true);
|
|
validate([/foo/ig, /foo/gi]) .should.equal(false);
|
|
validate([/foo/, {}]) .should.equal(true);
|
|
});
|
|
|
|
it('should allow different Dates', function() {
|
|
validate([new Date('2016-11-11'), new Date('2016-11-12')]) .should.equal(true);
|
|
validate([new Date('2016-11-11'), new Date('2016-11-11')]) .should.equal(false);
|
|
validate([new Date('2016-11-11'), {}]) .should.equal(true);
|
|
});
|
|
|
|
it('should allow undefined properties', function() {
|
|
validate([{}, {foo: undefined}]) .should.equal(true);
|
|
validate([{foo: undefined}, {}]) .should.equal(true);
|
|
validate([{foo: undefined}, {bar: undefined}]) .should.equal(true);
|
|
validate([{foo: undefined}, {foo: undefined}]) .should.equal(false);
|
|
});
|
|
});
|
|
|
|
|
|
describe('issue #388, code clean-up not working', function() {
|
|
it('should remove assignement to rootData if it is not used', function() {
|
|
var ajv = new Ajv;
|
|
var validate = ajv.compile({
|
|
type: 'object',
|
|
properties: {
|
|
foo: { type: 'string' }
|
|
}
|
|
});
|
|
var code = validate.toString();
|
|
code.match(/rootData/g).length .should.equal(1);
|
|
});
|
|
|
|
it('should remove assignement to errors if they are not used', function() {
|
|
var ajv = new Ajv;
|
|
var validate = ajv.compile({
|
|
type: 'object'
|
|
});
|
|
var code = validate.toString();
|
|
should.equal(code.match(/[^.]errors|vErrors/g), null);
|
|
});
|
|
});
|
|
|
|
|
|
describe('issue #485, order of type validation', function() {
|
|
it('should validate types befor keywords', function() {
|
|
var ajv = new Ajv({allErrors: true});
|
|
var validate = ajv.compile({
|
|
type: ['integer', 'string'],
|
|
required: ['foo'],
|
|
minimum: 2
|
|
});
|
|
|
|
validate(2) .should.equal(true);
|
|
validate('foo') .should.equal(true);
|
|
|
|
validate(1.5) .should.equal(false);
|
|
checkErrors(['type', 'minimum']);
|
|
|
|
validate({}) .should.equal(false);
|
|
checkErrors(['type', 'required']);
|
|
|
|
function checkErrors(expectedErrs) {
|
|
validate.errors .should.have.length(expectedErrs.length);
|
|
expectedErrs.forEach(function (keyword, i) {
|
|
validate.errors[i].keyword .should.equal(keyword);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
describe('issue #521, incorrect warning with "id" property', function() {
|
|
it('should not log warning', function() {
|
|
var ajv = new Ajv({schemaId: '$id'});
|
|
var consoleWarn = console.warn;
|
|
console.warn = function() {
|
|
throw new Error('should not log warning');
|
|
};
|
|
|
|
try {
|
|
ajv.compile({
|
|
"$id": "http://example.com/schema.json",
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {"type": "string"},
|
|
},
|
|
"required": [ "id"]
|
|
});
|
|
} finally {
|
|
console.warn = consoleWarn;
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
describe('issue #533, throwing missing ref exception with option missingRefs: "ignore"', function() {
|
|
var schema = {
|
|
"type": "object",
|
|
"properties": {
|
|
"foo": {"$ref": "#/definitions/missing"},
|
|
"bar": {"$ref": "#/definitions/missing"}
|
|
}
|
|
};
|
|
|
|
it('should pass validation without throwing exception', function() {
|
|
var ajv = new Ajv({missingRefs: 'ignore'});
|
|
var validate = ajv.compile(schema);
|
|
validate({foo: 'anything'}) .should.equal(true);
|
|
validate({foo: 'anything', bar: 'whatever'}) .should.equal(true);
|
|
});
|
|
|
|
it('should throw exception during schema compilation with option missingRefs: true', function() {
|
|
var ajv = new Ajv;
|
|
should.throw(function() {
|
|
ajv.compile(schema);
|
|
});
|
|
});
|
|
});
|