package announcement import ( "context" "testing" "hfb_sys/backend/internal/database" "hfb_sys/backend/internal/model" ) // TestPublicScopeAndSiteIsolation 覆盖公告中心的分站可见范围: // 主站只看主站、分站继承=主站+本站、分站仅本站=只有本站,详情也不能跨站读取。 func TestPublicScopeAndSiteIsolation(t *testing.T) { db := database.NewTestDB() repo := NewRepository(db) ctx := context.Background() mainAnnouncement := model.Announcement{Title: "主站公告", Content: "主站内容", Category: "notice", Status: "published"} if err := db.Create(&mainAnnouncement).Error; err != nil { t.Fatal(err) } inheritSite := model.DistributionSite{ SiteCode: "ann_inherit", SiteName: "继承站", Domain: "ann-inherit.example.com", Status: "active", AnnouncementMode: model.AnnouncementModeInherit, } ownOnlySite := model.DistributionSite{ SiteCode: "ann_own_only", SiteName: "独立站", Domain: "ann-own.example.com", Status: "active", AnnouncementMode: model.AnnouncementModeOwnOnly, } if err := db.Create(&inheritSite).Error; err != nil { t.Fatal(err) } if err := db.Create(&ownOnlySite).Error; err != nil { t.Fatal(err) } inheritOwn := model.Announcement{ DistributionSiteID: inheritSite.ID, Title: "继承站公告", Content: "本站内容", Category: "notice", Status: "published", } ownOnlyOwn := model.Announcement{ DistributionSiteID: ownOnlySite.ID, Title: "独立站公告", Content: "本站内容", Category: "notice", Status: "published", } if err := db.Create(&inheritOwn).Error; err != nil { t.Fatal(err) } if err := db.Create(&ownOnlyOwn).Error; err != nil { t.Fatal(err) } // 主站:仅 [0] mainScope, err := repo.ResolvePublicSiteScope(ctx, "") if err != nil { t.Fatal(err) } if len(mainScope) != 1 || mainScope[0] != 0 { t.Fatalf("main scope = %v, want [0]", mainScope) } mainList, err := repo.List(ctx, AnnouncementListQuery{AllowedSiteIDs: mainScope, Page: 1, PageSize: 20}) if err != nil { t.Fatal(err) } if mainList.Total != 1 { t.Fatalf("主站应只看到 1 条主站公告, got %d", mainList.Total) } // 分站 inherit:[0, siteID] → 主站公告 + 本站公告 inheritScope, err := repo.ResolvePublicSiteScope(ctx, inheritSite.SiteCode) if err != nil { t.Fatal(err) } if len(inheritScope) != 2 { t.Fatalf("inherit scope = %v, want [0, siteID]", inheritScope) } inheritList, err := repo.List(ctx, AnnouncementListQuery{AllowedSiteIDs: inheritScope, Page: 1, PageSize: 20}) if err != nil { t.Fatal(err) } if inheritList.Total != 2 { t.Fatalf("继承站应看到主站+本站共 2 条, got %d", inheritList.Total) } // 分站 own_only:只有本站公告(不含主站公告) ownScope, err := repo.ResolvePublicSiteScope(ctx, ownOnlySite.SiteCode) if err != nil { t.Fatal(err) } if len(ownScope) != 1 || ownScope[0] != ownOnlySite.ID { t.Fatalf("own_only scope = %v, want [siteID]", ownScope) } ownList, err := repo.List(ctx, AnnouncementListQuery{AllowedSiteIDs: ownScope, Page: 1, PageSize: 20}) if err != nil { t.Fatal(err) } if ownList.Total != 1 { t.Fatalf("仅本站站点应只看到本站 1 条, got %d", ownList.Total) } // 无效站点编码回落主站,避免把主站公告错误地当成某站的 fallbackScope, err := repo.ResolvePublicSiteScope(ctx, "not_exists_site") if err != nil { t.Fatal(err) } if len(fallbackScope) != 1 || fallbackScope[0] != 0 { t.Fatalf("未知站点应回落主站, got %v", fallbackScope) } // 详情跨站隔离:主站访客不能按 ID 读分站公告 if _, err := repo.GetByID(ctx, inheritOwn.ID, []uint64{0}); err == nil { t.Fatalf("主站不应能读取分站公告详情") } if _, err := repo.GetByID(ctx, inheritOwn.ID, inheritScope); err != nil { t.Fatalf("继承站应能读取本站公告详情: %v", err) } if _, err := repo.GetByID(ctx, ownOnlyOwn.ID, inheritScope); err == nil { t.Fatalf("继承站不应能读取其它分站公告详情") } } // TestAdminListFiltersBySite 管理端可按站点筛选,并带出站点名。 func TestAdminListFiltersBySite(t *testing.T) { db := database.NewTestDB() repo := NewRepository(db) ctx := context.Background() site := model.DistributionSite{ SiteCode: "ann_admin", SiteName: "筛选站", Domain: "ann-admin.example.com", Status: "active", AnnouncementMode: model.AnnouncementModeInherit, } if err := db.Create(&site).Error; err != nil { t.Fatal(err) } if err := db.Create(&model.Announcement{Title: "主站公告", Content: "x", Category: "notice", Status: "draft"}).Error; err != nil { t.Fatal(err) } if err := db.Create(&model.Announcement{ DistributionSiteID: site.ID, Title: "分站公告", Content: "y", Category: "notice", Status: "draft", }).Error; err != nil { t.Fatal(err) } all, err := repo.AdminList(ctx, AnnouncementListQuery{Page: 1, PageSize: 20}) if err != nil { t.Fatal(err) } if all.Total != 2 { t.Fatalf("不传站点应返回全部 2 条, got %d", all.Total) } mainOnly := uint64(0) mainList, err := repo.AdminList(ctx, AnnouncementListQuery{AdminSiteID: &mainOnly, Page: 1, PageSize: 20}) if err != nil { t.Fatal(err) } if mainList.Total != 1 { t.Fatalf("site_id=0 应只返回主站公告, got %d", mainList.Total) } siteID := site.ID siteList, err := repo.AdminList(ctx, AnnouncementListQuery{AdminSiteID: &siteID, Page: 1, PageSize: 20}) if err != nil { t.Fatal(err) } if siteList.Total != 1 { t.Fatalf("按站点筛选应只返回该站公告, got %d", siteList.Total) } items, ok := siteList.Items.([]AnnouncementDTO) if !ok || len(items) != 1 { t.Fatalf("unexpected items: %#v", siteList.Items) } if items[0].DistributionSiteID != site.ID || items[0].DistributionSiteName != "筛选站" { t.Fatalf("站点信息未透出: %+v", items[0]) } }