package distribution import ( "testing" "golang.org/x/crypto/bcrypt" "hfb_sys/backend/internal/model" ) func TestSiteCRUD_UpdateSiteAndDomainUniqueness(t *testing.T) { db := distributionTestDB(t) repo := NewRepository(db) ctx := t.Context() _, err := repo.CreateSite(ctx, CreateSiteRequest{ SiteCode: "site_crud_a", SiteName: "站点A", Domain: "a.domain.test", }) if err != nil { t.Fatalf("CreateSite A failed: %v", err) } siteB, err := repo.CreateSite(ctx, CreateSiteRequest{ SiteCode: "site_crud_b", SiteName: "站点B", Domain: "b.domain.test", }) if err != nil { t.Fatalf("CreateSite B failed: %v", err) } // 1. 尝试将站点B的域名更新为已存在的站点A域名 -> 应该报错 dupDomain := "a.domain.test" _, err = repo.UpdateSite(ctx, 0, siteB.ID, UpdateSiteRequest{ Domain: &dupDomain, }) if err == nil { t.Fatalf("expected error when updating to duplicate domain, got nil") } // 2. 正常更新站点B的名称、域名、状态为 disabled、展示主站账号为 false newName := "站点B更新后" newDomain := "b-new.domain.test" newStatus := "disabled" dispFalse := false updated, err := repo.UpdateSite(ctx, 0, siteB.ID, UpdateSiteRequest{ SiteName: &newName, Domain: &newDomain, Status: &newStatus, DisplayAccountEnabled: &dispFalse, }) if err != nil { t.Fatalf("UpdateSite failed: %v", err) } if updated.SiteName != newName || updated.Domain != newDomain || updated.Status != "disabled" || updated.DisplayAccountEnabled != false { t.Fatalf("updated site values mismatch: %+v", updated) } } func TestSiteCRUD_DeleteSiteProtectionAndCascade(t *testing.T) { db := distributionTestDB(t) repo := NewRepository(db) ctx := t.Context() // 1. 使用 OnboardSite 创建一个完整的测试分销站(包含成员与管理员) onboardResp, err := repo.OnboardSite(ctx, OnboardSiteRequest{ SiteCode: "del_test_site", SiteName: "待删除测试分销站", Domain: "del.domain.test", AdminUsername: "del_test_admin", AdminPassword: "password123", }) if err != nil { t.Fatalf("OnboardSite failed: %v", err) } siteID := onboardResp.Site.ID adminUserID := onboardResp.Member.AdminID // 2. 测试密码重置 newPwd := "new_secret_pwd_666" if err := repo.ResetSiteAdminPassword(ctx, 0, siteID, newPwd); err != nil { t.Fatalf("ResetSiteAdminPassword failed: %v", err) } // 校验密码是否成功重置 var adminUser model.AdminUser if err := db.First(&adminUser, adminUserID).Error; err != nil { t.Fatalf("failed to find admin user: %v", err) } if err := bcrypt.CompareHashAndPassword([]byte(adminUser.PasswordHash), []byte(newPwd)); err != nil { t.Fatalf("password hash does not match new password: %v", err) } // 3. 验证 ListSites 与 FindSite 能够正确携带管理员信息 sites, err := repo.ListSites(ctx) if err != nil { t.Fatalf("ListSites failed: %v", err) } var foundSite *SiteDTO for i := range sites { if sites[i].ID == siteID { foundSite = &sites[i] break } } if foundSite == nil { t.Fatalf("site not found in ListSites") } if foundSite.AdminUsername != "del_test_admin" { t.Fatalf("expected admin_username del_test_admin, got %s", foundSite.AdminUsername) } // 4. 测试有订单时的删除保护 // 创建一笔模拟关联订单 dummyOrder := model.RentalOrder{ OrderNo: "ORD_DEL_TEST_001", RenterID: 999, AccountID: 888, DistributionSiteID: &siteID, } if err := db.Create(&dummyOrder).Error; err != nil { t.Fatalf("failed to create dummy order: %v", err) } err = repo.DeleteSite(ctx, 0, siteID) if err == nil { t.Fatalf("expected error when deleting site with orders, got nil") } // 删除此测试订单,继续测试级联删除 if err := db.Delete(&dummyOrder).Error; err != nil { t.Fatalf("failed to remove dummy order: %v", err) } // 5. 测试无订单时的干净级联删除 if err := repo.DeleteSite(ctx, 0, siteID); err != nil { t.Fatalf("DeleteSite failed: %v", err) } // 校验站点主体已删除 var checkSite model.DistributionSite if err := db.First(&checkSite, siteID).Error; err == nil { t.Fatalf("site should be deleted from db") } // 校验专属管理员用户已同步清理 var checkAdmin model.AdminUser if err := db.First(&checkAdmin, adminUserID).Error; err == nil { t.Fatalf("dedicated admin user should be deleted from db") } // 校验规则已清理 var ruleCount int64 db.Model(&model.DistributionRuleVersion{}).Where("site_id = ?", siteID).Count(&ruleCount) if ruleCount > 0 { t.Fatalf("expected 0 rules for deleted site, got %d", ruleCount) } }