diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-05-22 14:01:32 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-22 14:01:32 -0400 |
commit | 1096bbfa33a4af53ae2bfeee459c313fddb7c20d (patch) | |
tree | da4714238cc0c7cb73a5532b78e04061467c9c7b /test/test-json-schema.js | |
parent | be23acf499bcdb530c1b8990aa3d6211323cbdb2 (diff) |
Update schema tests (#1693)
* Update helper functions
* Rename file
* Update deleteProperty
* Add some basic proxy tests
Diffstat (limited to 'test/test-json-schema.js')
-rw-r--r-- | test/test-json-schema.js | 823 |
1 files changed, 823 insertions, 0 deletions
diff --git a/test/test-json-schema.js b/test/test-json-schema.js new file mode 100644 index 00000000..d8e351be --- /dev/null +++ b/test/test-json-schema.js @@ -0,0 +1,823 @@ +/* + * Copyright (C) 2020-2021 Yomichan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +const assert = require('assert'); +const {testMain} = require('../dev/util'); +const {VM} = require('../dev/vm'); + +const vm = new VM(); +vm.execute([ + 'js/core.js', + 'js/general/cache-map.js', + 'js/data/json-schema.js' +]); +const JsonSchemaValidator = vm.get('JsonSchemaValidator'); + + +function schemaValidate(schema, value) { + return new JsonSchemaValidator().isValid(value, schema); +} + +function getValidValueOrDefault(schema, value) { + return new JsonSchemaValidator().getValidValueOrDefault(schema, value); +} + +function createProxy(schema, value) { + return new JsonSchemaValidator().createProxy(value, schema); +} + + +function testValidate1() { + const schema = { + allOf: [ + { + type: 'number' + }, + { + anyOf: [ + {minimum: 10, maximum: 100}, + {minimum: -100, maximum: -10} + ] + }, + { + oneOf: [ + {multipleOf: 3}, + {multipleOf: 5} + ] + }, + { + not: [ + {multipleOf: 20} + ] + } + ] + }; + + const jsValidate = (value) => { + return ( + typeof value === 'number' && + ( + (value >= 10 && value <= 100) || + (value >= -100 && value <= -10) + ) && + ( + ( + (value % 3) === 0 || + (value % 5) === 0 + ) && + (value % 15) !== 0 + ) && + (value % 20) !== 0 + ); + }; + + for (let i = -111; i <= 111; i++) { + const actual = schemaValidate(schema, i); + const expected = jsValidate(i); + assert.strictEqual(actual, expected); + } +} + +function testValidate2() { + const data = [ + // String tests + { + schema: { + type: 'string' + }, + inputs: [ + {expected: false, value: null}, + {expected: false, value: void 0}, + {expected: false, value: 0}, + {expected: false, value: {}}, + {expected: false, value: []}, + {expected: true, value: ''} + ] + }, + { + schema: { + type: 'string', + minLength: 2 + }, + inputs: [ + {expected: false, value: ''}, + {expected: false, value: '1'}, + {expected: true, value: '12'}, + {expected: true, value: '123'} + ] + }, + { + schema: { + type: 'string', + maxLength: 2 + }, + inputs: [ + {expected: true, value: ''}, + {expected: true, value: '1'}, + {expected: true, value: '12'}, + {expected: false, value: '123'} + ] + }, + { + schema: { + type: 'string', + pattern: 'test' + }, + inputs: [ + {expected: false, value: ''}, + {expected: true, value: 'test'}, + {expected: false, value: 'TEST'}, + {expected: true, value: 'ABCtestDEF'}, + {expected: false, value: 'ABCTESTDEF'} + ] + }, + { + schema: { + type: 'string', + pattern: '^test$' + }, + inputs: [ + {expected: false, value: ''}, + {expected: true, value: 'test'}, + {expected: false, value: 'TEST'}, + {expected: false, value: 'ABCtestDEF'}, + {expected: false, value: 'ABCTESTDEF'} + ] + }, + { + schema: { + type: 'string', + pattern: '^test$', + patternFlags: 'i' + }, + inputs: [ + {expected: false, value: ''}, + {expected: true, value: 'test'}, + {expected: true, value: 'TEST'}, + {expected: false, value: 'ABCtestDEF'}, + {expected: false, value: 'ABCTESTDEF'} + ] + }, + { + schema: { + type: 'string', + pattern: '*' + }, + inputs: [ + {expected: false, value: ''} + ] + }, + { + schema: { + type: 'string', + pattern: '.', + patternFlags: '?' + }, + inputs: [ + {expected: false, value: ''} + ] + }, + + // Const tests + { + schema: { + const: 32 + }, + inputs: [ + {expected: true, value: 32}, + {expected: false, value: 0}, + {expected: false, value: '32'}, + {expected: false, value: null}, + {expected: false, value: {a: 'b'}}, + {expected: false, value: [1, 2, 3]} + ] + }, + { + schema: { + const: '32' + }, + inputs: [ + {expected: false, value: 32}, + {expected: false, value: 0}, + {expected: true, value: '32'}, + {expected: false, value: null}, + {expected: false, value: {a: 'b'}}, + {expected: false, value: [1, 2, 3]} + ] + }, + { + schema: { + const: null + }, + inputs: [ + {expected: false, value: 32}, + {expected: false, value: 0}, + {expected: false, value: '32'}, + {expected: true, value: null}, + {expected: false, value: {a: 'b'}}, + {expected: false, value: [1, 2, 3]} + ] + }, + { + schema: { + const: {a: 'b'} + }, + inputs: [ + {expected: false, value: 32}, + {expected: false, value: 0}, + {expected: false, value: '32'}, + {expected: false, value: null}, + {expected: false, value: {a: 'b'}}, + {expected: false, value: [1, 2, 3]} + ] + }, + { + schema: { + const: [1, 2, 3] + }, + inputs: [ + {expected: false, value: 32}, + {expected: false, value: 0}, + {expected: false, value: '32'}, + {expected: false, value: null}, + {expected: false, value: {a: 'b'}}, + {expected: false, value: [1, 2, 3]} + ] + }, + + // Array contains tests + { + schema: { + type: 'array', + contains: {const: 32} + }, + inputs: [ + {expected: false, value: []}, + {expected: true, value: [32]}, + {expected: true, value: [1, 32]}, + {expected: true, value: [1, 32, 1]}, + {expected: false, value: [33]}, + {expected: false, value: [1, 33]}, + {expected: false, value: [1, 33, 1]} + ] + }, + + // Number limits tests + { + schema: { + type: 'number', + minimum: 0 + }, + inputs: [ + {expected: false, value: -1}, + {expected: true, value: 0}, + {expected: true, value: 1} + ] + }, + { + schema: { + type: 'number', + exclusiveMinimum: 0 + }, + inputs: [ + {expected: false, value: -1}, + {expected: false, value: 0}, + {expected: true, value: 1} + ] + }, + { + schema: { + type: 'number', + maximum: 0 + }, + inputs: [ + {expected: true, value: -1}, + {expected: true, value: 0}, + {expected: false, value: 1} + ] + }, + { + schema: { + type: 'number', + exclusiveMaximum: 0 + }, + inputs: [ + {expected: true, value: -1}, + {expected: false, value: 0}, + {expected: false, value: 1} + ] + }, + + // Integer limits tests + { + schema: { + type: 'integer', + minimum: 0 + }, + inputs: [ + {expected: false, value: -1}, + {expected: true, value: 0}, + {expected: true, value: 1} + ] + }, + { + schema: { + type: 'integer', + exclusiveMinimum: 0 + }, + inputs: [ + {expected: false, value: -1}, + {expected: false, value: 0}, + {expected: true, value: 1} + ] + }, + { + schema: { + type: 'integer', + maximum: 0 + }, + inputs: [ + {expected: true, value: -1}, + {expected: true, value: 0}, + {expected: false, value: 1} + ] + }, + { + schema: { + type: 'integer', + exclusiveMaximum: 0 + }, + inputs: [ + {expected: true, value: -1}, + {expected: false, value: 0}, + {expected: false, value: 1} + ] + }, + { + schema: { + type: 'integer', + multipleOf: 2 + }, + inputs: [ + {expected: true, value: -2}, + {expected: false, value: -1}, + {expected: true, value: 0}, + {expected: false, value: 1}, + {expected: true, value: 2} + ] + }, + + // Numeric type tests + { + schema: { + type: 'number' + }, + inputs: [ + {expected: true, value: 0}, + {expected: true, value: 0.5}, + {expected: true, value: 1}, + {expected: false, value: '0'}, + {expected: false, value: null}, + {expected: false, value: []}, + {expected: false, value: {}} + ] + }, + { + schema: { + type: 'integer' + }, + inputs: [ + {expected: true, value: 0}, + {expected: false, value: 0.5}, + {expected: true, value: 1}, + {expected: false, value: '0'}, + {expected: false, value: null}, + {expected: false, value: []}, + {expected: false, value: {}} + ] + } + ]; + + for (const {schema, inputs} of data) { + for (const {expected, value} of inputs) { + const actual = schemaValidate(schema, value); + assert.strictEqual(actual, expected); + } + } +} + + +function testGetValidValueOrDefault1() { + const data = [ + // Test value defaulting on objects with additionalProperties=false + { + schema: { + type: 'object', + required: ['test'], + properties: { + test: { + type: 'string', + default: 'default' + } + }, + additionalProperties: false + }, + inputs: [ + [ + void 0, + {test: 'default'} + ], + [ + null, + {test: 'default'} + ], + [ + 0, + {test: 'default'} + ], + [ + '', + {test: 'default'} + ], + [ + [], + {test: 'default'} + ], + [ + {}, + {test: 'default'} + ], + [ + {test: 'value'}, + {test: 'value'} + ], + [ + {test2: 'value2'}, + {test: 'default'} + ], + [ + {test: 'value', test2: 'value2'}, + {test: 'value'} + ] + ] + }, + + // Test value defaulting on objects with additionalProperties=true + { + schema: { + type: 'object', + required: ['test'], + properties: { + test: { + type: 'string', + default: 'default' + } + }, + additionalProperties: true + }, + inputs: [ + [ + {}, + {test: 'default'} + ], + [ + {test: 'value'}, + {test: 'value'} + ], + [ + {test2: 'value2'}, + {test: 'default', test2: 'value2'} + ], + [ + {test: 'value', test2: 'value2'}, + {test: 'value', test2: 'value2'} + ] + ] + }, + + // Test value defaulting on objects with additionalProperties={schema} + { + schema: { + type: 'object', + required: ['test'], + properties: { + test: { + type: 'string', + default: 'default' + } + }, + additionalProperties: { + type: 'number', + default: 10 + } + }, + inputs: [ + [ + {}, + {test: 'default'} + ], + [ + {test: 'value'}, + {test: 'value'} + ], + [ + {test2: 'value2'}, + {test: 'default', test2: 10} + ], + [ + {test: 'value', test2: 'value2'}, + {test: 'value', test2: 10} + ], + [ + {test2: 2}, + {test: 'default', test2: 2} + ], + [ + {test: 'value', test2: 2}, + {test: 'value', test2: 2} + ], + [ + {test: 'value', test2: 2, test3: null}, + {test: 'value', test2: 2, test3: 10} + ], + [ + {test: 'value', test2: 2, test3: void 0}, + {test: 'value', test2: 2, test3: 10} + ] + ] + }, + + // Test value defaulting where hasOwnProperty is false + { + schema: { + type: 'object', + required: ['test'], + properties: { + test: { + type: 'string', + default: 'default' + } + } + }, + inputs: [ + [ + {}, + {test: 'default'} + ], + [ + {test: 'value'}, + {test: 'value'} + ], + [ + Object.create({test: 'value'}), + {test: 'default'} + ] + ] + }, + { + schema: { + type: 'object', + required: ['toString'], + properties: { + toString: { + type: 'string', + default: 'default' + } + } + }, + inputs: [ + [ + {}, + {toString: 'default'} + ], + [ + {toString: 'value'}, + {toString: 'value'} + ], + [ + Object.create({toString: 'value'}), + {toString: 'default'} + ] + ] + }, + + // Test enum + { + schema: { + type: 'object', + required: ['test'], + properties: { + test: { + type: 'string', + default: 'value1', + enum: ['value1', 'value2', 'value3'] + } + } + }, + inputs: [ + [ + {test: 'value1'}, + {test: 'value1'} + ], + [ + {test: 'value2'}, + {test: 'value2'} + ], + [ + {test: 'value3'}, + {test: 'value3'} + ], + [ + {test: 'value4'}, + {test: 'value1'} + ] + ] + }, + + // Test valid vs invalid default + { + schema: { + type: 'object', + required: ['test'], + properties: { + test: { + type: 'integer', + default: 2, + minimum: 1 + } + } + }, + inputs: [ + [ + {test: -1}, + {test: 2} + ] + ] + }, + { + schema: { + type: 'object', + required: ['test'], + properties: { + test: { + type: 'integer', + default: 1, + minimum: 2 + } + } + }, + inputs: [ + [ + {test: -1}, + {test: -1} + ] + ] + } + ]; + + for (const {schema, inputs} of data) { + for (const [value, expected] of inputs) { + const actual = getValidValueOrDefault(schema, value); + vm.assert.deepStrictEqual(actual, expected); + } + } +} + + +function testProxy1() { + const data = [ + // Object tests + { + schema: { + type: 'object', + required: ['test'], + additionalProperties: false, + properties: { + test: { + type: 'string', + default: 'default' + } + } + }, + tests: [ + {error: false, value: {test: 'default'}, action: (value) => { value.test = 'string'; }}, + {error: true, value: {test: 'default'}, action: (value) => { value.test = null; }}, + {error: true, value: {test: 'default'}, action: (value) => { delete value.test; }}, + {error: true, value: {test: 'default'}, action: (value) => { value.test2 = 'string'; }}, + {error: false, value: {test: 'default'}, action: (value) => { delete value.test2; }} + ] + }, + { + schema: { + type: 'object', + required: ['test'], + additionalProperties: true, + properties: { + test: { + type: 'string', + default: 'default' + } + } + }, + tests: [ + {error: false, value: {test: 'default'}, action: (value) => { value.test = 'string'; }}, + {error: true, value: {test: 'default'}, action: (value) => { value.test = null; }}, + {error: true, value: {test: 'default'}, action: (value) => { delete value.test; }}, + {error: false, value: {test: 'default'}, action: (value) => { value.test2 = 'string'; }}, + {error: false, value: {test: 'default'}, action: (value) => { delete value.test2; }} + ] + }, + { + schema: { + type: 'object', + required: ['test1'], + additionalProperties: false, + properties: { + test1: { + type: 'object', + required: ['test2'], + additionalProperties: false, + properties: { + test2: { + type: 'object', + required: ['test3'], + additionalProperties: false, + properties: { + test3: { + type: 'string', + default: 'default' + } + } + } + } + } + } + }, + tests: [ + {error: false, action: (value) => { value.test1.test2.test3 = 'string'; }}, + {error: true, action: (value) => { value.test1.test2.test3 = null; }}, + {error: true, action: (value) => { delete value.test1.test2.test3; }}, + {error: true, action: (value) => { value.test1.test2 = null; }}, + {error: true, action: (value) => { value.test1 = null; }}, + {error: true, action: (value) => { value.test4 = 'string'; }}, + {error: false, action: (value) => { delete value.test4; }} + ] + }, + + // Array tests + { + schema: { + type: 'array', + items: { + type: 'string', + default: 'default' + } + }, + tests: [ + {error: false, value: ['default'], action: (value) => { value[0] = 'string'; }}, + {error: true, value: ['default'], action: (value) => { value[0] = null; }}, + {error: true, value: ['default'], action: (value) => { delete value[0]; }}, + {error: false, value: ['default'], action: (value) => { value[1] = 'string'; }} + ] + } + ]; + + for (const {schema, tests} of data) { + for (let {error, value, action} of tests) { + if (typeof value === 'undefined') { value = getValidValueOrDefault(schema, void 0); } + assert.ok(schemaValidate(schema, value)); + console.log(error, value, schema); + const valueProxy = createProxy(schema, value); + if (error) { + assert.throws(() => action(valueProxy)); + } else { + assert.doesNotThrow(() => action(valueProxy)); + } + } + } +} + + +function main() { + testValidate1(); + testValidate2(); + testGetValidValueOrDefault1(); + testProxy1(); +} + + +if (require.main === module) { testMain(main); } |