seed(ChecklistTemplateSeeder::class); $template = ChecklistTemplate::activeTemplate(); $service = app(FormulaService::class); $vars = ['SKOP1' => 100, 'SKOP2' => 200, 'SKOP3' => 50, 'EE' => 30, 'RE' => 20, 'D4I' => 10, 'D4II' => 15]; $results = collect($service->compute($template, $vars))->keyBy('result_token'); $this->assertEquals(350, $results['A']['value']); // 100 + 200 + 50 $this->assertEquals(50, $results['B']['value']); // 30 + 20 $this->assertEquals(300, $results['C']['value']); // A - B $this->assertEquals(325, $results['D']['value']); // A - 10 - 15 $this->assertEqualsWithDelta(92.857, $results['E']['value'], 0.01); // D / A * 100 } public function test_division_by_zero_is_guarded(): void { $this->seed(ChecklistTemplateSeeder::class); $template = ChecklistTemplate::activeTemplate(); $service = app(FormulaService::class); // Semua sifar => A = 0, formula E (D/A*100) mesti mengembalikan 0, bukan ralat. $vars = ['SKOP1' => 0, 'SKOP2' => 0, 'SKOP3' => 0, 'EE' => 0, 'RE' => 0, 'D4I' => 0, 'D4II' => 0]; $results = collect($service->compute($template, $vars))->keyBy('result_token'); $this->assertEquals(0, $results['E']['value']); } public function test_expression_evaluator_respects_operator_precedence(): void { $eval = app(ExpressionEvaluator::class); $this->assertEquals(14, $eval->evaluate('2 + 3 * 4')); $this->assertEquals(20, $eval->evaluate('(2 + 3) * 4')); $this->assertEquals(5, $eval->evaluate('A + B', ['A' => 2, 'B' => 3])); } public function test_expression_evaluator_rejects_invalid_characters(): void { $this->expectException(\RuntimeException::class); app(ExpressionEvaluator::class)->evaluate('A; DROP TABLE', ['A' => 1]); } }