Files

254 lines
8.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package announcement
import (
"context"
"testing"
"hfb_jysh/backend/internal/database"
"hfb_jysh/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])
}
}
// TestPublicPopupSelection 覆盖弹窗公告的选取口径:
// 只有 published + is_popup 才参与,站点范围内按优先级取最高的一条,无候选时返回 nil。
func TestPublicPopupSelection(t *testing.T) {
db := database.NewTestDB()
repo := NewRepository(db)
ctx := context.Background()
// 主站:非弹窗公告(不应参与) + 两条弹窗公告(优先级不同)
plain := model.Announcement{
Title: "普通公告", Content: "内容", Category: "notice", Status: "published",
}
lowPriority := model.Announcement{
Title: "低优先级弹窗", Content: "内容", Category: "notice",
Status: "published", IsPopup: true, Priority: 1,
}
highPriority := model.Announcement{
Title: "高优先级弹窗", Content: "内容", Category: "notice",
Status: "published", IsPopup: true, Priority: 9,
}
// 弹窗但未发布:不应参与
draftPopup := model.Announcement{
Title: "草稿弹窗", Content: "内容", Category: "notice",
Status: "draft", IsPopup: true, Priority: 99,
}
for _, row := range []*model.Announcement{&plain, &lowPriority, &highPriority, &draftPopup} {
if err := db.Create(row).Error; err != nil {
t.Fatal(err)
}
}
popup, err := repo.PublicPopup(ctx, []uint64{0})
if err != nil {
t.Fatal(err)
}
if popup == nil {
t.Fatal("主站应弹出一条弹窗公告, got nil")
}
if popup.ID != highPriority.ID || popup.Title != "高优先级弹窗" {
t.Fatalf("应取优先级最高的弹窗公告, got %+v", popup)
}
if !popup.IsPopup {
t.Fatalf("返回的公告应带 is_popup 标记: %+v", popup)
}
// 站点隔离:独立站只看到本站公告,主站弹窗不可见
site := model.DistributionSite{
SiteCode: "ann_popup_site", SiteName: "弹窗站", Domain: "ann-popup.example.com",
Status: "active", AnnouncementMode: model.AnnouncementModeOwnOnly,
}
if err := db.Create(&site).Error; err != nil {
t.Fatal(err)
}
none, err := repo.PublicPopup(ctx, []uint64{site.ID})
if err != nil {
t.Fatal(err)
}
if none != nil {
t.Fatalf("本站没有弹窗公告时应返回 nil, got %+v", none)
}
// 本站有自己的弹窗公告时应生效
sitePopup := model.Announcement{
DistributionSiteID: site.ID, Title: "本站弹窗", Content: "内容",
Category: "notice", Status: "published", IsPopup: true, Priority: 5,
}
if err := db.Create(&sitePopup).Error; err != nil {
t.Fatal(err)
}
got, err := repo.PublicPopup(ctx, []uint64{site.ID})
if err != nil {
t.Fatal(err)
}
if got == nil || got.ID != sitePopup.ID {
t.Fatalf("应取本站弹窗公告, got %+v", got)
}
}