feat(publish): 爬虫入库直写正式表,驳回行可复用重审
This commit is contained in:
131
internal/repository/ingest_single_table_integration_test.go
Normal file
131
internal/repository/ingest_single_table_integration_test.go
Normal file
@ -0,0 +1,131 @@
|
||||
//go:build integration
|
||||
|
||||
// 集成测试:入库直写正式表的三条分支(新建 / 放弃 / 复用驳回行)。
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"fashionapi/internal/model"
|
||||
)
|
||||
|
||||
func newRunwayForIngest(jobID uint32, brandID uint32, seasonCode, collectionType string, cover string) *model.BrandRunway {
|
||||
return &model.BrandRunway{
|
||||
JobID: jobID, BrandID: brandID, TitleEn: "ingest-test",
|
||||
SeasonCode: seasonCode, CollectionType: collectionType,
|
||||
Cover: cover, ImageCount: 1, Status: model.StatusPending,
|
||||
}
|
||||
}
|
||||
|
||||
func newRunwayImages(cover string) []model.BrandRunwayImage {
|
||||
return []model.BrandRunwayImage{{BrandID: 1, Image: cover, Name: "Look 1", SortOrder: 1, LookIndex: 1}}
|
||||
}
|
||||
|
||||
// TestIngestCreatesPendingRecord 新建分支:实体键不存在 → 写入正式表且 status=pending,图片落到正式图片表。
|
||||
func TestIngestCreatesPendingRecord(t *testing.T) {
|
||||
db := testDB(t)
|
||||
applyMigration(t, db, "2026-09-22-01-single-table-publish.sql")
|
||||
repo := NewIngestRepository(db)
|
||||
ctx := context.Background()
|
||||
|
||||
const season = "SS97"
|
||||
_, _, found, err := repo.RunwayEntityState(ctx, 1, season, "rtw")
|
||||
if err != nil {
|
||||
t.Fatalf("RunwayEntityState 出错: %v", err)
|
||||
}
|
||||
if found {
|
||||
t.Fatalf("测试前置:实体键 %s 不应已存在", season)
|
||||
}
|
||||
|
||||
id, err := repo.CreateRunwayWithImages(ctx, newRunwayForIngest(101, 1, season, "rtw", "ingest-ss97.jpg"), newRunwayImages("ingest-ss97.jpg"))
|
||||
if err != nil {
|
||||
t.Fatalf("CreateRunwayWithImages 出错: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
db.Exec("DELETE FROM brand_runway_images WHERE runway_id = ?", id)
|
||||
db.Exec("DELETE FROM brand_runways WHERE id = ?", id)
|
||||
})
|
||||
|
||||
var rw model.BrandRunway
|
||||
if err := db.Where("id = ?", id).First(&rw).Error; err != nil {
|
||||
t.Fatalf("读回记录失败: %v", err)
|
||||
}
|
||||
if rw.Status != model.StatusPending {
|
||||
t.Fatalf("新入库记录应为 pending,实际 %s", rw.Status)
|
||||
}
|
||||
|
||||
gotID, status, found, err := repo.RunwayEntityState(ctx, 1, season, "rtw")
|
||||
if err != nil {
|
||||
t.Fatalf("RunwayEntityState 出错: %v", err)
|
||||
}
|
||||
if !found || gotID != id || status != model.StatusPending {
|
||||
t.Fatalf("实体键应命中刚建的行,实际 found=%v id=%d status=%s", found, gotID, status)
|
||||
}
|
||||
|
||||
publicCount := int64(0)
|
||||
db.Table("public_brand_runways").Where("id = ?", id).Count(&publicCount)
|
||||
if publicCount != 0 {
|
||||
t.Fatalf("pending 记录不应出现在公开视图,实际 %d", publicCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestIngestReusesRejectedRecord 复用分支:实体键命中 rejected → 覆盖内容、软删旧图、写新图、置回 pending。
|
||||
func TestIngestReusesRejectedRecord(t *testing.T) {
|
||||
db := testDB(t)
|
||||
applyMigration(t, db, "2026-09-22-01-single-table-publish.sql")
|
||||
repo := NewIngestRepository(db)
|
||||
ctx := context.Background()
|
||||
|
||||
const season = "FW96"
|
||||
oldID, err := repo.CreateRunwayWithImages(ctx, newRunwayForIngest(90, 1, season, "rtw", "old.jpg"), newRunwayImages("old.jpg"))
|
||||
if err != nil {
|
||||
t.Fatalf("预置记录失败: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
db.Exec("DELETE FROM brand_runway_images WHERE runway_id = ?", oldID)
|
||||
db.Exec("DELETE FROM brand_runways WHERE id = ?", oldID)
|
||||
})
|
||||
if err := db.Model(&model.BrandRunway{}).Where("id = ?", oldID).
|
||||
Updates(map[string]any{"status": model.StatusRejected, "reviewer": "admin", "reject_reason": "图片缺失"}).Error; err != nil {
|
||||
t.Fatalf("置为已驳回失败: %v", err)
|
||||
}
|
||||
|
||||
_, status, found, err := repo.RunwayEntityState(ctx, 1, season, "rtw")
|
||||
if err != nil || !found || status != model.StatusRejected {
|
||||
t.Fatalf("前置:应命中 rejected 行,实际 found=%v status=%s err=%v", found, status, err)
|
||||
}
|
||||
|
||||
newRW := newRunwayForIngest(91, 1, season, "rtw", "new.jpg")
|
||||
newRW.TitleEn = "ingest-test-reused"
|
||||
if err := repo.ReuseRejectedRunway(ctx, oldID, newRW, newRunwayImages("new.jpg")); err != nil {
|
||||
t.Fatalf("ReuseRejectedRunway 出错: %v", err)
|
||||
}
|
||||
|
||||
var rw model.BrandRunway
|
||||
if err := db.Where("id = ?", oldID).First(&rw).Error; err != nil {
|
||||
t.Fatalf("读回记录失败: %v", err)
|
||||
}
|
||||
if rw.Status != model.StatusPending {
|
||||
t.Fatalf("复用后应回到 pending,实际 %s", rw.Status)
|
||||
}
|
||||
if rw.TitleEn != "ingest-test-reused" || rw.Cover != "new.jpg" {
|
||||
t.Fatalf("内容应被覆盖,实际 title=%s cover=%s", rw.TitleEn, rw.Cover)
|
||||
}
|
||||
if rw.Reviewer != "" || rw.RejectReason != "" {
|
||||
t.Fatalf("复用应清空审核痕迹,实际 reviewer=%s reason=%s", rw.Reviewer, rw.RejectReason)
|
||||
}
|
||||
|
||||
var alive []model.BrandRunwayImage
|
||||
if err := db.Where("runway_id = ? AND is_deleted = 0", oldID).Find(&alive).Error; err != nil {
|
||||
t.Fatalf("读图片失败: %v", err)
|
||||
}
|
||||
if len(alive) != 1 || alive[0].Image != "new.jpg" {
|
||||
t.Fatalf("旧图应被软删、只留新图,实际 %d 张", len(alive))
|
||||
}
|
||||
var oldAlive int64
|
||||
db.Model(&model.BrandRunwayImage{}).Where("runway_id = ? AND image = ? AND is_deleted = 0", oldID, "old.jpg").Count(&oldAlive)
|
||||
if oldAlive != 0 {
|
||||
t.Fatalf("旧图应被软删")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user