// SPDX-FileCopyrightText: 2022 Johannes Loher // // SPDX-License-Identifier: MIT import { describe, expect, it } from 'vitest'; import { literals, safeOperators } from '../../src/expression-evaluation/grammar'; import { Validator } from '../../src/expression-evaluation/validator'; describe('Validator', () => { it('allows identifier according to the given predicate', () => { // given const predicate = (identifier: string) => identifier === 'true'; const validator = new Validator(predicate); const input = 'true'; // when const validate = () => validator.validate(input); // then expect(validate).not.toThrow(); }); it('disallows identifier according to the given predicate', () => { // given const predicate = (identifier: string) => identifier === 'false'; const validator = new Validator(predicate); const input = 'true'; // when const validate = () => validator.validate(input); // then expect(validate).toThrowError("'true' is not an allowed identifier"); }); it('allows multiple identifiers according to the given predicate', () => { // given const predicate = (identifier: string) => identifier === 'true' || identifier === 'null'; const validator = new Validator(predicate); const input = 'true null'; // when const validate = () => validator.validate(input); // then expect(validate).not.toThrow(); }); it('allows multiple identifiers in a more complex expression according to the given rule', () => { // given const predicate = (identifier: string) => identifier === 'true' || identifier === 'null'; const validator = new Validator(predicate); const input = 'true === null'; // when const validate = () => validator.validate(input); // then expect(validate).not.toThrow(); }); it('mentions the first not allowed identifier in the thrown errror', () => { // given const predicate = (identifier: string) => identifier === 'true' || identifier === 'null'; const validator = new Validator(predicate); const input = 'true === null && undefined === false'; // when const validate = () => validator.validate(input); // then expect(validate).toThrowError("'undefined' is not an allowed identifier."); }); it('disallows invalid invalid tokens', () => { // given const validator = new Validator(); const input = ';'; // when const validate = () => validator.validate(input); // then expect(validate).toThrowError('Invalid or unexpected token (0)'); }); it('allows a complicated valid expression', () => { // given const predicate = (identifier: string) => [...safeOperators, ...literals, 'floor', 'random'].includes(identifier); const validator = new Validator(predicate); const input = "typeof (floor(random() * 5) / 2) === 'number' ? 42 : 'foo'"; // when const validate = () => validator.validate(input); // then expect(validate).not.toThrow(); }); it('disallows a complicated expression if it contains a disallowed identifier', () => { // given const predicate = (identifier: string) => [...safeOperators, ...literals, 'ceil'].includes(identifier); const validator = new Validator(predicate); const input = "ceil.constructor('alert(1); return 1;')()"; // when const validate = () => validator.validate(input); // then expect(validate).toThrowError("'constructor' is not an allowed identifier."); }); it('disallows arrow functions', () => { // given const validator = new Validator(); const input = '() => {}'; // when const validate = () => validator.validate(input); // then expect(validate).toThrowError('Invalid or unexpected token (3)'); }); });