package billcost import ( "testing" ) // TestCalculateShipCost_FlatBand verifies the flat-fee case // (weight <= BaseWeight pays BaseAmount with no continuation). // // TestCalculateShipCost_FlatBand 验一口价段 (weight <= BaseWeight // 按 BaseAmount, 不计续重). func TestCalculateShipCost_FlatBand(t *testing.T) { bands := []Band{{ LimitBottom: 0, LimitTop: 1000, BaseWeight: 1000, BaseAmount: 6.0, }} cases := []struct { w int want float64 }{ {1, 6.0}, {500, 6.0}, {1000, 6.0}, } for _, c := range cases { got, err := CalculateShipCost(bands, c.w) if err != nil { t.Errorf("weight %d: unexpected error %v", c.w, err) continue } if got != c.want { t.Errorf("weight %d: got %.2f, want %.2f", c.w, got, c.want) } } } // TestCalculateShipCost_Continuation verifies the // base-weight + continuation calculation, including ceil-rounding // of partial increment units. // // TestCalculateShipCost_Continuation 验首重 + 续重 (含部分增量 // ceil 取整). func TestCalculateShipCost_Continuation(t *testing.T) { // 1kg first weight = 6元, +3元 per 1kg continuation. bands := []Band{{ LimitBottom: 0, LimitTop: 50000, BaseWeight: 1000, BaseAmount: 6.0, IncrementWeight: 1000, IncrementPrice: 3.0, }} cases := []struct { w int want float64 }{ {1000, 6.0}, // exactly base {1001, 9.0}, // 1g over -> ceil(1/1000) = 1 unit -> +3 {2000, 9.0}, // 1kg continuation {2001, 12.0}, // 1.001kg continuation -> ceil(1001/1000) = 2 -> +6 {3000, 12.0}, // 2kg continuation {49000, 150.0}, // 48kg continuation -> +144 -> 150 } for _, c := range cases { got, err := CalculateShipCost(bands, c.w) if err != nil { t.Errorf("weight %d: unexpected error %v", c.w, err) continue } if got != c.want { t.Errorf("weight %d: got %.2f, want %.2f", c.w, got, c.want) } } } // TestCalculateShipCost_Uncovered ensures weights outside any band // raise an error rather than returning zero (silent miss). // // TestCalculateShipCost_Uncovered 验段外重量返错而非静默 0. func TestCalculateShipCost_Uncovered(t *testing.T) { bands := []Band{{ LimitBottom: 0, LimitTop: 1000, BaseWeight: 1000, BaseAmount: 6.0, }} if _, err := CalculateShipCost(bands, 1001); err == nil { t.Error("weight beyond LimitTop: want error, got nil") } if _, err := CalculateShipCost(bands, 0); err == nil { t.Error("weight 0 (boundary open lower): want error, got nil") } } // TestCalculateShipCost_MultiBand verifies multi-segment lookup // hits the correct band. // // TestCalculateShipCost_MultiBand 验多段表的命中段查找. func TestCalculateShipCost_MultiBand(t *testing.T) { bands := []Band{ {LimitBottom: 0, LimitTop: 1000, BaseWeight: 1000, BaseAmount: 5.0}, {LimitBottom: 1000, LimitTop: 3000, BaseWeight: 1000, BaseAmount: 5.0, IncrementWeight: 1000, IncrementPrice: 2.0}, {LimitBottom: 3000, LimitTop: 50000, BaseWeight: 3000, BaseAmount: 9.0, IncrementWeight: 1000, IncrementPrice: 1.5}, } cases := []struct { w int want float64 }{ {500, 5.0}, // band 1 flat {1500, 7.0}, // band 2: 5 + ceil(500/1000)*2 = 5+2 = 7 {3000, 9.0}, // band 2 last weight: 5 + ceil(2000/1000)*2 = 5+4 = 9 {3001, 10.5}, // band 3: 9 + ceil(1/1000)*1.5 = 10.5 {4000, 10.5}, // band 3: 9 + ceil(1000/1000)*1.5 = 10.5 {49000, 78.0}, // band 3: 9 + 46*1.5 = 78 } for _, c := range cases { got, err := CalculateShipCost(bands, c.w) if err != nil { t.Errorf("weight %d: unexpected error %v", c.w, err) continue } if got != c.want { t.Errorf("weight %d: got %.2f, want %.2f", c.w, got, c.want) } } } // TestCeilDiv covers the small helper. // // TestCeilDiv 小辅助函数. func TestCeilDiv(t *testing.T) { cases := []struct { a, b, want int }{ {0, 1000, 0}, {1, 1000, 1}, {999, 1000, 1}, {1000, 1000, 1}, {1001, 1000, 2}, {2000, 1000, 2}, {2001, 1000, 3}, {-1, 1000, 0}, {1000, 0, 0}, } for _, c := range cases { if got := ceilDiv(c.a, c.b); got != c.want { t.Errorf("ceilDiv(%d, %d) = %d, want %d", c.a, c.b, got, c.want) } } }