package adminstore import ( "context" "errors" "testing" ) func TestBrandLifecycle(t *testing.T) { s := newTestStore(t) ctx := context.Background() a, err := s.CreateBrand(ctx, "tester", "品牌A "+randomHex(4)) if err != nil { t.Fatalf("create A: %v", err) } t.Cleanup(func() { _, _ = s.pool.Exec(ctx, "DELETE FROM brand WHERE id = $1", a.ID) }) b, err := s.CreateBrand(ctx, "tester", "品牌B "+randomHex(4)) if err != nil { t.Fatalf("create B: %v", err) } t.Cleanup(func() { _, _ = s.pool.Exec(ctx, "DELETE FROM brand WHERE id = $1", b.ID) }) // Duplicate (normalized) name must be rejected. if _, err := s.CreateBrand(ctx, "tester", " "+a.Name+" "); !errors.Is(err, ErrDuplicateBrand) { t.Fatalf("expected ErrDuplicateBrand, got %v", err) } // Rename. renamed, err := s.UpdateBrand(ctx, a.ID, "tester", "品牌A改名") if err != nil { t.Fatalf("rename: %v", err) } if renamed.Name != "品牌A改名" { t.Fatalf("rename not applied: %q", renamed.Name) } // Attach a product to brand A so deletion is blocked and merge moves it. var prodID string err = s.pool.QueryRow(ctx, "INSERT INTO product (name, brand_id, status) VALUES ($1,$2,'active') RETURNING id", "测试商品 "+randomHex(4), a.ID).Scan(&prodID) if err != nil { t.Fatalf("insert product: %v", err) } t.Cleanup(func() { _, _ = s.pool.Exec(ctx, "DELETE FROM product WHERE id = $1", prodID) }) // Deleting an in-use brand must fail. if err := s.DeleteBrand(ctx, a.ID, "tester"); !errors.Is(err, ErrBrandInUse) { t.Fatalf("expected ErrBrandInUse, got %v", err) } // Merge A into B: product reassigned, A deleted. merged, err := s.MergeBrands(ctx, a.ID, b.ID, "tester") if err != nil { t.Fatalf("merge: %v", err) } if merged.ID != b.ID || merged.ProductCount < 1 { t.Fatalf("merge result wrong: %+v", merged) } if _, err := s.getBrand(ctx, a.ID); !errors.Is(err, ErrNotFound) { t.Fatalf("source brand should be gone, got %v", err) } // Self-merge is invalid. if _, err := s.MergeBrands(ctx, b.ID, b.ID, "tester"); !errors.Is(err, ErrInvalidMerge) { t.Fatalf("expected ErrInvalidMerge, got %v", err) } // After moving the product away from B, B can be deleted. if _, err := s.pool.Exec(ctx, "DELETE FROM product WHERE id = $1", prodID); err != nil { t.Fatalf("cleanup product: %v", err) } if err := s.DeleteBrand(ctx, b.ID, "tester"); err != nil { t.Fatalf("delete unused brand: %v", err) } }