137 lines
4.8 KiB
Go
137 lines
4.8 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"testing"
|
||
|
||
"fashionapi/internal/dto"
|
||
"fashionapi/internal/model"
|
||
"fashionapi/internal/repository"
|
||
)
|
||
|
||
// stubManualIngest 只实现建稿用到的两个方法(其余经嵌入接口转发,本测试不会调用)。
|
||
type stubManualIngest struct {
|
||
repository.IngestRepository
|
||
entityHit bool
|
||
created bool
|
||
gotRW *model.BrandRunway
|
||
gotImgs []model.BrandRunwayImage
|
||
}
|
||
|
||
func (s *stubManualIngest) RunwayEntityState(_ context.Context, _ uint32, _, _ string) (uint32, string, bool, bool, error) {
|
||
return 0, "", s.entityHit, false, nil
|
||
}
|
||
|
||
func (s *stubManualIngest) CreateRunwayWithImages(_ context.Context, rw *model.BrandRunway, imgs []model.BrandRunwayImage) (uint32, error) {
|
||
s.created, s.gotRW, s.gotImgs = true, rw, imgs
|
||
return 99, nil
|
||
}
|
||
|
||
// stubManualBrands 提供品牌精确匹配所需的一行。
|
||
type stubManualBrands struct {
|
||
repository.BrandRepository
|
||
name string
|
||
}
|
||
|
||
func (s *stubManualBrands) ListAdmin(_ context.Context, _ dto.BrandQuery, _ []uint32) ([]model.Brand, int64, error) {
|
||
if s.name == "" {
|
||
return []model.Brand{}, 0, nil
|
||
}
|
||
return []model.Brand{{ID: 7, NameEn: s.name}}, 1, nil
|
||
}
|
||
|
||
// stubManualUploader 记录上传的 key,用于断言 sha1 内容寻址与去重。
|
||
type stubManualUploader struct{ keys []string }
|
||
|
||
func (s *stubManualUploader) Upload(_ context.Context, key string, _ []byte, _ string) (string, error) {
|
||
s.keys = append(s.keys, key)
|
||
return "https://cdn/" + key, nil
|
||
}
|
||
func (s *stubManualUploader) Enabled() bool { return true }
|
||
|
||
func manualRunwayInput(images []ManualImage) ManualArticleInput {
|
||
return ManualArticleInput{
|
||
Kind: manualKindRunway, BrandName: "Gucci", TitleEn: "Spring 2027 RTW",
|
||
Year: 2027, Season: "spring", CollectionType: "rtw",
|
||
Images: images,
|
||
}
|
||
}
|
||
|
||
// 一张最小 JPEG 头,http.DetectContentType 会识别为 image/jpeg。
|
||
var fakeJPEG = append([]byte{0xFF, 0xD8, 0xFF, 0xE0}, []byte("manual-test-payload")...)
|
||
|
||
// TestManualCreateRequiresImages 没有图片必须直接拒绝,且既不上传也不写库。
|
||
func TestManualCreateRequiresImages(t *testing.T) {
|
||
up := &stubManualUploader{}
|
||
ing := &stubManualIngest{}
|
||
svc := NewManualArticleService(ing, &stubManualBrands{name: "Gucci"}, up, nil, nil, nil)
|
||
|
||
if _, err := svc.Create(context.Background(), manualRunwayInput(nil)); err == nil {
|
||
t.Fatal("没有图片应报错")
|
||
}
|
||
if len(up.keys) != 0 || ing.created {
|
||
t.Fatalf("校验失败不应上传/写库,实际 uploads=%d created=%v", len(up.keys), ing.created)
|
||
}
|
||
}
|
||
|
||
// TestManualCreateBrandNotFound 品牌名匹配不到时必须报错且不写库。
|
||
func TestManualCreateBrandNotFound(t *testing.T) {
|
||
up := &stubManualUploader{}
|
||
ing := &stubManualIngest{}
|
||
svc := NewManualArticleService(ing, &stubManualBrands{name: "SomeOtherBrand"}, up, nil, nil, nil)
|
||
|
||
in := manualRunwayInput([]ManualImage{{Filename: "a.jpg", Data: fakeJPEG}})
|
||
if _, err := svc.Create(context.Background(), in); err == nil {
|
||
t.Fatal("品牌不存在应报错")
|
||
}
|
||
if ing.created || len(up.keys) != 0 {
|
||
t.Fatalf("品牌不存在不应写库/上传,实际 created=%v uploads=%d", ing.created, len(up.keys))
|
||
}
|
||
}
|
||
|
||
// TestManualCreateRunwayUploadsAndInserts 正常提交:sha1 内容寻址、同内容去重、
|
||
// 封面取第一张、image_count 与实际行数一致。
|
||
func TestManualCreateRunwayUploadsAndInserts(t *testing.T) {
|
||
up := &stubManualUploader{}
|
||
ing := &stubManualIngest{}
|
||
svc := NewManualArticleService(ing, &stubManualBrands{name: "gucci"}, up, nil, nil, nil)
|
||
|
||
// 两张同内容(应去重成 1 张)+ 一张不同内容 → 共 2 张
|
||
other := append([]byte{0xFF, 0xD8, 0xFF, 0xE0}, []byte("another-payload")...)
|
||
in := manualRunwayInput([]ManualImage{
|
||
{Filename: "a.jpg", Data: fakeJPEG},
|
||
{Filename: "a-copy.jpg", Data: fakeJPEG},
|
||
{Filename: "b.jpg", Data: other},
|
||
})
|
||
id, err := svc.Create(context.Background(), in)
|
||
if err != nil {
|
||
t.Fatalf("正常提交不应报错: %v", err)
|
||
}
|
||
if id != 99 {
|
||
t.Fatalf("应返回仓储给的主键,实际 %d", id)
|
||
}
|
||
if len(up.keys) != 2 {
|
||
t.Fatalf("同内容图片应去重为 2 张上传,实际 %d: %v", len(up.keys), up.keys)
|
||
}
|
||
for _, k := range up.keys {
|
||
if len(k) < len("runway/")+40+len(".jpg") || k[:7] != "runway/" {
|
||
t.Fatalf("key 应为 runway/<sha1>.jpg,实际 %q", k)
|
||
}
|
||
}
|
||
if !ing.created {
|
||
t.Fatal("应写入正式表")
|
||
}
|
||
if len(ing.gotImgs) != 2 || ing.gotRW.ImageCount != 2 {
|
||
t.Fatalf("图片行数与 image_count 都应等于 2,实际 rows=%d count=%d", len(ing.gotImgs), ing.gotRW.ImageCount)
|
||
}
|
||
if ing.gotRW.Cover != up.keys[0] {
|
||
t.Fatalf("封面应取第一张,实际 cover=%q keys=%v", ing.gotRW.Cover, up.keys)
|
||
}
|
||
if ing.gotRW.Status != model.StatusPending {
|
||
t.Fatalf("新建记录必须是 pending(走审核),实际 %s", ing.gotRW.Status)
|
||
}
|
||
if ing.gotRW.BrandID != 7 {
|
||
t.Fatalf("品牌应按英文名匹配到 id=7(不区分大小写),实际 %d", ing.gotRW.BrandID)
|
||
}
|
||
}
|