diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-23 21:11:32 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-23 21:11:32 +0300 |
| commit | 5ea6e73fda9fc12dbccf472b116a69eed0871a37 (patch) | |
| tree | e62b1b0b1abbd3f4f1b43ab60ebfca7cfcbab105 /internal/perc | |
| parent | 440cd6adf44324584ab784389a373058fa93c3c8 (diff) | |
test: add unit tests for ParseCalculation() and Calculation.Format()
Add tests for ParseCalculation() exported API and Calculation struct:
- ParseCalculation returns correct *Calculation for all 3 types
(PercentOfY, IsWhatPercentOfY, IsYPercentOfWhat)
- Correct field values: Type, Percent, Base, Result, Steps
- 'what is' prefix stripping via ParseCalculation
- Case insensitivity via ParseCalculation
- Division by zero edge cases via ParseCalculation
- Invalid input returns error via ParseCalculation
- Calculation.Format() for each CalculationType
- Calculation.Format() with and without Steps
- Decimal and large value handling
Diffstat (limited to 'internal/perc')
| -rw-r--r-- | internal/perc/perc_test.go | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/internal/perc/perc_test.go b/internal/perc/perc_test.go index d96fabb..f8d6f97 100644 --- a/internal/perc/perc_test.go +++ b/internal/perc/perc_test.go @@ -280,3 +280,250 @@ func TestParseWhitespace(t *testing.T) { }) } } + +// TestParseCalculationPercentOfY tests ParseCalculation for "X% of Y" type +func TestParseCalculationPercentOfY(t *testing.T) { + calc, err := ParseCalculation("20% of 150") + if err != nil { + t.Fatalf("ParseCalculation returned error: %v", err) + } + if calc == nil { + t.Fatal("ParseCalculation returned nil") + } + if calc.Type != PercentOfY { + t.Errorf("Type = %v, want PercentOfY", calc.Type) + } + if calc.Percent != 20 { + t.Errorf("Percent = %v, want 20", calc.Percent) + } + if calc.Base != 150 { + t.Errorf("Base = %v, want 150", calc.Base) + } + if calc.Result != 30 { + t.Errorf("Result = %v, want 30", calc.Result) + } + if calc.Steps == "" { + t.Error("Steps should not be empty") + } +} + +// TestParseCalculationIsWhatPercentOfY tests ParseCalculation for "X is what % of Y" type +func TestParseCalculationIsWhatPercentOfY(t *testing.T) { + calc, err := ParseCalculation("30 is what % of 150") + if err != nil { + t.Fatalf("ParseCalculation returned error: %v", err) + } + if calc == nil { + t.Fatal("ParseCalculation returned nil") + } + if calc.Type != IsWhatPercentOfY { + t.Errorf("Type = %v, want IsWhatPercentOfY", calc.Type) + } + if calc.Percent != 20 { + t.Errorf("Percent = %v, want 20", calc.Percent) + } + if calc.Base != 150 { + t.Errorf("Base = %v, want 150", calc.Base) + } + if calc.Result != 30 { + t.Errorf("Result = %v, want 30", calc.Result) + } +} + +// TestParseCalculationIsYPercentOfWhat tests ParseCalculation for "X is Y% of what" type +func TestParseCalculationIsYPercentOfWhat(t *testing.T) { + calc, err := ParseCalculation("30 is 20% of what") + if err != nil { + t.Fatalf("ParseCalculation returned error: %v", err) + } + if calc == nil { + t.Fatal("ParseCalculation returned nil") + } + if calc.Type != IsYPercentOfWhat { + t.Errorf("Type = %v, want IsYPercentOfWhat", calc.Type) + } + if calc.Percent != 20 { + t.Errorf("Percent = %v, want 20", calc.Percent) + } + if calc.Base != 150 { + t.Errorf("Base = %v, want 150", calc.Base) + } + if calc.Result != 30 { + t.Errorf("Result = %v, want 30", calc.Result) + } +} + +// TestParseCalculationWhatIsPrefix tests "what is" prefix stripping +func TestParseCalculationWhatIsPrefix(t *testing.T) { + calc, err := ParseCalculation("what is 20% of 150") + if err != nil { + t.Fatalf("ParseCalculation returned error: %v", err) + } + if calc.Percent != 20 || calc.Base != 150 || calc.Result != 30 { + t.Errorf("ParseCalculation with 'what is' prefix: Percent=%v, Base=%v, Result=%v", + calc.Percent, calc.Base, calc.Result) + } +} + +// TestParseCalculationCaseInsensitive tests case insensitivity +func TestParseCalculationCaseInsensitive(t *testing.T) { + tests := []struct { + input string + wantType CalculationType + }{ + {"20% OF 150", PercentOfY}, + {"WHAT IS 20% OF 150", PercentOfY}, + {"30 IS WHAT % OF 150", IsWhatPercentOfY}, + {"30 IS 20% OF WHAT", IsYPercentOfWhat}, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + calc, err := ParseCalculation(tt.input) + if err != nil { + t.Fatalf("ParseCalculation(%q) returned error: %v", tt.input, err) + } + if calc.Type != tt.wantType { + t.Errorf("Type = %v, want %v", calc.Type, tt.wantType) + } + }) + } +} + +// TestParseCalculationDivisionByZero tests division by zero edge cases +func TestParseCalculationDivisionByZero(t *testing.T) { + tests := []struct { + name string + input string + }{ + {"X is what % of 0", "30 is what % of 0"}, + {"X is 0% of what", "30 is 0% of what"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := ParseCalculation(tt.input) + if err == nil { + t.Errorf("ParseCalculation(%q) should return error for division by zero", tt.input) + } + }) + } +} + +// TestParseCalculationInvalidInput tests invalid inputs return errors +func TestParseCalculationInvalidInput(t *testing.T) { + tests := []string{ + "hello world", + "20%", + "% of", + "calculate percentage", + "", + } + + for _, input := range tests { + t.Run(input, func(t *testing.T) { + _, err := ParseCalculation(input) + if err == nil { + t.Errorf("ParseCalculation(%q) should return error", input) + } + }) + } +} + +// TestCalculationFormatPercentOfY tests Format() for PercentOfY type +func TestCalculationFormatPercentOfY(t *testing.T) { + calc := &Calculation{ + Type: PercentOfY, + Percent: 20, + Base: 150, + Result: 30, + Steps: "(20 / 100) * 150 = 0.20 * 150 = 30", + } + + formatted := calc.Format() + if !strings.HasPrefix(formatted, "20.00% of 150.00 = 30.00") { + t.Errorf("Format() = %q, expected to start with '20.00%% of 150.00 = 30.00'", formatted) + } + if !strings.Contains(formatted, "Steps:") { + t.Error("Format() should contain Steps") + } +} + +// TestCalculationFormatIsWhatPercentOfY tests Format() for IsWhatPercentOfY type +func TestCalculationFormatIsWhatPercentOfY(t *testing.T) { + calc := &Calculation{ + Type: IsWhatPercentOfY, + Percent: 20, + Base: 150, + Result: 30, + Steps: "", + } + + formatted := calc.Format() + if !strings.HasPrefix(formatted, "30.00 is 20.00% of 150.00") { + t.Errorf("Format() = %q, expected to start with '30.00 is 20.00%% of 150.00'", formatted) + } +} + +// TestCalculationFormatIsYPercentOfWhat tests Format() for IsYPercentOfWhat type +func TestCalculationFormatIsYPercentOfWhat(t *testing.T) { + calc := &Calculation{ + Type: IsYPercentOfWhat, + Percent: 20, + Base: 150, + Result: 30, + Steps: "", + } + + formatted := calc.Format() + if !strings.HasPrefix(formatted, "30.00 is 20.00% of 150.00") { + t.Errorf("Format() = %q, expected to start with '30.00 is 20.00%% of 150.00'", formatted) + } +} + +// TestCalculationFormatWithoutSteps tests Format() without Steps field +func TestCalculationFormatWithoutSteps(t *testing.T) { + calc := &Calculation{ + Type: PercentOfY, + Percent: 25, + Base: 100, + Result: 25, + Steps: "", + } + + formatted := calc.Format() + if formatted != "25.00% of 100.00 = 25.00" { + t.Errorf("Format() without steps = %q, want '25.00%% of 100.00 = 25.00'", formatted) + } + if strings.Contains(formatted, "Steps") { + t.Error("Format() should not contain 'Steps' when Steps is empty") + } +} + +// TestParseCalculationDecimalValues tests decimal values in ParseCalculation +func TestParseCalculationDecimalValues(t *testing.T) { + calc, err := ParseCalculation("12.5% of 80") + if err != nil { + t.Fatalf("ParseCalculation returned error: %v", err) + } + if calc.Percent != 12.5 { + t.Errorf("Percent = %v, want 12.5", calc.Percent) + } + if calc.Result != 10 { + t.Errorf("Result = %v, want 10", calc.Result) + } +} + +// TestParseCalculationLargeValues tests large values in ParseCalculation +func TestParseCalculationLargeValues(t *testing.T) { + calc, err := ParseCalculation("50% of 1000000") + if err != nil { + t.Fatalf("ParseCalculation returned error: %v", err) + } + if calc.Percent != 50 { + t.Errorf("Percent = %v, want 50", calc.Percent) + } + if calc.Result != 500000 { + t.Errorf("Result = %v, want 500000", calc.Result) + } +} |
