feat: 回填店铺关联PDD空规格 (#194)
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cmautobuy/admin/repository"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func TestBuildSpecBackfillPlanCreatesUnknownSkeleton(t *testing.T) {
|
||||
now := time.Date(2026, 8, 12, 12, 0, 0, 0, time.UTC)
|
||||
sources := []repository.ShopSpecSource{{
|
||||
ShopeeGoodsID: "S1", PDDGoodsID: "P1", PDDExists: true,
|
||||
ShopeeSpecs: []repository.ComparableShopeeSpec{
|
||||
{Color: "黑", Size: "M", ParseOK: true},
|
||||
{Color: "白", Size: "L", ParseOK: true},
|
||||
},
|
||||
}}
|
||||
plan := BuildSpecBackfillPlan(" shop ", sources, now)
|
||||
if plan.Shop != "shop" || plan.CandidatePDDCount != 1 || plan.Summary[SpecBackfillCandidate] != 1 {
|
||||
t.Fatalf("候选统计错误: %+v", plan)
|
||||
}
|
||||
var skeleton struct {
|
||||
SpecSource string `json:"spec_source"`
|
||||
SKUs []struct {
|
||||
Options map[string]string `json:"options"`
|
||||
PriceCent *int64 `json:"price_cent"`
|
||||
AvailabilityStatus string `json:"availability_status"`
|
||||
Available *bool `json:"available"`
|
||||
} `json:"skus"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(plan.Candidates[0].NewSKUsJSON), &skeleton); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if skeleton.SpecSource != "shopee_backfill" || len(skeleton.SKUs) != 2 {
|
||||
t.Fatalf("规格骨架错误: %+v", skeleton)
|
||||
}
|
||||
for _, sku := range skeleton.SKUs {
|
||||
if sku.PriceCent != nil || sku.AvailabilityStatus != "unknown" || sku.Available != nil {
|
||||
t.Fatalf("价格和库存必须保持未知: %+v", sku)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildSpecBackfillPlanDoesNotOverwriteExistingOrInvalid(t *testing.T) {
|
||||
specs := []repository.ComparableShopeeSpec{{Color: "黑", Size: "M", ParseOK: true}}
|
||||
plan := BuildSpecBackfillPlan("shop", []repository.ShopSpecSource{
|
||||
{ShopeeGoodsID: "S1", PDDGoodsID: "P1", PDDExists: true, ShopeeSpecs: specs,
|
||||
PDDSKUsJSON: `{"skus":[{"options":{"color":"黑","size":"M"}}]}`},
|
||||
{ShopeeGoodsID: "S2", PDDGoodsID: "P2", PDDExists: true, ShopeeSpecs: specs,
|
||||
PDDSKUsJSON: `{坏数据`},
|
||||
}, time.Now())
|
||||
if plan.CandidatePDDCount != 0 || plan.Summary[SpecBackfillPDDExisting] != 1 || plan.Summary[SpecBackfillPDDInvalid] != 1 {
|
||||
t.Fatalf("不应覆盖已有或异常数据: %+v", plan)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildSpecBackfillPlanSkipsAmbiguousSharedPDD(t *testing.T) {
|
||||
plan := BuildSpecBackfillPlan("shop", []repository.ShopSpecSource{
|
||||
{ShopeeGoodsID: "S1", PDDGoodsID: "P1", PDDExists: true,
|
||||
ShopeeSpecs: []repository.ComparableShopeeSpec{{Color: "黑", Size: "M", ParseOK: true}}},
|
||||
{ShopeeGoodsID: "S2", PDDGoodsID: "P1", PDDExists: true,
|
||||
ShopeeSpecs: []repository.ComparableShopeeSpec{{Color: "白", Size: "L", ParseOK: true}}},
|
||||
}, time.Now())
|
||||
if plan.CandidatePDDCount != 0 || plan.Summary[SpecBackfillAmbiguousShared] != 2 {
|
||||
t.Fatalf("多对一歧义必须跳过: %+v", plan)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakePDDSpecSkeletonPreservesExistingTopLevelData(t *testing.T) {
|
||||
set := map[string]struct{}{`["黑","M"]`: {}}
|
||||
raw, status, err := makePDDSpecSkeleton(`{"title":"保留标题","skus":[]}`, "P1", set, time.Now())
|
||||
if err != nil || status != SpecBackfillCandidate {
|
||||
t.Fatalf("生成失败: status=%s err=%v", status, err)
|
||||
}
|
||||
var root map[string]any
|
||||
json.Unmarshal([]byte(raw), &root)
|
||||
if root["title"] != "保留标题" {
|
||||
t.Fatalf("应保留原有顶层数据: %s", raw)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildSpecBackfillPlanRecognizesVerifiedBackfill(t *testing.T) {
|
||||
specs := []repository.ComparableShopeeSpec{{Color: "黑", Size: "M", ParseOK: true}}
|
||||
raw := `{"spec_source":"shopee_backfill","skus":[{"options":{"color":"黑","size":"M"},"price_cent":null,"list_price_cent":null,"availability_status":"unknown"}]}`
|
||||
plan := BuildSpecBackfillPlan("shop", []repository.ShopSpecSource{{
|
||||
ShopeeGoodsID: "S1", PDDGoodsID: "P1", PDDExists: true,
|
||||
ShopeeSpecs: specs, PDDSKUsJSON: raw,
|
||||
}}, time.Now())
|
||||
if plan.BackfilledPDDCount != 1 || plan.Summary[SpecBackfillPDDBackfilled] != 1 || plan.CandidatePDDCount != 0 {
|
||||
t.Fatalf("应识别为已安全回填: %+v", plan)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplySpecBackfillRollsBackWholeBatchWhenOneTargetChanged(t *testing.T) {
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
db.SetMaxOpenConns(1)
|
||||
if _, err := db.Exec(`CREATE TABLE pdd_products(goods_id TEXT PRIMARY KEY, skus_json TEXT, deleted_at TEXT, updated_at TEXT)`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := db.Exec(`INSERT INTO pdd_products(goods_id,skus_json) VALUES('P1',''),('P2','已变化')`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
plan := SpecBackfillPlan{Candidates: []SpecBackfillCandidateItem{
|
||||
{PDDGoodsID: "P1", OriginalSKUsJSON: "", NewSKUsJSON: `{"skus":[1]}`},
|
||||
{PDDGoodsID: "P2", OriginalSKUsJSON: "", NewSKUsJSON: `{"skus":[2]}`},
|
||||
}}
|
||||
if _, err := ApplySpecBackfill(db, plan, time.Now()); err == nil {
|
||||
t.Fatal("第二件商品原值变化时应返回错误")
|
||||
}
|
||||
var raw string
|
||||
if err := db.QueryRow(`SELECT skus_json FROM pdd_products WHERE goods_id='P1'`).Scan(&raw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if raw != "" {
|
||||
t.Fatalf("整批事务应回滚,P1 实际为 %q", raw)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user