Files
cmautobuy/admin/service/spec_backfill_test.go
T

126 lines
4.9 KiB
Go

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