ajv/spec/async.spec.js

324 lines
8.8 KiB
JavaScript

'use strict';
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"
}
};
beforeEach(function() {
loadCallCount = 0;
ajv = new Ajv({ loadSchema: loadSchema });
});
it('should compile schemas loading missing schemas with options.loadSchema function', 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 (done) {
var schema = {
"id": "http://example.com/parent.json",
"properties": {
"a": { "$ref": "object.json#/properties/b" }
}
};
ajv.compileAsync(schema, function (err, validate) {
should.equal(loadCallCount, 2);
should.not.exist(err);
validate .should.be.a('function');
validate({ a: 2 }) .should.equal(true);
validate({ a: 1 }) .should.equal(false);
done();
});
});
it('should correctly compile with remote schemas that have mutual references', function (done) {
var schema = {
"id": "http://example.com/root.json",
"properties": {
"tree": { "$ref": "tree.json" }
}
};
ajv.compileAsync(schema, function (err, validate) {
should.not.exist(err);
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);
done();
});
});
it('should correctly compile with remote schemas that reference the compiled schema', function (done) {
var schema = {
"id": "http://example.com/parent.json",
"properties": {
"a": { "$ref": "recursive.json" }
}
};
ajv.compileAsync(schema, function (err, validate) {
should.equal(loadCallCount, 1);
should.not.exist(err);
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);
done();
});
});
it('should resolve reference containing "properties" segment with the same property (issue #220)', function (done) {
var schema = {
"id": "http://example.com/parent.json",
"properties": {
"a": {
"$ref": "object.json#/properties/a"
}
}
};
ajv.compileAsync(schema, function (err, validate) {
should.not.exist(err);
should.equal(loadCallCount, 2);
validate .should.be.a('function');
validate({ a: 'foo' }) .should.equal(true);
validate({ a: 42 }) .should.equal(false);
done();
});
});
it('should return compiled schema on the next tick if there are no references (#51)', function (done) {
var schema = {
"id": "http://example.com/int2plus.json",
"type": "integer",
"minimum": 2
};
var beforeCallback1;
ajv.compileAsync(schema, function (err, validate) {
beforeCallback1 .should.equal(true);
spec(err, validate);
var beforeCallback2;
ajv.compileAsync(schema, function (_err, _validate) {
beforeCallback2 .should.equal(true);
spec(_err, _validate);
done();
});
beforeCallback2 = true;
});
beforeCallback1 = true;
function spec(err, validate) {
should.not.exist(err);
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 (done) {
var schema = {
"id": "http://example.com/parent.json",
"properties": {
"a": { "$ref": "object.json" }
}
};
var completedCount = 0;
ajv.compileAsync(schema, spec);
ajv.compileAsync(schema, spec);
ajv.compileAsync(schema, spec);
function spec(err, validate) {
should.not.exist(err);
validate .should.be.a('function');
validate({ a: { b: 2 } }) .should.equal(true);
validate({ a: { b: 1 } }) .should.equal(false);
completed();
}
function completed() {
completedCount++;
if (completedCount == 3) {
should.equal(loadCallCount, 2);
done();
}
}
});
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, function (err, validate) {
should.exist(err);
should.not.exist(validate);
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, function (err, validate) {
should.exist(err);
should.not.exist(validate);
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, function (err, validate) {
should.exist(err);
should.not.exist(validate);
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, function (err, validate) {
should.exist(err);
should.not.exist(validate);
done();
});
function badLoadSchema(ref, callback) {
setTimeout(function() { callback(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, function (err, validate) {
should.exist(err);
should.not.exist(validate);
done();
});
function badCompile(/* schema */) {
throw new Error('cant compile keyword schema');
}
});
});
function loadSchema(uri, callback) {
loadCallCount++;
setTimeout(function() {
if (SCHEMAS[uri]) return callback(null, SCHEMAS[uri]);
callback(new Error('404'));
}, 10);
}
});