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