ajv/spec/resolve.spec.js

343 lines
9.8 KiB
JavaScript

'use strict';
var Ajv = require('./ajv')
, should = require('./chai').should()
, getAjvInstances = require('./ajv_instances');
describe('resolve', function () {
var instances;
beforeEach(function() {
instances = getAjvInstances({
allErrors: true,
verbose: true,
inlineRefs: false
});
});
describe('resolve.ids method', function() {
it('should resolve ids in schema', function() {
// Example from http://json-schema.org/latest/json-schema-core.html#anchor29
var schema = {
"$id": "http://x.y.z/rootschema.json#",
"schema1": {
"$id": "#foo",
"description": "schema1",
"type": "integer"
},
"schema2": {
"$id": "otherschema.json",
"description": "schema2",
"nested": {
"$id": "#bar",
"description": "nested",
"type": "string"
},
"alsonested": {
"$id": "t/inner.json#a",
"description": "alsonested",
"type": "boolean"
}
},
"schema3": {
"$id": "some://where.else/completely#",
"description": "schema3",
"type": "null"
},
"properties": {
"foo": { "$ref": "#foo" },
"bar": { "$ref": "otherschema.json#bar" },
"baz": { "$ref": "t/inner.json#a" },
"bax": { "$ref": "some://where.else/completely#" }
},
"required": [ "foo", "bar", "baz", "bax" ]
};
instances.forEach(function (ajv) {
var validate = ajv.compile(schema);
var data = { foo: 1, bar: 'abc', baz: true, bax: null };
validate(data) .should.equal(true);
});
});
it('should throw if the same id resolves to two different schemas', function() {
instances.forEach(function (ajv) {
ajv.compile({
"$id": "http://example.com/1.json",
"type": "integer"
});
should.throw(function() {
ajv.compile({
"additionalProperties": {
"$id": "http://example.com/1.json",
"type": "string"
}
});
});
should.throw(function() {
ajv.compile({
"items": {
"$id": "#int",
"type": "integer"
},
"additionalProperties": {
"$id": "#int",
"type": "string"
}
});
});
});
});
it('should resolve ids defined as urn\'s (issue #423)', function() {
var schema = {
"type": "object",
"properties": {
"ip1": {
"$id": "urn:some:ip:prop",
"type": "string",
"format": "ipv4"
},
"ip2": {
"$ref": "urn:some:ip:prop"
}
},
"required": [
"ip1",
"ip2"
]
};
var data = {
"ip1": "0.0.0.0",
"ip2": "0.0.0.0"
};
instances.forEach(function (ajv) {
var validate = ajv.compile(schema);
validate(data) .should.equal(true);
});
});
});
describe('protocol-relative URIs', function() {
it('should resolve fragment', function() {
instances.forEach(function(ajv) {
var schema = {
"$id": "//e.com/types",
"definitions": {
"int": { "type": "integer" }
}
};
ajv.addSchema(schema);
var validate = ajv.compile({ $ref: '//e.com/types#/definitions/int' });
validate(1) .should.equal(true);
validate('foo') .should.equal(false);
});
});
});
describe('missing schema error', function() {
this.timeout(4000);
it('should contain missingRef and missingSchema', function() {
testMissingSchemaError({
baseId: 'http://example.com/1.json',
ref: 'http://another.com/int.json',
expectedMissingRef: 'http://another.com/int.json',
expectedMissingSchema: 'http://another.com/int.json'
});
});
it('should resolve missingRef and missingSchema relative to base id', function() {
testMissingSchemaError({
baseId: 'http://example.com/folder/1.json',
ref: 'int.json',
expectedMissingRef: 'http://example.com/folder/int.json',
expectedMissingSchema: 'http://example.com/folder/int.json'
});
});
it('should resolve missingRef and missingSchema relative to base id from root', function() {
testMissingSchemaError({
baseId: 'http://example.com/folder/1.json',
ref: '/int.json',
expectedMissingRef: 'http://example.com/int.json',
expectedMissingSchema: 'http://example.com/int.json'
});
});
it('missingRef should and missingSchema should NOT include JSON path (hash fragment)', function() {
testMissingSchemaError({
baseId: 'http://example.com/1.json',
ref: 'int.json#/definitions/positive',
expectedMissingRef: 'http://example.com/int.json#/definitions/positive',
expectedMissingSchema: 'http://example.com/int.json'
});
});
it('should throw missing schema error if same path exist in the current schema but id is different (issue #220)', function() {
testMissingSchemaError({
baseId: 'http://example.com/parent.json',
ref: 'object.json#/properties/a',
expectedMissingRef: 'http://example.com/object.json#/properties/a',
expectedMissingSchema: 'http://example.com/object.json'
});
});
function testMissingSchemaError(opts) {
instances.forEach(function (ajv) {
try {
ajv.compile({
"$id": opts.baseId,
"properties": { "a": { "$ref": opts.ref } }
});
} catch(e) {
e.missingRef .should.equal(opts.expectedMissingRef);
e.missingSchema .should.equal(opts.expectedMissingSchema);
}
});
}
});
describe('inline referenced schemas without refs in them', function() {
var schemas = [
{ $id: 'http://e.com/obj.json#',
properties: { a: { $ref: 'int.json#' } } },
{ $id: 'http://e.com/int.json#',
type: 'integer', minimum: 2, maximum: 4 },
{ $id: 'http://e.com/obj1.json#',
definitions: { int: { type: 'integer', minimum: 2, maximum: 4 } },
properties: { a: { $ref: '#/definitions/int' } } },
{ $id: 'http://e.com/list.json#',
items: { $ref: 'obj.json#' } }
];
it('by default should inline schema if it doesn\'t contain refs', function() {
var ajv = new Ajv({ schemas: schemas });
testSchemas(ajv, true);
});
it('should NOT inline schema if option inlineRefs == false', function() {
var ajv = new Ajv({ schemas: schemas, inlineRefs: false });
testSchemas(ajv, false);
});
it('should inline schema if option inlineRefs is bigger than number of keys in referenced schema', function() {
var ajv = new Ajv({ schemas: schemas, inlineRefs: 3 });
testSchemas(ajv, true);
});
it('should NOT inline schema if option inlineRefs is less than number of keys in referenced schema', function() {
var ajv = new Ajv({ schemas: schemas, inlineRefs: 2 });
testSchemas(ajv, false);
});
it('should avoid schema substitution when refs are inlined (issue #77)', function() {
var ajv = new Ajv({ verbose: true });
var schemaMessage = {
$schema: "http://json-schema.org/draft-07/schema#",
$id: "http://e.com/message.json#",
type: "object",
required: ["header"],
properties: {
header: {
allOf: [
{ $ref: "header.json" },
{ properties: { msgType: { "enum": [0] } } }
]
}
}
};
// header schema
var schemaHeader = {
$schema: "http://json-schema.org/draft-07/schema#",
$id: "http://e.com/header.json#",
type: "object",
properties: {
version: {
type: "integer",
maximum: 5
},
msgType: { type: "integer" }
},
required: ["version", "msgType"]
};
// a good message
var validMessage = {
header: {
version: 4,
msgType: 0
}
};
// a bad message
var invalidMessage = {
header: {
version: 6,
msgType: 0
}
};
// add schemas and get validator function
ajv.addSchema(schemaHeader);
ajv.addSchema(schemaMessage);
var v = ajv.getSchema('http://e.com/message.json#');
v(validMessage) .should.equal(true);
v.schema.$id .should.equal('http://e.com/message.json#');
v(invalidMessage) .should.equal(false);
v.errors .should.have.length(1);
v.schema.$id .should.equal('http://e.com/message.json#');
v(validMessage) .should.equal(true);
v.schema.$id .should.equal('http://e.com/message.json#');
});
function testSchemas(ajv, expectedInlined) {
var v1 = ajv.getSchema('http://e.com/obj.json')
, v2 = ajv.getSchema('http://e.com/obj1.json')
, vl = ajv.getSchema('http://e.com/list.json');
testObjSchema(v1);
testObjSchema(v2);
testListSchema(vl);
testInlined(v1, expectedInlined);
testInlined(v2, expectedInlined);
testInlined(vl, false);
}
function testObjSchema(validate) {
validate({a:3}) .should.equal(true);
validate({a:1}) .should.equal(false);
validate({a:5}) .should.equal(false);
}
function testListSchema(validate) {
validate([{a:3}]) .should.equal(true);
validate([{a:1}]) .should.equal(false);
validate([{a:5}]) .should.equal(false);
}
function testInlined(validate, expectedInlined) {
var inlined = !(/refVal/.test(validate.toString()));
inlined .should.equal(expectedInlined);
}
});
});