123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816 |
- '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;
- }
- });
|