Files
backend_v2/internal/repository/image_id_stability_integration_test.go
toom1996 d15d2a4701 update
2026-09-28 10:52:50 +08:00

141 lines
5.0 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
// 集成测试:单表发布模型的头号收益 —— 审核通过不再重建图片,**图片行 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)
}
}