import {expect} from 'chai'; import {format} from '@e22m4u/js-format'; import {validateProjectionSchema} from './validate-projection-schema.js'; describe('validateProjectionSchema', function () { it('should require the "shallowMode" argument to be a Boolean', function () { const throwable = v => () => validateProjectionSchema({}, v); const error = s => format('Argument "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 require the "validatedSchemas" argument to be a Set instance', function () { const throwable = v => () => validateProjectionSchema({}, false, v); const error = s => format( 'Argument "validatedSchemas" must be ' + 'an instance of Set, 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('Function')); throwable(new Set())(); throwable(undefined)(); }); it('should skip validation if the given schema is already validated', function () { const validatedSchemas = new Set(); const schema = {foo: 10}; validatedSchemas.add(schema); validateProjectionSchema(schema, false, validatedSchemas); }); it('should require the "schema" argument to be a valid value', function () { const throwable = v => () => validateProjectionSchema(v); const error = s => format( 'Projection schema must be an Object, a Function ' + 'or 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(undefined)).to.throw(error('undefined')); expect(throwable(null)).to.throw(error('null')); throwable('str')(); throwable({})(); throwable(() => ({}))(); }); it('should require property options to be an object or a boolean', function () { const throwable = v => () => validateProjectionSchema({foo: v}); const error = s => format( 'Property options must be an Object or 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(null)).to.throw(error('null')); expect(throwable(() => ({}))).to.throw(error('Function')); throwable({})(); throwable(true)(); throwable(false)(); throwable(undefined)(); }); it('should require the "select" option to be a boolean', function () { const throwable = v => () => validateProjectionSchema({foo: {select: v}}); const error = s => format( 'Property option "select" 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('Object')); expect(throwable([])).to.throw(error('Array')); expect(throwable(null)).to.throw(error('null')); expect(throwable(() => ({}))).to.throw(error('Function')); throwable(true)(); throwable(false)(); throwable(undefined)(); }); it('should require the "scopes" option to be an object', function () { const throwable = v => () => validateProjectionSchema({foo: {scopes: v}}); const error = s => format( 'Property option "scopes" 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(() => ({}))).to.throw(error('Function')); throwable({})(); throwable(undefined)(); }); it('should require the scope options to be an object or a boolean', function () { const throwable = v => () => validateProjectionSchema({foo: {scopes: {input: v}}}); const error = s => format( 'Scope options must be an Object or 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(null)).to.throw(error('null')); expect(throwable(() => ({}))).to.throw(error('Function')); throwable({})(); throwable(true)(); throwable(false)(); throwable(undefined)(); }); it('should require the "select" option of scope to be a boolean', function () { const throwable = v => () => validateProjectionSchema({foo: {scopes: {input: {select: v}}}}); const error = s => format('Scope option "select" 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('Object')); expect(throwable([])).to.throw(error('Array')); expect(throwable(null)).to.throw(error('null')); expect(throwable(() => ({}))).to.throw(error('Function')); throwable(true)(); throwable(false)(); throwable(undefined)(); }); it('should require the "schema" option to be a valid value', function () { const throwable = v => () => validateProjectionSchema({foo: {schema: v}}); const error = s => format( 'Property option "schema" must be an Object, a Function ' + 'or 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(null)).to.throw(error('null')); throwable('str')(); throwable({})(); throwable(() => ({}))(); throwable(undefined)(); }); it('should validate the embedded schema', function () { const throwable = () => validateProjectionSchema({foo: {schema: {bar: 10}}}); expect(throwable).to.throw( 'Property options must be an Object or a Boolean, but 10 was given.', ); }); it('should validate the given schema in the shallow mode', function () { const throwable = () => validateProjectionSchema({foo: 10}, true); expect(throwable).to.throw( 'Property options must be an Object or a Boolean, but 10 was given.', ); }); it('should skip embedded schema validation in the shallow mode', function () { validateProjectionSchema({foo: {schema: {bar: 10}}}, true); }); it('should allow circular schema validation', function () { const schemaA = {foo: {select: true}}; const schemaB = {bar: {select: true}}; schemaA.foo.schema = schemaB; schemaB.bar.schema = schemaA; validateProjectionSchema(schemaA); }); it('should allow circular schema validation in the shallow mode', function () { const schemaA = {foo: {select: true}}; const schemaB = {bar: {select: true}}; schemaA.foo.schema = schemaB; schemaB.bar.schema = schemaA; validateProjectionSchema(schemaA, true); }); });