refactor: split issues.spec.js file

master
Evgeny Poberezkin 2019-02-10 12:46:47 +00:00
parent 71dc5dc27d
commit a7f78f2894
18 changed files with 881 additions and 815 deletions

View File

@ -1,815 +0,0 @@
'use strict';
var Ajv = require('./ajv')
, should = require('./chai').should();
describe('issue #8: schema with shared references', function() {
it('should be supported by addSchema', spec('addSchema'));
it('should be supported by compile', spec('compile'));
function spec(method) {
return function() {
var ajv = new Ajv;
var propertySchema = {
type: 'string',
maxLength: 4
};
var schema = {
$id: 'obj.json#',
type: 'object',
properties: {
foo: propertySchema,
bar: propertySchema
}
};
ajv[method](schema);
var result = ajv.validate('obj.json#', { foo: 'abc', bar: 'def' });
result .should.equal(true);
result = ajv.validate('obj.json#', { foo: 'abcde', bar: 'fghg' });
result .should.equal(false);
ajv.errors .should.have.length(1);
};
}
});
describe('issue #50: references with "definitions"', function () {
it('should be supported by addSchema', spec('addSchema'));
it('should be supported by compile', spec('addSchema'));
function spec(method) {
return function() {
var result;
var ajv = new Ajv;
ajv[method]({
$id: 'http://example.com/test/person.json#',
definitions: {
name: { type: 'string' }
},
type: 'object',
properties: {
name: { $ref: '#/definitions/name'}
}
});
ajv[method]({
$id: 'http://example.com/test/employee.json#',
type: 'object',
properties: {
person: { $ref: '/test/person.json#' },
role: { type: 'string' }
}
});
result = ajv.validate('http://example.com/test/employee.json#', {
person: {
name: 'Alice'
},
role: 'Programmer'
});
result. should.equal(true);
should.equal(ajv.errors, null);
};
}
});
describe('issue #182, NaN validation', function() {
it('should not pass minimum/maximum validation', function() {
testNaN({ minimum: 1 }, false);
testNaN({ maximum: 1 }, false);
});
it('should pass type: number validation', function() {
testNaN({ type: 'number' }, true);
});
it('should not pass type: integer validation', function() {
testNaN({ type: 'integer' }, false);
});
function testNaN(schema, NaNisValid) {
var ajv = new Ajv;
var validate = ajv.compile(schema);
validate(NaN) .should.equal(NaNisValid);
}
});
describe('issue #204, options schemas and $data used together', function() {
it('should use v5 metaschemas by default', function() {
var ajv = new Ajv({
schemas: [{$id: 'str', type: 'string'}],
$data: true
});
var schema = { const: 42 };
var validate = ajv.compile(schema);
validate(42) .should.equal(true);
validate(43) .should.equal(false);
ajv.validate('str', 'foo') .should.equal(true);
ajv.validate('str', 42) .should.equal(false);
});
});
describe('issue #181, custom keyword is not validated in allErrors mode if there were previous error', function() {
it('should validate custom keyword that doesn\'t create errors', function() {
testCustomKeywordErrors({
type:'object',
errors: true,
validate: function v(/* value */) {
return false;
}
});
});
it('should validate custom keyword that creates errors', function() {
testCustomKeywordErrors({
type:'object',
errors: true,
validate: function v(/* value */) {
v.errors = v.errors || [];
v.errors.push({
keyword: 'alwaysFails',
message: 'alwaysFails error',
params: {
keyword: 'alwaysFails'
}
});
return false;
}
});
});
function testCustomKeywordErrors(def) {
var ajv = new Ajv({ allErrors: true });
ajv.addKeyword('alwaysFails', def);
var schema = {
required: ['foo'],
alwaysFails: true
};
var validate = ajv.compile(schema);
validate({ foo: 1 }) .should.equal(false);
validate.errors .should.have.length(1);
validate.errors[0].keyword .should.equal('alwaysFails');
validate({}) .should.equal(false);
validate.errors .should.have.length(2);
validate.errors[0].keyword .should.equal('required');
validate.errors[1].keyword .should.equal('alwaysFails');
}
});
describe('issue #210, mutual recursive $refs that are schema fragments', function() {
it('should compile and validate schema when one ref is fragment', function() {
var ajv = new Ajv;
ajv.addSchema({
"$id" : "foo",
"definitions": {
"bar": {
"properties": {
"baz": {
"anyOf": [
{ "enum": [42] },
{ "$ref": "boo" }
]
}
}
}
}
});
ajv.addSchema({
"$id" : "boo",
"type": "object",
"required": ["quux"],
"properties": {
"quux": { "$ref": "foo#/definitions/bar" }
}
});
var validate = ajv.compile({ "$ref": "foo#/definitions/bar" });
validate({ baz: { quux: { baz: 42 } } }) .should.equal(true);
validate({ baz: { quux: { baz: "foo" } } }) .should.equal(false);
});
it('should compile and validate schema when both refs are fragments', function() {
var ajv = new Ajv;
ajv.addSchema({
"$id" : "foo",
"definitions": {
"bar": {
"properties": {
"baz": {
"anyOf": [
{ "enum": [42] },
{ "$ref": "boo#/definitions/buu" }
]
}
}
}
}
});
ajv.addSchema({
"$id" : "boo",
"definitions": {
"buu": {
"type": "object",
"required": ["quux"],
"properties": {
"quux": { "$ref": "foo#/definitions/bar" }
}
}
}
});
var validate = ajv.compile({ "$ref": "foo#/definitions/bar" });
validate({ baz: { quux: { baz: 42 } } }) .should.equal(true);
validate({ baz: { quux: { baz: "foo" } } }) .should.equal(false);
});
});
describe('issue #240, mutually recursive fragment refs reference a common schema', function() {
var apiSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://api.schema#',
resource: {
$id: '#resource',
properties: {
id: { type: 'string' }
}
},
resourceIdentifier: {
$id: '#resource_identifier',
properties: {
id: { type: 'string' },
type: { type: 'string' }
}
}
};
var domainSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://domain.schema#',
properties: {
data: {
oneOf: [
{ $ref: 'schema://library.schema#resource_identifier' },
{ $ref: 'schema://catalog_item.schema#resource_identifier' },
]
}
}
};
it('should compile and validate schema when one ref is fragment', function() {
var ajv = new Ajv;
var librarySchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://library.schema#',
properties: {
name: { type: 'string' },
links: {
properties: {
catalogItems: {
type: 'array',
items: { $ref: 'schema://catalog_item_resource_identifier.schema#' }
}
}
}
},
definitions: {
resource_identifier: {
$id: '#resource_identifier',
allOf: [
{
properties: {
type: {
type: 'string',
'enum': ['Library']
}
}
},
{ $ref: 'schema://api.schema#resource_identifier' }
]
}
}
};
var catalogItemSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://catalog_item.schema#',
properties: {
name: { type: 'string' },
links: {
properties: {
library: { $ref: 'schema://library.schema#resource_identifier' }
}
}
},
definitions: {
resource_identifier: {
$id: '#resource_identifier',
allOf: [
{
properties: {
type: {
type: 'string',
'enum': ['CatalogItem']
}
}
},
{ $ref: 'schema://api.schema#resource_identifier' }
]
}
}
};
var catalogItemResourceIdentifierSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://catalog_item_resource_identifier.schema#',
allOf: [
{
properties: {
type: {
type: 'string',
enum: ['CatalogItem']
}
}
},
{
$ref: 'schema://api.schema#resource_identifier'
}
]
};
ajv.addSchema(librarySchema);
ajv.addSchema(catalogItemSchema);
ajv.addSchema(catalogItemResourceIdentifierSchema);
ajv.addSchema(apiSchema);
var validate = ajv.compile(domainSchema);
testSchema(validate);
});
it('should compile and validate schema when both refs are fragments', function() {
var ajv = new Ajv;
var librarySchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://library.schema#',
properties: {
name: { type: 'string' },
links: {
properties: {
catalogItems: {
type: 'array',
items: { $ref: 'schema://catalog_item.schema#resource_identifier' }
}
}
}
},
definitions: {
resource_identifier: {
$id: '#resource_identifier',
allOf: [
{
properties: {
type: {
type: 'string',
'enum': ['Library']
}
}
},
{ $ref: 'schema://api.schema#resource_identifier' }
]
}
}
};
var catalogItemSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://catalog_item.schema#',
properties: {
name: { type: 'string' },
links: {
properties: {
library: { $ref: 'schema://library.schema#resource_identifier' }
}
}
},
definitions: {
resource_identifier: {
$id: '#resource_identifier',
allOf: [
{
properties: {
type: {
type: 'string',
'enum': ['CatalogItem']
}
}
},
{ $ref: 'schema://api.schema#resource_identifier' }
]
}
}
};
ajv.addSchema(librarySchema);
ajv.addSchema(catalogItemSchema);
ajv.addSchema(apiSchema);
var validate = ajv.compile(domainSchema);
testSchema(validate);
});
function testSchema(validate) {
validate({ data: { type: 'Library', id: '123' } }) .should.equal(true);
validate({ data: { type: 'Library', id: 123 } }) .should.equal(false);
validate({ data: { type: 'CatalogItem', id: '123' } }) .should.equal(true);
validate({ data: { type: 'CatalogItem', id: 123 } }) .should.equal(false);
validate({ data: { type: 'Foo', id: '123' } }) .should.equal(false);
}
});
describe('issue #259, support validating [meta-]schemas against themselves', function() {
it('should add schema before validation if "id" is the same as "$schema"', function() {
var ajv = new Ajv;
var hyperSchema = require('./remotes/hyper-schema.json');
ajv.addMetaSchema(hyperSchema);
});
});
describe.skip('issue #273, schemaPath in error in referenced schema', function() {
it('should have canonic reference with hash after file name', function() {
test(new Ajv);
test(new Ajv({inlineRefs: false}));
function test(ajv) {
var schema = {
"properties": {
"a": { "$ref": "int" }
}
};
var referencedSchema = {
"id": "int",
"type": "integer"
};
ajv.addSchema(referencedSchema);
var validate = ajv.compile(schema);
validate({ "a": "foo" }) .should.equal(false);
validate.errors[0].schemaPath .should.equal('int#/type');
}
});
});
describe('issue #342, support uniqueItems with some non-JSON objects', function() {
var validate;
before(function() {
var ajv = new Ajv;
validate = ajv.compile({ uniqueItems: true });
});
it('should allow different RegExps', function() {
validate([/foo/, /bar/]) .should.equal(true);
validate([/foo/ig, /foo/gi]) .should.equal(false);
validate([/foo/, {}]) .should.equal(true);
});
it('should allow different Dates', function() {
validate([new Date('2016-11-11'), new Date('2016-11-12')]) .should.equal(true);
validate([new Date('2016-11-11'), new Date('2016-11-11')]) .should.equal(false);
validate([new Date('2016-11-11'), {}]) .should.equal(true);
});
it('should allow undefined properties', function() {
validate([{}, {foo: undefined}]) .should.equal(true);
validate([{foo: undefined}, {}]) .should.equal(true);
validate([{foo: undefined}, {bar: undefined}]) .should.equal(true);
validate([{foo: undefined}, {foo: undefined}]) .should.equal(false);
});
});
describe('issue #388, code clean-up not working', function() {
it('should remove assignement to rootData if it is not used', function() {
var ajv = new Ajv;
var validate = ajv.compile({
type: 'object',
properties: {
foo: { type: 'string' }
}
});
var code = validate.toString();
code.match(/rootData/g).length .should.equal(1);
});
it('should remove assignement to errors if they are not used', function() {
var ajv = new Ajv;
var validate = ajv.compile({
type: 'object'
});
var code = validate.toString();
should.equal(code.match(/[^.]errors|vErrors/g), null);
});
});
describe('issue #485, order of type validation', function() {
it('should validate types befor keywords', function() {
var ajv = new Ajv({allErrors: true});
var validate = ajv.compile({
type: ['integer', 'string'],
required: ['foo'],
minimum: 2
});
validate(2) .should.equal(true);
validate('foo') .should.equal(true);
validate(1.5) .should.equal(false);
checkErrors(['type', 'minimum']);
validate({}) .should.equal(false);
checkErrors(['type', 'required']);
function checkErrors(expectedErrs) {
validate.errors .should.have.length(expectedErrs.length);
expectedErrs.forEach(function (keyword, i) {
validate.errors[i].keyword .should.equal(keyword);
});
}
});
});
describe('issue #521, incorrect warning with "id" property', function() {
it('should not log warning', function() {
var ajv = new Ajv({schemaId: '$id'});
var consoleWarn = console.warn;
console.warn = function() {
throw new Error('should not log warning');
};
try {
ajv.compile({
"$id": "http://example.com/schema.json",
"type": "object",
"properties": {
"id": {"type": "string"},
},
"required": [ "id"]
});
} finally {
console.warn = consoleWarn;
}
});
});
describe('issue #533, throwing missing ref exception with option missingRefs: "ignore"', function() {
var schema = {
"type": "object",
"properties": {
"foo": {"$ref": "#/definitions/missing"},
"bar": {"$ref": "#/definitions/missing"}
}
};
it('should pass validation without throwing exception', function() {
var ajv = new Ajv({missingRefs: 'ignore'});
var validate = ajv.compile(schema);
validate({foo: 'anything'}) .should.equal(true);
validate({foo: 'anything', bar: 'whatever'}) .should.equal(true);
});
it('should throw exception during schema compilation with option missingRefs: true', function() {
var ajv = new Ajv;
should.throw(function() {
ajv.compile(schema);
});
});
});
describe('full date format validation should understand leap years', function () {
it('should handle non leap year affected dates with date-time', function() {
var ajv = new Ajv({ format: 'full' });
var schema = { format: 'date-time' };
var validDateTime = '2016-01-31T00:00:00Z';
ajv.validate(schema, validDateTime).should.equal(true);
});
it('should handle non leap year affected dates with date', function () {
var ajv = new Ajv({ format: 'full' });
var schema = { format: 'date' };
var validDate = '2016-11-30';
ajv.validate(schema, validDate).should.equal(true);
});
it('should handle year leaps as date-time', function() {
var ajv = new Ajv({ format: 'full' });
var schema = { format: 'date-time' };
var validDateTime = '2016-02-29T00:00:00Z';
var invalidDateTime = '2017-02-29T00:00:00Z';
ajv.validate(schema, validDateTime) .should.equal(true);
ajv.validate(schema, invalidDateTime) .should.equal(false);
});
it('should handle year leaps as date', function() {
var ajv = new Ajv({ format: 'full' });
var schema = { format: 'date' };
var validDate = '2016-02-29';
var invalidDate = '2017-02-29';
ajv.validate(schema, validDate) .should.equal(true);
ajv.validate(schema, invalidDate) .should.equal(false);
});
});
describe('property __proto__ should be removed with removeAdditional option, issue #743', function() {
it('should remove additional properties', function() {
var ajv = new Ajv({removeAdditional: true});
var schema = {
properties: {
obj: {
additionalProperties: false,
properties: {
a: { type: 'string' },
b: { type: 'string' },
c: { type: 'string' },
d: { type: 'string' },
e: { type: 'string' },
f: { type: 'string' },
g: { type: 'string' },
h: { type: 'string' },
i: { type: 'string' }
}
}
}
};
var obj= Object.create(null);
obj.__proto__ = null; // should be removed
obj.additional = 'will be removed';
obj.a = 'valid';
obj.b = 'valid';
var data = {obj: obj};
ajv.validate(schema, data) .should.equal(true);
Object.keys(data.obj) .should.eql(['a', 'b']);
});
});
describe('issue #768, fix passContext in recursive $ref', function() {
var ajv, contexts;
beforeEach(function() {
contexts = [];
});
describe('passContext = true', function() {
it('should pass this value as context to custom keyword validation function', function() {
var validate = getValidate(true);
var self = {};
validate.call(self, { bar: 'a', baz: { bar: 'b' } });
contexts .should.have.length(2);
contexts.forEach(function(ctx) {
ctx .should.equal(self);
});
});
});
describe('passContext = false', function() {
it('should pass ajv instance as context to custom keyword validation function', function() {
var validate = getValidate(false);
validate({ bar: 'a', baz: { bar: 'b' } });
contexts .should.have.length(2);
contexts.forEach(function(ctx) {
ctx .should.equal(ajv);
});
});
});
describe('ref is fragment and passContext = true', function() {
it('should pass this value as context to custom keyword validation function', function() {
var validate = getValidateFragments(true);
var self = {};
validate.call(self, { baz: { corge: 'a', quux: { baz: { corge: 'b' } } } });
contexts .should.have.length(2);
contexts.forEach(function(ctx) {
ctx .should.equal(self);
});
});
});
describe('ref is fragment and passContext = false', function() {
it('should pass ajv instance as context to custom keyword validation function', function() {
var validate = getValidateFragments(false);
validate({ baz: { corge: 'a', quux: { baz: { corge: 'b' } } } });
contexts .should.have.length(2);
contexts.forEach(function(ctx) {
ctx .should.equal(ajv);
});
});
});
function getValidate(passContext) {
ajv = new Ajv({ passContext: passContext });
ajv.addKeyword('testValidate', { validate: storeContext });
var schema = {
"$id" : "foo",
"type": "object",
"required": ["bar"],
"properties": {
"bar": { "testValidate": true },
"baz": {
"$ref": "foo"
}
}
};
return ajv.compile(schema);
}
function getValidateFragments(passContext) {
ajv = new Ajv({ passContext: passContext });
ajv.addKeyword('testValidate', { validate: storeContext });
ajv.addSchema({
"$id" : "foo",
"definitions": {
"bar": {
"properties": {
"baz": {
"$ref": "boo"
}
}
}
}
});
ajv.addSchema({
"$id" : "boo",
"type": "object",
"required": ["corge"],
"properties": {
"quux": { "$ref": "foo#/definitions/bar" },
"corge": { "testValidate": true }
}
});
return ajv.compile({ "$ref": "foo#/definitions/bar" });
}
function storeContext() {
contexts.push(this);
return true;
}
});

View File

@ -0,0 +1,58 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #181, custom keyword is not validated in allErrors mode if there were previous error', function() {
it('should validate custom keyword that doesn\'t create errors', function() {
testCustomKeywordErrors({
type:'object',
errors: true,
validate: function v(/* value */) {
return false;
}
});
});
it('should validate custom keyword that creates errors', function() {
testCustomKeywordErrors({
type:'object',
errors: true,
validate: function v(/* value */) {
v.errors = v.errors || [];
v.errors.push({
keyword: 'alwaysFails',
message: 'alwaysFails error',
params: {
keyword: 'alwaysFails'
}
});
return false;
}
});
});
function testCustomKeywordErrors(def) {
var ajv = new Ajv({ allErrors: true });
ajv.addKeyword('alwaysFails', def);
var schema = {
required: ['foo'],
alwaysFails: true
};
var validate = ajv.compile(schema);
validate({ foo: 1 }) .should.equal(false);
validate.errors .should.have.length(1);
validate.errors[0].keyword .should.equal('alwaysFails');
validate({}) .should.equal(false);
validate.errors .should.have.length(2);
validate.errors[0].keyword .should.equal('required');
validate.errors[1].keyword .should.equal('alwaysFails');
}
});

View File

@ -0,0 +1,26 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #182, NaN validation', function() {
it('should not pass minimum/maximum validation', function() {
testNaN({ minimum: 1 }, false);
testNaN({ maximum: 1 }, false);
});
it('should pass type: number validation', function() {
testNaN({ type: 'number' }, true);
});
it('should not pass type: integer validation', function() {
testNaN({ type: 'integer' }, false);
});
function testNaN(schema, NaNisValid) {
var ajv = new Ajv;
var validate = ajv.compile(schema);
validate(NaN) .should.equal(NaNisValid);
}
});

View File

@ -0,0 +1,23 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #204, options schemas and $data used together', function() {
it('should use v5 metaschemas by default', function() {
var ajv = new Ajv({
schemas: [{$id: 'str', type: 'string'}],
$data: true
});
var schema = { const: 42 };
var validate = ajv.compile(schema);
validate(42) .should.equal(true);
validate(43) .should.equal(false);
ajv.validate('str', 'foo') .should.equal(true);
ajv.validate('str', 42) .should.equal(false);
});
});

View File

@ -0,0 +1,79 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #210, mutual recursive $refs that are schema fragments', function() {
it('should compile and validate schema when one ref is fragment', function() {
var ajv = new Ajv;
ajv.addSchema({
"$id" : "foo",
"definitions": {
"bar": {
"properties": {
"baz": {
"anyOf": [
{ "enum": [42] },
{ "$ref": "boo" }
]
}
}
}
}
});
ajv.addSchema({
"$id" : "boo",
"type": "object",
"required": ["quux"],
"properties": {
"quux": { "$ref": "foo#/definitions/bar" }
}
});
var validate = ajv.compile({ "$ref": "foo#/definitions/bar" });
validate({ baz: { quux: { baz: 42 } } }) .should.equal(true);
validate({ baz: { quux: { baz: "foo" } } }) .should.equal(false);
});
it('should compile and validate schema when both refs are fragments', function() {
var ajv = new Ajv;
ajv.addSchema({
"$id" : "foo",
"definitions": {
"bar": {
"properties": {
"baz": {
"anyOf": [
{ "enum": [42] },
{ "$ref": "boo#/definitions/buu" }
]
}
}
}
}
});
ajv.addSchema({
"$id" : "boo",
"definitions": {
"buu": {
"type": "object",
"required": ["quux"],
"properties": {
"quux": { "$ref": "foo#/definitions/bar" }
}
}
}
});
var validate = ajv.compile({ "$ref": "foo#/definitions/bar" });
validate({ baz: { quux: { baz: 42 } } }) .should.equal(true);
validate({ baz: { quux: { baz: "foo" } } }) .should.equal(false);
});
});

View File

@ -0,0 +1,210 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #240, mutually recursive fragment refs reference a common schema', function() {
var apiSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://api.schema#',
resource: {
$id: '#resource',
properties: {
id: { type: 'string' }
}
},
resourceIdentifier: {
$id: '#resource_identifier',
properties: {
id: { type: 'string' },
type: { type: 'string' }
}
}
};
var domainSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://domain.schema#',
properties: {
data: {
oneOf: [
{ $ref: 'schema://library.schema#resource_identifier' },
{ $ref: 'schema://catalog_item.schema#resource_identifier' },
]
}
}
};
it('should compile and validate schema when one ref is fragment', function() {
var ajv = new Ajv;
var librarySchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://library.schema#',
properties: {
name: { type: 'string' },
links: {
properties: {
catalogItems: {
type: 'array',
items: { $ref: 'schema://catalog_item_resource_identifier.schema#' }
}
}
}
},
definitions: {
resource_identifier: {
$id: '#resource_identifier',
allOf: [
{
properties: {
type: {
type: 'string',
'enum': ['Library']
}
}
},
{ $ref: 'schema://api.schema#resource_identifier' }
]
}
}
};
var catalogItemSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://catalog_item.schema#',
properties: {
name: { type: 'string' },
links: {
properties: {
library: { $ref: 'schema://library.schema#resource_identifier' }
}
}
},
definitions: {
resource_identifier: {
$id: '#resource_identifier',
allOf: [
{
properties: {
type: {
type: 'string',
'enum': ['CatalogItem']
}
}
},
{ $ref: 'schema://api.schema#resource_identifier' }
]
}
}
};
var catalogItemResourceIdentifierSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://catalog_item_resource_identifier.schema#',
allOf: [
{
properties: {
type: {
type: 'string',
enum: ['CatalogItem']
}
}
},
{
$ref: 'schema://api.schema#resource_identifier'
}
]
};
ajv.addSchema(librarySchema);
ajv.addSchema(catalogItemSchema);
ajv.addSchema(catalogItemResourceIdentifierSchema);
ajv.addSchema(apiSchema);
var validate = ajv.compile(domainSchema);
testSchema(validate);
});
it('should compile and validate schema when both refs are fragments', function() {
var ajv = new Ajv;
var librarySchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://library.schema#',
properties: {
name: { type: 'string' },
links: {
properties: {
catalogItems: {
type: 'array',
items: { $ref: 'schema://catalog_item.schema#resource_identifier' }
}
}
}
},
definitions: {
resource_identifier: {
$id: '#resource_identifier',
allOf: [
{
properties: {
type: {
type: 'string',
'enum': ['Library']
}
}
},
{ $ref: 'schema://api.schema#resource_identifier' }
]
}
}
};
var catalogItemSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'schema://catalog_item.schema#',
properties: {
name: { type: 'string' },
links: {
properties: {
library: { $ref: 'schema://library.schema#resource_identifier' }
}
}
},
definitions: {
resource_identifier: {
$id: '#resource_identifier',
allOf: [
{
properties: {
type: {
type: 'string',
'enum': ['CatalogItem']
}
}
},
{ $ref: 'schema://api.schema#resource_identifier' }
]
}
}
};
ajv.addSchema(librarySchema);
ajv.addSchema(catalogItemSchema);
ajv.addSchema(apiSchema);
var validate = ajv.compile(domainSchema);
testSchema(validate);
});
function testSchema(validate) {
validate({ data: { type: 'Library', id: '123' } }) .should.equal(true);
validate({ data: { type: 'Library', id: 123 } }) .should.equal(false);
validate({ data: { type: 'CatalogItem', id: '123' } }) .should.equal(true);
validate({ data: { type: 'CatalogItem', id: 123 } }) .should.equal(false);
validate({ data: { type: 'Foo', id: '123' } }) .should.equal(false);
}
});

View File

@ -0,0 +1,13 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #259, support validating [meta-]schemas against themselves', function() {
it('should add schema before validation if "id" is the same as "$schema"', function() {
var ajv = new Ajv;
var hyperSchema = require('../remotes/hyper-schema.json');
ajv.addMetaSchema(hyperSchema);
});
});

View File

@ -0,0 +1,31 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe.skip('issue #273, schemaPath in error in referenced schema', function() {
it('should have canonic reference with hash after file name', function() {
test(new Ajv);
test(new Ajv({inlineRefs: false}));
function test(ajv) {
var schema = {
"properties": {
"a": { "$ref": "int" }
}
};
var referencedSchema = {
"id": "int",
"type": "integer"
};
ajv.addSchema(referencedSchema);
var validate = ajv.compile(schema);
validate({ "a": "foo" }) .should.equal(false);
validate.errors[0].schemaPath .should.equal('int#/type');
}
});
});

View File

@ -0,0 +1,33 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #342, support uniqueItems with some non-JSON objects', function() {
var validate;
before(function() {
var ajv = new Ajv;
validate = ajv.compile({ uniqueItems: true });
});
it('should allow different RegExps', function() {
validate([/foo/, /bar/]) .should.equal(true);
validate([/foo/ig, /foo/gi]) .should.equal(false);
validate([/foo/, {}]) .should.equal(true);
});
it('should allow different Dates', function() {
validate([new Date('2016-11-11'), new Date('2016-11-12')]) .should.equal(true);
validate([new Date('2016-11-11'), new Date('2016-11-11')]) .should.equal(false);
validate([new Date('2016-11-11'), {}]) .should.equal(true);
});
it('should allow undefined properties', function() {
validate([{}, {foo: undefined}]) .should.equal(true);
validate([{foo: undefined}, {}]) .should.equal(true);
validate([{foo: undefined}, {bar: undefined}]) .should.equal(true);
validate([{foo: undefined}, {foo: undefined}]) .should.equal(false);
});
});

View File

@ -0,0 +1,28 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('issue #388, code clean-up not working', function() {
it('should remove assignement to rootData if it is not used', function() {
var ajv = new Ajv;
var validate = ajv.compile({
type: 'object',
properties: {
foo: { type: 'string' }
}
});
var code = validate.toString();
code.match(/rootData/g).length .should.equal(1);
});
it('should remove assignement to errors if they are not used', function() {
var ajv = new Ajv;
var validate = ajv.compile({
type: 'object'
});
var code = validate.toString();
should.equal(code.match(/[^.]errors|vErrors/g), null);
});
});

View File

@ -0,0 +1,32 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #485, order of type validation', function() {
it('should validate types before keywords', function() {
var ajv = new Ajv({allErrors: true});
var validate = ajv.compile({
type: ['integer', 'string'],
required: ['foo'],
minimum: 2
});
validate(2) .should.equal(true);
validate('foo') .should.equal(true);
validate(1.5) .should.equal(false);
checkErrors(['type', 'minimum']);
validate({}) .should.equal(false);
checkErrors(['type', 'required']);
function checkErrors(expectedErrs) {
validate.errors .should.have.length(expectedErrs.length);
expectedErrs.forEach(function (keyword, i) {
validate.errors[i].keyword .should.equal(keyword);
});
}
});
});

View File

@ -0,0 +1,49 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('issue #50: references with "definitions"', function () {
it('should be supported by addSchema', spec('addSchema'));
it('should be supported by compile', spec('addSchema'));
function spec(method) {
return function() {
var result;
var ajv = new Ajv;
ajv[method]({
$id: 'http://example.com/test/person.json#',
definitions: {
name: { type: 'string' }
},
type: 'object',
properties: {
name: { $ref: '#/definitions/name'}
}
});
ajv[method]({
$id: 'http://example.com/test/employee.json#',
type: 'object',
properties: {
person: { $ref: '/test/person.json#' },
role: { type: 'string' }
}
});
result = ajv.validate('http://example.com/test/employee.json#', {
person: {
name: 'Alice'
},
role: 'Programmer'
});
result. should.equal(true);
should.equal(ajv.errors, null);
};
}
});

View File

@ -0,0 +1,28 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #521, incorrect warning with "id" property', function() {
it('should not log warning', function() {
var ajv = new Ajv({schemaId: '$id'});
var consoleWarn = console.warn;
console.warn = function() {
throw new Error('should not log warning');
};
try {
ajv.compile({
"$id": "http://example.com/schema.json",
"type": "object",
"properties": {
"id": {"type": "string"},
},
"required": [ "id"]
});
} finally {
console.warn = consoleWarn;
}
});
});

View File

@ -0,0 +1,29 @@
'use strict';
var Ajv = require('../ajv');
var should = require('../chai').should();
describe('issue #533, throwing missing ref exception with option missingRefs: "ignore"', function() {
var schema = {
"type": "object",
"properties": {
"foo": {"$ref": "#/definitions/missing"},
"bar": {"$ref": "#/definitions/missing"}
}
};
it('should pass validation without throwing exception', function() {
var ajv = new Ajv({missingRefs: 'ignore'});
var validate = ajv.compile(schema);
validate({foo: 'anything'}) .should.equal(true);
validate({foo: 'anything', bar: 'whatever'}) .should.equal(true);
});
it('should throw exception during schema compilation with option missingRefs: true', function() {
var ajv = new Ajv;
should.throw(function() {
ajv.compile(schema);
});
});
});

View File

@ -0,0 +1,47 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('PR #617, full date format validation should understand leap years', function () {
it('should handle non leap year affected dates with date-time', function() {
var ajv = new Ajv({ format: 'full' });
var schema = { format: 'date-time' };
var validDateTime = '2016-01-31T00:00:00Z';
ajv.validate(schema, validDateTime).should.equal(true);
});
it('should handle non leap year affected dates with date', function () {
var ajv = new Ajv({ format: 'full' });
var schema = { format: 'date' };
var validDate = '2016-11-30';
ajv.validate(schema, validDate).should.equal(true);
});
it('should handle year leaps as date-time', function() {
var ajv = new Ajv({ format: 'full' });
var schema = { format: 'date-time' };
var validDateTime = '2016-02-29T00:00:00Z';
var invalidDateTime = '2017-02-29T00:00:00Z';
ajv.validate(schema, validDateTime) .should.equal(true);
ajv.validate(schema, invalidDateTime) .should.equal(false);
});
it('should handle year leaps as date', function() {
var ajv = new Ajv({ format: 'full' });
var schema = { format: 'date' };
var validDate = '2016-02-29';
var invalidDate = '2017-02-29';
ajv.validate(schema, validDate) .should.equal(true);
ajv.validate(schema, invalidDate) .should.equal(false);
});
});

View File

@ -0,0 +1,41 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #743, property __proto__ should be removed with removeAdditional option', function() {
it('should remove additional properties', function() {
var ajv = new Ajv({removeAdditional: true});
var schema = {
properties: {
obj: {
additionalProperties: false,
properties: {
a: { type: 'string' },
b: { type: 'string' },
c: { type: 'string' },
d: { type: 'string' },
e: { type: 'string' },
f: { type: 'string' },
g: { type: 'string' },
h: { type: 'string' },
i: { type: 'string' }
}
}
}
};
var obj= Object.create(null);
obj.__proto__ = null; // should be removed
obj.additional = 'will be removed';
obj.a = 'valid';
obj.b = 'valid';
var data = {obj: obj};
ajv.validate(schema, data) .should.equal(true);
Object.keys(data.obj) .should.eql(['a', 'b']);
});
});

View File

@ -0,0 +1,114 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #768, fix passContext in recursive $ref', function() {
var ajv, contexts;
beforeEach(function() {
contexts = [];
});
describe('passContext = true', function() {
it('should pass this value as context to custom keyword validation function', function() {
var validate = getValidate(true);
var self = {};
validate.call(self, { bar: 'a', baz: { bar: 'b' } });
contexts .should.have.length(2);
contexts.forEach(function(ctx) {
ctx .should.equal(self);
});
});
});
describe('passContext = false', function() {
it('should pass ajv instance as context to custom keyword validation function', function() {
var validate = getValidate(false);
validate({ bar: 'a', baz: { bar: 'b' } });
contexts .should.have.length(2);
contexts.forEach(function(ctx) {
ctx .should.equal(ajv);
});
});
});
describe('ref is fragment and passContext = true', function() {
it('should pass this value as context to custom keyword validation function', function() {
var validate = getValidateFragments(true);
var self = {};
validate.call(self, { baz: { corge: 'a', quux: { baz: { corge: 'b' } } } });
contexts .should.have.length(2);
contexts.forEach(function(ctx) {
ctx .should.equal(self);
});
});
});
describe('ref is fragment and passContext = false', function() {
it('should pass ajv instance as context to custom keyword validation function', function() {
var validate = getValidateFragments(false);
validate({ baz: { corge: 'a', quux: { baz: { corge: 'b' } } } });
contexts .should.have.length(2);
contexts.forEach(function(ctx) {
ctx .should.equal(ajv);
});
});
});
function getValidate(passContext) {
ajv = new Ajv({ passContext: passContext });
ajv.addKeyword('testValidate', { validate: storeContext });
var schema = {
"$id" : "foo",
"type": "object",
"required": ["bar"],
"properties": {
"bar": { "testValidate": true },
"baz": {
"$ref": "foo"
}
}
};
return ajv.compile(schema);
}
function getValidateFragments(passContext) {
ajv = new Ajv({ passContext: passContext });
ajv.addKeyword('testValidate', { validate: storeContext });
ajv.addSchema({
"$id" : "foo",
"definitions": {
"bar": {
"properties": {
"baz": {
"$ref": "boo"
}
}
}
}
});
ajv.addSchema({
"$id" : "boo",
"type": "object",
"required": ["corge"],
"properties": {
"quux": { "$ref": "foo#/definitions/bar" },
"corge": { "testValidate": true }
}
});
return ajv.compile({ "$ref": "foo#/definitions/bar" });
}
function storeContext() {
contexts.push(this);
return true;
}
});

View File

@ -0,0 +1,40 @@
'use strict';
var Ajv = require('../ajv');
require('../chai').should();
describe('issue #8: schema with shared references', function() {
it('should be supported by addSchema', spec('addSchema'));
it('should be supported by compile', spec('compile'));
function spec(method) {
return function() {
var ajv = new Ajv;
var propertySchema = {
type: 'string',
maxLength: 4
};
var schema = {
$id: 'obj.json#',
type: 'object',
properties: {
foo: propertySchema,
bar: propertySchema
}
};
ajv[method](schema);
var result = ajv.validate('obj.json#', { foo: 'abc', bar: 'def' });
result .should.equal(true);
result = ajv.validate('obj.json#', { foo: 'abcde', bar: 'fghg' });
result .should.equal(false);
ajv.errors .should.have.length(1);
};
}
});