more tests, fixed/optimized refs
parent
4db4008e2c
commit
64b80cbfff
|
@ -16,8 +16,10 @@ var RULES = require('./rules')
|
|||
module.exports = compile;
|
||||
|
||||
|
||||
function compile(schema) {
|
||||
var self = this;
|
||||
function compile(schema, _rootSchema) {
|
||||
var self = this, refs = [], refIds = {};
|
||||
_rootSchema = _rootSchema || schema;
|
||||
|
||||
var validateCode = validateGenerator({
|
||||
isRoot: true,
|
||||
schema: schema,
|
||||
|
@ -32,7 +34,8 @@ function compile(schema) {
|
|||
});
|
||||
|
||||
if (this.opts.beautify) {
|
||||
if (beautify) validateCode = beautify(validateCode, { indent_size: 2 });
|
||||
var opts = this.opts.beautify === true ? { indent_size: 2 } : this.opts.beautify;
|
||||
if (beautify) validateCode = beautify(validateCode, opts);
|
||||
else console.error('"npm install js-beautify" to use beautify option');
|
||||
}
|
||||
// console.log('\n\n\n *** \n', validateCode);
|
||||
|
@ -45,13 +48,14 @@ function compile(schema) {
|
|||
return validate;
|
||||
|
||||
function resolveRef(ref) {
|
||||
return resolve.call(self, compile, schema, ref);
|
||||
}
|
||||
|
||||
function validateRef(ref, data) {
|
||||
var v = ref == '#' ? validate : self._schemas[ref];
|
||||
var valid = v(data);
|
||||
return { valid: valid, errors: v.errors };
|
||||
if (refIds[ref]) return refIds[ref];
|
||||
var v = resolve.call(self, compile, _rootSchema, ref);
|
||||
if (v) {
|
||||
var id = refs.length;
|
||||
refs.push(v);
|
||||
refIds[ref] = id;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,26 +2,25 @@
|
|||
|
||||
|
||||
module.exports = function resolve(compile, rootSchema, ref) {
|
||||
var schema = rootSchema;
|
||||
if (ref[0] != '#')
|
||||
schema = undefined;
|
||||
else if (ref != '#') {
|
||||
if (this._schemas[ref])
|
||||
schema = this._schemas[ref];
|
||||
else {
|
||||
var parts = ref.split('/');
|
||||
for (var i = 1; i < parts.length; i++) {
|
||||
if (!schema) break;
|
||||
var part = unescape(parts[i]);
|
||||
schema = schema[part];
|
||||
if (schema.$ref)
|
||||
schema = resolve.call(this, compile, rootSchema, schema.$ref);
|
||||
}
|
||||
if (schema) this._schemas[ref] = compile.call(this, schema);
|
||||
}
|
||||
if (ref[0] != '#') return;
|
||||
if (this._schemas[ref]) return this._schemas[ref];
|
||||
var schema = _resolve(rootSchema, ref);
|
||||
if (schema) return this._schemas[ref] = compile.call(this, schema, rootSchema);
|
||||
};
|
||||
|
||||
|
||||
function _resolve(rootSchema, ref) {
|
||||
var schema = rootSchema
|
||||
, parts = ref.split('/');
|
||||
for (var i = 1; i < parts.length; i++) {
|
||||
if (!schema) break;
|
||||
var part = unescape(parts[i]);
|
||||
schema = schema[part];
|
||||
if (schema.$ref)
|
||||
schema = _resolve(rootSchema, schema.$ref);
|
||||
}
|
||||
return schema;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function unescape(str) {
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
{{# def.definitions }}
|
||||
{{# def.setup:'$ref' }}
|
||||
|
||||
{{? it.resolveRef($schema) }}
|
||||
var result{{=$lvl}} = validateRef('{{=$schema}}', {{=$data}}, (dataPath || '') + {{= it.errorPath }});
|
||||
var {{=$valid}} = result{{=$lvl}}.valid;
|
||||
if (!{{=$valid}}) validate.errors.push.apply(validate.errors, result{{=$lvl}}.errors);
|
||||
{{? $schema == '#' }}
|
||||
var {{=$valid}} = validate({{=$data}}, (dataPath || '') + {{= it.errorPath }});
|
||||
{{??}}
|
||||
{{# def.error:'$ref' }}
|
||||
var {{=$valid}} = false;
|
||||
{{ $refId = it.resolveRef($schema); }}
|
||||
{{? $refId === undefined }}
|
||||
{{# def.error:'$ref' }}
|
||||
var {{=$valid}} = false;
|
||||
{{??}}
|
||||
var {{=$valid}} = refs[{{=$refId}}]({{=$data}}, (dataPath || '') + {{= it.errorPath }});
|
||||
if (!{{=$valid}}) validate.errors.push.apply(validate.errors, refs[{{=$refId}}].errors);
|
||||
{{?}}
|
||||
{{?}}
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
{{# def.setup:'pattern' }}
|
||||
|
||||
{{ new RegExp($schema); /* test if regexp is valid to fail at compile time rather than in eval */}}
|
||||
var {{=$valid}} = /{{=$schema}}/.test({{=$data}});
|
||||
var {{=$valid}} = new RegExp("{{=$schema}}").test({{=$data}});
|
||||
|
||||
{{# def.checkError:'pattern' }}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ajv",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"description": "Another JSON schema Validator",
|
||||
"main": "lib/ajv.js",
|
||||
"scripts": {
|
||||
|
|
|
@ -38,50 +38,50 @@ for (var id in remoteRefs) {
|
|||
}
|
||||
|
||||
|
||||
describe('JSON-Schema tests', function () {
|
||||
addTests('draft4: ', './json-schema-test-suite/tests/draft4/{**/,}*.json');
|
||||
addTests('JSON-Schema tests draft4', './json-schema-test-suite/tests/draft4/{**/,}*.json');
|
||||
addTests('Advanced schema tests', './tests/{**/,}*.json');
|
||||
|
||||
function addTests(description, testsPath) {
|
||||
describe(description, function() {
|
||||
var files = getTestFiles(testsPath);
|
||||
|
||||
files.forEach(function (file) {
|
||||
if (ONLY_RULES && ONLY_RULES.indexOf(file.name) == -1) return;
|
||||
if (SKIP_RULES && SKIP_RULES.indexOf(file.name) >= 0) return;
|
||||
function addTests(description, testsPath) {
|
||||
describe(description, function() {
|
||||
var files = getTestFiles(testsPath);
|
||||
|
||||
describe(file.name, function() {
|
||||
var testSets = require(file.path);
|
||||
testSets.forEach(function (testSet) {
|
||||
// if (testSet.description != 'allOf with base schema') return;
|
||||
describe(testSet.description, function() {
|
||||
// it(testSet.description, function() {
|
||||
var validate = ajv.compile(testSet.schema);
|
||||
var fullValidate = fullAjv.compile(testSet.schema);
|
||||
files.forEach(function (file) {
|
||||
if (ONLY_RULES && ONLY_RULES.indexOf(file.name) == -1) return;
|
||||
if (SKIP_RULES && SKIP_RULES.indexOf(file.name) >= 0) return;
|
||||
|
||||
testSet.tests.forEach(function (test) {
|
||||
// if (test.description != 'one supplementary Unicode code point is not long enough') return;
|
||||
// console.log(testSet.schema, '\n\n***\n\n', validate.toString());
|
||||
it(test.description, function() {
|
||||
var valid = validate(test.data);
|
||||
// console.log('result', valid, validate.errors);
|
||||
assert.equal(valid, test.valid);
|
||||
if (valid) assert(validate.errors.length == 0);
|
||||
else assert(validate.errors.length > 0);
|
||||
describe(file.name, function() {
|
||||
var testSets = require(file.path);
|
||||
testSets.forEach(function (testSet) {
|
||||
// if (testSet.description != 'allOf with base schema') return;
|
||||
describe(testSet.description, function() {
|
||||
// it(testSet.description, function() {
|
||||
var validate = ajv.compile(testSet.schema);
|
||||
var fullValidate = fullAjv.compile(testSet.schema);
|
||||
|
||||
var valid = fullValidate(test.data);
|
||||
// console.log('full result', valid, fullValidate.errors);
|
||||
assert.equal(valid, test.valid);
|
||||
if (valid) assert(fullValidate.errors.length == 0);
|
||||
else assert(fullValidate.errors.length > 0);
|
||||
});
|
||||
testSet.tests.forEach(function (test) {
|
||||
// if (test.description != 'one supplementary Unicode code point is not long enough') return;
|
||||
// console.log(testSet.schema, '\n\n***\n\n', validate.toString());
|
||||
it(test.description, function() {
|
||||
var valid = validate(test.data);
|
||||
// console.log('result', valid, validate.errors);
|
||||
assert.equal(valid, test.valid);
|
||||
if (valid) assert(validate.errors.length == 0);
|
||||
else assert(validate.errors.length > 0);
|
||||
|
||||
var valid = fullValidate(test.data);
|
||||
// console.log('full result', valid, fullValidate.errors);
|
||||
assert.equal(valid, test.valid);
|
||||
if (valid) assert(fullValidate.errors.length == 0);
|
||||
else assert(fullValidate.errors.length > 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getTestFiles(testsPath) {
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
[
|
||||
{
|
||||
"description": "advanced schema from z-schema benchmark (https://github.com/zaggino/z-schema)",
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"/": { "$ref": "#/definitions/entry" }
|
||||
},
|
||||
"patternProperties": {
|
||||
"^(/[^/]+)+$": { "$ref": "#/definitions/entry" }
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [ "/" ],
|
||||
"definitions": {
|
||||
"entry": {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"description": "schema for an fstab entry",
|
||||
"type": "object",
|
||||
"required": [ "storage" ],
|
||||
"properties": {
|
||||
"storage": {
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/entry/definitions/diskDevice" },
|
||||
{ "$ref": "#/definitions/entry/definitions/diskUUID" },
|
||||
{ "$ref": "#/definitions/entry/definitions/nfs" },
|
||||
{ "$ref": "#/definitions/entry/definitions/tmpfs" }
|
||||
]
|
||||
},
|
||||
"fstype": {
|
||||
"enum": [ "ext3", "ext4", "btrfs" ]
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "type": "string" },
|
||||
"uniqueItems": true
|
||||
},
|
||||
"readonly": { "type": "boolean" }
|
||||
},
|
||||
"definitions": {
|
||||
"diskDevice": {
|
||||
"properties": {
|
||||
"type": { "enum": [ "disk" ] },
|
||||
"device": {
|
||||
"type": "string",
|
||||
"pattern": "^/dev/[^/]+(/[^/]+)*$"
|
||||
}
|
||||
},
|
||||
"required": [ "type", "device" ],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"diskUUID": {
|
||||
"properties": {
|
||||
"type": { "enum": [ "disk" ] },
|
||||
"label": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
|
||||
}
|
||||
},
|
||||
"required": [ "type", "label" ],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"nfs": {
|
||||
"properties": {
|
||||
"type": { "enum": [ "nfs" ] },
|
||||
"remotePath": {
|
||||
"type": "string",
|
||||
"pattern": "^(/[^/]+)+$"
|
||||
},
|
||||
"server": {
|
||||
"type": "string",
|
||||
"anyOf": [
|
||||
{ "format": "host-name" },
|
||||
{ "format": "ipv4" },
|
||||
{ "format": "ipv6" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [ "type", "server", "remotePath" ],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"tmpfs": {
|
||||
"properties": {
|
||||
"type": { "enum": [ "tmpfs" ] },
|
||||
"sizeInMB": {
|
||||
"type": "integer",
|
||||
"minimum": 16,
|
||||
"maximum": 512
|
||||
}
|
||||
},
|
||||
"required": [ "type", "sizeInMB" ],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid object from z-schema benchmark",
|
||||
"data": {
|
||||
"/": {
|
||||
"storage": {
|
||||
"type": "disk",
|
||||
"device": "/dev/sda1"
|
||||
},
|
||||
"fstype": "btrfs",
|
||||
"readonly": true
|
||||
},
|
||||
"/var": {
|
||||
"storage": {
|
||||
"type": "disk",
|
||||
"label": "8f3ba6f4-5c70-46ec-83af-0d5434953e5f"
|
||||
},
|
||||
"fstype": "ext4",
|
||||
"options": [ "nosuid" ]
|
||||
},
|
||||
"/tmp": {
|
||||
"storage": {
|
||||
"type": "tmpfs",
|
||||
"sizeInMB": 64
|
||||
}
|
||||
},
|
||||
"/var/www": {
|
||||
"storage": {
|
||||
"type": "nfs",
|
||||
"server": "my.nfs.server",
|
||||
"remotePath": "/exports/mypath"
|
||||
}
|
||||
}
|
||||
},
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "not array",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,136 @@
|
|||
[
|
||||
{
|
||||
"description": "basic schema from z-schema benchmark (https://github.com/zaggino/z-schema)",
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Product set",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"title": "Product",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The unique identifier for a product",
|
||||
"type": "number"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"price": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"exclusiveMinimum": true
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"dimensions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"length": {"type": "number"},
|
||||
"width": {"type": "number"},
|
||||
"height": {"type": "number"}
|
||||
},
|
||||
"required": ["length", "width", "height"]
|
||||
},
|
||||
"warehouseLocation": {
|
||||
"description": "Coordinates of the warehouse with the product"
|
||||
}
|
||||
},
|
||||
"required": ["id", "name", "price"]
|
||||
}
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"description": "valid array from z-schema benchmark",
|
||||
"data": [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "An ice sculpture",
|
||||
"price": 12.50,
|
||||
"tags": ["cold", "ice"],
|
||||
"dimensions": {
|
||||
"length": 7.0,
|
||||
"width": 12.0,
|
||||
"height": 9.5
|
||||
},
|
||||
"warehouseLocation": {
|
||||
"latitude": -78.75,
|
||||
"longitude": 20.4
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "A blue mouse",
|
||||
"price": 25.50,
|
||||
"dimensions": {
|
||||
"length": 3.1,
|
||||
"width": 1.0,
|
||||
"height": 1.0
|
||||
},
|
||||
"warehouseLocation": {
|
||||
"latitude": 54.4,
|
||||
"longitude": -32.7
|
||||
}
|
||||
}
|
||||
],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "not array",
|
||||
"data": 1,
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "array of not onjects",
|
||||
"data": [1,2,3],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "missing required properties",
|
||||
"data": [{}],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "required property of wrong type",
|
||||
"data": [{"id": 1, "name": "product", "price": "not valid"}],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "smallest valid product",
|
||||
"data": [{"id": 1, "name": "product", "price": 100}],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "tags should be array",
|
||||
"data": [{"tags":{}, "id": 1, "name": "product", "price": 100}],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "dimensions should be object",
|
||||
"data": [{"dimensions":[], "id": 1, "name": "product", "price": 100}],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "valid product with tag",
|
||||
"data": [{"tags":["product"], "id": 1, "name": "product", "price": 100}],
|
||||
"valid": true
|
||||
},
|
||||
{
|
||||
"description": "dimensions miss required properties",
|
||||
"data": [{"dimensions":{}, "tags":["product"], "id": 1, "name": "product", "price": 100}],
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "valid product with tag and dimensions",
|
||||
"data": [{"dimensions":{"length": 7,"width": 12,"height": 9.5}, "tags":["product"], "id": 1, "name": "product", "price": 100}],
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue