141 lines
5.0 KiB
Go
141 lines
5.0 KiB
Go
//go:build integration
|
||
|
||
// 集成测试:单表发布模型的头号收益 —— 审核通过不再重建图片,**图片行 id 终身不变**。
|
||
//
|
||
// 旧流程(草稿表 → 晋升)会在通过时把草稿图片重新插入正式图片表,图片行 id 必然改变;
|
||
// 任何按 id 引用的下游(CDN 缓存键、分享链接、埋点)都会随之失效。改造把「通过」降级为
|
||
// 只改 status,本文件用「id 集合一字不差」把这条收益钉死。
|
||
//
|
||
// 为什么现有测试看不到它:
|
||
// - router/backstage_test.go 的桩只断言 op == "approve",根本看不到图片行 id;
|
||
// - 公开视图测试都是直接 Update("status"),绕过了 SetRecordStatus / SetStreetRecordStatus。
|
||
//
|
||
// 反证(本断言为何能区分「重建图片」的假实现):若把 SetRecordStatus 换成任何会重建图片的实现
|
||
// (先删旧图再插入、或从别处拷贝重建),通过前记下的 id 与通过后必然不同(旧 id 消失、出现新 id),
|
||
// 本断言立即变红。断言只比对「同一记录存活图片行的 id 序列」,不依赖计数或其它副作用,
|
||
// 因此不会被与重建无关的改动误伤,也不会因无关改动静默通过。
|
||
//
|
||
// 运行:go test -tags integration ./internal/repository/ -run TestApproveKeepsImageRowIDs -v
|
||
package repository
|
||
|
||
import (
|
||
"context"
|
||
"testing"
|
||
|
||
"fashionapi/internal/model"
|
||
)
|
||
|
||
// sameUint32IDs 逐元素比较两个 id 序列(长度 + 顺序)。
|
||
func sameUint32IDs(a, b []uint32) bool {
|
||
if len(a) != len(b) {
|
||
return false
|
||
}
|
||
for i := range a {
|
||
if a[i] != b[i] {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// TestApproveKeepsImageRowIDs 走秀:pending 记录(3 张图)经 SetRecordStatus(published) 通过后,
|
||
// status 变为 published,且图片行 id 集合与通过前完全一致。
|
||
func TestApproveKeepsImageRowIDs(t *testing.T) {
|
||
db := testDB(t)
|
||
applyMigration(t, db, "2026-09-22-01-single-table-publish.sql")
|
||
repo := NewReviewRepository(db)
|
||
ctx := context.Background()
|
||
|
||
id := seedRunway(t, db, model.StatusPending, 1992) // seedRunway 已插 1 张主图
|
||
now := uint32(1_800_000_000)
|
||
for i, name := range []string{"second", "third"} {
|
||
img := model.BrandRunwayImage{
|
||
RunwayID: id, BrandID: 1, Image: name + ".jpg", Name: name,
|
||
SortOrder: uint32(i + 2), CreatedAt: now, UpdatedAt: now,
|
||
}
|
||
if err := db.Create(&img).Error; err != nil {
|
||
t.Fatalf("插入走秀图片失败: %v", err)
|
||
}
|
||
}
|
||
|
||
imageIDs := func() []uint32 {
|
||
var ids []uint32
|
||
if err := db.Model(&model.BrandRunwayImage{}).
|
||
Where("runway_id = ?", id).Order("id ASC").Pluck("id", &ids).Error; err != nil {
|
||
t.Fatalf("读图片行 id 失败: %v", err)
|
||
}
|
||
return ids
|
||
}
|
||
|
||
before := imageIDs()
|
||
if len(before) != 3 {
|
||
t.Fatalf("前置应恰有 3 行图片,实际 %d", len(before))
|
||
}
|
||
|
||
if err := repo.SetRecordStatus(ctx, id, model.StatusPublished, "admin"); err != nil {
|
||
t.Fatalf("SetRecordStatus(published) 出错: %v", err)
|
||
}
|
||
|
||
var rw model.BrandRunway
|
||
if err := db.Where("id = ?", id).First(&rw).Error; err != nil {
|
||
t.Fatalf("读回记录失败: %v", err)
|
||
}
|
||
if rw.Status != model.StatusPublished {
|
||
t.Fatalf("status 应为 published,实际 %s", rw.Status)
|
||
}
|
||
|
||
if after := imageIDs(); !sameUint32IDs(before, after) {
|
||
t.Fatalf("通过审核不得重建图片:图片行 id 应完全一致,通过前 %v,通过后 %v", before, after)
|
||
}
|
||
}
|
||
|
||
// TestApproveKeepsSnapImageRowIDs 街拍:与走秀同形,覆盖另一组记录/图片表与方法。
|
||
func TestApproveKeepsSnapImageRowIDs(t *testing.T) {
|
||
db := testDB(t)
|
||
applyMigration(t, db, "2026-09-22-01-single-table-publish.sql")
|
||
repo := NewReviewRepository(db)
|
||
ctx := context.Background()
|
||
|
||
id := seedStreetSnap(t, db, model.StatusPending, 1986) // 已插 1 张图
|
||
now := uint32(1_800_000_000)
|
||
for i, name := range []string{"second", "third"} {
|
||
img := model.StreetSnapImage{
|
||
SnapID: id, Image: name + ".jpg", Name: name,
|
||
SortOrder: uint32(i + 2), CreatedAt: now, UpdatedAt: now,
|
||
}
|
||
if err := db.Create(&img).Error; err != nil {
|
||
t.Fatalf("插入街拍图片失败: %v", err)
|
||
}
|
||
}
|
||
|
||
imageIDs := func() []uint32 {
|
||
var ids []uint32
|
||
if err := db.Model(&model.StreetSnapImage{}).
|
||
Where("snap_id = ?", id).Order("id ASC").Pluck("id", &ids).Error; err != nil {
|
||
t.Fatalf("读图片行 id 失败: %v", err)
|
||
}
|
||
return ids
|
||
}
|
||
|
||
before := imageIDs()
|
||
if len(before) != 3 {
|
||
t.Fatalf("前置应恰有 3 行图片,实际 %d", len(before))
|
||
}
|
||
|
||
if err := repo.SetStreetRecordStatus(ctx, id, model.StatusPublished, "admin"); err != nil {
|
||
t.Fatalf("SetStreetRecordStatus(published) 出错: %v", err)
|
||
}
|
||
|
||
var snap model.StreetSnap
|
||
if err := db.Where("id = ?", id).First(&snap).Error; err != nil {
|
||
t.Fatalf("读回记录失败: %v", err)
|
||
}
|
||
if snap.Status != model.StatusPublished {
|
||
t.Fatalf("status 应为 published,实际 %s", snap.Status)
|
||
}
|
||
|
||
if after := imageIDs(); !sameUint32IDs(before, after) {
|
||
t.Fatalf("通过审核不得重建图片:图片行 id 应完全一致,通过前 %v,通过后 %v", before, after)
|
||
}
|
||
}
|