package fixture import ( "database/sql" "testing" ) // 本文件单测第三批(org 域)的纯映射规则:D1、status 值域翻译、bit(1)、NULL 列、哨兵时间。 func TestLegacyStatusToTarget(t *testing.T) { if v, err := legacyStatusToTarget(1, "x"); err != nil || v != "active" { t.Errorf("1 应映射为 active,得到 %q / %v", v, err) } if v, err := legacyStatusToTarget(0, "x"); err != nil || v != "disabled" { t.Errorf("0 应映射为 disabled,得到 %q / %v", v, err) } // 1/0 之外必须报错,绝不默认 active(否则会把已停用的机构悄悄复活) for _, bad := range []int8{2, -1, 9} { if _, err := legacyStatusToTarget(bad, "x"); err == nil { t.Errorf("status=%d 应报错(拒绝猜测),实际通过", bad) } } } func TestMapInstitutionD1AndNulls(t *testing.T) { l := LegacySchool{ ID: 63317, Name: "源思维", ProvinceID: 20, CityID: 218, DistrictID: 1869, GradePeriod: "1,2,3", Status: 1, } got, warns, err := MapInstitution(l) if err != nil { t.Fatalf("MapInstitution 出错:%v", err) } if got.InstitutionID != 63317 { t.Errorf("D1:institution_id 必须是 63317,实际 %d", got.InstitutionID) } if got.InstitutionName != "源思维" { t.Errorf("名称应原样,实际 %q", got.InstitutionName) } if got.Status != "active" { t.Errorf("status=1 → active,实际 %q", got.Status) } // legacy 无 code/address/phone 三列 → 必须 NULL,不编造 if got.InstitutionCode.Valid || got.Address.Valid || got.Phone.Valid { t.Error("legacy 无 code/address/phone,必须落 NULL") } // 有省市区 id 时必须产生一条告警(提示 address 无法还原) if len(warns) == 0 { t.Error("有省市区 id 时应产生告警(target 无对应列)") } if !got.CreatedAt.Equal(orgEpoch()) || !got.UpdatedAt.Equal(orgEpoch()) { t.Error("legacy 无时间列,created_at/updated_at 应落 epoch 哨兵") } } func TestMapInstitutionRejectsEmptyName(t *testing.T) { if _, _, err := MapInstitution(LegacySchool{ID: 1, Name: "", Status: 1}); err == nil { t.Error("name 为空且目标列 NOT NULL 时应报错(不得填占位值),实际未报错") } } func TestMapCampusD1R17AndBit(t *testing.T) { // R17:campus 1 的 school_id=1,必须**如实保留**,不能"修正"成数据里常见的 63317 l := LegacyCampus{ID: 1, Name: "城东校区", SchoolID: 1, Status: 1, SelfSupport: 0, Code: sql.NullString{String: "CD", Valid: true}} got, _, err := MapCampus(l) if err != nil { t.Fatalf("MapCampus 出错:%v", err) } if got.CampusID != 1 { t.Errorf("D1:campus_id 必须是 1,实际 %d", got.CampusID) } if got.InstitutionID != 1 { t.Errorf("R17:campus 1 的 institution_id 必须是 1,实际 %d", got.InstitutionID) } if got.SelfSupport != 0 { t.Errorf("bit(1)=0 应映射为 0,实际 %d", got.SelfSupport) } if got.CampusCode.String != "CD" { t.Errorf("code 应原样,实际 %q", got.CampusCode.String) } // bit(1)=1 → 1 l2 := LegacyCampus{ID: 5, Name: "金湖校区", SchoolID: 63317, Status: 1, SelfSupport: 1} got2, _, err := MapCampus(l2) if err != nil { t.Fatalf("MapCampus 出错:%v", err) } if got2.SelfSupport != 1 { t.Errorf("bit(1)=1 应映射为 1,实际 %d", got2.SelfSupport) } // bit(1) 只应取 0/1 bad := LegacyCampus{ID: 9, Name: "x", SchoolID: 1, Status: 1, SelfSupport: 48} if _, _, err := MapCampus(bad); err == nil { t.Error("self_support=48('0' 的 ASCII)应报错,不得当成 0/1") } } func TestMapCampusRejectsCampusZero(t *testing.T) { if _, _, err := MapCampus(LegacyCampus{ID: 0, Name: "x", SchoolID: 1, Status: 1}); err == nil { t.Error("campus_id=0 不应建行,应报错") } } func TestMapCampusRejectsOrphan(t *testing.T) { if _, _, err := MapCampus(LegacyCampus{ID: 9, Name: "x", SchoolID: 0, Status: 1}); err == nil { t.Error("school_id=0 会造出孤儿校区,应报错") } } func TestIsCrossOrgCampus(t *testing.T) { owner := map[int64]int64{1: 1, 2: 1, 5: 63317} // (63317, 1):校区 1 真实属于机构 1 → 跨机构 if !IsCrossOrgCampus(63317, 1, owner) { t.Error("(63317,1) 应判为 cross-org-campus") } // (1, 1):一致 if IsCrossOrgCampus(1, 1, owner) { t.Error("(1,1) 不应判为 cross-org-campus") } // campusid=0:机构级,永远不算跨机构 if IsCrossOrgCampus(63317, 0, owner) { t.Error("campusid=0 不应判为 cross-org-campus") } // 未知校区:不判 if IsCrossOrgCampus(63317, 999, owner) { t.Error("未知校区不应判为 cross-org-campus") } }