refactor: split options.spec.js file

master
Evgeny Poberezkin 2019-02-10 12:05:05 +00:00
parent 51685b8eef
commit 71dc5dc27d
15 changed files with 1638 additions and 1582 deletions

View File

@ -12,9 +12,9 @@
".tonic_example.js"
],
"scripts": {
"eslint": "eslint lib/*.js lib/compile/*.js spec/*.js scripts",
"jshint": "jshint lib/*.js lib/**/*.js --exclude lib/dotjs/**/*",
"test-spec": "mocha spec/*.spec.js -R spec",
"eslint": "eslint lib/{compile/,}*.js spec/{**/,}*.js scripts --ignore-pattern spec/JSON-Schema-Test-Suite",
"jshint": "jshint lib/{compile/,}*.js",
"test-spec": "mocha spec/{**/,}*.spec.js -R spec",
"test-fast": "AJV_FAST_TEST=true npm run test-spec",
"test-debug": "mocha spec/*.spec.js --debug-brk -R spec",
"test-cov": "nyc npm run test-spec",

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,92 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('$comment option', function() {
describe('= true', function() {
var logCalls, consoleLog;
beforeEach(function () {
consoleLog = console.log;
console.log = log;
});
afterEach(function () {
console.log = consoleLog;
});
function log() {
logCalls.push(Array.prototype.slice.call(arguments));
}
it('should log the text from $comment keyword', function() {
var schema = {
properties: {
foo: {$comment: 'property foo'},
bar: {$comment: 'property bar', type: 'integer'}
}
};
var ajv = new Ajv({$comment: true});
var fullAjv = new Ajv({allErrors: true, $comment: true});
[ajv, fullAjv].forEach(function (_ajv) {
var validate = _ajv.compile(schema);
test({}, true, []);
test({foo: 1}, true, [['property foo']]);
test({foo: 1, bar: 2}, true, [['property foo'], ['property bar']]);
test({foo: 1, bar: 'baz'}, false, [['property foo'], ['property bar']]);
function test(data, valid, expectedLogCalls) {
logCalls = [];
validate(data) .should.equal(valid);
logCalls .should.eql(expectedLogCalls);
}
});
console.log = consoleLog;
});
});
describe('function hook', function() {
var hookCalls;
function hook() {
hookCalls.push(Array.prototype.slice.call(arguments));
}
it('should pass the text from $comment keyword to the hook', function() {
var schema = {
properties: {
foo: {$comment: 'property foo'},
bar: {$comment: 'property bar', type: 'integer'}
}
};
var ajv = new Ajv({$comment: hook});
var fullAjv = new Ajv({allErrors: true, $comment: hook});
[ajv, fullAjv].forEach(function (_ajv) {
var validate = _ajv.compile(schema);
test({}, true, []);
test({foo: 1}, true, [['property foo', '#/properties/foo/$comment', schema]]);
test({foo: 1, bar: 2}, true,
[['property foo', '#/properties/foo/$comment', schema],
['property bar', '#/properties/bar/$comment', schema]]);
test({foo: 1, bar: 'baz'}, false,
[['property foo', '#/properties/foo/$comment', schema],
['property bar', '#/properties/bar/$comment', schema]]);
function test(data, valid, expectedHookCalls) {
hookCalls = [];
validate(data) .should.equal(valid);
hookCalls .should.eql(expectedHookCalls);
}
});
});
});
});

View File

@ -0,0 +1,79 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('meta and validateSchema options', function() {
it('should add draft-7 meta schema by default', function() {
testOptionMeta(new Ajv);
testOptionMeta(new Ajv({ meta: true }));
function testOptionMeta(ajv) {
ajv.getSchema('http://json-schema.org/draft-07/schema') .should.be.a('function');
ajv.validateSchema({ type: 'integer' }) .should.equal(true);
ajv.validateSchema({ type: 123 }) .should.equal(false);
should.not.throw(function() { ajv.addSchema({ type: 'integer' }); });
should.throw(function() { ajv.addSchema({ type: 123 }); });
}
});
it('should throw if meta: false and validateSchema: true', function() {
var ajv = new Ajv({ meta: false });
should.not.exist(ajv.getSchema('http://json-schema.org/draft-07/schema'));
should.not.throw(function() { ajv.addSchema({ type: 'wrong_type' }, 'integer'); });
});
it('should skip schema validation with validateSchema: false', function() {
var ajv = new Ajv;
should.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); });
ajv = new Ajv({ validateSchema: false });
should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); });
ajv = new Ajv({ validateSchema: false, meta: false });
should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); });
});
it('should not throw on invalid schema with validateSchema: "log"', function() {
var logError = console.error;
var loggedError = false;
console.error = function() { loggedError = true; logError.apply(console, arguments); };
var ajv = new Ajv({ validateSchema: 'log' });
should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); });
loggedError .should.equal(true);
loggedError = false;
ajv = new Ajv({ validateSchema: 'log', meta: false });
should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); });
loggedError .should.equal(false);
console.error = logError;
});
it('should validate v6 schema', function() {
var ajv = new Ajv;
ajv.validateSchema({ contains: { minimum: 2 } }) .should.equal(true);
ajv.validateSchema({ contains: 2 }). should.equal(false);
});
it('should use option meta as default meta schema', function() {
var meta = {
$schema: 'http://json-schema.org/draft-07/schema',
properties: {
myKeyword: { type: 'boolean' }
}
};
var ajv = new Ajv({ meta: meta });
ajv.validateSchema({ myKeyword: true }) .should.equal(true);
ajv.validateSchema({ myKeyword: 2 }) .should.equal(false);
ajv.validateSchema({
$schema: 'http://json-schema.org/draft-07/schema',
myKeyword: 2
}) .should.equal(true);
ajv = new Ajv;
ajv.validateSchema({ myKeyword: true }) .should.equal(true);
ajv.validateSchema({ myKeyword: 2 }) .should.equal(true);
});
});

View File

@ -0,0 +1,97 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('nullable option', function() {
var ajv;
describe('= true', function() {
beforeEach(function () {
ajv = new Ajv({
nullable: true
});
});
it('should add keyword "nullable"', function() {
testNullable({
type: 'number',
nullable: true
});
testNullable({
type: ['number'],
nullable: true
});
testNullable({
type: ['number', 'null']
});
testNullable({
type: ['number', 'null'],
nullable: true
});
testNotNullable({type: 'number'});
testNotNullable({type: ['number']});
});
it('should respect "nullable" == false with opts.nullable == true', function() {
testNotNullable({
type: 'number',
nullable: false
});
testNotNullable({
type: ['number'],
nullable: false
});
});
});
describe('without option "nullable"', function() {
it('should ignore keyword nullable', function() {
ajv = new Ajv;
testNotNullable({
type: 'number',
nullable: true
});
testNotNullable({
type: ['number'],
nullable: true
});
testNullable({
type: ['number', 'null'],
});
testNullable({
type: ['number', 'null'],
nullable: true
});
should.not.throw(function () {
ajv.compile({nullable: false});
});
});
});
function testNullable(schema) {
var validate = ajv.compile(schema);
validate(1) .should.equal(true);
validate(null) .should.equal(true);
validate('1') .should.equal(false);
}
function testNotNullable(schema) {
var validate = ajv.compile(schema);
validate(1) .should.equal(true);
validate(null) .should.equal(false);
validate('1') .should.equal(false);
}
});

View File

@ -0,0 +1,130 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('options to add schemas', function() {
describe('schemas', function() {
it('should add schemas from object', function() {
var ajv = new Ajv({ schemas: {
int: { type: 'integer' },
str: { type: 'string' }
}});
ajv.validate('int', 123) .should.equal(true);
ajv.validate('int', 'foo') .should.equal(false);
ajv.validate('str', 'foo') .should.equal(true);
ajv.validate('str', 123) .should.equal(false);
});
it('should add schemas from array', function() {
var ajv = new Ajv({ schemas: [
{ $id: 'int', type: 'integer' },
{ $id: 'str', type: 'string' },
{ $id: 'obj', properties: { int: { $ref: 'int' }, str: { $ref: 'str' } } }
]});
ajv.validate('obj', { int: 123, str: 'foo' }) .should.equal(true);
ajv.validate('obj', { int: 'foo', str: 'bar' }) .should.equal(false);
ajv.validate('obj', { int: 123, str: 456 }) .should.equal(false);
});
});
describe('addUsedSchema', function() {
[true, undefined].forEach(function (optionValue) {
describe('= ' + optionValue, function() {
var ajv;
beforeEach(function() {
ajv = new Ajv({ addUsedSchema: optionValue });
});
describe('compile and validate', function() {
it('should add schema', function() {
var schema = { $id: 'str', type: 'string' };
var validate = ajv.compile(schema);
validate('abc') .should.equal(true);
validate(1) .should.equal(false);
ajv.getSchema('str') .should.equal(validate);
schema = { $id: 'int', type: 'integer' };
ajv.validate(schema, 1) .should.equal(true);
ajv.validate(schema, 'abc') .should.equal(false);
ajv.getSchema('int') .should.be.a('function');
});
it('should throw with duplicate ID', function() {
ajv.compile({ $id: 'str', type: 'string' });
should.throw(function() {
ajv.compile({ $id: 'str', minLength: 2 });
});
var schema = { $id: 'int', type: 'integer' };
var schema2 = { $id: 'int', minimum: 0 };
ajv.validate(schema, 1) .should.equal(true);
should.throw(function() {
ajv.validate(schema2, 1);
});
});
});
});
});
describe('= false', function() {
var ajv;
beforeEach(function() {
ajv = new Ajv({ addUsedSchema: false });
});
describe('compile and validate', function() {
it('should NOT add schema', function() {
var schema = { $id: 'str', type: 'string' };
var validate = ajv.compile(schema);
validate('abc') .should.equal(true);
validate(1) .should.equal(false);
should.equal(ajv.getSchema('str'), undefined);
schema = { $id: 'int', type: 'integer' };
ajv.validate(schema, 1) .should.equal(true);
ajv.validate(schema, 'abc') .should.equal(false);
should.equal(ajv.getSchema('int'), undefined);
});
it('should NOT throw with duplicate ID', function() {
ajv.compile({ $id: 'str', type: 'string' });
should.not.throw(function() {
ajv.compile({ $id: 'str', minLength: 2 });
});
var schema = { $id: 'int', type: 'integer' };
var schema2 = { $id: 'int', minimum: 0 };
ajv.validate(schema, 1) .should.equal(true);
should.not.throw(function() {
ajv.validate(schema2, 1) .should.equal(true);
});
});
});
});
});
describe('serialize', function() {
var serializeCalled;
it('should use custom function to serialize schema to string', function() {
serializeCalled = undefined;
var ajv = new Ajv({ serialize: serialize });
ajv.addSchema({ type: 'string' });
should.equal(serializeCalled, true);
});
function serialize(schema) {
serializeCalled = true;
return JSON.stringify(schema);
}
});
});

View File

@ -0,0 +1,115 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('code generation options', function () {
describe('sourceCode', function() {
describe('= true', function() {
it('should add source.code property', function() {
test(new Ajv({sourceCode: true}));
function test(ajv) {
var validate = ajv.compile({ "type": "number" });
validate.source.code .should.be.a('string');
}
});
});
describe('= false and default', function() {
it('should not add source and sourceCode properties', function() {
test(new Ajv);
test(new Ajv({sourceCode: false}));
function test(ajv) {
var validate = ajv.compile({ "type": "number" });
should.not.exist(validate.source);
should.not.exist(validate.sourceCode);
}
});
});
});
describe('processCode', function() {
it('should process generated code', function() {
var ajv = new Ajv;
var validate = ajv.compile({type: 'string'});
validate.toString().split('\n').length .should.equal(1);
var beautify = require('js-beautify').js_beautify;
var ajvPC = new Ajv({processCode: beautify});
validate = ajvPC.compile({type: 'string'});
validate.toString().split('\n').length .should.be.above(1);
validate('foo') .should.equal(true);
validate(1) .should.equal(false);
});
});
describe('passContext option', function() {
var ajv, contexts;
beforeEach(function() {
contexts = [];
});
describe('= true', function() {
it('should pass this value as context to custom keyword validation function', function() {
var validate = getValidate(true);
var self = {};
validate.call(self, {});
contexts .should.have.length(4);
contexts.forEach(function(ctx) {
ctx .should.equal(self);
});
});
});
describe('= false', function() {
it('should pass ajv instance as context to custom keyword validation function', function() {
var validate = getValidate(false);
var self = {};
validate.call(self, {});
contexts .should.have.length(4);
contexts.forEach(function(ctx) {
ctx .should.equal(ajv);
});
});
});
function getValidate(passContext) {
ajv = new Ajv({ passContext: passContext, inlineRefs: false });
ajv.addKeyword('testValidate', { validate: storeContext });
ajv.addKeyword('testCompile', { compile: compileTestValidate });
var schema = {
definitions: {
test1: {
testValidate: true,
testCompile: true,
},
test2: {
allOf: [ { $ref: '#/definitions/test1' } ]
}
},
allOf: [
{ $ref: '#/definitions/test1' },
{ $ref: '#/definitions/test2' }
]
};
return ajv.compile(schema);
}
function storeContext() {
contexts.push(this);
return true;
}
function compileTestValidate() {
return storeContext;
}
});
});

View File

@ -0,0 +1,167 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('referenced schema options', function() {
describe('extendRefs', function() {
describe('= true', function() {
it('should allow extending $ref with other keywords', function() {
test(new Ajv({ extendRefs: true }), true);
});
it('should NOT log warning if extendRefs is true', function() {
testWarning(new Ajv({ extendRefs: true }));
});
});
describe('= "ignore" and default', function() {
it('should ignore other keywords when $ref is used', function() {
test(new Ajv);
test(new Ajv({ extendRefs: 'ignore' }), false);
});
it('should log warning when other keywords are used with $ref', function() {
testWarning(new Ajv, /keywords\signored/);
testWarning(new Ajv({ extendRefs: 'ignore' }), /keywords\signored/);
});
});
describe('= "fail"', function() {
it('should fail schema compilation if other keywords are used with $ref', function() {
testFail(new Ajv({ extendRefs: 'fail' }));
function testFail(ajv) {
should.throw(function() {
var schema = {
"definitions": {
"int": { "type": "integer" }
},
"$ref": "#/definitions/int",
"minimum": 10
};
ajv.compile(schema);
});
should.not.throw(function() {
var schema = {
"definitions": {
"int": { "type": "integer" }
},
"allOf": [
{ "$ref": "#/definitions/int" },
{ "minimum": 10 }
]
};
ajv.compile(schema);
});
}
});
});
function test(ajv, shouldExtendRef) {
var schema = {
"definitions": {
"int": { "type": "integer" }
},
"$ref": "#/definitions/int",
"minimum": 10
};
var validate = ajv.compile(schema);
validate(10) .should.equal(true);
validate(1) .should.equal(!shouldExtendRef);
schema = {
"definitions": {
"int": { "type": "integer" }
},
"type": "object",
"properties": {
"foo": {
"$ref": "#/definitions/int",
"minimum": 10
},
"bar": {
"allOf": [
{ "$ref": "#/definitions/int" },
{ "minimum": 10 }
]
}
}
};
validate = ajv.compile(schema);
validate({ foo: 10, bar: 10 }) .should.equal(true);
validate({ foo: 1, bar: 10 }) .should.equal(!shouldExtendRef);
validate({ foo: 10, bar: 1 }) .should.equal(false);
}
function testWarning(ajv, msgPattern) {
var oldConsole;
try {
oldConsole = console.warn;
var consoleMsg;
console.warn = function() {
consoleMsg = Array.prototype.join.call(arguments, ' ');
};
var schema = {
"definitions": {
"int": { "type": "integer" }
},
"$ref": "#/definitions/int",
"minimum": 10
};
ajv.compile(schema);
if (msgPattern) consoleMsg .should.match(msgPattern);
else should.not.exist(consoleMsg);
} finally {
console.warn = oldConsole;
}
}
});
describe('missingRefs', function() {
it('should throw if ref is missing without this option', function() {
var ajv = new Ajv;
should.throw(function() {
ajv.compile({ $ref: 'missing_reference' });
});
});
it('should not throw and pass validation with missingRef == "ignore"', function() {
testMissingRefsIgnore(new Ajv({ missingRefs: 'ignore' }));
testMissingRefsIgnore(new Ajv({ missingRefs: 'ignore', allErrors: true }));
function testMissingRefsIgnore(ajv) {
var validate = ajv.compile({ $ref: 'missing_reference' });
validate({}) .should.equal(true);
}
});
it('should not throw and fail validation with missingRef == "fail" if the ref is used', function() {
testMissingRefsFail(new Ajv({ missingRefs: 'fail' }));
testMissingRefsFail(new Ajv({ missingRefs: 'fail', verbose: true }));
testMissingRefsFail(new Ajv({ missingRefs: 'fail', allErrors: true }));
testMissingRefsFail(new Ajv({ missingRefs: 'fail', allErrors: true, verbose: true }));
function testMissingRefsFail(ajv) {
var validate = ajv.compile({
anyOf: [
{ type: 'number' },
{ $ref: 'missing_reference' }
]
});
validate(123) .should.equal(true);
validate('foo') .should.equal(false);
validate = ajv.compile({ $ref: 'missing_reference' });
validate({}) .should.equal(false);
}
});
});
});

View File

@ -0,0 +1,158 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('reporting options', function () {
describe('verbose', function() {
it('should add schema, parentSchema and data to errors with verbose option == true', function() {
testVerbose(new Ajv({ verbose: true }));
testVerbose(new Ajv({ verbose: true, allErrors: true }));
function testVerbose(ajv) {
var schema = { properties: { foo: { minimum: 5 } } };
var validate = ajv.compile(schema);
var data = { foo: 3 };
validate(data) .should.equal(false);
validate.errors .should.have.length(1);
var err = validate.errors[0];
should.equal(err.schema, 5);
err.parentSchema .should.eql({ minimum: 5 });
err.parentSchema .should.equal(schema.properties.foo); // by reference
should.equal(err.data, 3);
}
});
});
describe('allErrors', function() {
it('should be disabled inside "not" keyword', function() {
test(new Ajv, false);
test(new Ajv({ allErrors: true }), true);
function test(ajv, allErrors) {
var format1called = false
, format2called = false;
ajv.addFormat('format1', function() {
format1called = true;
return false;
});
ajv.addFormat('format2', function() {
format2called = true;
return false;
});
var schema1 = {
allOf: [
{ format: 'format1' },
{ format: 'format2' }
]
};
ajv.validate(schema1, 'abc') .should.equal(false);
ajv.errors .should.have.length(allErrors ? 2 : 1);
format1called .should.equal(true);
format2called .should.equal(allErrors);
var schema2 = {
not: schema1
};
format1called = format2called = false;
ajv.validate(schema2, 'abc') .should.equal(true);
should.equal(ajv.errors, null);
format1called .should.equal(true);
format2called .should.equal(false);
}
});
});
describe('logger', function() {
/**
* The logger option tests are based on the meta scenario which writes into the logger.warn
*/
var origConsoleWarn = console.warn;
var consoleCalled;
beforeEach(function() {
consoleCalled = false;
console.warn = function() {
consoleCalled = true;
};
});
afterEach(function() {
console.warn = origConsoleWarn;
});
it('no custom logger is given - global console should be used', function() {
var ajv = new Ajv({
meta: false
});
ajv.compile({
type: 'number',
minimum: 1
});
should.equal(consoleCalled, true);
});
it('custom logger is an object - logs should only report to it', function() {
var loggerCalled = false;
var logger = {
warn: log,
log: log,
error: log
};
var ajv = new Ajv({
meta: false,
logger: logger
});
ajv.compile({
type: 'number',
minimum: 1
});
should.equal(loggerCalled, true);
should.equal(consoleCalled, false);
function log() {
loggerCalled = true;
}
});
it('logger option is false - no logs should be reported', function() {
var ajv = new Ajv({
meta: false,
logger: false
});
ajv.compile({
type: 'number',
minimum: 1
});
should.equal(consoleCalled, false);
});
it('logger option is an object without required methods - an error should be thrown', function() {
(function(){
new Ajv({
meta: false,
logger: {}
});
}).should.throw(Error, /logger must implement log, warn and error methods/);
});
});
});

View File

@ -0,0 +1,94 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('validation options', function() {
describe('format', function() {
it('should not validate formats if option format == false', function() {
var ajv = new Ajv
, ajvFF = new Ajv({ format: false });
var schema = { format: 'date-time' };
var invalideDateTime = '06/19/1963 08:30:06 PST';
ajv.validate(schema, invalideDateTime) .should.equal(false);
ajvFF.validate(schema, invalideDateTime) .should.equal(true);
});
});
describe('formats', function() {
it('should add formats from options', function() {
var ajv = new Ajv({ formats: {
identifier: /^[a-z_$][a-z0-9_$]*$/i
}});
var validate = ajv.compile({ format: 'identifier' });
validate('Abc1') .should.equal(true);
validate('123') .should.equal(false);
validate(123) .should.equal(true);
});
});
describe('uniqueItems', function() {
it('should not validate uniqueItems with uniqueItems option == false', function() {
testUniqueItems(new Ajv({ uniqueItems: false }));
testUniqueItems(new Ajv({ uniqueItems: false, allErrors: true }));
function testUniqueItems(ajv) {
var validate = ajv.compile({ uniqueItems: true });
validate([1,2,3]) .should.equal(true);
validate([1,1,1]) .should.equal(true);
}
});
});
describe('unicode', function() {
it('should use String.prototype.length with unicode option == false', function() {
var ajvUnicode = new Ajv;
testUnicode(new Ajv({ unicode: false }));
testUnicode(new Ajv({ unicode: false, allErrors: true }));
function testUnicode(ajv) {
var validateWithUnicode = ajvUnicode.compile({ minLength: 2 });
var validate = ajv.compile({ minLength: 2 });
validateWithUnicode('😀') .should.equal(false);
validate('😀') .should.equal(true);
validateWithUnicode = ajvUnicode.compile({ maxLength: 1 });
validate = ajv.compile({ maxLength: 1 });
validateWithUnicode('😀') .should.equal(true);
validate('😀') .should.equal(false);
}
});
});
describe('multipleOfPrecision', function() {
it('should allow for some deviation from 0 when validating multipleOf with value < 1', function() {
test(new Ajv({ multipleOfPrecision: 7 }));
test(new Ajv({ multipleOfPrecision: 7, allErrors: true }));
function test(ajv) {
var schema = { multipleOf: 0.01 };
var validate = ajv.compile(schema);
validate(4.18) .should.equal(true);
validate(4.181) .should.equal(false);
schema = { multipleOf: 0.0000001 };
validate = ajv.compile(schema);
validate(53.198098) .should.equal(true);
validate(53.1980981) .should.equal(true);
validate(53.19809811) .should.equal(false);
}
});
});
});

View File

@ -0,0 +1,178 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('ownProperties option', function() {
var ajv, ajvOP, ajvOP1;
beforeEach(function() {
ajv = new Ajv({ allErrors: true });
ajvOP = new Ajv({ ownProperties: true, allErrors: true });
ajvOP1 = new Ajv({ ownProperties: true });
});
it('should only validate own properties with additionalProperties', function() {
var schema = {
properties: { a: { type: 'number' } },
additionalProperties: false
};
var obj = { a: 1 };
var proto = { b: 2 };
test(schema, obj, proto);
});
it('should only validate own properties with properties keyword', function() {
var schema = {
properties: {
a: { type: 'number' },
b: { type: 'number' }
}
};
var obj = { a: 1 };
var proto = { b: 'not a number' };
test(schema, obj, proto);
});
it('should only validate own properties with required keyword', function() {
var schema = {
required: ['a', 'b']
};
var obj = { a: 1 };
var proto = { b: 2 };
test(schema, obj, proto, 1, true);
});
it('should only validate own properties with required keyword - many properties', function() {
ajv = new Ajv({ allErrors: true, loopRequired: 1 });
ajvOP = new Ajv({ ownProperties: true, allErrors: true, loopRequired: 1 });
ajvOP1 = new Ajv({ ownProperties: true, loopRequired: 1 });
var schema = {
required: ['a', 'b', 'c', 'd']
};
var obj = { a: 1, b: 2 };
var proto = { c: 3, d: 4 };
test(schema, obj, proto, 2, true);
});
it('should only validate own properties with required keyword as $data', function() {
ajv = new Ajv({ allErrors: true, $data: true });
ajvOP = new Ajv({ ownProperties: true, allErrors: true, $data: true });
ajvOP1 = new Ajv({ ownProperties: true, $data: true });
var schema = {
required: { $data: '0/req' },
properties: {
req: {
type: 'array',
items: { type: 'string' }
}
}
};
var obj = {
req: ['a', 'b'],
a: 1
};
var proto = { b: 2 };
test(schema, obj, proto, 1, true);
});
it('should only validate own properties with properties and required keyword', function() {
var schema = {
properties: {
a: { type: 'number' },
b: { type: 'number' }
},
required: ['a', 'b']
};
var obj = { a: 1 };
var proto = { b: 2 };
test(schema, obj, proto, 1, true);
});
it('should only validate own properties with dependencies keyword', function() {
var schema = {
dependencies: {
a: ['c'],
b: ['d']
}
};
var obj = { a: 1, c: 3 };
var proto = { b: 2 };
test(schema, obj, proto);
obj = { a: 1, b: 2, c: 3 };
proto = { d: 4 };
test(schema, obj, proto, 1, true);
});
it('should only validate own properties with schema dependencies', function() {
var schema = {
dependencies: {
a: { not: { required: ['c'] } },
b: { not: { required: ['d'] } }
}
};
var obj = { a: 1, d: 3 };
var proto = { b: 2 };
test(schema, obj, proto);
obj = { a: 1, b: 2 };
proto = { d: 4 };
test(schema, obj, proto);
});
it('should only validate own properties with patternProperties', function() {
var schema = {
patternProperties: { 'f.*o': { type: 'integer' } },
};
var obj = { fooo: 1 };
var proto = { foo: 'not a number' };
test(schema, obj, proto);
});
it('should only validate own properties with propertyNames', function() {
var schema = {
propertyNames: {
format: 'email'
}
};
var obj = { 'e@example.com': 2 };
var proto = { 'not email': 1 };
test(schema, obj, proto, 2);
});
function test(schema, obj, proto, errors, reverse) {
errors = errors || 1;
var validate = ajv.compile(schema);
var validateOP = ajvOP.compile(schema);
var validateOP1 = ajvOP1.compile(schema);
var data = Object.create(proto);
for (var key in obj) data[key] = obj[key];
if (reverse) {
validate(data) .should.equal(true);
validateOP(data) .should.equal(false);
validateOP.errors .should.have.length(errors);
validateOP1(data) .should.equal(false);
validateOP1.errors .should.have.length(1);
} else {
validate(data) .should.equal(false);
validate.errors .should.have.length(errors);
validateOP(data) .should.equal(true);
validateOP1(data) .should.equal(true);
}
}
});

View File

@ -0,0 +1,122 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('removeAdditional option', function() {
it('should remove all additional properties', function() {
var ajv = new Ajv({ removeAdditional: 'all' });
ajv.addSchema({
$id: '//test/fooBar',
properties: { foo: { type: 'string' }, bar: { type: 'string' } }
});
var object = {
foo: 'foo', bar: 'bar', baz: 'baz-to-be-removed'
};
ajv.validate('//test/fooBar', object).should.equal(true);
object.should.have.property('foo');
object.should.have.property('bar');
object.should.not.have.property('baz');
});
it('should remove properties that would error when `additionalProperties = false`', function() {
var ajv = new Ajv({ removeAdditional: true });
ajv.addSchema({
$id: '//test/fooBar',
properties: { foo: { type: 'string' }, bar: { type: 'string' } },
additionalProperties: false
});
var object = {
foo: 'foo', bar: 'bar', baz: 'baz-to-be-removed'
};
ajv.validate('//test/fooBar', object).should.equal(true);
object.should.have.property('foo');
object.should.have.property('bar');
object.should.not.have.property('baz');
});
it('should remove properties that would error when `additionalProperties = false` (many properties, boolean schema)', function() {
var ajv = new Ajv({removeAdditional: true});
var schema = {
properties: {
obj: {
additionalProperties: false,
properties: {
a: { type: 'string' },
b: false,
c: { type: 'string' },
d: { type: 'string' },
e: { type: 'string' },
f: { type: 'string' },
g: { type: 'string' },
h: { type: 'string' },
i: { type: 'string' }
}
}
}
};
var data = {
obj: {
a: 'valid',
b: 'should not be removed',
additional: 'will be removed'
}
};
ajv.validate(schema, data) .should.equal(false);
data .should.eql({
obj: {
a: 'valid',
b: 'should not be removed'
}
});
});
it('should remove properties that would error when `additionalProperties` is a schema', function() {
var ajv = new Ajv({ removeAdditional: 'failing' });
ajv.addSchema({
$id: '//test/fooBar',
properties: { foo: { type: 'string' }, bar: { type: 'string' } },
additionalProperties: { type: 'string' }
});
var object = {
foo: 'foo', bar: 'bar', baz: 'baz-to-be-kept', fizz: 1000
};
ajv.validate('//test/fooBar', object).should.equal(true);
object.should.have.property('foo');
object.should.have.property('bar');
object.should.have.property('baz');
object.should.not.have.property('fizz');
ajv.addSchema({
$id: '//test/fooBar2',
properties: { foo: { type: 'string' }, bar: { type: 'string' } },
additionalProperties: { type: 'string', pattern: '^to-be-', maxLength: 10 }
});
object = {
foo: 'foo', bar: 'bar', baz: 'to-be-kept', quux: 'to-be-removed', fizz: 1000
};
ajv.validate('//test/fooBar2', object).should.equal(true);
object.should.have.property('foo');
object.should.have.property('bar');
object.should.have.property('baz');
object.should.not.have.property('fizz');
});
});

View File

@ -0,0 +1,72 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('schemaId option', function() {
describe('= "$id" (default)', function() {
it('should use $id and ignore id', function() {
test(new Ajv);
test(new Ajv({schemaId: '$id'}));
function test(ajv) {
ajv.addSchema({ $id: 'mySchema1', type: 'string' });
var validate = ajv.getSchema('mySchema1');
validate('foo') .should.equal(true);
validate(1) .should.equal(false);
validate = ajv.compile({ id: 'mySchema2', type: 'string' });
should.not.exist(ajv.getSchema('mySchema2'));
}
});
});
describe('= "id"', function() {
it('should use id and ignore $id', function() {
var ajv = new Ajv({schemaId: 'id', meta: false});
ajv.addMetaSchema(require('../../lib/refs/json-schema-draft-04.json'));
ajv._opts.defaultMeta = 'http://json-schema.org/draft-04/schema#';
ajv.addSchema({ id: 'mySchema1', type: 'string' });
var validate = ajv.getSchema('mySchema1');
validate('foo') .should.equal(true);
validate(1) .should.equal(false);
validate = ajv.compile({ $id: 'mySchema2', type: 'string' });
should.not.exist(ajv.getSchema('mySchema2'));
});
});
describe('= "auto"', function() {
it('should use both id and $id', function() {
var ajv = new Ajv({schemaId: 'auto'});
ajv.addSchema({ $id: 'mySchema1', type: 'string' });
var validate = ajv.getSchema('mySchema1');
validate('foo') .should.equal(true);
validate(1) .should.equal(false);
ajv.addSchema({ id: 'mySchema2', type: 'string' });
validate = ajv.getSchema('mySchema2');
validate('foo') .should.equal(true);
validate(1) .should.equal(false);
});
it('should throw if both id and $id are available and different', function() {
var ajv = new Ajv({schemaId: 'auto'});
ajv.compile({
id: 'mySchema',
$id: 'mySchema'
});
should.throw(function() {
ajv.compile({
id: 'mySchema1',
$id: 'mySchema2'
});
});
});
});
});

View File

@ -0,0 +1,108 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('unknownFormats option', function() {
describe('= true (default)', function() {
it('should fail schema compilation if unknown format is used', function() {
test(new Ajv);
test(new Ajv({unknownFormats: true}));
function test(ajv) {
should.throw(function() {
ajv.compile({ format: 'unknown' });
});
}
});
it('should fail validation if unknown format is used via $data', function() {
test(new Ajv({$data: true}));
test(new Ajv({$data: true, unknownFormats: true}));
function test(ajv) {
var validate = ajv.compile({
properties: {
foo: { format: { $data: '1/bar' } },
bar: { type: 'string' }
}
});
validate({foo: 1, bar: 'unknown'}) .should.equal(false);
validate({foo: '2016-10-16', bar: 'date'}) .should.equal(true);
validate({foo: '20161016', bar: 'date'}) .should.equal(false);
validate({foo: '20161016'}) .should.equal(true);
validate({foo: '2016-10-16', bar: 'unknown'}) .should.equal(false);
}
});
});
describe('= "ignore (default before 5.0.0)"', function() {
it('should pass schema compilation and be valid if unknown format is used', function() {
test(new Ajv({unknownFormats: 'ignore'}));
function test(ajv) {
var validate = ajv.compile({ format: 'unknown' });
validate('anything') .should.equal(true);
}
});
it('should be valid if unknown format is used via $data', function() {
test(new Ajv({$data: true, unknownFormats: 'ignore'}));
function test(ajv) {
var validate = ajv.compile({
properties: {
foo: { format: { $data: '1/bar' } },
bar: { type: 'string' }
}
});
validate({foo: 1, bar: 'unknown'}) .should.equal(true);
validate({foo: '2016-10-16', bar: 'date'}) .should.equal(true);
validate({foo: '20161016', bar: 'date'}) .should.equal(false);
validate({foo: '20161016'}) .should.equal(true);
validate({foo: '2016-10-16', bar: 'unknown'}) .should.equal(true);
}
});
});
describe('= [String]', function() {
it('should pass schema compilation and be valid if whitelisted unknown format is used', function() {
test(new Ajv({unknownFormats: ['allowed']}));
function test(ajv) {
var validate = ajv.compile({ format: 'allowed' });
validate('anything') .should.equal(true);
should.throw(function() {
ajv.compile({ format: 'unknown' });
});
}
});
it('should be valid if whitelisted unknown format is used via $data', function() {
test(new Ajv({$data: true, unknownFormats: ['allowed']}));
function test(ajv) {
var validate = ajv.compile({
properties: {
foo: { format: { $data: '1/bar' } },
bar: { type: 'string' }
}
});
validate({foo: 1, bar: 'allowed'}) .should.equal(true);
validate({foo: 1, bar: 'unknown'}) .should.equal(false);
validate({foo: '2016-10-16', bar: 'date'}) .should.equal(true);
validate({foo: '20161016', bar: 'date'}) .should.equal(false);
validate({foo: '20161016'}) .should.equal(true);
validate({foo: '2016-10-16', bar: 'allowed'}) .should.equal(true);
validate({foo: '2016-10-16', bar: 'unknown'}) .should.equal(false);
}
});
});
});

View File

@ -0,0 +1,223 @@
'use strict';
var Ajv = require('../ajv');
var getAjvInstances = require('../ajv_instances');
require('../chai').should();
describe('useDefaults options', function() {
it('should replace undefined property with default value', function() {
var instances = getAjvInstances({
allErrors: true,
loopRequired: 3
}, { useDefaults: true });
instances.forEach(test);
function test(ajv) {
var schema = {
properties: {
foo: { type: 'string', default: 'abc' },
bar: { type: 'number', default: 1 },
baz: { type: 'boolean', default: false },
nil: { type: 'null', default: null },
obj: { type: 'object', default: {} },
arr: { type: 'array', default: [] }
},
required: ['foo', 'bar', 'baz', 'nil', 'obj', 'arr'],
minProperties: 6
};
var validate = ajv.compile(schema);
var data = {};
validate(data) .should.equal(true);
data .should.eql({ foo: 'abc', bar: 1, baz: false, nil: null, obj: {}, arr:[] });
data = { foo: 'foo', bar: 2, obj: { test: true } };
validate(data) .should.equal(true);
data .should.eql({ foo: 'foo', bar: 2, baz: false, nil: null, obj: { test: true }, arr:[] });
}
});
it('should replace undefined item with default value', function() {
test(new Ajv({ useDefaults: true }));
test(new Ajv({ useDefaults: true, allErrors: true }));
function test(ajv) {
var schema = {
items: [
{ type: 'string', default: 'abc' },
{ type: 'number', default: 1 },
{ type: 'boolean', default: false }
],
minItems: 3
};
var validate = ajv.compile(schema);
var data = [];
validate(data) .should.equal(true);
data .should.eql([ 'abc', 1, false ]);
data = [ 'foo' ];
validate(data) .should.equal(true);
data .should.eql([ 'foo', 1, false ]);
data = ['foo', 2,'false'];
validate(data) .should.equal(false);
validate.errors .should.have.length(1);
data .should.eql([ 'foo', 2, 'false' ]);
}
});
it('should apply default in "then" subschema (issue #635)', function() {
test(new Ajv({ useDefaults: true }));
test(new Ajv({ useDefaults: true, allErrors: true }));
function test(ajv) {
var schema = {
if: { required: ['foo'] },
then: {
properties: {
bar: { default: 2 }
}
},
else: {
properties: {
foo: { default: 1 }
}
}
};
var validate = ajv.compile(schema);
var data = {};
validate(data) .should.equal(true);
data .should.eql({foo: 1});
data = {foo: 1};
validate(data) .should.equal(true);
data .should.eql({foo: 1, bar: 2});
}
});
describe('useDefaults: by value / by reference', function() {
describe('using by value', function() {
it('should NOT modify underlying defaults when modifying validated data', function() {
test('value', new Ajv({ useDefaults: true }));
test('value', new Ajv({ useDefaults: true, allErrors: true }));
});
});
describe('using by reference', function() {
it('should modify underlying defaults when modifying validated data', function() {
test('reference', new Ajv({ useDefaults: 'shared' }));
test('reference', new Ajv({ useDefaults: 'shared', allErrors: true }));
});
});
function test(useDefaultsMode, ajv) {
var schema = {
properties: {
items: {
type: 'array',
default: ['a-default']
}
}
};
var validate = ajv.compile(schema);
var data = {};
validate(data) .should.equal(true);
data.items .should.eql([ 'a-default' ]);
data.items.push('another-value');
data.items .should.eql([ 'a-default', 'another-value' ]);
var data2 = {};
validate(data2) .should.equal(true);
if (useDefaultsMode == 'reference')
data2.items .should.eql([ 'a-default', 'another-value' ]);
else if (useDefaultsMode == 'value')
data2.items .should.eql([ 'a-default' ]);
else
throw new Error('unknown useDefaults mode');
}
});
describe('defaults with "empty" values', function() {
var schema, data;
beforeEach(function() {
schema = {
properties: {
obj: {
properties: {
str: {default: 'foo'},
n1: {default: 1},
n2: {default: 2},
n3: {default: 3}
}
},
arr: {
items: [
{default: 'foo'},
{default: 1},
{default: 2},
{default: 3}
]
}
}
};
data = {
obj: {
str: '',
n1: null,
n2: undefined
},
arr: ['', null, undefined]
};
});
it('should NOT assign defaults when useDefaults is true/"shared"', function() {
test(new Ajv({useDefaults: true}));
test(new Ajv({useDefaults: 'shared'}));
function test(ajv) {
var validate = ajv.compile(schema);
validate(data) .should.equal(true);
data .should.eql({
obj: {
str: '',
n1: null,
n2: 2,
n3: 3
},
arr: ['', null, 2, 3]
});
}
});
it('should assign defaults when useDefaults = "empty"', function() {
var ajv = new Ajv({useDefaults: 'empty'});
var validate = ajv.compile(schema);
validate(data) .should.equal(true);
data .should.eql({
obj: {
str: 'foo',
n1: 1,
n2: 2,
n3: 3
},
arr: ['foo', 1, 2, 3]
});
});
});
});