|
|
@@ -0,0 +1,629 @@
|
|
|
+import {expect} from 'chai';
|
|
|
+import {DataType} from './data-type.js';
|
|
|
+import {format} from '@e22m4u/js-format';
|
|
|
+import {DataValidator} from './data-validator.js';
|
|
|
+import {DataValidationError} from './errors/index.js';
|
|
|
+import {DataSchemaRegistry} from './data-schema-registry.js';
|
|
|
+
|
|
|
+import {
|
|
|
+ arrayTypeValidator,
|
|
|
+ objectTypeValidator,
|
|
|
+ stringTypeValidator,
|
|
|
+ numberTypeValidator,
|
|
|
+ booleanTypeValidator,
|
|
|
+ requiredValueValidator,
|
|
|
+} from './data-validators/index.js';
|
|
|
+
|
|
|
+describe('DataValidator', function () {
|
|
|
+ describe('getValidators', function () {
|
|
|
+ it('should return a default validator list', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const res = S.getValidators();
|
|
|
+ expect(res).to.be.eql([
|
|
|
+ stringTypeValidator,
|
|
|
+ booleanTypeValidator,
|
|
|
+ numberTypeValidator,
|
|
|
+ objectTypeValidator,
|
|
|
+ arrayTypeValidator,
|
|
|
+ requiredValueValidator,
|
|
|
+ ]);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should return a modified validator list', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const validator1 = () => undefined;
|
|
|
+ const validator2 = () => undefined;
|
|
|
+ S.setValidators([validator1, validator2]);
|
|
|
+ const res = S.getValidators();
|
|
|
+ expect(res).to.be.eql([validator1, validator2]);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('setValidators', function () {
|
|
|
+ it('should require a given value to be an array', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const throwable = v => () => S.setValidators(v);
|
|
|
+ const error = s =>
|
|
|
+ format('Data validators must be an Array, but %s was given.', s);
|
|
|
+ expect(throwable('str')).to.throw(error('"str"'));
|
|
|
+ expect(throwable('')).to.throw(error('""'));
|
|
|
+ expect(throwable(10)).to.throw(error('10'));
|
|
|
+ expect(throwable(0)).to.throw(error('0'));
|
|
|
+ expect(throwable(true)).to.throw(error('true'));
|
|
|
+ expect(throwable(false)).to.throw(error('false'));
|
|
|
+ expect(throwable({})).to.throw(error('Object'));
|
|
|
+ expect(throwable(null)).to.throw(error('null'));
|
|
|
+ expect(throwable(undefined)).to.throw(error('undefined'));
|
|
|
+ throwable([])();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should require given validators to be a function', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const throwable = v => () => S.setValidators([v]);
|
|
|
+ const error = s =>
|
|
|
+ format('Data validator must be a Function, but %s was given.', s);
|
|
|
+ expect(throwable('str')).to.throw(error('"str"'));
|
|
|
+ expect(throwable('')).to.throw(error('""'));
|
|
|
+ expect(throwable(10)).to.throw(error('10'));
|
|
|
+ expect(throwable(0)).to.throw(error('0'));
|
|
|
+ expect(throwable(true)).to.throw(error('true'));
|
|
|
+ expect(throwable(false)).to.throw(error('false'));
|
|
|
+ expect(throwable([])).to.throw(error('Array'));
|
|
|
+ expect(throwable({})).to.throw(error('Object'));
|
|
|
+ expect(throwable(null)).to.throw(error('null'));
|
|
|
+ expect(throwable(undefined)).to.throw(error('undefined'));
|
|
|
+ throwable(() => undefined)();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should set the validator list', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const validator1 = () => undefined;
|
|
|
+ const validator2 = () => undefined;
|
|
|
+ S.setValidators([validator1, validator2]);
|
|
|
+ const res1 = S.getValidators();
|
|
|
+ expect(res1).to.be.eql([validator1, validator2]);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should able to clean the validator list', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const validator1 = () => undefined;
|
|
|
+ const validator2 = () => undefined;
|
|
|
+ S.setValidators([validator1, validator2]);
|
|
|
+ const res1 = S.getValidators();
|
|
|
+ expect(res1).to.be.eql([validator1, validator2]);
|
|
|
+ S.setValidators([]);
|
|
|
+ const res2 = S.getValidators();
|
|
|
+ expect(res2).to.be.eql([]);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('defineSchema', function() {
|
|
|
+ it('should pass a given schema definition to the registry', function() {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const registry = S.getService(DataSchemaRegistry);
|
|
|
+ const schemaDef = {name: 'mySchema', schema: {}};
|
|
|
+ S.defineSchema(schemaDef);
|
|
|
+ const res = registry.getDefinition(schemaDef.name);
|
|
|
+ expect(res).to.be.eql(schemaDef);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should return a current instance', function() {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const schemaDef = {name: 'mySchema', schema: {}};
|
|
|
+ const res = S.defineSchema(schemaDef);
|
|
|
+ expect(res).to.be.eq(S);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('hasSchema', function() {
|
|
|
+ it('should return true if a given name is registered', function() {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const schemaDef = {name: 'mySchema', schema: {}};
|
|
|
+ expect(S.hasSchema(schemaDef.name)).to.be.false;
|
|
|
+ S.defineSchema(schemaDef);
|
|
|
+ expect(S.hasSchema(schemaDef.name)).to.be.true;
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('getSchema', function() {
|
|
|
+ it('should return a register schema for a given name', function() {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const schemaDef = {name: 'mySchema', schema: {}};
|
|
|
+ S.defineSchema(schemaDef);
|
|
|
+ const res = S.getSchema(schemaDef.name);
|
|
|
+ expect(res).to.be.eql(schemaDef.schema);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should throw an error if a given name is not registered', function() {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const throwable = () => S.getSchema('mySchema');
|
|
|
+ expect(throwable).to.throw('Data schema "mySchema" is not found.');
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('validate', function () {
|
|
|
+ it('should require the "options" argument to be an object', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const throwable = v => () => S.validate(10, {}, v);
|
|
|
+ const error = s =>
|
|
|
+ format('Validation options must be an Object, but %s was given.', s);
|
|
|
+ expect(throwable('str')).to.throw(error('"str"'));
|
|
|
+ expect(throwable('')).to.throw(error('""'));
|
|
|
+ expect(throwable(10)).to.throw(error('10'));
|
|
|
+ expect(throwable(0)).to.throw(error('0'));
|
|
|
+ expect(throwable(true)).to.throw(error('true'));
|
|
|
+ expect(throwable(false)).to.throw(error('false'));
|
|
|
+ expect(throwable([])).to.throw(error('Array'));
|
|
|
+ expect(throwable(null)).to.throw(error('null'));
|
|
|
+ expect(throwable(() => undefined)).to.throw(error('Function'));
|
|
|
+ throwable({})();
|
|
|
+ throwable(undefined)();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should require the "sourcePath" argument to be a non-empty string', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const throwable = v => () => S.validate(10, {}, {sourcePath: v});
|
|
|
+ const error = s =>
|
|
|
+ format(
|
|
|
+ 'Option "sourcePath" must be a non-empty String, but %s was given.',
|
|
|
+ s,
|
|
|
+ );
|
|
|
+ expect(throwable('')).to.throw(error('""'));
|
|
|
+ expect(throwable(10)).to.throw(error('10'));
|
|
|
+ expect(throwable(0)).to.throw(error('0'));
|
|
|
+ expect(throwable(true)).to.throw(error('true'));
|
|
|
+ expect(throwable(false)).to.throw(error('false'));
|
|
|
+ expect(throwable([])).to.throw(error('Array'));
|
|
|
+ expect(throwable({})).to.throw(error('Object'));
|
|
|
+ expect(throwable(null)).to.throw(error('null'));
|
|
|
+ expect(throwable(() => undefined)).to.throw(error('Function'));
|
|
|
+ throwable('str')();
|
|
|
+ throwable(undefined)();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should require the "shallowMode" argument to be a boolean', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const throwable = v => () => S.validate(10, {}, {shallowMode: v});
|
|
|
+ const error = s =>
|
|
|
+ format('Option "shallowMode" must be a Boolean, but %s was given.', s);
|
|
|
+ expect(throwable('str')).to.throw(error('"str"'));
|
|
|
+ expect(throwable('')).to.throw(error('""'));
|
|
|
+ expect(throwable(10)).to.throw(error('10'));
|
|
|
+ expect(throwable(0)).to.throw(error('0'));
|
|
|
+ expect(throwable([])).to.throw(error('Array'));
|
|
|
+ expect(throwable({})).to.throw(error('Object'));
|
|
|
+ expect(throwable(null)).to.throw(error('null'));
|
|
|
+ expect(throwable(() => undefined)).to.throw(error('Function'));
|
|
|
+ throwable(true)();
|
|
|
+ throwable(false)();
|
|
|
+ throwable(undefined)();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should validate a given schema in the shallow mode', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const throwable = () => S.validate({}, {required: 10});
|
|
|
+ expect(throwable).to.throw(
|
|
|
+ 'Schema option "required" must be a Boolean, but 10 was given.',
|
|
|
+ );
|
|
|
+ S.validate([], {type: DataType.ARRAY, items: {type: 10}});
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should resolve the data schema from a given factory', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ const throwable = () => S.validate(10, () => ({type: DataType.STRING}));
|
|
|
+ expect(throwable).to.throw(DataValidationError);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should resolve the data schema from a schema name', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ S.getService(DataSchemaRegistry).defineSchema({
|
|
|
+ name: 'mySchema',
|
|
|
+ schema: {type: DataType.STRING},
|
|
|
+ });
|
|
|
+ const throwable = () => S.validate(10, 'mySchema');
|
|
|
+ expect(throwable).to.throw(DataValidationError);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should resolve a schema name from a given factory', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ S.getService(DataSchemaRegistry).defineSchema({
|
|
|
+ name: 'mySchema',
|
|
|
+ schema: {type: DataType.STRING},
|
|
|
+ });
|
|
|
+ const throwable = () => S.validate(10, () => 'mySchema');
|
|
|
+ expect(throwable).to.throw(DataValidationError);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should resolve a schema factory from a named schema', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ S.getService(DataSchemaRegistry).defineSchema({
|
|
|
+ name: 'mySchema',
|
|
|
+ schema: () => ({type: DataType.STRING}),
|
|
|
+ });
|
|
|
+ const throwable = () => S.validate(10, 'mySchema');
|
|
|
+ expect(throwable).to.throw(DataValidationError);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should pass specific arguments to data validators', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = 10;
|
|
|
+ const schema = {type: DataType.NUMBER};
|
|
|
+ const options = {sourcePath: 'aSource'};
|
|
|
+ let invoked = 0;
|
|
|
+ const validator = (...args) => {
|
|
|
+ invoked++;
|
|
|
+ expect(args).to.be.eql([value, schema, options, S.container]);
|
|
|
+ };
|
|
|
+ S.setValidators([validator]);
|
|
|
+ S.validate(value, schema, options);
|
|
|
+ expect(invoked).to.be.eq(1);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should propagate an error from the data validator', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const validator = () => {
|
|
|
+ throw new Error('Caught!');
|
|
|
+ };
|
|
|
+ S.setValidators([validator]);
|
|
|
+ const throwable = () => S.validate(10, {});
|
|
|
+ expect(throwable).to.throw('Caught!');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should apply parsers sequentially to a given value', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = [1, 2, 3];
|
|
|
+ const schema = {
|
|
|
+ type: DataType.ARRAY,
|
|
|
+ items: {type: DataType.NUMBER},
|
|
|
+ };
|
|
|
+ const expectedCalls = [
|
|
|
+ ['A', value, schema, undefined, S.container],
|
|
|
+ ['B', value, schema, undefined, S.container],
|
|
|
+ ['A', 1, schema.items, {sourcePath: 'array[0]'}, S.container],
|
|
|
+ ['B', 1, schema.items, {sourcePath: 'array[0]'}, S.container],
|
|
|
+ ['A', 2, schema.items, {sourcePath: 'array[1]'}, S.container],
|
|
|
+ ['B', 2, schema.items, {sourcePath: 'array[1]'}, S.container],
|
|
|
+ ['A', 3, schema.items, {sourcePath: 'array[2]'}, S.container],
|
|
|
+ ['B', 3, schema.items, {sourcePath: 'array[2]'}, S.container],
|
|
|
+ ];
|
|
|
+ const calls = [];
|
|
|
+ const validator = name => {
|
|
|
+ return (...args) => {
|
|
|
+ calls.push([name, ...args]);
|
|
|
+ };
|
|
|
+ };
|
|
|
+ S.setValidators([validator('A'), validator('B')]);
|
|
|
+ S.validate(value, schema);
|
|
|
+ expect(expectedCalls).to.be.eql(calls);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should ignore array items in the shallow mode even if the items schema is provided', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = [1, 2, 3];
|
|
|
+ const schema = {
|
|
|
+ type: DataType.ARRAY,
|
|
|
+ items: {type: DataType.STRING},
|
|
|
+ };
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ S.validate(value, schema, {shallowMode: true});
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should ignore array items when the items schema is not provided', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = [1, 2, 3];
|
|
|
+ const schema = {type: DataType.ARRAY};
|
|
|
+ let invoked = 0;
|
|
|
+ const validator = (...args) => {
|
|
|
+ invoked++;
|
|
|
+ expect(args[0]).to.be.eql(value);
|
|
|
+ };
|
|
|
+ S.setValidators([validator]);
|
|
|
+ S.validate(value, schema);
|
|
|
+ expect(invoked).to.be.eq(1);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should validate array items when the items schema is provided', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = [1, 2, 3];
|
|
|
+ const schema = {
|
|
|
+ type: DataType.ARRAY,
|
|
|
+ items: {type: DataType.NUMBER},
|
|
|
+ };
|
|
|
+ const expectedCalls = [
|
|
|
+ [value, schema, undefined, S.container],
|
|
|
+ [1, schema.items, {sourcePath: 'array[0]'}, S.container],
|
|
|
+ [2, schema.items, {sourcePath: 'array[1]'}, S.container],
|
|
|
+ [3, schema.items, {sourcePath: 'array[2]'}, S.container],
|
|
|
+ ];
|
|
|
+ const calls = [];
|
|
|
+ const validator = (...args) => {
|
|
|
+ calls.push(args);
|
|
|
+ };
|
|
|
+ S.setValidators([validator]);
|
|
|
+ S.validate(value, schema);
|
|
|
+ expect(expectedCalls).to.be.eql(calls);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should add an array index to a provided source path', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = [1, 2, 3];
|
|
|
+ const schema = {
|
|
|
+ type: DataType.ARRAY,
|
|
|
+ items: {type: DataType.NUMBER},
|
|
|
+ };
|
|
|
+ const options = {sourcePath: 'mySource'};
|
|
|
+ const expectedCalls = [
|
|
|
+ [value, schema, options, S.container],
|
|
|
+ [1, schema.items, {sourcePath: 'mySource[0]'}, S.container],
|
|
|
+ [2, schema.items, {sourcePath: 'mySource[1]'}, S.container],
|
|
|
+ [3, schema.items, {sourcePath: 'mySource[2]'}, S.container],
|
|
|
+ ];
|
|
|
+ const calls = [];
|
|
|
+ const validator = (...args) => {
|
|
|
+ calls.push(args);
|
|
|
+ };
|
|
|
+ S.setValidators([validator]);
|
|
|
+ S.validate(value, schema, options);
|
|
|
+ expect(expectedCalls).to.be.eql(calls);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should resolve a schema factory from the "items" option', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ const factory = () => ({type: DataType.STRING});
|
|
|
+ const schema = {type: DataType.ARRAY, items: factory};
|
|
|
+ const throwable = () => S.validate([10], schema);
|
|
|
+ expect(throwable).to.throw(DataValidationError);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should resolve a schema name from the "items" option', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ const schemaA = {
|
|
|
+ type: DataType.ARRAY,
|
|
|
+ items: 'schemaB',
|
|
|
+ };
|
|
|
+ S.getService(DataSchemaRegistry).defineSchema({
|
|
|
+ name: 'schemaB',
|
|
|
+ schema: {type: DataType.STRING},
|
|
|
+ });
|
|
|
+ const throwable = () => S.validate([10], schemaA);
|
|
|
+ expect(throwable).to.throw(DataValidationError);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should ignore object properties in the shallow mode even if the properties schema is provided', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = {p1: 1, p2: 2, p3: 3};
|
|
|
+ const schema = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: {
|
|
|
+ p1: {type: DataType.STRING},
|
|
|
+ p2: {type: DataType.STRING},
|
|
|
+ p3: {type: DataType.STRING},
|
|
|
+ },
|
|
|
+ };
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ S.validate(value, schema, {shallowMode: true});
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should ignore object properties when the properties schema is not provided', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = {p1: 1, p2: 2, p3: 3};
|
|
|
+ const schema = {type: DataType.OBJECT};
|
|
|
+ let invoked = 0;
|
|
|
+ const validator = (...args) => {
|
|
|
+ invoked++;
|
|
|
+ expect(args).to.be.eql([value, schema, undefined, S.container]);
|
|
|
+ };
|
|
|
+ S.setValidators([validator]);
|
|
|
+ S.validate(value, schema);
|
|
|
+ expect(invoked).to.be.eq(1);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should validate object properties when the properties schema is provided', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = {p1: 1, p2: 2, p3: 3};
|
|
|
+ const schema = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: {
|
|
|
+ p1: {type: DataType.NUMBER},
|
|
|
+ p2: {type: DataType.NUMBER},
|
|
|
+ p3: {type: DataType.NUMBER},
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const expectedCalls = [
|
|
|
+ [value, schema, undefined, S.container],
|
|
|
+ [1, schema.properties.p1, {sourcePath: 'p1'}, S.container],
|
|
|
+ [2, schema.properties.p2, {sourcePath: 'p2'}, S.container],
|
|
|
+ [3, schema.properties.p3, {sourcePath: 'p3'}, S.container],
|
|
|
+ ];
|
|
|
+ const calls = [];
|
|
|
+ const validator = (...args) => {
|
|
|
+ calls.push(args);
|
|
|
+ };
|
|
|
+ S.setValidators([validator]);
|
|
|
+ S.validate(value, schema);
|
|
|
+ expect(expectedCalls).to.be.eql(calls);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should apply validators sequentially to a given object and its properties', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = {p1: 1, p2: 2, p3: 3};
|
|
|
+ const schema = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: {
|
|
|
+ p1: {type: DataType.NUMBER},
|
|
|
+ p2: {type: DataType.NUMBER},
|
|
|
+ p3: {type: DataType.NUMBER},
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const expectedCalls = [
|
|
|
+ ['A', value, schema, undefined, S.container],
|
|
|
+ ['B', value, schema, undefined, S.container],
|
|
|
+ ['A', 1, schema.properties.p1, {sourcePath: 'p1'}, S.container],
|
|
|
+ ['B', 1, schema.properties.p1, {sourcePath: 'p1'}, S.container],
|
|
|
+ ['A', 2, schema.properties.p2, {sourcePath: 'p2'}, S.container],
|
|
|
+ ['B', 2, schema.properties.p2, {sourcePath: 'p2'}, S.container],
|
|
|
+ ['A', 3, schema.properties.p3, {sourcePath: 'p3'}, S.container],
|
|
|
+ ['B', 3, schema.properties.p3, {sourcePath: 'p3'}, S.container],
|
|
|
+ ];
|
|
|
+ const calls = [];
|
|
|
+ const validator = name => {
|
|
|
+ return (...args) => {
|
|
|
+ calls.push([name, ...args]);
|
|
|
+ };
|
|
|
+ };
|
|
|
+ S.setValidators([validator('A'), validator('B')]);
|
|
|
+ S.validate(value, schema);
|
|
|
+ expect(expectedCalls).to.be.eql(calls);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should ignore object properties without a specified schema', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = {p1: 1, p2: 2, p3: 3};
|
|
|
+ const schema = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: {
|
|
|
+ p1: {type: DataType.NUMBER},
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const expectedCalls = [
|
|
|
+ [value, schema, undefined, S.container],
|
|
|
+ [value.p1, schema.properties.p1, {sourcePath: 'p1'}, S.container],
|
|
|
+ ];
|
|
|
+ const calls = [];
|
|
|
+ const validator = (...args) => {
|
|
|
+ calls.push(args);
|
|
|
+ };
|
|
|
+ S.setValidators([validator]);
|
|
|
+ S.validate(value, schema);
|
|
|
+ expect(expectedCalls).to.be.eql(calls);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should add property name to a provided source path', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const value = {p1: 1, p2: 2, p3: 3};
|
|
|
+ const schema = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: {
|
|
|
+ p1: {type: DataType.NUMBER},
|
|
|
+ p2: {type: DataType.NUMBER},
|
|
|
+ p3: {type: DataType.NUMBER},
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const options = {sourcePath: 'mySource'};
|
|
|
+ const expectedCalls = [
|
|
|
+ [value, schema, options, S.container],
|
|
|
+ [1, schema.properties.p1, {sourcePath: 'mySource.p1'}, S.container],
|
|
|
+ [2, schema.properties.p2, {sourcePath: 'mySource.p2'}, S.container],
|
|
|
+ [3, schema.properties.p3, {sourcePath: 'mySource.p3'}, S.container],
|
|
|
+ ];
|
|
|
+ const calls = [];
|
|
|
+ const validator = (...args) => {
|
|
|
+ calls.push(args);
|
|
|
+ };
|
|
|
+ S.setValidators([validator]);
|
|
|
+ S.validate(value, schema, options);
|
|
|
+ expect(expectedCalls).to.be.eql(calls);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should resolve a schema factory from the "properties" option', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ const factory = () => ({
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: {
|
|
|
+ prop: {type: DataType.STRING},
|
|
|
+ },
|
|
|
+ });
|
|
|
+ const schema = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: factory,
|
|
|
+ };
|
|
|
+ const throwable = () => S.validate({prop: 10}, schema);
|
|
|
+ expect(throwable).to.throw(DataValidationError);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should resolve a schema name from the "properties" option', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ const schemaA = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: 'schemaB',
|
|
|
+ };
|
|
|
+ S.getService(DataSchemaRegistry).defineSchema({
|
|
|
+ name: 'schemaB',
|
|
|
+ schema: {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: {
|
|
|
+ prop: {type: DataType.STRING},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ });
|
|
|
+ const throwable = () => S.validate({prop: 10}, schemaA);
|
|
|
+ expect(throwable).to.throw(DataValidationError);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should throw an error if a properties schema from the schema factory is a non-array schema', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const factory = () => ({type: DataType.BOOLEAN});
|
|
|
+ const schema = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: factory,
|
|
|
+ };
|
|
|
+ const throwable = () => S.validate({prop: 10}, schema);
|
|
|
+ expect(throwable).to.throw(
|
|
|
+ 'Unable to get the "properties" option ' +
|
|
|
+ 'from the data schema of "boolean" type.',
|
|
|
+ );
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should throw an error if a properties schema from the schema name is a non-array schema', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ const schemaA = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: 'schemaB',
|
|
|
+ };
|
|
|
+ S.getService(DataSchemaRegistry).defineSchema({
|
|
|
+ name: 'schemaB',
|
|
|
+ schema: {type: DataType.BOOLEAN},
|
|
|
+ });
|
|
|
+ const throwable = () => S.validate({prop: 10}, schemaA);
|
|
|
+ expect(throwable).to.throw(
|
|
|
+ 'Unable to get the "properties" option ' +
|
|
|
+ 'from the data schema of "boolean" type.',
|
|
|
+ );
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should resolve a schema factory from the property schema', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ const factory = () => ({type: DataType.STRING});
|
|
|
+ const schema = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: {prop: factory},
|
|
|
+ };
|
|
|
+ const throwable = () => S.validate({prop: 10}, schema);
|
|
|
+ expect(throwable).to.throw(DataValidationError);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should resolve a schema name from the property schema', function () {
|
|
|
+ const S = new DataValidator();
|
|
|
+ S.setValidators([stringTypeValidator]);
|
|
|
+ const schemaA = {
|
|
|
+ type: DataType.OBJECT,
|
|
|
+ properties: {prop: 'schemaB'},
|
|
|
+ };
|
|
|
+ S.getService(DataSchemaRegistry).defineSchema({
|
|
|
+ name: 'schemaB',
|
|
|
+ schema: {type: DataType.STRING},
|
|
|
+ });
|
|
|
+ const throwable = () => S.validate({prop: 10}, schemaA);
|
|
|
+ expect(throwable).to.throw(DataValidationError);
|
|
|
+ });
|
|
|
+ });
|
|
|
+});
|