123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- 'use strict';
- var Ajv = require('./ajv')
- , Promise = require('./promise')
- , getAjvInstances = require('./ajv_async_instances')
- , should = require('./chai').should();
- describe('async schemas, formats and keywords', function() {
- this.timeout(30000);
- var ajv, instances;
- beforeEach(function () {
- instances = getAjvInstances();
- ajv = instances[0];
- });
- describe('async schemas without async elements', function() {
- it('should return result as promise', function() {
- var schema = {
- $async: true,
- type: 'string',
- maxLength: 3
- };
- return repeat(function() { return Promise.all(instances.map(test)); });
- function test(_ajv) {
- var validate = _ajv.compile(schema);
- return Promise.all([
- shouldBeValid( validate('abc'), 'abc' ),
- shouldBeInvalid( validate('abcd') ),
- shouldBeInvalid( validate(1) ),
- ]);
- }
- });
- it('should fail compilation if async schema is inside sync schema', function() {
- var schema = {
- properties: {
- foo: {
- $async: true,
- type: 'string',
- maxLength: 3
- }
- }
- };
- shouldThrowFunc('async schema in sync schema', function() {
- ajv.compile(schema);
- });
- schema.$async = true;
- ajv.compile(schema);
- });
- });
- describe('async formats', function() {
- beforeEach(addFormatEnglishWord);
- it('should fail compilation if async format is inside sync schema', function() {
- instances.forEach(function (_ajv) {
- var schema = {
- type: 'string',
- format: 'english_word'
- };
- shouldThrowFunc('async format in sync schema', function() {
- _ajv.compile(schema);
- });
- schema.$async = true;
- _ajv.compile(schema);
- });
- });
- });
- describe('async custom keywords', function() {
- beforeEach(function() {
- instances.forEach(function (_ajv) {
- _ajv.addKeyword('idExists', {
- async: true,
- type: 'number',
- validate: checkIdExists,
- errors: false
- });
- _ajv.addKeyword('idExistsWithError', {
- async: true,
- type: 'number',
- validate: checkIdExistsWithError,
- errors: true
- });
- });
- });
- it('should fail compilation if async keyword is inside sync schema', function() {
- instances.forEach(function (_ajv) {
- var schema = {
- type: 'object',
- properties: {
- userId: {
- type: 'integer',
- idExists: { table: 'users' }
- }
- }
- };
- shouldThrowFunc('async keyword in sync schema', function() {
- _ajv.compile(schema);
- });
- schema.$async = true;
- _ajv.compile(schema);
- });
- });
- it('should return custom error', function() {
- return Promise.all(instances.map(function (_ajv) {
- var schema = {
- $async: true,
- type: 'object',
- properties: {
- userId: {
- type: 'integer',
- idExistsWithError: { table: 'users' }
- },
- postId: {
- type: 'integer',
- idExistsWithError: { table: 'posts' }
- }
- }
- };
- var validate = _ajv.compile(schema);
- return Promise.all([
- shouldBeInvalid( validate({ userId: 5, postId: 10 }), [ 'id not found in table posts' ] ),
- shouldBeInvalid( validate({ userId: 9, postId: 25 }), [ 'id not found in table users' ] )
- ]);
- }));
- });
- function checkIdExists(schema, data) {
- switch (schema.table) {
- case 'users': return check([1, 5, 8]);
- case 'posts': return check([21, 25, 28]);
- default: throw new Error('no such table');
- }
- function check(IDs) {
- return Promise.resolve(IDs.indexOf(data) >= 0);
- }
- }
- function checkIdExistsWithError(schema, data) {
- var table = schema.table;
- switch (table) {
- case 'users': return check(table, [1, 5, 8]);
- case 'posts': return check(table, [21, 25, 28]);
- default: throw new Error('no such table');
- }
- function check(_table, IDs) {
- if (IDs.indexOf(data) >= 0) return Promise.resolve(true);
- var error = {
- keyword: 'idExistsWithError',
- message: 'id not found in table ' + _table
- };
- return Promise.reject(new Ajv.ValidationError([error]));
- }
- }
- });
- describe('async referenced schemas', function() {
- beforeEach(function() {
- instances = getAjvInstances({ inlineRefs: false, extendRefs: 'ignore' });
- addFormatEnglishWord();
- });
- it('should validate referenced async schema', function() {
- var schema = {
- $async: true,
- definitions: {
- english_word: {
- $async: true,
- type: 'string',
- format: 'english_word'
- }
- },
- properties: {
- word: { $ref: '#/definitions/english_word' }
- }
- };
- return repeat(function() { return Promise.all(instances.map(function (_ajv) {
- var validate = _ajv.compile(schema);
- var validData = { word: 'tomorrow' };
- return Promise.all([
- shouldBeValid( validate(validData), validData ),
- shouldBeInvalid( validate({ word: 'manana' }) ),
- shouldBeInvalid( validate({ word: 1 }) ),
- shouldThrow( validate({ word: 'today' }), 'unknown word' )
- ]);
- })); });
- });
- it('should validate recursive async schema', function() {
- var schema = {
- $async: true,
- definitions: {
- english_word: {
- $async: true,
- type: 'string',
- format: 'english_word'
- }
- },
- type: 'object',
- properties: {
- foo: {
- anyOf: [
- { $ref: '#/definitions/english_word' },
- { $ref: '#' }
- ]
- }
- }
- };
- return recursiveTest(schema);
- });
- it('should validate recursive ref to async sub-schema, issue #612', function() {
- var schema = {
- $async: true,
- type: 'object',
- properties: {
- foo: {
- $async: true,
- anyOf: [
- {
- type: 'string',
- format: 'english_word'
- },
- {
- type: 'object',
- properties: {
- foo: { $ref: '#/properties/foo' }
- }
- }
- ]
- }
- }
- };
- return recursiveTest(schema);
- });
- it('should validate ref from referenced async schema to root schema', function() {
- var schema = {
- $async: true,
- definitions: {
- wordOrRoot: {
- $async: true,
- anyOf: [
- {
- type: 'string',
- format: 'english_word'
- },
- { $ref: '#' }
- ]
- }
- },
- type: 'object',
- properties: {
- foo: { $ref: '#/definitions/wordOrRoot' }
- }
- };
- return recursiveTest(schema);
- });
- it('should validate refs between two async schemas', function() {
- var schemaObj = {
- $id: 'http://e.com/obj.json#',
- $async: true,
- type: 'object',
- properties: {
- foo: { $ref: 'http://e.com/word.json#' }
- }
- };
- var schemaWord = {
- $id: 'http://e.com/word.json#',
- $async: true,
- anyOf: [
- {
- type: 'string',
- format: 'english_word'
- },
- { $ref: 'http://e.com/obj.json#' }
- ]
- };
- return recursiveTest(schemaObj, schemaWord);
- });
- it('should fail compilation if sync schema references async schema', function() {
- var schema = {
- $id: 'http://e.com/obj.json#',
- type: 'object',
- properties: {
- foo: { $ref: 'http://e.com/word.json#' }
- }
- };
- var schemaWord = {
- $id: 'http://e.com/word.json#',
- $async: true,
- anyOf: [
- {
- type: 'string',
- format: 'english_word'
- },
- { $ref: 'http://e.com/obj.json#' }
- ]
- };
- ajv.addSchema(schemaWord);
- ajv.addFormat('english_word', {
- async: true,
- validate: checkWordOnServer
- });
- shouldThrowFunc('async schema referenced by sync schema', function() {
- ajv.compile(schema);
- });
- schema.$id = 'http://e.com/obj2.json#';
- schema.$async = true;
- ajv.compile(schema);
- });
- function recursiveTest(schema, refSchema) {
- return repeat(function() { return Promise.all(instances.map(function (_ajv) {
- if (refSchema) try { _ajv.addSchema(refSchema); } catch(e) {}
- var validate = _ajv.compile(schema);
- var data;
- return Promise.all([
- shouldBeValid( validate(data = { foo: 'tomorrow' }), data ),
- shouldBeInvalid( validate({ foo: 'manana' }) ),
- shouldBeInvalid( validate({ foo: 1 }) ),
- shouldThrow( validate({ foo: 'today' }), 'unknown word' ),
- shouldBeValid( validate(data = { foo: { foo: 'tomorrow' }}), data ),
- shouldBeInvalid( validate({ foo: { foo: 'manana' }}) ),
- shouldBeInvalid( validate({ foo: { foo: 1 }}) ),
- shouldThrow( validate({ foo: { foo: 'today' }}), 'unknown word' ),
- shouldBeValid( validate(data = { foo: { foo: { foo: 'tomorrow' }}}), data ),
- shouldBeInvalid( validate({ foo: { foo: { foo: 'manana' }}}) ),
- shouldBeInvalid( validate({ foo: { foo: { foo: 1 }}}) ),
- shouldThrow( validate({ foo: { foo: { foo: 'today' }}}), 'unknown word' )
- ]);
- })); });
- }
- });
- function addFormatEnglishWord() {
- instances.forEach(function (_ajv) {
- _ajv.addFormat('english_word', {
- async: true,
- validate: checkWordOnServer
- });
- });
- }
- });
- function checkWordOnServer(str) {
- return str == 'tomorrow' ? Promise.resolve(true)
- : str == 'manana' ? Promise.resolve(false)
- : Promise.reject(new Error('unknown word'));
- }
- function shouldThrowFunc(message, func) {
- var err;
- should.throw(function() {
- try { func(); }
- catch(e) { err = e; throw e; }
- });
- err.message .should.equal(message);
- }
- function shouldBeValid(p, data) {
- return p.then(function (valid) {
- valid .should.equal(data);
- });
- }
- var SHOULD_BE_INVALID = 'test: should be invalid';
- function shouldBeInvalid(p, expectedMessages) {
- return checkNotValid(p)
- .then(function (err) {
- err .should.be.instanceof(Ajv.ValidationError);
- err.errors .should.be.an('array');
- err.validation .should.equal(true);
- if (expectedMessages) {
- var messages = err.errors.map(function (e) {
- return e.message;
- });
- messages .should.eql(expectedMessages);
- }
- });
- }
- function shouldThrow(p, exception) {
- return checkNotValid(p)
- .then(function (err) {
- err.message .should.equal(exception);
- });
- }
- function checkNotValid(p) {
- return p.then(function (/* valid */) {
- throw new Error(SHOULD_BE_INVALID);
- })
- .catch(function (err) {
- err. should.be.instanceof(Error);
- if (err.message == SHOULD_BE_INVALID) throw err;
- return err;
- });
- }
- function repeat(func) {
- return func();
- // var promises = [];
- // for (var i=0; i<1000; i++) promises.push(func());
- // return Promise.all(promises);
- }
|