This commit is contained in:
toom1996
2026-10-18 17:42:10 +08:00
parent 1b06bd5bda
commit 1de1ec46a6
58 changed files with 5281 additions and 19191 deletions

View 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)
}
}