This commit is contained in:
2026-08-16 21:35:48 +02:00
parent 106f602232
commit 389608ea56
28 changed files with 2491 additions and 115 deletions
@@ -133,6 +133,9 @@ func TestFilterCategoryIfInvalid(t *testing.T) {
if out.Category != "" {
t.Fatalf("invalid unique_id kept: %q", out.Category)
}
if src, _ := out.FieldSources["category"].(string); src != "cleared_invalid" {
t.Fatalf("field_sources.category=%v want cleared_invalid", out.FieldSources["category"])
}
out.Category = "50"
filterCategoryIfInvalid(&out, nil)
if out.Category != "50" {
@@ -179,6 +182,76 @@ func TestCoerceCategoryToCompanyUniqueID_nameToUID(t *testing.T) {
if out.Category != "" {
t.Fatalf("unknown should clear: %q", out.Category)
}
if src, _ := out.FieldSources["category"].(string); src != "cleared_invalid" {
t.Fatalf("field_sources.category=%v want cleared_invalid", out.FieldSources["category"])
}
}
// Product titles (and nested category.name copies of the title) must never land
// in Category — only taxonomy unique_ids (+ display names from categories).
func TestRunSteps_productTitleNeverBecomesCategory(t *testing.T) {
t.Parallel()
const productTitle = "Bosch Serie 6 WAU28PH0BY 9kg White Washing Machine"
names := map[string]string{"50": "Pralni stroji", "28": "TV mounts"}
e := &Engine{Vector: NoopVectorCategorizer{}}
out, err := e.RunSteps(context.Background(), "co", ProductInput{
GTIN: "4242005191234",
Name: productTitle,
Mapped: map[string]any{
"name": productTitle,
"category": map[string]any{
"name": productTitle, // feed pollution: product title as category.name
},
},
CategoryNamesByUID: names,
}, "normalize_only", nil, StepPolicy{})
if err != nil {
t.Fatal(err)
}
if out.Category == productTitle || strings.EqualFold(out.Category, productTitle) {
t.Fatalf("Category must not be product title: %q", out.Category)
}
if out.CategoryName == productTitle || strings.EqualFold(out.CategoryName, productTitle) {
t.Fatalf("CategoryName must not be product title: %q", out.CategoryName)
}
if cat := categoryDisplayLabel(out); cat == productTitle || strings.EqualFold(cat, productTitle) {
t.Fatalf("categoryDisplayLabel leaked product title: %q", cat)
}
if out.Category != "" {
t.Fatalf("Category=%q want empty (unresolved product-title token scrubbed)", out.Category)
}
// Plain string category == product title must also be scrubbed.
out2, err := e.RunSteps(context.Background(), "co", ProductInput{
GTIN: "4242005191235",
Name: productTitle,
Mapped: map[string]any{
"name": productTitle,
"category": productTitle,
},
CategoryNamesByUID: names,
}, "normalize_only", nil, StepPolicy{})
if err != nil {
t.Fatal(err)
}
if out2.Category != "" {
t.Fatalf("plain title-as-category kept: %q", out2.Category)
}
// Real taxonomy display name still coerces via processOne helpers.
out3 := StepResult{Category: "Pralni stroji", Name: productTitle, ProcessedName: productTitle}
valid := map[string]struct{}{"50": {}, "28": {}}
coerceCategoryToCompanyUniqueID(&out3, names, valid)
filterCategoryIfInvalid(&out3, valid)
scrubCategoryPollution(&out3, names, valid)
syncCategoryName(&out3, names)
if out3.Category != "50" {
t.Fatalf("taxonomy name coerce: Category=%q want 50", out3.Category)
}
if out3.CategoryName != "Pralni stroji" {
t.Fatalf("CategoryName=%q want Pralni stroji", out3.CategoryName)
}
}
func TestResolveCompanyCategoryUniqueID_usedByV1Projection(t *testing.T) {