Files
backend_v2/internal/repository/publish_status_integration_test.go

141 lines
5.5 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.

//go:build integration
// 集成测试:公开视图必须屏蔽未发布内容 —— 这是「单表发布模型」的核心保证。
//
// 运行(库需先就绪,见 internal/repository/dedup_integration_test.go 顶部说明):
//
// go test -tags integration ./internal/repository/ -run TestPublicViews -v
package repository
import (
"context"
"fmt"
"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)
// 实体键必须非空且按 year 派生:2026-09-22-03 的唯一索引落库后,brand_id=0 且
// season_code / collection_type 均为空会让同一次运行里的多条种子行(乃至上次运行
// 未清理的遗留行)被当成「同一实体」而互相冲突,失败点还会伪装成迁移体检。
rw := model.BrandRunway{
TitleEn: "view-test", Year: year, Status: status, CreatedAt: now, UpdatedAt: now,
SeasonCode: fmt.Sprintf("T%04d", year), CollectionType: "t",
}
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)
}
}