425 lines
12 KiB
JavaScript
425 lines
12 KiB
JavaScript
'use strict';
|
|
/* global Promise */
|
|
|
|
var Ajv = require('./ajv')
|
|
, should = require('./chai').should();
|
|
|
|
|
|
describe('compileAsync method', function() {
|
|
var ajv, loadCallCount;
|
|
|
|
var SCHEMAS = {
|
|
"http://example.com/object.json": {
|
|
"id": "http://example.com/object.json",
|
|
"properties": {
|
|
"a": { "type": "string" },
|
|
"b": { "$ref": "int2plus.json" }
|
|
}
|
|
},
|
|
"http://example.com/int2plus.json": {
|
|
"id": "http://example.com/int2plus.json",
|
|
"type": "integer",
|
|
"minimum": 2
|
|
},
|
|
"http://example.com/tree.json": {
|
|
"id": "http://example.com/tree.json",
|
|
"type": "array",
|
|
"items": { "$ref": "leaf.json" }
|
|
},
|
|
"http://example.com/leaf.json": {
|
|
"id": "http://example.com/leaf.json",
|
|
"properties": {
|
|
"name": { "type": "string" },
|
|
"subtree": { "$ref": "tree.json" }
|
|
}
|
|
},
|
|
"http://example.com/recursive.json": {
|
|
"id": "http://example.com/recursive.json",
|
|
"properties": {
|
|
"b": { "$ref": "parent.json" }
|
|
},
|
|
"required": ["b"]
|
|
},
|
|
"http://example.com/invalid.json": {
|
|
"id": "http://example.com/recursive.json",
|
|
"properties": {
|
|
"invalid": { "type": "number" }
|
|
},
|
|
"required": "invalid"
|
|
},
|
|
"http://example.com/foobar.json": {
|
|
"id": "http://example.com/foobar.json",
|
|
"$schema": "http://example.com/foobar_meta.json",
|
|
"myFooBar": "foo"
|
|
},
|
|
"http://example.com/foobar_meta.json": {
|
|
"id": "http://example.com/foobar_meta.json",
|
|
"type": "object",
|
|
"properties": {
|
|
"myFooBar": {
|
|
"enum": ["foo", "bar"]
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
beforeEach(function() {
|
|
loadCallCount = 0;
|
|
ajv = new Ajv({ loadSchema: loadSchema });
|
|
});
|
|
|
|
|
|
it('should compile schemas loading missing schemas with options.loadSchema function', function() {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "object.json" }
|
|
}
|
|
};
|
|
return ajv.compileAsync(schema).then(function (validate) {
|
|
should.equal(loadCallCount, 2);
|
|
validate .should.be.a('function');
|
|
validate({ a: { b: 2 } }) .should.equal(true);
|
|
validate({ a: { b: 1 } }) .should.equal(false);
|
|
});
|
|
});
|
|
|
|
|
|
it('should compile schemas loading missing schemas and return function via callback', function (done) {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "object.json" }
|
|
}
|
|
};
|
|
ajv.compileAsync(schema, function (err, validate) {
|
|
should.equal(loadCallCount, 2);
|
|
should.not.exist(err);
|
|
validate .should.be.a('function');
|
|
validate({ a: { b: 2 } }) .should.equal(true);
|
|
validate({ a: { b: 1 } }) .should.equal(false);
|
|
done();
|
|
});
|
|
});
|
|
|
|
|
|
it('should correctly load schemas when missing reference has JSON path', function() {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "object.json#/properties/b" }
|
|
}
|
|
};
|
|
return ajv.compileAsync(schema).then(function (validate) {
|
|
should.equal(loadCallCount, 2);
|
|
validate .should.be.a('function');
|
|
validate({ a: 2 }) .should.equal(true);
|
|
validate({ a: 1 }) .should.equal(false);
|
|
});
|
|
});
|
|
|
|
|
|
it('should correctly compile with remote schemas that have mutual references', function() {
|
|
var schema = {
|
|
"id": "http://example.com/root.json",
|
|
"properties": {
|
|
"tree": { "$ref": "tree.json" }
|
|
}
|
|
};
|
|
return ajv.compileAsync(schema).then(function (validate) {
|
|
validate .should.be.a('function');
|
|
var validData = { tree: [
|
|
{ name: 'a', subtree: [ { name: 'a.a' } ] },
|
|
{ name: 'b' }
|
|
] };
|
|
var invalidData = { tree: [
|
|
{ name: 'a', subtree: [ { name: 1 } ] }
|
|
] };
|
|
validate(validData) .should.equal(true);
|
|
validate(invalidData) .should.equal(false);
|
|
});
|
|
});
|
|
|
|
|
|
it('should correctly compile with remote schemas that reference the compiled schema', function() {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "recursive.json" }
|
|
}
|
|
};
|
|
return ajv.compileAsync(schema).then(function (validate) {
|
|
should.equal(loadCallCount, 1);
|
|
validate .should.be.a('function');
|
|
var validData = { a: { b: { a: { b: {} } } } };
|
|
var invalidData = { a: { b: { a: {} } } };
|
|
validate(validData) .should.equal(true);
|
|
validate(invalidData) .should.equal(false);
|
|
});
|
|
});
|
|
|
|
|
|
it('should resolve reference containing "properties" segment with the same property (issue #220)', function() {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": {
|
|
"$ref": "object.json#/properties/a"
|
|
}
|
|
}
|
|
};
|
|
return ajv.compileAsync(schema).then(function (validate) {
|
|
should.equal(loadCallCount, 2);
|
|
validate .should.be.a('function');
|
|
validate({ a: 'foo' }) .should.equal(true);
|
|
validate({ a: 42 }) .should.equal(false);
|
|
});
|
|
});
|
|
|
|
|
|
describe('loading metaschemas (#334)', function() {
|
|
it('should load metaschema if not available', function() {
|
|
return test(SCHEMAS['http://example.com/foobar.json'], 1);
|
|
});
|
|
|
|
it('should load metaschema of referenced schema if not available', function() {
|
|
return test({ "$ref": "http://example.com/foobar.json" }, 2);
|
|
});
|
|
|
|
function test(schema, expectedLoadCallCount) {
|
|
ajv.addKeyword('myFooBar', {
|
|
type: 'string',
|
|
validate: function (sch, data) {
|
|
return sch == data;
|
|
}
|
|
});
|
|
|
|
return ajv.compileAsync(schema).then(function (validate) {
|
|
should.equal(loadCallCount, expectedLoadCallCount);
|
|
validate .should.be.a('function');
|
|
validate('foo') .should.equal(true);
|
|
validate('bar') .should.equal(false);
|
|
});
|
|
}
|
|
});
|
|
|
|
|
|
it('should return compiled schema on the next tick if there are no references (#51)', function() {
|
|
var schema = {
|
|
"id": "http://example.com/int2plus.json",
|
|
"type": "integer",
|
|
"minimum": 2
|
|
};
|
|
var beforeCallback1;
|
|
var p1 = ajv.compileAsync(schema).then(function (validate) {
|
|
beforeCallback1 .should.equal(true);
|
|
spec(validate);
|
|
var beforeCallback2;
|
|
var p2 = ajv.compileAsync(schema).then(function (_validate) {
|
|
beforeCallback2 .should.equal(true);
|
|
spec(_validate);
|
|
});
|
|
beforeCallback2 = true;
|
|
return p2;
|
|
});
|
|
beforeCallback1 = true;
|
|
return p1;
|
|
|
|
function spec(validate) {
|
|
should.equal(loadCallCount, 0);
|
|
validate .should.be.a('function');
|
|
var validData = 2;
|
|
var invalidData = 1;
|
|
validate(validData) .should.equal(true);
|
|
validate(invalidData) .should.equal(false);
|
|
}
|
|
});
|
|
|
|
|
|
it('should queue calls so only one compileAsync executes at a time (#52)', function() {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "object.json" }
|
|
}
|
|
};
|
|
|
|
return Promise.all([
|
|
ajv.compileAsync(schema).then(spec),
|
|
ajv.compileAsync(schema).then(spec),
|
|
ajv.compileAsync(schema).then(spec)
|
|
]);
|
|
|
|
function spec(validate) {
|
|
should.equal(loadCallCount, 2);
|
|
validate .should.be.a('function');
|
|
validate({ a: { b: 2 } }) .should.equal(true);
|
|
validate({ a: { b: 1 } }) .should.equal(false);
|
|
}
|
|
});
|
|
|
|
|
|
it('should throw exception if loadSchema is not passed', function (done) {
|
|
var schema = {
|
|
"id": "http://example.com/int2plus.json",
|
|
"type": "integer",
|
|
"minimum": 2
|
|
};
|
|
ajv = new Ajv;
|
|
should.throw(function() {
|
|
ajv.compileAsync(schema, function() {
|
|
done(new Error('it should have thrown exception'));
|
|
});
|
|
});
|
|
setTimeout(function() {
|
|
// function is needed for the test to pass in Firefox 4
|
|
done();
|
|
});
|
|
});
|
|
|
|
|
|
describe('should return error via callback', function() {
|
|
it('if passed schema is invalid', function (done) {
|
|
var invalidSchema = {
|
|
"id": "http://example.com/int2plus.json",
|
|
"type": "integer",
|
|
"minimum": "invalid"
|
|
};
|
|
ajv.compileAsync(invalidSchema, shouldFail(done));
|
|
});
|
|
|
|
it('if loaded schema is invalid', function (done) {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "invalid.json" }
|
|
}
|
|
};
|
|
ajv.compileAsync(schema, shouldFail(done));
|
|
});
|
|
|
|
it('if required schema is loaded but the reference cannot be resolved', function (done) {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "object.json#/definitions/not_found" }
|
|
}
|
|
};
|
|
ajv.compileAsync(schema, shouldFail(done));
|
|
});
|
|
|
|
it('if loadSchema returned error', function (done) {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "object.json" }
|
|
}
|
|
};
|
|
ajv = new Ajv({ loadSchema: badLoadSchema });
|
|
ajv.compileAsync(schema, shouldFail(done));
|
|
|
|
function badLoadSchema() {
|
|
return Promise.reject(new Error('cant load'));
|
|
}
|
|
});
|
|
|
|
it('if schema compilation throws some other exception', function (done) {
|
|
ajv.addKeyword('badkeyword', { compile: badCompile });
|
|
var schema = { badkeyword: true };
|
|
ajv.compileAsync(schema, shouldFail(done));
|
|
|
|
function badCompile(/* schema */) {
|
|
throw new Error('cant compile keyword schema');
|
|
}
|
|
});
|
|
|
|
function shouldFail(done) {
|
|
return function (err, validate) {
|
|
should.exist(err);
|
|
should.not.exist(validate);
|
|
done();
|
|
};
|
|
}
|
|
});
|
|
|
|
|
|
describe('should return error via promise', function() {
|
|
it('if passed schema is invalid', function() {
|
|
var invalidSchema = {
|
|
"id": "http://example.com/int2plus.json",
|
|
"type": "integer",
|
|
"minimum": "invalid"
|
|
};
|
|
return shouldReject(ajv.compileAsync(invalidSchema));
|
|
});
|
|
|
|
it('if loaded schema is invalid', function() {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "invalid.json" }
|
|
}
|
|
};
|
|
return shouldReject(ajv.compileAsync(schema));
|
|
});
|
|
|
|
it('if required schema is loaded but the reference cannot be resolved', function() {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "object.json#/definitions/not_found" }
|
|
}
|
|
};
|
|
return shouldReject(ajv.compileAsync(schema));
|
|
});
|
|
|
|
it('if loadSchema returned error', function() {
|
|
var schema = {
|
|
"id": "http://example.com/parent.json",
|
|
"properties": {
|
|
"a": { "$ref": "object.json" }
|
|
}
|
|
};
|
|
ajv = new Ajv({ loadSchema: badLoadSchema });
|
|
return shouldReject(ajv.compileAsync(schema));
|
|
|
|
function badLoadSchema() {
|
|
return Promise.reject(new Error('cant load'));
|
|
}
|
|
});
|
|
|
|
it('if schema compilation throws some other exception', function() {
|
|
ajv.addKeyword('badkeyword', { compile: badCompile });
|
|
var schema = { badkeyword: true };
|
|
return shouldReject(ajv.compileAsync(schema));
|
|
|
|
function badCompile(/* schema */) {
|
|
throw new Error('cant compile keyword schema');
|
|
}
|
|
});
|
|
|
|
function shouldReject(p) {
|
|
return p.then(
|
|
function(validate) {
|
|
should.not.exist(validate);
|
|
throw new Error('Promise has resolved; it should have rejected');
|
|
},
|
|
function(err) {
|
|
should.exist(err);
|
|
}
|
|
);
|
|
}
|
|
});
|
|
|
|
|
|
function loadSchema(uri) {
|
|
loadCallCount++;
|
|
return new Promise(function (resolve, reject) {
|
|
setTimeout(function() {
|
|
if (SCHEMAS[uri]) resolve(SCHEMAS[uri]);
|
|
else reject(new Error('404'));
|
|
}, 10);
|
|
});
|
|
}
|
|
});
|