summaryrefslogtreecommitdiff
path: root/apps/web/src/utils/__tests__/math.test.ts
blob: 4bdb959f9753299661e60ba087a1b5bc3fdd2d22 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { describe, expect, it } from "bun:test";
import { evaluateMathExpression } from "../math";

describe("evaluateMathExpression", () => {
	describe("basic arithmetic", () => {
		it("should add numbers", () => {
			expect(evaluateMathExpression({ input: "1+2" })).toBe(3);
			expect(evaluateMathExpression({ input: "10 + 20" })).toBe(30);
		});

		it("should subtract numbers", () => {
			expect(evaluateMathExpression({ input: "5-3" })).toBe(2);
			expect(evaluateMathExpression({ input: "100 - 50" })).toBe(50);
		});

		it("should multiply numbers", () => {
			expect(evaluateMathExpression({ input: "4*5" })).toBe(20);
			expect(evaluateMathExpression({ input: "10 * 10" })).toBe(100);
		});

		it("should divide numbers", () => {
			expect(evaluateMathExpression({ input: "20/4" })).toBe(5);
			expect(evaluateMathExpression({ input: "100 / 10" })).toBe(10);
		});
	});

	describe("operator precedence", () => {
		it("should respect multiplication over addition", () => {
			expect(evaluateMathExpression({ input: "2+3*4" })).toBe(14);
			expect(evaluateMathExpression({ input: "10+5*2" })).toBe(20);
		});

		it("should respect division over subtraction", () => {
			expect(evaluateMathExpression({ input: "10-6/2" })).toBe(7);
		});
	});

	describe("parentheses", () => {
		it("should evaluate expressions in parentheses first", () => {
			expect(evaluateMathExpression({ input: "(2+3)*4" })).toBe(20);
			expect(evaluateMathExpression({ input: "(10-5)*2" })).toBe(10);
		});

		it("should handle nested parentheses", () => {
			expect(evaluateMathExpression({ input: "((2+3)*4)" })).toBe(20);
			expect(evaluateMathExpression({ input: "(1+(2*3))" })).toBe(7);
		});
	});

	describe("decimal numbers", () => {
		it("should handle decimal numbers", () => {
			expect(evaluateMathExpression({ input: "3.5+2.5" })).toBe(6);
			expect(evaluateMathExpression({ input: "10.5 * 2" })).toBe(21);
		});
	});

	describe("negative numbers", () => {
		it("should handle negative numbers", () => {
			expect(evaluateMathExpression({ input: "-5+3" })).toBe(-2);
			expect(evaluateMathExpression({ input: "10+-5" })).toBe(5);
		});
	});

	describe("whitespace handling", () => {
		it("should handle various whitespace", () => {
			expect(evaluateMathExpression({ input: "  1  +  2  " })).toBe(3);
			expect(evaluateMathExpression({ input: "10*   5" })).toBe(50);
		});
	});

	describe("edge cases", () => {
		it("should handle single number", () => {
			expect(evaluateMathExpression({ input: "42" })).toBe(42);
			expect(evaluateMathExpression({ input: "3.14" })).toBe(3.14);
		});

		it("should return null for empty string", () => {
			expect(evaluateMathExpression({ input: "" })).toBeNull();
		});

		it("should return null for invalid characters", () => {
			expect(evaluateMathExpression({ input: "1+2a" })).toBeNull();
			expect(evaluateMathExpression({ input: "alert(1)" })).toBeNull();
			expect(evaluateMathExpression({ input: "1+2; doSomething()" })).toBeNull();
		});

		it("should return null for unbalanced parentheses", () => {
			expect(evaluateMathExpression({ input: "(1+2" })).toBeNull();
			expect(evaluateMathExpression({ input: "1+2)" })).toBeNull();
		});

		it("should return null for division by zero", () => {
			expect(evaluateMathExpression({ input: "1/0" })).toBeNull();
		});

		it("should return null for incomplete expressions", () => {
			expect(evaluateMathExpression({ input: "1+" })).toBeNull();
			expect(evaluateMathExpression({ input: "*2" })).toBeNull();
		});
	});

	describe("security - code injection prevention", () => {
		it("should reject code injection attempts", () => {
			// These would be dangerous with new Function() but are safe here
			expect(evaluateMathExpression({ input: "alert('xss')" })).toBeNull();
			expect(evaluateMathExpression({ input: "process.exit()" })).toBeNull();
			expect(evaluateMathExpression({ input: "require('fs')" })).toBeNull();
			expect(evaluateMathExpression({ input: "eval('1+1')" })).toBeNull();
			expect(evaluateMathExpression({ input: "Function('return 1')()" })).toBeNull();
			expect(evaluateMathExpression({ input: "constructor.constructor('return 1')()" })).toBeNull();
			expect(evaluateMathExpression({ input: "this.toString" })).toBeNull();
			expect(evaluateMathExpression({ input: "__proto__" })).toBeNull();
		});

		it("should reject prototype pollution attempts", () => {
			expect(evaluateMathExpression({ input: "[].constructor" })).toBeNull();
			expect(evaluateMathExpression({ input: "({}).constructor" })).toBeNull();
		});
	});
});