refactor async tests

master
Evgeny Poberezkin 2016-02-03 22:14:14 +00:00
parent b4f756d403
commit 38827f50d4
7 changed files with 312 additions and 146 deletions

View File

@ -58,7 +58,7 @@
"istanbul": "^0.4.2",
"js-beautify": "^1.5.6",
"jshint": "^2.8.0",
"json-schema-test": "^1.0.0",
"json-schema-test": "^1.1.0",
"karma": "^0.13.3",
"karma-chrome-launcher": "^0.2.0",
"karma-mocha": "^0.2.0",

79
spec/async/format.json Normal file
View File

@ -0,0 +1,79 @@
[
{
"description": "async custom formats",
"schema": {
"$async": true,
"type": "string",
"format": "english_word"
},
"tests": [
{
"description": "'tomorrow' is a valid english word",
"data": "tomorrow",
"valid": true
},
{
"description": "'manana' is an invalid english word",
"data": "manana",
"valid": false
},
{
"description": "number is invalid",
"data": 1,
"valid": false
},
{
"description": "'today' throws an exception, not in the dictionary",
"data": "today",
"error": "unknown word"
}
]
},
{
"description": "async formats when $data ref resolves to async format name",
"schema": {
"$async": true,
"additionalProperties": {
"type": "string",
"format": { "$data": "0#" }
}
},
"tests": [
{
"description": "'tomorrow' is a valid english word",
"data": { "english_word": "tomorrow" },
"valid": true
},
{
"description": "'manana' is an invalid english word",
"data": { "english_word": "manana" },
"valid": false
},
{
"description": "number is invalid",
"data": { "english_word": 1 },
"valid": false
},
{
"description": "'today' throws an exception, not in the dictionary",
"data": { "english_word": "today" },
"error": "unknown word"
},
{
"description": "valid date",
"data": { "date": "2016-01-25" },
"valid": true
},
{
"description": "invalid date",
"data": { "date": "01/25/2016" },
"valid": false
},
{
"description": "number is invalid",
"data": { "date": 1 },
"valid": false
}
]
}
]

88
spec/async/keyword.json Normal file
View File

@ -0,0 +1,88 @@
[
{
"description": "async custom keywords (validated)",
"schema": {
"$async": true,
"properties": {
"userId": {
"type": "integer",
"idExists": { "table": "users" }
},
"postId": {
"type": "integer",
"idExists": { "table": "posts" }
},
"categoryId": {
"description": "will throw if present, no such table",
"type": "integer",
"idExists": { "table": "categories" }
}
}
},
"tests": [
{
"description": "valid object",
"data": { "userId": 1, "postId": 21 },
"valid": true
},
{
"description": "another valid object",
"data": { "userId": 5, "postId": 25 },
"valid": true
},
{
"description": "invalid - no such post",
"data": { "userId": 5, "postId": 10 },
"valid": false
},
{
"description": "invalid - no such user",
"data": { "userId": 9, "postId": 25 },
"valid": false
},
{
"description": "should throw exception during validation - no such table",
"data": { "postId": 25, "categoryId": 1 },
"error": "no such table"
}
]
},
{
"description": "async custom keywords (compiled)",
"schema": {
"$async": true,
"properties": {
"userId": {
"type": "integer",
"idExistsCompiled": { "table": "users" }
},
"postId": {
"type": "integer",
"idExistsCompiled": { "table": "posts" }
}
}
},
"tests": [
{
"description": "valid object",
"data": { "userId": 1, "postId": 21 },
"valid": true
},
{
"description": "another valid object",
"data": { "userId": 5, "postId": 25 },
"valid": true
},
{
"description": "invalid - no such post",
"data": { "userId": 5, "postId": 10 },
"valid": false
},
{
"description": "invalid - no such user",
"data": { "userId": 9, "postId": 25 },
"valid": false
}
]
}
]

27
spec/async/no_async.json Normal file
View File

@ -0,0 +1,27 @@
[
{
"description": "async schema without async elements",
"schema": {
"$async": true,
"type": "string",
"maxLength": 3
},
"tests": [
{
"description": "string <= 3 chars is valid",
"data": "abc",
"valid": true
},
{
"description": "string > 3 chars is invalid",
"data": "abcd",
"valid": false
},
{
"description": "number is invalid",
"data": 1,
"valid": false
}
]
}
]

103
spec/async_schemas.spec.js Normal file
View File

@ -0,0 +1,103 @@
'use strict';
var jsonSchemaTest = require('json-schema-test')
, Promise = require('./promise')
, getAjvInstances = require('./ajv_async_instances')
, assert = require('./chai').assert;
var instances = getAjvInstances({ v5: true });
instances.forEach(addAsyncFormatsAndKeywords);
jsonSchemaTest(instances, {
description: 'asynchronous schemas tests of ' + instances.length + ' ajv instances with different options',
suites: testSuites(),
async: true,
assert: require('./chai').assert,
Promise: Promise,
afterError: function (res) {
console.log('ajv options:', res.validator._opts);
},
// afterEach: function (res) {
// console.log(res.errors);
// },
cwd: __dirname,
hideFolder: 'async/',
timeout: 90000
});
function testSuites() {
if (typeof window == 'object') {
var suites = {
'async schemas': require('./async/{**/,}*.json', {mode: 'list'})
};
for (var suiteName in suites) {
suites[suiteName].forEach(function (suite) {
suite.test = suite.module;
});
}
} else {
var suites = {
'async schemas': './async/{**/,}*.json'
}
}
return suites;
}
function addAsyncFormatsAndKeywords (ajv) {
ajv.addFormat('english_word', {
async: true,
validate: checkWordOnServer
});
ajv.addKeyword('idExists', {
async: true,
type: 'number',
validate: checkIdExists
});
ajv.addKeyword('idExistsCompiled', {
async: true,
type: 'number',
compile: compileCheckIdExists
});
}
function checkWordOnServer(str) {
return str == 'tomorrow' ? Promise.resolve(true)
: str == 'manana' ? Promise.resolve(false)
: Promise.reject(new Error('unknown word'));
}
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 compileCheckIdExists(schema) {
switch (schema.table) {
case 'users': return compileCheck([1, 5, 8]);
case 'posts': return compileCheck([21, 25, 28]);
default: throw new Error('no such table');
}
function compileCheck(IDs) {
return function (data) {
return Promise.resolve(IDs.indexOf(data) >= 0);
};
}
}

View File

@ -1,17 +1,10 @@
'use strict';
var Ajv = require('./ajv')
, Promise = require('./promise')
, getAjvInstances = require('./ajv_async_instances')
, should = require('./chai').should()
, co = require('co')
, Promise = require('bluebird');
Promise.config({ warnings: false });
var g = typeof global == 'object' ? global :
typeof window == 'object' ? window : this;
g.Promise = g.Promise || Promise;
, co = require('co');
describe('async schemas, formats and keywords', function() {
@ -48,9 +41,6 @@ describe('async schemas, formats and keywords', function() {
shouldBeValid( _co(validate('abc')) ),
shouldBeInvalid( _co(validate('abcd')) ),
shouldBeInvalid( _co(validate(1)) ),
shouldBeValid( ajv.validate(schema, 'abc') ),
shouldBeInvalid( ajv.validate(schema, 'abcd') ),
shouldBeInvalid( ajv.validate(schema, 1) )
]);
}
});
@ -80,28 +70,6 @@ describe('async schemas, formats and keywords', function() {
describe('async formats', function() {
beforeEach(addFormatEnglishWord);
it('should return promise that resolves as true or rejects with array of errors', function() {
var schema = {
$async: true,
type: 'string',
format: 'english_word'
};
return repeat(function() { return Promise.map(instances, test); });
function test(ajv) {
var validate = ajv.compile(schema);
var _co = useCo(ajv);
return Promise.all([
shouldBeValid( _co(validate('tomorrow')) ),
shouldBeInvalid( _co(validate('manana')) ),
shouldBeInvalid( _co(validate(1)) ),
shouldThrow( _co(validate('today')), 'unknown word' )
]);
}
});
it('should fail compilation if async format is inside sync schema', function() {
instances.forEach(function (ajv) {
@ -117,38 +85,6 @@ describe('async schemas, formats and keywords', function() {
ajv.compile(schema);
});
});
it('should support async formats when $data ref resolves to async format name', function() {
instances = getAjvInstances({ v5: true });
addFormatEnglishWord();
var schema = {
$async: true,
additionalProperties: {
type: 'string',
format: { $data: '0#' }
}
};
return repeat(function() { return Promise.map(instances, test); });
function test(ajv, index) {
var validate = ajv.compile(schema);
var _co = useCo(ajv);
return Promise.all([
shouldBeValid( _co(validate({ english_word: 'tomorrow' })) ),
shouldBeInvalid( _co(validate({ english_word: 'manana' })) ),
shouldBeInvalid( _co(validate({ english_word: 1 })) ),
shouldThrow( _co(validate({ english_word: 'today' })), 'unknown word' ),
shouldBeValid( _co(validate({ date: '2016-01-25' })) ),
shouldBeInvalid( _co(validate({ date: '01/25/2016' })) ),
shouldBeInvalid( _co(validate({ date: 1 })) ),
]);
}
});
});
@ -160,74 +96,10 @@ describe('async schemas, formats and keywords', function() {
type: 'number',
validate: checkIdExists
});
ajv.addKeyword('idExistsCompiled', {
async: true,
type: 'number',
compile: compileCheckIdExists
});
});
});
it('should validate custom keyword that returns promise', function() {
var schema1 = {
$async: true,
properties: {
userId: {
type: 'integer',
idExists: { table: 'users' }
},
postId: {
type: 'integer',
idExists: { table: 'posts' }
},
categoryId: {
description: 'will throw if present, no such table',
type: 'integer',
idExists: { table: 'categories' }
}
}
};
var schema2 = {
$async: true,
properties: {
userId: {
type: 'integer',
idExistsCompiled: { table: 'users' }
},
postId: {
type: 'integer',
idExistsCompiled: { table: 'posts' }
}
}
};
return repeat(function() { return Promise.all([
test(instances, schema1, true),
test(instances, schema2)
]); });
function test(instances, schema, checkThrow) {
return Promise.map(instances, function (ajv) {
var validate = ajv.compile(schema);
var _co = useCo(ajv);
return Promise.all([
shouldBeValid( _co(validate({ userId: 1, postId: 21 })) ),
shouldBeValid( _co(validate({ userId: 5, postId: 25 })) ),
shouldBeInvalid( _co(validate({ userId: 5, postId: 10 })) ), // no post
shouldBeInvalid( _co(validate({ userId: 9, postId: 25 })) ), // no user
checkThrow
? shouldThrow( _co(validate({ postId: 25, categoryId: 1 })), 'no such table' )
: undefined
]);
});
}
});
it('should fail compilation if async keyword is inside sync schema', function() {
instances.forEach(function (ajv) {
var schema = {
@ -261,21 +133,6 @@ describe('async schemas, formats and keywords', function() {
return Promise.resolve(IDs.indexOf(data) >= 0);
}
}
function compileCheckIdExists(schema) {
switch (schema.table) {
case 'users': return compileCheck([1, 5, 8]);
case 'posts': return compileCheck([21, 25, 28]);
default: throw new Error('no such table');
}
function compileCheck(IDs) {
return function (data) {
return Promise.resolve(IDs.indexOf(data) >= 0);
};
}
}
});

12
spec/promise.js Normal file
View File

@ -0,0 +1,12 @@
'use strict'
var Promise = require('bluebird');
Promise.config({ warnings: false });
var g = typeof global == 'object' ? global :
typeof window == 'object' ? window : this;
g.Promise = g.Promise || Promise;
module.exports = Promise;