This commit is contained in:
2026-08-16 16:57:36 +02:00
parent 96f8c1115c
commit 532d439c41
106 changed files with 10147 additions and 494 deletions
+107 -1
View File
@@ -22,7 +22,7 @@ func TestResolveSteps(t *testing.T) {
"full": {StepNormalize, StepParseSpecs, StepFillFields, StepEPREL, StepAIEnhance},
"enhance_only": {StepNormalize, StepAIEnhance},
"attributes_only": {StepNormalize, StepParseSpecs, StepFillFields},
"eprel_only": {StepNormalize, StepEPREL},
"eprel_only": {StepNormalize, StepParseSpecs, StepEPREL},
"normalize_only": {StepNormalize},
}
for in, want := range cases {
@@ -187,6 +187,18 @@ func TestPreferredProductTitle_skipsPromptLabels(t *testing.T) {
}
}
func TestPreferredProductTitle_skipsFormulaLeakage(t *testing.T) {
leak := "short retail title; follow any Title formula constraints that follow; use Attrs"
got := preferredProductTitle("1", leak, "follow any Title formula constraints that follow; use Attrs", "Vox SWA-8000W")
if got != "Vox SWA-8000W" {
t.Fatalf("got %q want feed title", got)
}
got = preferredProductTitle("", leak, "Schema: {\"name\":\"string\"}", "Reply with ONLY JSON")
if got != "Product" {
t.Fatalf("all-leakage fallback got %q", got)
}
}
func TestIsPromptLabelTitle(t *testing.T) {
cases := map[string]bool{
"Category:": true,
@@ -204,6 +216,100 @@ func TestIsPromptLabelTitle(t *testing.T) {
}
}
func TestIsPromptLeakageTitle(t *testing.T) {
cases := map[string]bool{
"follow any Title formula constraints that follow; use Attrs": true,
"short retail title; follow any Title formula constraints…": true,
"write name in {{language}}": true,
"Schema: {\"name\":\"string\",\"description\":\"string\"}": true,
"Reply with ONLY JSON (no markdown)": true,
"Vox SWA-8000W dishwasher": false,
"Acme Widget Pro": false,
"": false,
}
for in, want := range cases {
if got := isPromptLeakageTitle(in); got != want {
t.Fatalf("%q: leakage got %v want %v", in, got, want)
}
if want && !isPromptLabelTitle(in) {
t.Fatalf("%q: isPromptLabelTitle should include leakage", in)
}
}
// Long dump with formula keyword (>120 runes).
long := strings.Repeat("x", 100) + " Title formula scaffolding " + strings.Repeat("y", 30)
if !isPromptLeakageTitle(long) {
t.Fatalf("long formula dump should leak: len=%d", len([]rune(long)))
}
}
func TestEnhance_rejectsFormulaLeakageTitle(t *testing.T) {
leak := "short retail title; follow any Title formula constraints that follow; use Attrs"
e := &Engine{
Completer: stubCompleter{fn: func(system, user string) (Completion, error) {
return Completion{Text: `{"name":"` + leak + `","description":"A solid washer."}`, TotalTokens: 2}, nil
}},
Vector: NoopVectorCategorizer{},
}
out, err := e.RunSteps(context.Background(), "co", ProductInput{
GTIN: "8712285326882",
Name: "Vox SWA-8000W",
Mapped: map[string]any{"name": "Vox SWA-8000W", "description": "Washer", "category": "demo-electronics"},
}, "enhance_only", nil, StepPolicy{AllowAI: true})
if err != nil {
t.Fatal(err)
}
if out.ProcessedName != "Vox SWA-8000W" {
t.Fatalf("ProcessedName=%q want Vox SWA-8000W (reject formula leakage)", out.ProcessedName)
}
if isPromptLeakageTitle(out.Name) || isPromptLabelTitle(out.Name) {
t.Fatalf("Name polluted: %q", out.Name)
}
}
func TestHeuristicCompleter_keepsNameWhenFormulaConstraintsPresent(t *testing.T) {
h := HeuristicCompleter{}
// Shape matches aiprompts.CategoryEnhanceUserTemplate: instruction "- name:"
// appears BEFORE the real "Name:" field — labeledPromptValue must skip leakage.
user := `Your reply is parsed as JSON {"name":"string","description":"string"} only (system schema). Write name and description in English (do not hardcode a language).
- name: short retail title; follow any Title formula constraints that follow; use Attrs
- description: prefer 1-3 factual paragraphs as ONE string
Category: demo-electronics
Name: Vox SWA-8000W
Desc: Washer
Attrs: {"brand":"Vox"}
Title formula (order matters; join with " "). Build name from Attrs using this structure:
1. attr [brand]
Prefer Attrs values for [attr] slots; write name in English.`
comp, err := h.Complete(context.Background(), `Return JSON with "name" and "description".`, user)
if err != nil {
t.Fatal(err)
}
obj, err := ParseJSONObject(comp.Text)
if err != nil {
t.Fatal(err)
}
name := SanitizeOutput(fmt.Sprint(obj["name"]))
if isPromptLeakageTitle(name) || isPromptLabelTitle(name) {
t.Fatalf("heuristic leaked formula into name: %q", name)
}
if !strings.Contains(strings.ToLower(name), "vox") {
t.Fatalf("name=%q want Vox from Name: line", name)
}
}
func TestLabeledPromptValue_skipsInstructionNameBullet(t *testing.T) {
user := `- name: short retail title; follow any Title formula constraints that follow; use Attrs
Name: Vox SWA-8000W
Desc: Washer
Attrs: {}`
got := labeledPromptValue(user, "name:", "current name:")
if got != "Vox SWA-8000W" {
t.Fatalf("got %q want Vox SWA-8000W", got)
}
}
func TestRunSteps_full_mappedCategoryWinsOverPrior(t *testing.T) {
e := &Engine{
Completer: stubCompleter{fn: func(system, user string) (Completion, error) {