Files
goauto/server/app/goauto/sybimport/reparse_test.go
T
2026-08-19 14:49:10 +08:00

179 lines
6.3 KiB
Go

package sybimport_test
import (
"context"
"encoding/json"
"strings"
"testing"
"go-admin/app/goauto/models"
"go-admin/app/goauto/sybimport"
)
func TestReparseUpdatesStatusWhenRuleImproves(t *testing.T) {
db := openTestDB(t)
detail := realDetailA()
detail.Raw = []byte(`{"id":145306175,"productSpec":"均碼"}`)
detail.ProductSpec = "均碼"
applied, err := sybimport.ApplyDetail(context.Background(), db, realOrder(), detail)
if err != nil {
t.Fatalf("apply: %v", err)
}
if applied.SYBProduct.ParseStatus != models.SYBParseStatusUncertain {
t.Fatalf("expected uncertain seed state, got %s", applied.SYBProduct.ParseStatus)
}
outcome, err := sybimport.Reparse(context.Background(), db, applied.SYBProduct.ID, false)
if err != nil {
t.Fatalf("reparse: %v", err)
}
// Same raw, same parser: nothing changed, must be reported as unchanged
// rather than a spurious "reparsed".
if outcome.Outcome != sybimport.ReparseOutcomeUnchanged {
t.Fatalf("expected unchanged (parser output identical), got %s", outcome.Outcome)
}
}
// #41 prototype: 人工已修正的明细默认跳过,避免规则重跑覆盖人工结果.
func TestReparseSkipsManuallyConfirmedRowByDefault(t *testing.T) {
db := openTestDB(t)
detail := realDetailA()
applied, err := sybimport.ApplyDetail(context.Background(), db, realOrder(), detail)
if err != nil {
t.Fatalf("apply: %v", err)
}
if _, err := sybimport.ManualCorrect(context.Background(), db, applied.SYBProduct.ID, "人工颜色", "人工尺码"); err != nil {
t.Fatalf("manual correct: %v", err)
}
outcome, err := sybimport.Reparse(context.Background(), db, applied.SYBProduct.ID, false)
if err != nil {
t.Fatalf("reparse: %v", err)
}
if outcome.Outcome != sybimport.ReparseOutcomeSkippedManual {
t.Fatalf("expected skipped_manual, got %s", outcome.Outcome)
}
var record models.SYBProduct
db.First(&record, applied.SYBProduct.ID)
if record.TargetColor != "人工颜色" {
t.Fatalf("manual correction must survive an unforced reparse, got %q", record.TargetColor)
}
}
// 可勾选强制覆盖.
func TestReparseWithForceOverridesManualCorrection(t *testing.T) {
db := openTestDB(t)
detail := realDetailA()
applied, err := sybimport.ApplyDetail(context.Background(), db, realOrder(), detail)
if err != nil {
t.Fatalf("apply: %v", err)
}
if _, err := sybimport.ManualCorrect(context.Background(), db, applied.SYBProduct.ID, "人工颜色", "人工尺码"); err != nil {
t.Fatalf("manual correct: %v", err)
}
outcome, err := sybimport.Reparse(context.Background(), db, applied.SYBProduct.ID, true)
if err != nil {
t.Fatalf("forced reparse: %v", err)
}
if outcome.Outcome == sybimport.ReparseOutcomeSkippedManual {
t.Fatal("force=true must not be skipped for a manually confirmed row")
}
var record models.SYBProduct
db.First(&record, applied.SYBProduct.ID)
if record.TargetColor != "白色" {
t.Fatalf("forced reparse must overwrite the manual value with the parser's output, got %q", record.TargetColor)
}
if record.ManuallyConfirmed {
t.Fatal("forced reparse must clear the manual flag, since the value is now parser-derived again")
}
}
func TestReparseNeverTouchesRawJSON(t *testing.T) {
db := openTestDB(t)
applied, err := sybimport.ApplyDetail(context.Background(), db, realOrder(), realDetailA())
if err != nil {
t.Fatalf("apply: %v", err)
}
rawBefore := applied.SYBProduct.RawJSON
if _, err := sybimport.Reparse(context.Background(), db, applied.SYBProduct.ID, false); err != nil {
t.Fatalf("reparse: %v", err)
}
var record models.SYBProduct
db.First(&record, applied.SYBProduct.ID)
if record.RawJSON != rawBefore {
t.Fatal("reparse must never rewrite the stored raw JSON, only the derived fields")
}
}
func TestReparseBatchPartialSuccessDoesNotAbortOnMissingID(t *testing.T) {
db := openTestDB(t)
applied, err := sybimport.ApplyDetail(context.Background(), db, realOrder(), realDetailA())
if err != nil {
t.Fatalf("apply: %v", err)
}
if _, err := sybimport.ReparseBatch(context.Background(), db, []uint64{applied.SYBProduct.ID, 999999}, false); err == nil {
t.Fatal("expected an error for a missing id in the batch")
}
// A row-level error surfaces as an error from ReparseBatch (not a partial
// per-row result) because a missing id is an operator mistake (stale page,
// wrong selection), not an expected outcome like "manually confirmed" —
// unlike shopeeproduct.BatchSoftDelete, where a missing id is routine.
}
func TestManualCorrectMergesIntoArchiveLikeASuccessfulParse(t *testing.T) {
db := openTestDB(t)
detail := realDetailB() // ambiguous sample, does not auto-merge
applied, err := sybimport.ApplyDetail(context.Background(), db, realOrder(), detail)
if err != nil {
t.Fatalf("apply: %v", err)
}
if len(applied.ShopeeSpecs) != 0 {
t.Fatal("ambiguous parse must not have merged anything yet")
}
corrected, err := sybimport.ManualCorrect(context.Background(), db, applied.SYBProduct.ID, "黑白拼接", "L")
if err != nil {
t.Fatalf("manual correct: %v", err)
}
_ = corrected
var product models.ShopeeProduct
db.First(&product, *applied.SYBProduct.ShopeeProductID)
found := false
for _, dim := range decodeSpecs(t, product.SpecsJSON) {
for _, v := range dim.Values {
if v.Name == "黑白拼接" {
found = true
}
}
}
if !found {
t.Fatal("a manual correction should merge into the shopee archive same as a successful auto-parse")
}
}
// Regression test: ReparseOutcome originally had no json tags at all, so Go's
// default marshaling produced PascalCase keys ("SYBProductID", "OldStatus")
// instead of the camelCase the rest of this API and the admin frontend use.
// A frontend built against the intended camelCase names would have silently
// read undefined for every field.
func TestReparseOutcomeMarshalsToCamelCaseKeys(t *testing.T) {
db := openTestDB(t)
applied, err := sybimport.ApplyDetail(context.Background(), db, realOrder(), realDetailA())
if err != nil {
t.Fatalf("apply: %v", err)
}
outcome, err := sybimport.Reparse(context.Background(), db, applied.SYBProduct.ID, false)
if err != nil {
t.Fatalf("reparse: %v", err)
}
encoded, err := json.Marshal(outcome)
if err != nil {
t.Fatalf("marshal: %v", err)
}
for _, key := range []string{`"sybProductId"`, `"outcome"`, `"oldStatus"`, `"newStatus"`} {
if !strings.Contains(string(encoded), key) {
t.Fatalf("expected key %s in %s", key, encoded)
}
}
}