// SPDX-FileCopyrightText: 2022 Johannes Loher
//
// SPDX-License-Identifier: MIT

import { describe, expect, it } from "vitest";

import { defaultEvaluator, Evaluator, mathEvaluator } from "../../src/expression-evaluation/evaluator";

describe("Evaluator", () => {
    it("evaluates expressions that only use identifiers according to the given predicate", () => {
        // given
        const expression = "typeof 'foo' === 'string' ? 42 : null";

        // when
        const result = defaultEvaluator.evaluate(expression);

        // then
        expect(result).toEqual(42);
    });

    it("fails to evaluate expressions that contain identifiers that are not allowed by the predicate", () => {
        // given
        const expression = "typeof 'foo' === 'string' ? 42 : function (){}";

        // when
        const evaluate = () => defaultEvaluator.evaluate(expression);

        // then
        expect(evaluate).toThrowError("'function' is not an allowed identifier.");
    });

    it("fails to evaluate expressions that contain invalid tokens", () => {
        // given
        const expression = "1;";

        // when
        const evaluate = () => defaultEvaluator.evaluate(expression);

        // then
        expect(evaluate).toThrowError("Invalid or unexpected token (1)");
    });

    it("fails to evaluate expressions that contain arrow functions", () => {
        // given
        const expression = "(() => 1)()";

        // when
        const evaluate = () => defaultEvaluator.evaluate(expression);

        // then
        expect(evaluate).toThrowError("Invalid or unexpected token (4)");
    });

    it("makes the given context available", () => {
        // given
        const context = { floor: Math.floor };
        const evaluator = new Evaluator({ context });
        const expression = "floor(0.5)";

        // when
        const result = evaluator.evaluate(expression);

        // then
        expect(result).toEqual(0);
    });

    describe("mathEvaluator", () => {
        it("makes the given context available", () => {
            // given
            const expression = "sqrt(sin(PI))";

            // when
            const result = mathEvaluator.evaluate(expression);

            // then
            expect(result).toEqual(Math.sqrt(Math.sin(Math.PI)));
        });

        it("does not give acces to the function constructor", () => {
            // given
            const expression = "sqrt.constructor";

            // when
            const evaluate = () => mathEvaluator.evaluate(expression);

            // then
            expect(evaluate).toThrowError("'constructor' is not an allowed identifier.");
        });
    });
});