1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 var AssertTrue = requireNative('assert').AssertTrue;
6 var JSONSchemaValidator = require('json_schema').JSONSchemaValidator;
7 var LOG = requireNative('logging').LOG;
9 function assertValid(type, instance, schema, types) {
10 var validator = new JSONSchemaValidator();
12 validator.addTypes(types);
13 validator["validate" + type](instance, schema, "");
15 if (validator.errors.length != 0) {
16 LOG("Got unexpected errors");
17 for (var i = 0; i < validator.errors.length; i++) {
18 LOG(validator.errors[i].message + " path: " + validator.errors[i].path);
25 function assertNotValid(type, instance, schema, errors, types) {
26 var validator = new JSONSchemaValidator();
28 validator.addTypes(types);
29 validator["validate" + type](instance, schema, "");
30 AssertTrue(validator.errors.length === errors.length);
32 for (var i = 0; i < errors.length; i++) {
33 if (validator.errors[i].message == errors[i]) {
34 LOG("Got expected error: " + validator.errors[i].message +
35 " for path: " + validator.errors[i].path);
37 LOG("Missed expected error: " + errors[i] + ". Got: " +
38 validator.errors[i].message + " instead.");
45 function assertListConsistsOfElements(list, elements) {
47 for (var li = 0; li < list.length; li++) {
48 for (var ei = 0; ei < elements.length && list[li] != elements[ei]; ei++) { }
49 if (ei == elements.length) {
50 LOG("Expected type not found: " + list[li]);
57 function assertEqualSets(set1, set2) {
58 assertListConsistsOfElements(set1, set2);
59 assertListConsistsOfElements(set2, set1);
62 function formatError(key, replacements) {
63 return JSONSchemaValidator.formatError(key, replacements);
66 function testFormatError() {
67 AssertTrue(formatError("propertyRequired") == "Property is required.");
68 AssertTrue(formatError("invalidEnum", ["foo, bar"]) ==
69 "Value must be one of: [foo, bar].");
70 AssertTrue(formatError("invalidType", ["foo", "bar"]) ==
71 "Expected 'foo' but got 'bar'.");
74 function testComplex() {
101 { type: "function", optional: true },
102 { type: "function", optional: true }
109 url: "http://www.google.com/",
117 assertValid("", instance, schema);
119 assertValid("", instance, schema);
122 assertNotValid("", instance, schema,
123 [formatError("invalidType", ["function", "object"])]);
124 instance[1] = function(){};
126 instance[0].url = "foo";
127 assertNotValid("", instance, schema,
128 [formatError("stringPattern",
129 [schema.items[0].properties.url.pattern])]);
130 delete instance[0].url;
131 assertValid("", instance, schema);
134 assertNotValid("", instance, schema,
135 [formatError("numberMinValue",
136 [schema.items[0].properties.id.minimum])]);
139 function testEnum() {
141 enum: ["foo", 42, false]
144 assertValid("", "foo", schema);
145 assertValid("", 42, schema);
146 assertValid("", false, schema);
147 assertNotValid("", "42", schema, [formatError("invalidEnum",
148 [schema.enum.join(", ")])]);
149 assertNotValid("", null, schema, [formatError("invalidEnum",
150 [schema.enum.join(", ")])]);
153 function testChoices() {
157 { type: "undefined" },
158 { type: "integer", minimum:42, maximum:42 },
159 { type: "object", properties: { foo: { type: "string" } } }
162 assertValid("", null, schema);
163 assertValid("", undefined, schema);
164 assertValid("", 42, schema);
165 assertValid("", {foo: "bar"}, schema);
167 assertNotValid("", "foo", schema, [formatError("invalidChoice", [])]);
168 assertNotValid("", [], schema, [formatError("invalidChoice", [])]);
169 assertNotValid("", {foo: 42}, schema, [formatError("invalidChoice", [])]);
172 function testExtends() {
180 assertValid("", 42, schema);
181 assertNotValid("", "42", schema,
182 [formatError("invalidType", ["number", "string"])]);
184 // Make the derived schema more restrictive
186 assertNotValid("", 42, schema, [formatError("numberMinValue", [43])]);
187 assertValid("", 43, schema);
190 function testObject() {
202 assertValid("Object", {foo:"foo", bar:42}, schema);
203 assertNotValid("Object", {foo:"foo", bar:42,"extra":true}, schema,
204 [formatError("unexpectedProperty")]);
205 assertNotValid("Object", {foo:"foo"}, schema,
206 [formatError("propertyRequired")]);
207 assertNotValid("Object", {foo:"foo", bar:"42"}, schema,
208 [formatError("invalidType", ["integer", "string"])]);
210 schema.additionalProperties = { type: "any" };
211 assertValid("Object", {foo:"foo", bar:42, "extra":true}, schema);
212 assertValid("Object", {foo:"foo", bar:42, "extra":"foo"}, schema);
214 schema.additionalProperties = { type: "boolean" };
215 assertValid("Object", {foo:"foo", bar:42, "extra":true}, schema);
216 assertNotValid("Object", {foo:"foo", bar:42, "extra":"foo"}, schema,
217 [formatError("invalidType", ["boolean", "string"])]);
219 schema.properties.bar.optional = true;
220 assertValid("Object", {foo:"foo", bar:42}, schema);
221 assertValid("Object", {foo:"foo"}, schema);
222 assertValid("Object", {foo:"foo", bar:null}, schema);
223 assertValid("Object", {foo:"foo", bar:undefined}, schema);
224 assertNotValid("Object", {foo:"foo", bar:"42"}, schema,
225 [formatError("invalidType", ["integer", "string"])]);
228 function testTypeReference() {
229 var referencedTypes = [
231 id: "MinLengthString",
252 $ref: "MinLengthString"
257 var schemaInlineReference = {
274 // Valid type references externally added.
275 assertValid("", {foo:"foo",bar:4,baz:"ab"}, schema, referencedTypes);
277 // Valida type references internally defined.
278 assertValid("", {foo:"foo",bar:-4,baz:-2}, schemaInlineReference);
280 // Failures in validation, but succesful schema reference.
281 assertNotValid("", {foo:"foo",bar:4,baz:"a"}, schema,
282 [formatError("stringMinLength", [2])], referencedTypes);
283 assertNotValid("", {foo:"foo",bar:20,baz:"abc"}, schema,
284 [formatError("numberMaxValue", [10])], referencedTypes);
286 // Remove MinLengthString type.
287 referencedTypes.shift();
288 assertNotValid("", {foo:"foo",bar:4,baz:"ab"}, schema,
289 [formatError("unknownSchemaReference", ["MinLengthString"])],
292 // Remove internal type "NegativeInt"
293 delete schemaInlineReference.properties.bar;
294 assertNotValid("", {foo:"foo",baz:-2}, schemaInlineReference,
295 [formatError("unknownSchemaReference", ["NegativeInt"])]);
298 function testArrayTuple() {
310 assertValid("Array", ["42", 42], schema);
311 assertNotValid("Array", ["42", 42, "anything"], schema,
312 [formatError("arrayMaxItems", [schema.items.length])]);
313 assertNotValid("Array", ["42"], schema, [formatError("itemRequired")]);
314 assertNotValid("Array", [42, 42], schema,
315 [formatError("invalidType", ["string", "integer"])]);
317 schema.additionalProperties = { type: "any" };
318 assertValid("Array", ["42", 42, "anything"], schema);
319 assertValid("Array", ["42", 42, []], schema);
321 schema.additionalProperties = { type: "boolean" };
322 assertNotValid("Array", ["42", 42, "anything"], schema,
323 [formatError("invalidType", ["boolean", "string"])]);
324 assertValid("Array", ["42", 42, false], schema);
326 schema.items[0].optional = true;
327 assertValid("Array", ["42", 42], schema);
328 assertValid("Array", [, 42], schema);
329 assertValid("Array", [null, 42], schema);
330 assertValid("Array", [undefined, 42], schema);
331 assertNotValid("Array", [42, 42], schema,
332 [formatError("invalidType", ["string", "integer"])]);
335 function testArrayNonTuple() {
344 assertValid("Array", ["x", "x"], schema);
345 assertValid("Array", ["x", "x", "x"], schema);
347 assertNotValid("Array", ["x"], schema,
348 [formatError("arrayMinItems", [schema.minItems])]);
349 assertNotValid("Array", ["x", "x", "x", "x"], schema,
350 [formatError("arrayMaxItems", [schema.maxItems])]);
351 assertNotValid("Array", [42], schema,
352 [formatError("arrayMinItems", [schema.minItems]),
353 formatError("invalidType", ["string", "integer"])]);
356 function testString() {
363 assertValid("String", "x", schema);
364 assertValid("String", "xxxxxxxxxx", schema);
366 assertNotValid("String", "y", schema,
367 [formatError("stringPattern", [schema.pattern])]);
368 assertNotValid("String", "xxxxxxxxxxx", schema,
369 [formatError("stringMaxLength", [schema.maxLength])]);
370 assertNotValid("String", "", schema,
371 [formatError("stringMinLength", [schema.minLength]),
372 formatError("stringPattern", [schema.pattern])]);
375 function testNumber() {
382 assertValid("Number", 1, schema);
383 assertValid("Number", 50, schema);
384 assertValid("Number", 100, schema);
385 assertValid("Number", 88.88, schema);
387 assertNotValid("Number", 0.5, schema,
388 [formatError("numberMinValue", [schema.minimum])]);
389 assertNotValid("Number", 100.1, schema,
390 [formatError("numberMaxValue", [schema.maximum])]);
391 assertNotValid("Number", 100.111, schema,
392 [formatError("numberMaxValue", [schema.maximum]),
393 formatError("numberMaxDecimal", [schema.maxDecimal])]);
396 AssertTrue(isNaN(nan));
397 assertNotValid("Number", nan, schema,
398 [formatError("numberFiniteNotNan", ["NaN"])]);
400 assertNotValid("Number", Number.POSITIVE_INFINITY, schema,
401 [formatError("numberFiniteNotNan", ["Infinity"]),
402 formatError("numberMaxValue", [schema.maximum])
405 assertNotValid("Number", Number.NEGATIVE_INFINITY, schema,
406 [formatError("numberFiniteNotNan", ["-Infinity"]),
407 formatError("numberMinValue", [schema.minimum])
411 function testIntegerBounds() {
412 assertValid("Number", 0, {type:"integer"});
413 assertValid("Number", -1, {type:"integer"});
414 assertValid("Number", 2147483647, {type:"integer"});
415 assertValid("Number", -2147483648, {type:"integer"});
416 assertNotValid("Number", 0.5, {type:"integer"},
417 [formatError("numberIntValue", [])]);
418 assertNotValid("Number", 10000000000, {type:"integer"},
419 [formatError("numberIntValue", [])]);
420 assertNotValid("Number", 2147483647.5, {type:"integer"},
421 [formatError("numberIntValue", [])]);
422 assertNotValid("Number", 2147483648, {type:"integer"},
423 [formatError("numberIntValue", [])]);
424 assertNotValid("Number", 2147483649, {type:"integer"},
425 [formatError("numberIntValue", [])]);
426 assertNotValid("Number", -2147483649, {type:"integer"},
427 [formatError("numberIntValue", [])]);
430 function testType() {
432 assertValid("Type", {}, {type:"object"});
433 assertValid("Type", [], {type:"array"});
434 assertValid("Type", function(){}, {type:"function"});
435 assertValid("Type", "foobar", {type:"string"});
436 assertValid("Type", "", {type:"string"});
437 assertValid("Type", 88.8, {type:"number"});
438 assertValid("Type", 42, {type:"number"});
439 assertValid("Type", 0, {type:"number"});
440 assertValid("Type", 42, {type:"integer"});
441 assertValid("Type", 0, {type:"integer"});
442 assertValid("Type", true, {type:"boolean"});
443 assertValid("Type", false, {type:"boolean"});
444 assertValid("Type", null, {type:"null"});
445 assertValid("Type", undefined, {type:"undefined"});
446 assertValid("Type", new ArrayBuffer(1), {type:"binary"});
447 assertValid("Type", otherContextArrayBufferContainer.value, {type:"binary"});
450 assertNotValid("Type", [], {type: "object"},
451 [formatError("invalidType", ["object", "array"])]);
452 assertNotValid("Type", null, {type: "object"},
453 [formatError("invalidType", ["object", "null"])]);
454 assertNotValid("Type", function(){}, {type: "object"},
455 [formatError("invalidType", ["object", "function"])]);
456 assertNotValid("Type", 42, {type: "array"},
457 [formatError("invalidType", ["array", "integer"])]);
458 assertNotValid("Type", 42, {type: "string"},
459 [formatError("invalidType", ["string", "integer"])]);
460 assertNotValid("Type", "42", {type: "number"},
461 [formatError("invalidType", ["number", "string"])]);
462 assertNotValid("Type", 88.8, {type: "integer"},
463 [formatError("invalidTypeIntegerNumber")]);
464 assertNotValid("Type", 1, {type: "boolean"},
465 [formatError("invalidType", ["boolean", "integer"])]);
466 assertNotValid("Type", false, {type: "null"},
467 [formatError("invalidType", ["null", "boolean"])]);
468 assertNotValid("Type", undefined, {type: "null"},
469 [formatError("invalidType", ["null", "undefined"])]);
470 assertNotValid("Type", {}, {type: "function"},
471 [formatError("invalidType", ["function", "object"])]);
474 function testGetAllTypesForSchema() {
475 var referencedTypes = [
493 var choicesSchema = {
500 var objectRefSchema = {
504 var complexSchema = {
506 { $ref: "ChoicesRef" },
507 { type: "function" },
508 { $ref: "ObjectRef" }
512 var validator = new JSONSchemaValidator();
513 validator.addTypes(referencedTypes);
515 var arraySchemaTypes = validator.getAllTypesForSchema(arraySchema);
516 assertEqualSets(arraySchemaTypes, ["array"]);
518 var choicesSchemaTypes = validator.getAllTypesForSchema(choicesSchema);
519 assertEqualSets(choicesSchemaTypes, ["object", "function"]);
521 var objectRefSchemaTypes = validator.getAllTypesForSchema(objectRefSchema);
522 assertEqualSets(objectRefSchemaTypes, ["object"]);
524 var complexSchemaTypes = validator.getAllTypesForSchema(complexSchema);
525 assertEqualSets(complexSchemaTypes,
526 ["integer", "string", "function", "object"]);
529 function testIsValidSchemaType() {
530 var referencedTypes = [
545 var complexSchema = {
547 { $ref: "ChoicesRef" },
548 { type: "function" },
552 var validator = new JSONSchemaValidator();
553 validator.addTypes(referencedTypes);
555 AssertTrue(validator.isValidSchemaType("object", objectSchema));
556 AssertTrue(!validator.isValidSchemaType("integer", objectSchema));
557 AssertTrue(!validator.isValidSchemaType("array", objectSchema));
558 AssertTrue(validator.isValidSchemaType("null", objectSchema));
559 AssertTrue(validator.isValidSchemaType("undefined", objectSchema));
561 AssertTrue(validator.isValidSchemaType("integer", complexSchema));
562 AssertTrue(validator.isValidSchemaType("function", complexSchema));
563 AssertTrue(validator.isValidSchemaType("string", complexSchema));
564 AssertTrue(!validator.isValidSchemaType("object", complexSchema));
565 AssertTrue(!validator.isValidSchemaType("null", complexSchema));
566 AssertTrue(!validator.isValidSchemaType("undefined", complexSchema));
569 function testCheckSchemaOverlap() {
570 var referencedTypes = [
588 var choicesSchema = {
595 var objectRefSchema = {
599 var complexSchema = {
601 { $ref: "ChoicesRef" },
602 { type: "function" },
603 { $ref: "ObjectRef" }
607 var validator = new JSONSchemaValidator();
608 validator.addTypes(referencedTypes);
610 AssertTrue(!validator.checkSchemaOverlap(arraySchema, choicesSchema));
611 AssertTrue(!validator.checkSchemaOverlap(arraySchema, objectRefSchema));
612 AssertTrue(!validator.checkSchemaOverlap(arraySchema, complexSchema));
613 AssertTrue(validator.checkSchemaOverlap(choicesSchema, objectRefSchema));
614 AssertTrue(validator.checkSchemaOverlap(choicesSchema, complexSchema));
615 AssertTrue(validator.checkSchemaOverlap(objectRefSchema, complexSchema));
618 function testInstanceOf() {
619 function Animal() {};
622 Cat.prototype = new Animal;
623 Cat.prototype.constructor = Cat;
624 Dog.prototype = new Animal;
625 Dog.prototype.constructor = Dog;
628 var num = new Number(1);
630 // instanceOf should check type by walking up prototype chain.
631 assertValid("", cat, {type:"object", isInstanceOf:"Cat"});
632 assertValid("", cat, {type:"object", isInstanceOf:"Animal"});
633 assertValid("", cat, {type:"object", isInstanceOf:"Object"});
634 assertValid("", dog, {type:"object", isInstanceOf:"Dog"});
635 assertValid("", dog, {type:"object", isInstanceOf:"Animal"});
636 assertValid("", dog, {type:"object", isInstanceOf:"Object"});
637 assertValid("", num, {type:"object", isInstanceOf:"Number"});
638 assertValid("", num, {type:"object", isInstanceOf:"Object"});
640 assertNotValid("", cat, {type:"object", isInstanceOf:"Dog"},
641 [formatError("notInstance", ["Dog"])]);
642 assertNotValid("", dog, {type:"object", isInstanceOf:"Cat"},
643 [formatError("notInstance", ["Cat"])]);
644 assertNotValid("", cat, {type:"object", isInstanceOf:"String"},
645 [formatError("notInstance", ["String"])]);
646 assertNotValid("", dog, {type:"object", isInstanceOf:"String"},
647 [formatError("notInstance", ["String"])]);
648 assertNotValid("", num, {type:"object", isInstanceOf:"Array"},
649 [formatError("notInstance", ["Array"])]);
650 assertNotValid("", num, {type:"object", isInstanceOf:"String"},
651 [formatError("notInstance", ["String"])]);
654 // Tests exposed to schema_unittest.cc.
655 exports.testFormatError = testFormatError;
656 exports.testComplex = testComplex;
657 exports.testEnum = testEnum;
658 exports.testExtends = testExtends;
659 exports.testObject = testObject;
660 exports.testArrayTuple = testArrayTuple;
661 exports.testArrayNonTuple = testArrayNonTuple;
662 exports.testString = testString;
663 exports.testNumber = testNumber;
664 exports.testIntegerBounds = testIntegerBounds;
665 exports.testType = testType;
666 exports.testTypeReference = testTypeReference;
667 exports.testGetAllTypesForSchema = testGetAllTypesForSchema;
668 exports.testIsValidSchemaType = testIsValidSchemaType;
669 exports.testCheckSchemaOverlap = testCheckSchemaOverlap;
670 exports.testInstanceOf = testInstanceOf;