ds4/spec/expression-evaluation/validator.spec.ts

126 lines
3.7 KiB
TypeScript
Raw Normal View History

// 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)');
});
});