update
This commit is contained in:
136
internal/service/manual_article_service_test.go
Normal file
136
internal/service/manual_article_service_test.go
Normal file
@ -0,0 +1,136 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user