feat(publish): 正式表加发布状态与公开只读视图

This commit is contained in:
toom1996
2026-09-23 09:56:35 +08:00
parent bfe890bd56
commit 83823a8d49
6 changed files with 225 additions and 1 deletions

View File

@ -0,0 +1,133 @@
//go:build integration
// 集成测试:公开视图必须屏蔽未发布内容 —— 这是「单表发布模型」的核心保证。
//
// 运行(库需先就绪,见 internal/repository/dedup_integration_test.go 顶部说明):
//
// go test -tags integration ./internal/repository/ -run TestPublicViews -v
package repository
import (
"context"
"os"
"path/filepath"
"testing"
"fashionapi/internal/model"
"gorm.io/gorm"
)
// applyMigration 读取并执行 db/migrations 下的迁移文件。
// 迁移 SQL 因此每次测试都被真实跑一遍:语法错、视图列集漂移会立刻暴露。
// 迁移文件必须幂等,重复调用安全。
func applyMigration(t *testing.T, db *gorm.DB, name string) {
t.Helper()
path := filepath.Join("..", "..", "db", "migrations", name)
sqlBytes, err := os.ReadFile(path)
if err != nil {
t.Fatalf("读取迁移失败 %s: %v", path, err)
}
if err := db.Exec(string(sqlBytes)).Error; err != nil {
t.Fatalf("执行迁移失败 %s: %v", name, err)
}
}
// seedRunway 插入一条指定状态的走秀记录 + 一张主图,返回记录 id。
// year 用测试专用值(入参),便于列表按年份隔离断言;created_at 取未来时间戳,
// 保证不会被迁移里「存量行置已发布」的时间戳护栏误刷。
func seedRunway(t *testing.T, db *gorm.DB, status string, year uint16) uint32 {
t.Helper()
now := uint32(1_800_000_000)
rw := model.BrandRunway{TitleEn: "view-test", Year: year, Status: status, CreatedAt: now, UpdatedAt: now}
if err := db.Create(&rw).Error; err != nil {
t.Fatalf("插入走秀失败: %v", err)
}
img := model.BrandRunwayImage{
RunwayID: rw.ID, BrandID: 1, Image: "view-test.jpg", Name: "look",
SortOrder: 1, CreatedAt: now, UpdatedAt: now,
}
if err := db.Create(&img).Error; err != nil {
t.Fatalf("插入走秀图片失败: %v", err)
}
t.Cleanup(func() {
db.Exec("DELETE FROM brand_runway_images WHERE runway_id = ?", rw.ID)
db.Exec("DELETE FROM brand_runways WHERE id = ?", rw.ID)
})
return rw.ID
}
// seedStreetSnap 插入一条指定状态的街拍记录 + 一张图,返回记录 id。
func seedStreetSnap(t *testing.T, db *gorm.DB, status string, year uint16) uint32 {
t.Helper()
now := uint32(1_800_000_000)
snap := model.StreetSnap{Title: "view-test", City: "ViewTestCity", Year: year, Status: status, CreatedAt: now, UpdatedAt: now}
if err := db.Create(&snap).Error; err != nil {
t.Fatalf("插入街拍失败: %v", err)
}
img := model.StreetSnapImage{SnapID: snap.ID, Image: "view-test.jpg", Name: "look", SortOrder: 1, CreatedAt: now, UpdatedAt: now}
if err := db.Create(&img).Error; err != nil {
t.Fatalf("插入街拍图片失败: %v", err)
}
t.Cleanup(func() {
db.Exec("DELETE FROM street_snap_images WHERE snap_id = ?", snap.ID)
db.Exec("DELETE FROM street_snaps WHERE id = ?", snap.ID)
})
return snap.ID
}
// TestPublicViewsHideUnpublished 走秀:pending / rejected 的记录与图片都不得出现在公开视图,published 才可见。
func TestPublicViewsHideUnpublished(t *testing.T) {
db := testDB(t)
applyMigration(t, db, "2026-09-22-01-single-table-publish.sql")
ctx := context.Background()
id := seedRunway(t, db, model.StatusPending, 1999)
count := func() (records, images int64) {
db.WithContext(ctx).Table("public_brand_runways").Where("id = ?", id).Count(&records)
db.WithContext(ctx).Table("public_brand_runway_images").Where("runway_id = ?", id).Count(&images)
return
}
if r, i := count(); r != 0 || i != 0 {
t.Fatalf("pending 不应出现在公开视图,实际 records=%d images=%d", r, i)
}
if err := db.Model(&model.BrandRunway{}).Where("id = ?", id).Update("status", model.StatusPublished).Error; err != nil {
t.Fatalf("置为已发布失败: %v", err)
}
if r, i := count(); r != 1 || i != 1 {
t.Fatalf("published 应出现在公开视图,实际 records=%d images=%d", r, i)
}
if err := db.Model(&model.BrandRunway{}).Where("id = ?", id).Update("status", model.StatusRejected).Error; err != nil {
t.Fatalf("置为已驳回失败: %v", err)
}
if r, i := count(); r != 0 || i != 0 {
t.Fatalf("rejected 不应出现在公开视图,实际 records=%d images=%d", r, i)
}
}
// TestPublicViewsHideUnpublishedStreet 街拍:同上,覆盖另外两个视图。
func TestPublicViewsHideUnpublishedStreet(t *testing.T) {
db := testDB(t)
applyMigration(t, db, "2026-09-22-01-single-table-publish.sql")
ctx := context.Background()
id := seedStreetSnap(t, db, model.StatusPending, 1998)
count := func() (records, images int64) {
db.WithContext(ctx).Table("public_street_snaps").Where("id = ?", id).Count(&records)
db.WithContext(ctx).Table("public_street_snap_images").Where("snap_id = ?", id).Count(&images)
return
}
if r, i := count(); r != 0 || i != 0 {
t.Fatalf("pending 不应出现在公开视图,实际 records=%d images=%d", r, i)
}
if err := db.Model(&model.StreetSnap{}).Where("id = ?", id).Update("status", model.StatusPublished).Error; err != nil {
t.Fatalf("置为已发布失败: %v", err)
}
if r, i := count(); r != 1 || i != 1 {
t.Fatalf("published 应出现在公开视图,实际 records=%d images=%d", r, i)
}
}