update
This commit is contained in:
174
internal/repository/admin_mixed_integration_test.go
Normal file
174
internal/repository/admin_mixed_integration_test.go
Normal file
@ -0,0 +1,174 @@
|
||||
//go:build integration
|
||||
|
||||
// 集成测试:文章管理混合列表必须跨 brand_runways / street_snaps 统一排序与过滤。
|
||||
//
|
||||
// 运行(库需先就绪,见 dedup_integration_test.go 顶部说明):
|
||||
//
|
||||
// go test -tags integration ./internal/repository/ -run TestAdminMixed -v
|
||||
package repository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// seedMixedRecords 各插一条走秀(已发布)与街拍(待审),返回它们的内部 id。
|
||||
// 用测试专用 city / season_code 隔离,避免与库里既有行撞唯一实体键。
|
||||
func seedMixedRecords(t *testing.T, db *gorm.DB) (runwayID, streetID uint32) {
|
||||
t.Helper()
|
||||
if err := db.Raw(`
|
||||
INSERT INTO brand_runways (brand_id, title_en, image_count, status, is_deleted, year, season_code, collection_type, created_at)
|
||||
VALUES (999999, 'AdminRepoTestRunway', 3, ?, 0, 2099, 'ZZZ', 'test', 1800000000)
|
||||
RETURNING id`, "published").Scan(&runwayID).Error; err != nil {
|
||||
t.Fatalf("插入走秀种子失败: %v", err)
|
||||
}
|
||||
if err := db.Raw(`
|
||||
INSERT INTO street_snaps (city, title, image_count, status, is_deleted, year, created_at)
|
||||
VALUES ('TestCityZZZ', 'AdminRepoTestStreet', 2, ?, 0, 2099, 1800000000)
|
||||
RETURNING id`, "pending").Scan(&streetID).Error; err != nil {
|
||||
t.Fatalf("插入街拍种子失败: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = db.Exec("DELETE FROM brand_runways WHERE id = ?", runwayID).Error
|
||||
_ = db.Exec("DELETE FROM street_snaps WHERE id = ?", streetID).Error
|
||||
})
|
||||
return runwayID, streetID
|
||||
}
|
||||
|
||||
// TestAdminMixedListFilterAndOrder 验证:
|
||||
// 1. 全部视图同时返回走秀与街拍;
|
||||
// 2. 待审核视图只返回 pending 的街拍,不含已发布的走秀;
|
||||
// 3. 排序为「pending 优先、其次 id 倒序」—— 任一 pending 都排在所有 published 之前。
|
||||
func TestAdminMixedListFilterAndOrder(t *testing.T) {
|
||||
db := testDB(t)
|
||||
runwayID, streetID := seedMixedRecords(t, db)
|
||||
|
||||
repo := NewAdminRepository(db)
|
||||
|
||||
all, total, err := repo.ListMixed(t.Context(), "", "", 1, 100)
|
||||
if err != nil {
|
||||
t.Fatalf("ListMixed 全部失败: %v", err)
|
||||
}
|
||||
if total < 2 {
|
||||
t.Fatalf("全部视图至少应含 2 条种子,实际 total=%d", total)
|
||||
}
|
||||
var sawRunway, sawStreet bool
|
||||
for _, c := range all {
|
||||
if c.Kind == "runway" && c.ID == runwayID {
|
||||
sawRunway = true
|
||||
}
|
||||
if c.Kind == "street" && c.ID == streetID {
|
||||
sawStreet = true
|
||||
}
|
||||
}
|
||||
if !sawRunway || !sawStreet {
|
||||
t.Fatalf("全部视图应同时含走秀与街拍: runway=%v street=%v", sawRunway, sawStreet)
|
||||
}
|
||||
|
||||
// 待审核过滤:街拍(pending) 出现,走秀(published) 不出现。
|
||||
pending, _, err := repo.ListMixed(t.Context(), "pending", "", 1, 100)
|
||||
if err != nil {
|
||||
t.Fatalf("ListMixed pending 失败: %v", err)
|
||||
}
|
||||
var pendingHasStreet, pendingHasRunway bool
|
||||
for _, c := range pending {
|
||||
if c.Kind == "street" && c.ID == streetID {
|
||||
pendingHasStreet = true
|
||||
}
|
||||
if c.Kind == "runway" && c.ID == runwayID {
|
||||
pendingHasRunway = true
|
||||
}
|
||||
}
|
||||
if !pendingHasStreet {
|
||||
t.Fatalf("待审核视图应含待审街拍")
|
||||
}
|
||||
if pendingHasRunway {
|
||||
t.Fatalf("待审核视图不应含已发布走秀")
|
||||
}
|
||||
|
||||
// 排序:任一 pending 必须排在所有 published 之前。
|
||||
var maxPendingIdx, minPublishedIdx = -1, 1 << 30
|
||||
for i, c := range all {
|
||||
if c.Status == "pending" {
|
||||
if i > maxPendingIdx {
|
||||
maxPendingIdx = i
|
||||
}
|
||||
} else if c.Status == "published" {
|
||||
if i < minPublishedIdx {
|
||||
minPublishedIdx = i
|
||||
}
|
||||
}
|
||||
}
|
||||
if maxPendingIdx >= 0 && minPublishedIdx < 1<<30 && maxPendingIdx > minPublishedIdx {
|
||||
t.Fatalf("排序错误:存在 published 排在 pending 之前 (pending@%d, published@%d)", maxPendingIdx, minPublishedIdx)
|
||||
}
|
||||
|
||||
// CountMixed 与列表口径一致。
|
||||
if n, err := repo.CountMixed(t.Context(), "pending", ""); err != nil || n < 1 {
|
||||
t.Fatalf("CountMixed(pending) 应 >=1,实际 n=%d err=%v", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminMixedKindFilter 验证 kind 筛选:只取走秀 / 只取街拍 / 全部。
|
||||
func TestAdminMixedKindFilter(t *testing.T) {
|
||||
db := testDB(t)
|
||||
applyMigration(t, db, "2026-09-22-01-single-table-publish.sql")
|
||||
repo := NewAdminRepository(db)
|
||||
ctx := t.Context()
|
||||
|
||||
// 直接插入各一条,用独特 city / title 隔离唯一实体键。
|
||||
var runwayID, streetID uint32
|
||||
if err := db.Raw(`
|
||||
INSERT INTO brand_runways (brand_id, title_en, image_count, status, is_deleted, year, season_code, collection_type, created_at)
|
||||
VALUES (999999, 'KindFilterRunway', 1, 'published', 0, 2097, 'KFZ', 'test', 1800000000)
|
||||
RETURNING id`,
|
||||
).Scan(&runwayID).Error; err != nil {
|
||||
t.Fatalf("插入走秀种子失败: %v", err)
|
||||
}
|
||||
if err := db.Raw(`
|
||||
INSERT INTO street_snaps (city, title, image_count, status, is_deleted, year, created_at)
|
||||
VALUES ('KindFilterCity', 'KindFilterStreet', 1, 'published', 0, 2097, 1800000000)
|
||||
RETURNING id`,
|
||||
).Scan(&streetID).Error; err != nil {
|
||||
t.Fatalf("插入街拍种子失败: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = db.Exec("DELETE FROM brand_runways WHERE id = ?", runwayID).Error
|
||||
_ = db.Exec("DELETE FROM street_snaps WHERE id = ?", streetID).Error
|
||||
})
|
||||
|
||||
runwayOnly, _, err := repo.ListMixed(ctx, "", "runway", 1, 50)
|
||||
if err != nil {
|
||||
t.Fatalf("kind=runway 失败: %v", err)
|
||||
}
|
||||
for _, c := range runwayOnly {
|
||||
if c.Kind != "runway" {
|
||||
t.Fatalf("kind=runway 不应含 %s", c.Kind)
|
||||
}
|
||||
}
|
||||
if len(runwayOnly) == 0 {
|
||||
t.Fatalf("kind=runway 至少应含 1 条")
|
||||
}
|
||||
|
||||
streetOnly, _, err := repo.ListMixed(ctx, "", "street", 1, 50)
|
||||
if err != nil {
|
||||
t.Fatalf("kind=street 失败: %v", err)
|
||||
}
|
||||
for _, c := range streetOnly {
|
||||
if c.Kind != "street" {
|
||||
t.Fatalf("kind=street 不应含 %s", c.Kind)
|
||||
}
|
||||
}
|
||||
if len(streetOnly) == 0 {
|
||||
t.Fatalf("kind=street 至少应含 1 条")
|
||||
}
|
||||
|
||||
all, _, err := repo.ListMixed(ctx, "", "", 1, 50)
|
||||
if err != nil {
|
||||
t.Fatalf("kind='' 失败: %v", err)
|
||||
}
|
||||
if len(all) != len(runwayOnly)+len(streetOnly) {
|
||||
t.Fatalf("全部应等于两类型之和:all=%d runway=%d street=%d", len(all), len(runwayOnly), len(streetOnly))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user