Files
tilbudgivern/backend/__tests__/formulas.test.js
T

108 lines
3.4 KiB
JavaScript

const { Parser } = require('expr-eval');
describe('Formula Evaluation Safety', () => {
test('Parser evaluates simple math expressions', () => {
const parser = new Parser();
const expr = parser.parse('2 + 2');
expect(expr.evaluate({})).toBe(4);
});
test('Parser evaluates formulas with variables', () => {
const parser = new Parser();
const expr = parser.parse('area_m2 * 1.5');
const result = expr.evaluate({ area_m2: 10 });
expect(result).toBe(15);
});
test('Parser evaluates nested variable access', () => {
const parser = new Parser();
const expr = parser.parse('GEOM.area_m2 * 2');
const result = expr.evaluate({ GEOM: { area_m2: 20 } });
expect(result).toBe(40);
});
test('Parser handles formulas with operator precedence', () => {
const parser = new Parser();
// expr-eval follows math precedence: * before +
const expr = parser.parse('2 + 3 * 4');
const result = expr.evaluate({});
expect(result).toBe(14); // 2 + (3*4) = 2 + 12 = 14
});
test('Parser safely handles division by zero', () => {
const parser = new Parser();
const expr = parser.parse('10 / x');
const result = expr.evaluate({ x: 0 });
expect(result).toBe(Infinity);
});
test('Parser treats unknown functions as variables', () => {
// expr-eval allows function-like syntax but treats them as variables if not defined
const parser = new Parser();
const expr = parser.parse('eval');
const result = expr.evaluate({ eval: 42 });
expect(result).toBe(42);
});
test('Parser only evaluates math expressions safely', () => {
// expr-eval doesn't execute arbitrary JavaScript
const parser = new Parser();
const expr = parser.parse('x + y');
expect(() => {
expr.evaluate({ x: 1, y: 2 });
}).not.toThrow();
});
test('Handles missing variables gracefully', () => {
const parser = new Parser();
const expr = parser.parse('area * 2');
expect(() => {
expr.evaluate({});
}).toThrow(); // Should throw because 'area' is undefined
});
test('Formula with waste factor calculation', () => {
const parser = new Parser();
const expr = parser.parse('area_m2 * 1.2'); // 20% waste
const result = expr.evaluate({ area_m2: 100 });
expect(result).toBe(120);
});
});
describe('Material Quantity Calculations', () => {
test('Calculates material quantity with formula and rounding', () => {
const geometry = { area_m2: 25.7, vindskede_lbm: 12.3 };
const formula = 'area_m2 * 1.5';
const parser = new Parser();
const expr = parser.parse(formula);
const calculatedValue = expr.evaluate(geometry);
const quantity = Math.ceil(calculatedValue);
expect(calculatedValue).toBeCloseTo(38.55);
expect(quantity).toBe(39);
});
test('Handles zero geometry values', () => {
const geometry = { area_m2: 0, vindskede_lbm: 0 };
const formula = 'area_m2 * 2';
const parser = new Parser();
const expr = parser.parse(formula);
const result = expr.evaluate(geometry);
expect(result).toBe(0);
});
test('Complex formula with multiple geometry inputs', () => {
const geometry = { area_m2: 50, vindskede_lbm: 20 };
const formula = '(area_m2 / 10) + (vindskede_lbm * 0.5)';
const parser = new Parser();
const expr = parser.parse(formula);
const result = expr.evaluate(geometry);
expect(result).toBe(15); // (50/10) + (20*0.5) = 5 + 10
});
});