package billcost import ( "testing" ) // validRemoteBands constructs a single-row remote-zone band that // matches the design PM signed off on (1kg first weight + 1kg // continuation, single row covering 0-50kg). // // validRemoteBands 构造 PM 拍板的偏远区单行 band (首重 1kg + 续重 // 1kg, 单行覆盖 0-50kg). func validRemoteBands(province string) []Band { return []Band{{ Partition: province, LimitBottom: 0, LimitTop: 50000, BaseWeight: 1000, BaseAmount: 6.0, IncrementWeight: 1000, IncrementPrice: 3.0, }} } // validMainlandBands constructs a typical mainland multi-band layout // (4 first-weight bands + 1 continuation) with monotonic prices. // // validMainlandBands 构造典型大陆多段布局 (4 首重段 + 1 续重段), // 价格单调. func validMainlandBands(province string) []Band { return []Band{ {Partition: province, LimitBottom: 0, LimitTop: 500, BaseWeight: 500, BaseAmount: 1.5}, {Partition: province, LimitBottom: 500, LimitTop: 1000, BaseWeight: 1000, BaseAmount: 2.0}, {Partition: province, LimitBottom: 1000, LimitTop: 2000, BaseWeight: 2000, BaseAmount: 3.0}, {Partition: province, LimitBottom: 2000, LimitTop: 3000, BaseWeight: 3000, BaseAmount: 4.0}, {Partition: province, LimitBottom: 3000, LimitTop: 50000, BaseWeight: 3000, BaseAmount: 4.0, IncrementWeight: 1000, IncrementPrice: 0.5}, } } func cfg() ReflectConfig { return ReflectConfig{ MaxWeightG: 50000, } } // TestReflect_ValidPasses sanity check: a well-formed table emits // no violations. // // TestReflect_ValidPasses 烟雾测: 正确表无 Violation. func TestReflect_ValidPasses(t *testing.T) { out := Output{ Bands: append(validRemoteBands("内蒙古"), validMainlandBands("北京")...), } v := Reflect(out, cfg()) if len(v) != 0 { t.Errorf("valid table: want 0 violations, got %d: %+v", len(v), v) } } // TestReflect_FirstLessIncrement triggers the PM-flagged // reverse-logic rule (first-kg cheaper than continuation-kg). // // TestReflect_FirstLessIncrement 命中 PM 抓的反逻辑规则 (首重 kg 比 // 续重 kg 便宜). func TestReflect_FirstLessIncrement(t *testing.T) { out := Output{ Bands: []Band{{ Partition: "内蒙古", LimitBottom: 0, LimitTop: 50000, BaseWeight: 1000, BaseAmount: 3.0, // 3元/kg first IncrementWeight: 1000, IncrementPrice: 6.0, // 6元/kg continuation (反逻辑) }}, } v := Reflect(out, cfg()) if !hasRule(v, RuleFirstLessIncrement) { t.Errorf("expected first_lt_increment violation, got %+v", v) } } // TestReflect_SegmentGap raises a gap violation when adjacent bands // do not share their boundary. // // TestReflect_SegmentGap 段间缺口违规. func TestReflect_SegmentGap(t *testing.T) { out := Output{ Bands: []Band{ {Partition: "北京", LimitBottom: 0, LimitTop: 1000, BaseWeight: 1000, BaseAmount: 5.0}, {Partition: "北京", LimitBottom: 2000, LimitTop: 50000, BaseWeight: 2000, BaseAmount: 8.0}, }, } v := Reflect(out, cfg()) if !hasRule(v, RuleSegmentGap) { t.Errorf("expected segment_gap violation, got %+v", v) } } // TestReflect_SegmentOverlap raises an overlap violation when bands // re-cover weight ranges. // // TestReflect_SegmentOverlap 段间重叠违规. func TestReflect_SegmentOverlap(t *testing.T) { out := Output{ Bands: []Band{ {Partition: "北京", LimitBottom: 0, LimitTop: 1500, BaseWeight: 1000, BaseAmount: 5.0}, {Partition: "北京", LimitBottom: 1000, LimitTop: 50000, BaseWeight: 2000, BaseAmount: 8.0}, }, } v := Reflect(out, cfg()) if !hasRule(v, RuleSegmentOverlap) { t.Errorf("expected segment_overlap violation, got %+v", v) } } // TestReflect_SegmentStartZero raises when the first band's lower // bound is non-zero. // // TestReflect_SegmentStartZero 首段下限非 0 违规. func TestReflect_SegmentStartZero(t *testing.T) { out := Output{ Bands: []Band{{ Partition: "北京", LimitBottom: 100, LimitTop: 50000, BaseWeight: 1000, BaseAmount: 5.0, IncrementWeight: 1000, IncrementPrice: 1.0, }}, } v := Reflect(out, cfg()) if !hasRule(v, RuleSegmentStartZero) { t.Errorf("expected segment_start_nonzero violation, got %+v", v) } } // TestReflect_SegmentEndShort raises when the last band falls short // of cfg.MaxWeightG. // // TestReflect_SegmentEndShort 末段不到期望最大重量违规. func TestReflect_SegmentEndShort(t *testing.T) { out := Output{ Bands: []Band{{ Partition: "北京", LimitBottom: 0, LimitTop: 10000, BaseWeight: 1000, BaseAmount: 5.0, IncrementWeight: 1000, IncrementPrice: 1.0, }}, } v := Reflect(out, cfg()) if !hasRule(v, RuleSegmentEndShort) { t.Errorf("expected segment_end_short violation, got %+v", v) } } // TestReflect_BandIncomplete raises when both base and increment // prices are zero. // // TestReflect_BandIncomplete 段无任何价位违规. func TestReflect_BandIncomplete(t *testing.T) { out := Output{ Bands: []Band{{ Partition: "北京", LimitBottom: 0, LimitTop: 50000, BaseWeight: 1000, BaseAmount: 0, IncrementWeight: 1000, IncrementPrice: 0, }}, } v := Reflect(out, cfg()) if !hasRule(v, RuleBandIncomplete) { t.Errorf("expected band_incomplete violation, got %+v", v) } } // TestReflect_PartitionMissing raises when an expected partition is // absent from the output. // // TestReflect_PartitionMissing 期望分区缺失违规. func TestReflect_PartitionMissing(t *testing.T) { out := Output{ Bands: validMainlandBands("北京"), } c := cfg() c.ExpectedPartitions = []string{"北京", "上海"} v := Reflect(out, c) if !hasRule(v, RuleCoverage) { t.Errorf("expected partition_missing violation, got %+v", v) } } // TestReflect_DerivedSamplesAdaptive verifies the reflector adapts // to band count: a single-row remote band emits no synthetic // "weight 500g" sample (boundary-derived only). // // TestReflect_DerivedSamplesAdaptive 验采样点跟段数自适应: 单行偏远 // 段不生成 "500g" 等固定网格采样. func TestReflect_DerivedSamplesAdaptive(t *testing.T) { bands := validRemoteBands("内蒙古") samples := derivedSamples(bands) // Expected derivation from the single band: // LimitBottom+1 = 1, BaseWeight = 1000, BaseWeight+1 = 1001, // LimitTop = 50000. want := map[int]bool{1: true, 1000: true, 1001: true, 50000: true} if len(samples) != len(want) { t.Errorf("samples size: want %d, got %d (%v)", len(want), len(samples), samples) } for _, s := range samples { if !want[s] { t.Errorf("unexpected sample %d", s) } } } // hasRule returns true when the violations slice contains a // violation with the given rule id. // // hasRule 检查 violations 切片是否含指定 rule id. func hasRule(violations []Violation, ruleID string) bool { for _, v := range violations { if v.RuleID == ruleID { return true } } return false } // countRule counts violations with the given rule id. // // countRule 统计指定 rule id 的 Violation 数量. func countRule(violations []Violation, ruleID string) int { n := 0 for _, v := range violations { if v.RuleID == ruleID { n++ } } return n } // TestReflect_DuplicateBand verifies an exact duplicate (same // Partition, same LimitBottom, same LimitTop) cost band is caught. // Distinct from segment_overlap which targets partial-range overlaps. // // TestReflect_DuplicateBand 验证完全相同的 (Partition, LimitBottom, // LimitTop) 普通段会被抓. 与 segment_overlap (部分相交) 区分. func TestReflect_DuplicateBand(t *testing.T) { bands := validMainlandBands("北京") // Inject an exact duplicate of the first 0-500g band. dup := bands[0] bands = append(bands, dup) out := Output{ Bands: append(validRemoteBands("内蒙古"), bands...), } v := Reflect(out, cfg()) if !hasRule(v, RuleDuplicateBand) { t.Errorf("expected %s violation, got %+v", RuleDuplicateBand, v) } if got := countRule(v, RuleDuplicateBand); got != 1 { t.Errorf("duplicate twice should yield 1 violation, got %d", got) } } // TestReflect_DuplicateBand_TripleEmitsTwo verifies a 3-fold repeat // produces 2 violations (2nd + 3rd occurrences) -- not 1, not 3 -- // matching the godoc contract. // // TestReflect_DuplicateBand_TripleEmitsTwo 验证 3 次重复出 2 条 // violations (第 2 / 第 3 次), 与 godoc 契约对齐. func TestReflect_DuplicateBand_TripleEmitsTwo(t *testing.T) { bands := validMainlandBands("北京") dup := bands[0] bands = append(bands, dup, dup) // 3 copies total out := Output{ Bands: append(validRemoteBands("内蒙古"), bands...), } v := Reflect(out, cfg()) if got := countRule(v, RuleDuplicateBand); got != 2 { t.Errorf("triple repeat should yield 2 violations (2nd+3rd), got %d: %+v", got, v) } } // TestReflect_DuplicateStripFee verifies a duplicate strip-fee row // (same City, same LimitBottom, same LimitTop) is caught. Cost-band // duplicate detection ignores StripFees, this rule covers them. // // TestReflect_DuplicateStripFee 验证 (City, LimitBottom, LimitTop) // 单带费重复会被抓. 普通段去重忽略 IsStripFee=true, 此规则覆盖. func TestReflect_DuplicateStripFee(t *testing.T) { out := Output{ Bands: append(validRemoteBands("内蒙古"), validMainlandBands("北京")...), StripFees: []Band{ {IsStripFee: true, Partition: "上海", City: "上海", LimitBottom: 0, LimitTop: 500, BaseWeight: 500, BaseAmount: 0.5}, {IsStripFee: true, Partition: "上海", City: "上海", LimitBottom: 0, LimitTop: 500, BaseWeight: 500, BaseAmount: 0.5}, }, } v := Reflect(out, cfg()) if !hasRule(v, RuleDuplicateStripFee) { t.Errorf("expected %s violation, got %+v", RuleDuplicateStripFee, v) } } // TestReflect_DuplicateBand_DifferentPartitionsPasses verifies the // rule only flags within-partition duplicates -- different partitions // with the same segment range are legal (every province has a 0-500g // band, that's normal). // // TestReflect_DuplicateBand_DifferentPartitionsPasses 验证规则只跨同 // partition 抓 -- 不同 partition 的同段是合法 (每省都有 0-500g 段是常 // 态). func TestReflect_DuplicateBand_DifferentPartitionsPasses(t *testing.T) { out := Output{ Bands: append(validMainlandBands("北京"), validMainlandBands("上海")...), } v := Reflect(out, cfg()) if hasRule(v, RuleDuplicateBand) { t.Errorf("different partitions same segment should NOT be duplicate, got %+v", v) } }