package decimal import ( "math" "testing" ) const epsilon = 0.001 func almostEqual(a, b float64) bool { return math.Abs(a-b) < epsilon } func TestPricePerLiter(t *testing.T) { cases := []struct { name string total, liters float64 expect float64 }{ {"plan example 176.33/23.67", 176.33, 23.67, 7.45}, {"simple 320/40", 320, 40, 8.0}, {"simple 256/32", 256, 32, 8.0}, {"zero liters", 100, 0, 0}, {"negative liters", 100, -1, 0}, {"precision 23.67/3.18", 23.67, 3.18, 7.4434}, // 23.67/3.18 = 7.4433... } for _, c := range cases { t.Run(c.name, func(t *testing.T) { got := PricePerLiter(c.total, c.liters) if !almostEqual(got, c.expect) { t.Errorf("PricePerLiter(%v,%v) = %v, want %v", c.total, c.liters, got, c.expect) } }) } } func TestConsumptionL100km(t *testing.T) { cases := []struct { name string liters, mileage float64 expect float64 }{ {"plan example (40/500*100)", 40, 500, 8.0}, {"32/500*100", 32, 500, 6.4}, {"zero mileage", 40, 0, 0}, {"negative mileage", 40, -1, 0}, {"rounding 2dp", 7.123, 100, 7.12}, // 7.123/100*100 = 7.123 -> round 2dp } for _, c := range cases { t.Run(c.name, func(t *testing.T) { got := ConsumptionL100km(c.liters, c.mileage) if !almostEqual(got, c.expect) { t.Errorf("ConsumptionL100km(%v,%v) = %v, want %v", c.liters, c.mileage, got, c.expect) } }) } } func TestCostPerKm(t *testing.T) { cases := []struct { name string amount, mileage float64 expect float64 }{ {"plan example 256/500", 256, 500, 0.512}, {"zero mileage", 100, 0, 0}, {"negative mileage", 100, -1, 0}, {"typical 200/400", 200, 400, 0.5}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { got := CostPerKm(c.amount, c.mileage) if !almostEqual(got, c.expect) { t.Errorf("CostPerKm(%v,%v) = %v, want %v", c.amount, c.mileage, got, c.expect) } }) } } func TestMileage(t *testing.T) { cases := []struct { name string current, prev float64 expect float64 }{ {"normal increase", 11000, 10500, 500}, {"equal (no travel)", 10500, 10500, 0}, {"odometer rollback (typo)", 10400, 10500, 0}, {"zero prev (first record)", 10000, 0, 10000}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { got := Mileage(c.current, c.prev) if !almostEqual(got, c.expect) { t.Errorf("Mileage(%v,%v) = %v, want %v", c.current, c.prev, got, c.expect) } }) } } func TestAvgFloat64(t *testing.T) { cases := []struct { name string xs []float64 expect float64 }{ {"empty", nil, 0}, {"single", []float64{8.0}, 8.0}, {"two", []float64{7.0, 9.0}, 8.0}, {"three typical", []float64{6.0, 7.0, 8.0}, 7.0}, {"rounding", []float64{0.123, 0.456, 0.789}, 0.46}, // avg = 0.456 } for _, c := range cases { t.Run(c.name, func(t *testing.T) { got := AvgFloat64(c.xs) if !almostEqual(got, c.expect) { t.Errorf("AvgFloat64(%v) = %v, want %v", c.xs, got, c.expect) } }) } }