"""Tests for the safe formula evaluator.""" import math import pytest from physcom.engine.formula import FormulaError, compile_formula, evaluate_formula class TestArithmetic: def test_constant(self): assert evaluate_formula(compile_formula("42"), {}) == 42.0 def test_basic_ops(self): assert evaluate_formula(compile_formula("2 + 3 * 4"), {}) == 14.0 assert evaluate_formula(compile_formula("(2 + 3) * 4"), {}) == 20.0 assert evaluate_formula(compile_formula("10 / 4"), {}) == 2.5 assert evaluate_formula(compile_formula("2 ** 3"), {}) == 8.0 def test_unary_minus(self): assert evaluate_formula(compile_formula("-5 + 2"), {}) == -3.0 def test_variable_lookup(self): result = evaluate_formula(compile_formula("mass * 2"), {"mass": 3.0}) assert result == 6.0 def test_unknown_variable_raises(self): with pytest.raises(FormulaError): evaluate_formula(compile_formula("unknown_var"), {}) def test_division_by_zero_raises_formula_error(self): with pytest.raises(FormulaError): evaluate_formula(compile_formula("1 / 0"), {}) class TestFunctions: def test_default_math_functions(self): assert evaluate_formula(compile_formula("sqrt(16)"), {}) == 4.0 assert evaluate_formula(compile_formula("max(1, 2, 3)"), {}) == 3.0 assert evaluate_formula(compile_formula("min(1, 2, 3)"), {}) == 1.0 assert evaluate_formula(compile_formula("abs(-5)"), {}) == 5.0 assert evaluate_formula(compile_formula("exp(0)"), {}) == 1.0 assert math.isclose(evaluate_formula(compile_formula("log(exp(1))"), {}), 1.0) def test_custom_injected_function(self): formula = compile_formula('dep("power_density", "provides")') result = evaluate_formula( formula, {}, functions={"dep": lambda key, constraint_type: 99.0} ) assert result == 99.0 def test_unknown_function_raises(self): with pytest.raises(FormulaError): evaluate_formula(compile_formula("unknown_fn(1)"), {}) def test_string_constant_passthrough_to_function(self): formula = compile_formula('dep("mass")') result = evaluate_formula(formula, {}, functions={"dep": lambda key: len(key)}) assert result == 4.0 class TestSecurity: @pytest.mark.parametrize("source", [ "__import__('os').system('echo hi')", "().__class__", "[1, 2, 3]", "{1: 2}", "{1, 2}", "(x for x in [1])", "lambda: 1", "1 if True else 0", "1 == 1", "x.__class__", "x[0]", "(lambda: 1)()", "1; 2", ]) def test_disallowed_constructs_rejected(self, source): with pytest.raises(FormulaError): compile_formula(source) def test_unregistered_function_name_never_executes(self): """exec/eval/__import__ etc. parse as ordinary Call nodes -- the actual guarantee is that no function name is callable unless it's explicitly in DEFAULT_FUNCTIONS or caller-supplied, checked at evaluate time, not that the bare name is rejected at compile time.""" with pytest.raises(FormulaError): evaluate_formula(compile_formula("exec('1')"), {}) def test_dunder_name_rejected(self): with pytest.raises(FormulaError): compile_formula("__builtins__") def test_indirect_call_rejected(self): with pytest.raises(FormulaError): compile_formula("(a + b)(1)") def test_invalid_syntax_raises_formula_error(self): with pytest.raises(FormulaError): compile_formula("2 +") def test_boolean_constant_rejected(self): with pytest.raises(FormulaError): compile_formula("True") def test_large_exponent_overflows_cleanly_not_hangs(self): with pytest.raises(FormulaError): evaluate_formula(compile_formula("9 ** 9 ** 9 ** 9"), {})