blob: 7e187ed960f1ad47c9d4ff3d6c74578336a23141 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var validator = require('./validator');
var Validator = validator.Validator;
var ProtocolValidator = validator.ProtocolValidator;
exports['test'] = {
setUp: function(done) {
done();
},
'nonexistent/null/undefined': function(test) {
test.throws(function() { return new Validator(); });
test.throws(function() { return new Validator(null); });
test.throws(function() { return new Validator(undefined); });
test.done();
},
'unrecognized primitive type name': function(test) {
test.throws(function() { return new Validator('badtype'); });
test.done();
},
'invalid schema javascript type': function(test) {
test.throws(function() { return new Validator(123); });
test.throws(function() { return new Validator(function() { }); });
test.done();
},
// Primitive types
'null': function(test) {
test.ok(Validator.validate('null', null));
test.ok(Validator.validate('null', undefined));
test.throws(function() { Validator.validate('null', 1); });
test.throws(function() { Validator.validate('null', 'a'); });
test.done();
},
'boolean': function(test) {
test.ok(Validator.validate('boolean', true));
test.ok(Validator.validate('boolean', false));
test.throws(function() { Validator.validate('boolean', null); });
test.throws(function() { Validator.validate('boolean', 1); });
test.throws(function() { Validator.validate('boolean', 'a'); });
test.done();
},
'int': function(test) {
test.ok(Validator.validate('int', 1));
test.ok(Validator.validate('long', Math.pow(2, 31) - 1));
test.throws(function() { Validator.validate('int', 1.5); });
test.throws(function() { Validator.validate('int', Math.pow(2, 40)); });
test.throws(function() { Validator.validate('int', null); });
test.throws(function() { Validator.validate('int', 'a'); });
test.done();
},
'long': function(test) {
test.ok(Validator.validate('long', 1));
test.ok(Validator.validate('long', Math.pow(2, 63) - 1));
test.throws(function() { Validator.validate('long', 1.5); });
test.throws(function() { Validator.validate('long', Math.pow(2, 70)); });
test.throws(function() { Validator.validate('long', null); });
test.throws(function() { Validator.validate('long', 'a'); });
test.done();
},
'float': function(test) {
test.ok(Validator.validate('float', 1));
test.ok(Validator.validate('float', 1.5));
test.throws(function() { Validator.validate('float', 'a'); });
test.throws(function() { Validator.validate('float', null); });
test.done();
},
'double': function(test) {
test.ok(Validator.validate('double', 1));
test.ok(Validator.validate('double', 1.5));
test.throws(function() { Validator.validate('double', 'a'); });
test.throws(function() { Validator.validate('double', null); });
test.done();
},
'bytes': function(test) {
// not implemented yet
test.throws(function() { Validator.validate('bytes', 1); });
test.done();
},
'string': function(test) {
test.ok(Validator.validate('string', 'a'));
test.throws(function() { Validator.validate('string', 1); });
test.throws(function() { Validator.validate('string', null); });
test.done();
},
// Records
'empty-record': function(test) {
var schema = {type: 'record', name: 'EmptyRecord', fields: []};
test.ok(Validator.validate(schema, {}));
test.throws(function() { Validator.validate(schema, 1); });
test.throws(function() { Validator.validate(schema, null); });
test.throws(function() { Validator.validate(schema, 'a'); });
test.done();
},
'record-with-string': function(test) {
var schema = {type: 'record', name: 'EmptyRecord', fields: [{name: 'stringField', type: 'string'}]};
test.ok(Validator.validate(schema, {stringField: 'a'}));
test.throws(function() { Validator.validate(schema, {}); });
test.throws(function() { Validator.validate(schema, {stringField: 1}); });
test.throws(function() { Validator.validate(schema, {stringField: []}); });
test.throws(function() { Validator.validate(schema, {stringField: {}}); });
test.throws(function() { Validator.validate(schema, {stringField: null}); });
test.throws(function() { Validator.validate(schema, {stringField: 'a', unexpectedField: 'a'}); });
test.done();
},
'record-with-string-and-number': function(test) {
var schema = {type: 'record', name: 'EmptyRecord', fields: [{name: 'stringField', type: 'string'}, {name: 'intField', type: 'int'}]};
test.ok(Validator.validate(schema, {stringField: 'a', intField: 1}));
test.throws(function() { Validator.validate(schema, {}); });
test.throws(function() { Validator.validate(schema, {stringField: 'a'}); });
test.throws(function() { Validator.validate(schema, {intField: 1}); });
test.throws(function() { Validator.validate(schema, {stringField: 'a', intField: 1, unexpectedField: 'a'}); });
test.done();
},
'nested-record-with-namespace-relative': function(test) {
var schema = {type: 'record', namespace: 'x.y.z', name: 'RecordA', fields: [{name: 'recordBField1', type: ['null', {type: 'record', name: 'RecordB', fields: []}]}, {name: 'recordBField2', type: 'RecordB'}]};
test.ok(Validator.validate(schema, {recordBField1: null, recordBField2: {}}));
test.ok(Validator.validate(schema, {recordBField1: {'x.y.z.RecordB': {}}, recordBField2: {}}));
test.throws(function() { Validator.validate(schema, {}); });
test.throws(function() { Validator.validate(schema, {recordBField1: null}); });
test.throws(function() { Validator.validate(schema, {recordBField2: {}}); });
test.throws(function() { Validator.validate(schema, {recordBField1: {'RecordB': {}}, recordBField2: {}}); });
test.done();
},
'nested-record-with-namespace-absolute': function(test) {
var schema = {type: 'record', namespace: 'x.y.z', name: 'RecordA', fields: [{name: 'recordBField1', type: ['null', {type: 'record', name: 'RecordB', fields: []}]}, {name: 'recordBField2', type: 'x.y.z.RecordB'}]};
test.ok(Validator.validate(schema, {recordBField1: null, recordBField2: {}}));
test.ok(Validator.validate(schema, {recordBField1: {'x.y.z.RecordB': {}}, recordBField2: {}}));
test.throws(function() { Validator.validate(schema, {}); });
test.throws(function() { Validator.validate(schema, {recordBField1: null}); });
test.throws(function() { Validator.validate(schema, {recordBField2: {}}); });
test.throws(function() { Validator.validate(schema, {recordBField1: {'RecordB': {}}, recordBField2: {}}); });
test.done();
},
// Enums
'enum': function(test) {
var schema = {type: 'enum', name: 'Colors', symbols: ['Red', 'Blue']};
test.ok(Validator.validate(schema, 'Red'));
test.ok(Validator.validate(schema, 'Blue'));
test.throws(function() { Validator.validate(schema, null); });
test.throws(function() { Validator.validate(schema, undefined); });
test.throws(function() { Validator.validate(schema, 'NotAColor'); });
test.throws(function() { Validator.validate(schema, ''); });
test.throws(function() { Validator.validate(schema, {}); });
test.throws(function() { Validator.validate(schema, []); });
test.throws(function() { Validator.validate(schema, 1); });
test.done();
},
// Unions
'union': function(test) {
var schema = ['string', 'int'];
test.ok(Validator.validate(schema, {string: 'a'}));
test.ok(Validator.validate(schema, {int: 1}));
test.throws(function() { Validator.validate(schema, null); });
test.throws(function() { Validator.validate(schema, undefined); });
test.throws(function() { Validator.validate(schema, 'a'); });
test.throws(function() { Validator.validate(schema, 1); });
test.throws(function() { Validator.validate(schema, {string: 'a', int: 1}); });
test.throws(function() { Validator.validate(schema, []); });
test.done();
},
'union with null': function(test) {
var schema = ['string', 'null'];
test.ok(Validator.validate(schema, {string: 'a'}));
test.ok(Validator.validate(schema, null));
test.throws(function() { Validator.validate(schema, undefined); });
test.done();
},
'nested union': function(test) {
var schema = ['string', {type: 'int'}];
test.ok(Validator.validate(schema, {string: 'a'}));
test.ok(Validator.validate(schema, {int: 1}));
test.throws(function() { Validator.validate(schema, null); });
test.throws(function() { Validator.validate(schema, undefined); });
test.throws(function() { Validator.validate(schema, 'a'); });
test.throws(function() { Validator.validate(schema, 1); });
test.throws(function() { Validator.validate(schema, {string: 'a', int: 1}); });
test.throws(function() { Validator.validate(schema, []); });
test.done();
},
// Arrays
'array': function(test) {
var schema = {type: "array", items: "string"};
test.ok(Validator.validate(schema, []));
test.ok(Validator.validate(schema, ["a"]));
test.ok(Validator.validate(schema, ["a", "b", "a"]));
test.throws(function() { Validator.validate(schema, null); });
test.throws(function() { Validator.validate(schema, undefined); });
test.throws(function() { Validator.validate(schema, 'a'); });
test.throws(function() { Validator.validate(schema, 1); });
test.throws(function() { Validator.validate(schema, {}); });
test.throws(function() { Validator.validate(schema, {"1": "a"}); });
test.throws(function() { Validator.validate(schema, {1: "a"}); });
test.throws(function() { Validator.validate(schema, {1: "a", "b": undefined}); });
test.throws(function() { var a = {}; a[0] = "a"; Validator.validate(schema, a); });
test.throws(function() { Validator.validate(schema, [1]); });
test.throws(function() { Validator.validate(schema, [1, "a"]); });
test.throws(function() { Validator.validate(schema, ["a", 1]); });
test.throws(function() { Validator.validate(schema, [null, 1]); });
test.done();
},
// Maps
'map': function(test) {
var schema = {type: "map", values: "string"};
test.ok(Validator.validate(schema, {}));
test.ok(Validator.validate(schema, {"a": "b"}));
test.ok(Validator.validate(schema, {"a": "b", "c": "d"}));
test.throws(function() { Validator.validate(schema, null); });
test.throws(function() { Validator.validate(schema, undefined); });
test.throws(function() { Validator.validate(schema, 'a'); });
test.throws(function() { Validator.validate(schema, 1); });
test.throws(function() { Validator.validate(schema, [1]); });
test.throws(function() { Validator.validate(schema, {"a": 1}); });
test.throws(function() { Validator.validate(schema, {"a": "b", "c": 1}); });
test.done();
},
// Protocols
'protocol': function(test) {
var protocol = {protocol: "Protocol1", namespace: "x.y.z", types: [
{type: "record", name: "RecordA", fields: []},
{type: "record", name: "RecordB", fields: [{name: "recordAField", type: "RecordA"}]}
]};
test.ok(ProtocolValidator.validate(protocol, 'RecordA', {}));
test.ok(ProtocolValidator.validate(protocol, 'x.y.z.RecordA', {}));
test.ok(ProtocolValidator.validate(protocol, 'RecordB', {recordAField: {}}));
test.ok(ProtocolValidator.validate(protocol, 'x.y.z.RecordB', {recordAField: {}}));
test.throws(function() { ProtocolValidator.validate(protocol, 'RecordDoesNotExist', {}); });
test.throws(function() { ProtocolValidator.validate(protocol, 'RecordDoesNotExist', null); });
test.throws(function() { ProtocolValidator.validate(protocol, 'RecordB', {}); });
test.throws(function() { ProtocolValidator.validate(protocol, null, {}); });
test.throws(function() { ProtocolValidator.validate(protocol, '', {}); });
test.throws(function() { ProtocolValidator.validate(protocol, {}, {}); });
test.done();
},
// Samples
'link': function(test) {
var schema = {
"type" : "record",
"name" : "Bundle",
"namespace" : "aa.bb.cc",
"fields" : [ {
"name" : "id",
"type" : "string"
}, {
"name" : "type",
"type" : "string"
}, {
"name" : "data_",
"type" : [ "null", {
"type" : "record",
"name" : "LinkData",
"fields" : [ {
"name" : "address",
"type" : "string"
}, {
"name" : "title",
"type" : [ "null", "string" ],
"default" : null
}, {
"name" : "excerpt",
"type" : [ "null", "string" ],
"default" : null
}, {
"name" : "image",
"type" : [ "null", {
"type" : "record",
"name" : "Image",
"fields" : [ {
"name" : "url",
"type" : "string"
}, {
"name" : "width",
"type" : "int"
}, {
"name" : "height",
"type" : "int"
} ]
} ],
"default" : null
}, {
"name" : "meta",
"type" : {
"type" : "map",
"values" : "string"
},
"default" : {
}
} ]
} ],
"default" : null
}, {
"name" : "atoms_",
"type" : {
"type" : "map",
"values" : {
"type" : "map",
"values" : {
"type" : "record",
"name" : "Atom",
"fields" : [ {
"name" : "index_",
"type" : {
"type" : "record",
"name" : "AtomIndex",
"fields" : [ {
"name" : "type_",
"type" : "string"
}, {
"name" : "id",
"type" : "string"
} ]
}
}, {
"name" : "data_",
"type" : [ "LinkData" ]
} ]
}
}
},
"default" : {
}
}, {
"name" : "meta_",
"type" : {
"type" : "record",
"name" : "BundleMetadata",
"fields" : [ {
"name" : "date",
"type" : "long",
"default" : 0
}, {
"name" : "members",
"type" : {
"type" : "map",
"values" : "string"
},
"default" : {
}
}, {
"name" : "tags",
"type" : {
"type" : "map",
"values" : "string"
},
"default" : {
}
}, {
"name" : "meta",
"type" : {
"type" : "map",
"values" : "string"
},
"default" : {
}
}, {
"name" : "votes",
"type" : {
"type" : "map",
"values" : {
"type" : "record",
"name" : "VoteData",
"fields" : [ {
"name" : "date",
"type" : "long"
}, {
"name" : "userName",
"type" : [ "null", "string" ],
"default" : null
}, {
"name" : "direction",
"type" : {
"type" : "enum",
"name" : "VoteDirection",
"symbols" : [ "Up", "Down", "None" ]
}
} ]
}
},
"default" : {
}
}, {
"name" : "views",
"type" : {
"type" : "map",
"values" : {
"type" : "record",
"name" : "ViewData",
"fields" : [ {
"name" : "userName",
"type" : "string"
}, {
"name" : "count",
"type" : "int"
} ]
}
},
"default" : {
}
}, {
"name" : "relevance",
"type" : {
"type" : "map",
"values" : "string"
},
"default" : {
}
}, {
"name" : "clicks",
"type" : {
"type" : "map",
"values" : "string"
},
"default" : {
}
} ]
}
} ]
};
var okObj = {
"id": "https://github.com/sqs/akka-kryo-serialization/subscription",
"type": "link",
"data_": {
"aa.bb.cc.LinkData": {
"address": "https://github.com/sqs/akka-kryo-serialization/subscription",
"title": {
"string": "Sign in · GitHub"
},
"excerpt": {
"string": "Signup and Pricing Explore GitHub Features Blog Sign in Sign in (Pricing and Signup) Username or Email Password (forgot password) GitHub Links GitHub About Blog Feat"
},
"image": {
"aa.bb.cc.Image": {
"url": "https://a248.e.akamai.net/assets.github.com/images/modules/header/logov7@4x.png?1340659561",
"width": 280,
"height": 120
}
},
"meta": {}
}
},
"atoms_": {
"link": {
"https://github.com/sqs/akka-kryo-serialization/subscription": {
"index_": {
"type_": "link",
"id": "https://github.com/sqs/akka-kryo-serialization/subscription"
},
"data_": {
"aa.bb.cc.LinkData": {
"address": "https://github.com/sqs/akka-kryo-serialization/subscription",
"title": {
"string": "Sign in · GitHub"
},
"excerpt": {
"string": "Signup and Pricing Explore GitHub Features Blog Sign in Sign in (Pricing and Signup) Username or Email Password (forgot password) GitHub Links GitHub About Blog Feat"
},
"image": {
"aa.bb.cc.Image": {
"url": "https://a248.e.akamai.net/assets.github.com/images/modules/header/logov7@4x.png?1340659561",
"width": 280,
"height": 120
}
},
"meta": {}
}
}
}
}
},
"meta_": {
"date": 1345537530000,
"members": {
"a@a.com": "1"
},
"tags": {
"blue": "1"
},
"meta": {},
"votes": {},
"views": {
"a@a.com": {
"userName": "John Smith",
"count": 100
}
},
"relevance": {
"a@a.com": "1",
"b@b.com": "2"
},
"clicks": {}
}
};
test.ok(Validator.validate(schema, okObj));
var badObj = okObj; // no deep copy since we won't reuse okObj
badObj.meta_.clicks['a@a.com'] = 123;
test.throws(function() { Validator.validate(schema, badObj); });
test.done();
}
};