diff options
| author | Darius Jahandarie <djahandarie@gmail.com> | 2023-11-09 13:30:31 +0000 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-09 13:30:31 +0000 | 
| commit | 5c5a167b4792af379cdacf633513cebf20728cd2 (patch) | |
| tree | 5b6be3620a557d0b9177047003f6d742d9d2a32d /test/json-schema.test.js | |
| parent | b64f51c3b13a46af4dd7f1e43048ac19c781ca7b (diff) | |
| parent | 0f4d36938fd0d844f548aa5a7f7e7842df8dfb41 (diff) | |
Merge pull request #307 from themoeway/modernize
Modernize codebase
Diffstat (limited to 'test/json-schema.test.js')
| -rw-r--r-- | test/json-schema.test.js | 1009 | 
1 files changed, 1009 insertions, 0 deletions
diff --git a/test/json-schema.test.js b/test/json-schema.test.js new file mode 100644 index 00000000..5370e8da --- /dev/null +++ b/test/json-schema.test.js @@ -0,0 +1,1009 @@ +/* + * Copyright (C) 2023  Yomitan Authors + * Copyright (C) 2020-2022  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/>. + */ + +import {expect, test} from 'vitest'; +import {JsonSchema} from '../ext/js/data/json-schema.js'; + +function schemaValidate(schema, value) { +    return new JsonSchema(schema).isValid(value); +} + +function getValidValueOrDefault(schema, value) { +    return new JsonSchema(schema).getValidValueOrDefault(value); +} + +function createProxy(schema, value) { +    return new JsonSchema(schema).createProxy(value); +} + +function clone(value) { +    return JSON.parse(JSON.stringify(value)); +} + + +function testValidate1() { +    test('Validate1', () => { +        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); +            expect(actual).toStrictEqual(expected); +        } +    }); +} + +function testValidate2() { +    test('Validate2', () => { +        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: {}} +                ] +            }, + +            // Reference tests +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'number' +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                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: { +                    definitions: { +                        example: { +                            type: 'integer' +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                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: {}} +                ] +            }, +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'object', +                            additionalProperties: false, +                            properties: { +                                test: { +                                    $ref: '#/definitions/example' +                                } +                            } +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                inputs: [ +                    {expected: false, value: 0}, +                    {expected: false, value: 0.5}, +                    {expected: false, value: 1}, +                    {expected: false, value: '0'}, +                    {expected: false, value: null}, +                    {expected: false, value: []}, +                    {expected: true,  value: {}}, +                    {expected: false, value: {test: 0}}, +                    {expected: false, value: {test: 0.5}}, +                    {expected: false, value: {test: 1}}, +                    {expected: false, value: {test: '0'}}, +                    {expected: false, value: {test: null}}, +                    {expected: false, value: {test: []}}, +                    {expected: true,  value: {test: {}}}, +                    {expected: true,  value: {test: {test: {}}}}, +                    {expected: true,  value: {test: {test: {test: {}}}}} +                ] +            } +        ]; + +        for (const {schema, inputs} of data) { +            for (const {expected, value} of inputs) { +                const actual = schemaValidate(schema, value); +                expect(actual).toStrictEqual(expected); +            } +        } +    }); +} + + +function testGetValidValueOrDefault1() { +    test('GetValidValueOrDefault1', () => { +        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} +                    ] +                ] +            }, + +            // Test references +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'number', +                            default: 0 +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                inputs: [ +                    [ +                        1, +                        1 +                    ], +                    [ +                        null, +                        0 +                    ], +                    [ +                        'test', +                        0 +                    ], +                    [ +                        {test: 'value'}, +                        0 +                    ] +                ] +            }, +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'object', +                            additionalProperties: false, +                            properties: { +                                test: { +                                    $ref: '#/definitions/example' +                                } +                            } +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                inputs: [ +                    [ +                        1, +                        {} +                    ], +                    [ +                        null, +                        {} +                    ], +                    [ +                        'test', +                        {} +                    ], +                    [ +                        {}, +                        {} +                    ], +                    [ +                        {test: {}}, +                        {test: {}} +                    ], +                    [ +                        {test: 'value'}, +                        {test: {}} +                    ], +                    [ +                        {test: {test: {}}}, +                        {test: {test: {}}} +                    ] +                ] +            } +        ]; + +        for (const {schema, inputs} of data) { +            for (const [value, expected] of inputs) { +                const actual = getValidValueOrDefault(schema, value); +                expect(actual).toStrictEqual(expected); +            } +        } +    }); +} + + +function testProxy1() { +    test('Proxy1', () => { +        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: false, value: ['default'], action: (value) => { delete value[0]; }}, +                    {error: false, value: ['default'], action: (value) => { value[1] = 'string'; }}, +                    {error: false, value: ['default'], action: (value) => { +                        value[1] = 'string'; +                        if (value.length !== 2) { throw new Error(`Invalid length; expected=2; actual=${value.length}`); } +                        if (typeof value.push !== 'function') { throw new Error(`Invalid push; expected=function; actual=${typeof value.push}`); } +                    }} +                ] +            }, + +            // Reference tests +            { +                schema: { +                    definitions: { +                        example: { +                            type: 'object', +                            additionalProperties: false, +                            properties: { +                                test: { +                                    $ref: '#/definitions/example' +                                } +                            } +                        } +                    }, +                    $ref: '#/definitions/example' +                }, +                tests: [ +                    {error: false, value: {}, action: (value) => { value.test = {}; }}, +                    {error: false, value: {}, action: (value) => { value.test = {}; value.test.test = {}; }}, +                    {error: false, value: {}, action: (value) => { value.test = {test: {}}; }}, +                    {error: true,  value: {}, action: (value) => { value.test = null; }}, +                    {error: true,  value: {}, action: (value) => { value.test = 'string'; }}, +                    {error: true,  value: {}, action: (value) => { value.test = {}; value.test.test = 'string'; }}, +                    {error: true,  value: {}, action: (value) => { value.test = {test: 'string'}; }} +                ] +            } +        ]; + +        for (const {schema, tests} of data) { +            for (let {error, value, action} of tests) { +                if (typeof value === 'undefined') { value = getValidValueOrDefault(schema, void 0); } +                value = clone(value); +                expect(schemaValidate(schema, value)).toBe(true); +                const valueProxy = createProxy(schema, value); +                if (error) { +                    expect(() => action(valueProxy)).toThrow(); +                } else { +                    expect(() => action(valueProxy)).not.toThrow(); +                } +            } +        } +    }); +} + + +function main() { +    testValidate1(); +    testValidate2(); +    testGetValidValueOrDefault1(); +    testProxy1(); +} + + +main();  |