| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- package migrate
- import "testing"
- // TestPlannerDecide 验证置信分级 + 冲突检测(P0-07 §2.2 匹配优先级)。
- func TestPlannerDecide(t *testing.T) {
- // 构造测试数据:
- // - user 1: phone 13800000001(唯一)+ 现映射种子 → 应判 seed/100
- // - user 2: phone 13800000002(唯一,无种子)→ 应判 phone/90
- // - user 3: phone 13900000003(撞号,user 4 同号)→ 应判 conflict/0
- // - user 4: phone 13900000003(撞号)→ 应判 conflict/0
- // - user 5: 无 phone,有微信 unionid → 应判 unionid/70
- // - user 6: 无 phone、无微信 → 应判 unmapped/50
- // - user 7: phone 归一失败(非大陆号)→ 应判 unmapped/50 + PhoneInvalid
- users := []LegacyUser{
- {ID: 1, PhoneNumber: ptr("13800000001")},
- {ID: 2, PhoneNumber: ptr("13800000002")},
- {ID: 3, PhoneNumber: ptr("13900000003")},
- {ID: 4, PhoneNumber: ptr("13900000003")},
- {ID: 5},
- {ID: 6},
- {ID: 7, PhoneNumber: ptr("12345")},
- }
- assoc := []LegacyAccountAssociation{
- {UserID: 1, MarkingSystemUserID: ptr[int32](999)}, // user 1 有现映射
- }
- wx := []LegacyWeixinUser{
- {OpenID: "openid5", UserID: 5, UnionID: ptr("union5"), AppID: ptr("appid5")},
- }
- p := NewPlanner(users, assoc, wx)
- t.Run("seed 优先", func(t *testing.T) {
- d := p.Decide(1, users[0].PhoneNumber)
- if d.MatchSource != MatchSourceSeed || d.Confidence != ConfidenceSeed {
- t.Fatalf("user1 期望 seed/100, 得 %s/%d", d.MatchSource, d.Confidence)
- }
- })
- t.Run("phone 唯一", func(t *testing.T) {
- d := p.Decide(2, users[1].PhoneNumber)
- if d.MatchSource != MatchSourcePhone || d.Confidence != ConfidencePhone {
- t.Fatalf("user2 期望 phone/90, 得 %s/%d", d.MatchSource, d.Confidence)
- }
- })
- t.Run("phone 撞号冲突", func(t *testing.T) {
- for _, id := range []int32{3, 4} {
- raw := users[id-1].PhoneNumber
- d := p.Decide(id, raw)
- if d.MatchSource != MatchSourceConflict || !d.PhoneConflict || d.Confidence != ConfidenceConflict {
- t.Fatalf("user%d 期望 conflict/0+PhoneConflict, 得 %s/%d conflict=%v",
- id, d.MatchSource, d.Confidence, d.PhoneConflict)
- }
- }
- })
- t.Run("unionid 锚点", func(t *testing.T) {
- d := p.Decide(5, nil)
- if d.MatchSource != MatchSourceUnionid || d.Confidence != ConfidenceUnionid || !d.HasWechat {
- t.Fatalf("user5 期望 unionid/70+HasWechat, 得 %s/%d wechat=%v", d.MatchSource, d.Confidence, d.HasWechat)
- }
- })
- t.Run("unmapped", func(t *testing.T) {
- d := p.Decide(6, nil)
- if d.MatchSource != MatchSourceUnmapped || d.Confidence != ConfidenceUnmapped {
- t.Fatalf("user6 期望 unmapped/50, 得 %s/%d", d.MatchSource, d.Confidence)
- }
- })
- t.Run("phone 归一失败", func(t *testing.T) {
- d := p.Decide(7, users[6].PhoneNumber)
- if !d.PhoneInvalid {
- t.Fatalf("user7 期望 PhoneInvalid=true, 得 false")
- }
- })
- // 撞号组:仅 13900000003 一组(user 3,4)
- groups := p.PhoneConflictGroups()
- if len(groups) != 1 {
- t.Fatalf("期望 1 组撞号, 得 %d 组", len(groups))
- }
- if g, ok := groups["13900000003"]; !ok || len(g) != 2 {
- t.Fatalf("撞号组 13900000003 应含 2 账号, 得 %v", g)
- }
- }
- // TestPlannerSeedPriorityOverPhone 验证现映射种子优先级高于 phone(即使 phone 也撞号)。
- func TestPlannerSeedPriorityOverPhone(t *testing.T) {
- users := []LegacyUser{
- {ID: 10, PhoneNumber: ptr("13700000000")},
- {ID: 11, PhoneNumber: ptr("13700000000")}, // 撞号
- }
- assoc := []LegacyAccountAssociation{{UserID: 10, MarkingSystemUserID: ptr[int32](1)}} // user 10 有种子
- p := NewPlanner(users, assoc, nil)
- d := p.Decide(10, users[0].PhoneNumber)
- // 即使 phone 撞号,user 10 有现映射种子 → seed/100(优先级最高)
- if d.MatchSource != MatchSourceSeed || d.Confidence != ConfidenceSeed {
- t.Fatalf("user10 期望 seed/100(种子优先), 得 %s/%d", d.MatchSource, d.Confidence)
- }
- // user 11 无种子 → conflict
- d2 := p.Decide(11, users[1].PhoneNumber)
- if d2.MatchSource != MatchSourceConflict {
- t.Fatalf("user11 期望 conflict, 得 %s", d2.MatchSource)
- }
- }
- // TestPlannerEmptyPhoneSkipsIndex 验证空/无效 phone 不进撞号索引。
- func TestPlannerEmptyPhoneSkipsIndex(t *testing.T) {
- users := []LegacyUser{
- {ID: 1, PhoneNumber: nil},
- {ID: 2, PhoneNumber: ptr("")},
- {ID: 3, PhoneNumber: ptr(" ")},
- }
- p := NewPlanner(users, nil, nil)
- if len(p.phoneIndex) != 0 {
- t.Fatalf("空 phone 不进索引, 得 %d 项", len(p.phoneIndex))
- }
- }
- func ptr[T any](v T) *T { return &v }
|