package processing import ( "strings" "testing" ) func TestFormatTitleFormulaConstraint(t *testing.T) { t.Parallel() got := FormatTitleFormulaConstraint(map[string]any{ "separator": " ", "elements": []any{ map[string]any{"type": "variable", "value": "brand"}, map[string]any{"type": "text", "value": "TV"}, map[string]any{"type": "variable", "value": "product_model"}, }, }) if !strings.Contains(got, `join with " "`) { t.Fatalf("missing separator: %s", got) } if !strings.Contains(got, "1. attr [brand]") || !strings.Contains(got, `2. text "TV"`) || !strings.Contains(got, "3. attr [product_model]") { t.Fatalf("missing ordered elements: %s", got) } if !strings.Contains(got, "{{language}}") { t.Fatalf("missing language placeholder: %s", got) } if FormatTitleFormulaConstraint(nil) != "" || FormatTitleFormulaConstraint(map[string]any{}) != "" { t.Fatal("empty templates should yield empty constraint") } } func TestFormatDescriptionFormulaConstraint(t *testing.T) { t.Parallel() got := FormatDescriptionFormulaConstraint(map[string]any{ "sections": []any{ map[string]any{"type": "h1", "instructions": "Main product heading"}, map[string]any{"type": "p", "instructions": "Two factual sentences"}, map[string]any{"type": "ul", "instructions": "3 bullets of specs"}, }, }) if !strings.Contains(got, "REQUIRED") || !strings.Contains(got, "") { t.Fatalf("missing required HTML guidance: %s", got) } if !strings.Contains(got, "- h1: Main product heading") { t.Fatalf("missing h1: %s", got) } if !strings.Contains(got, "- p: Two factual sentences") || !strings.Contains(got, "- ul: 3 bullets of specs") { t.Fatalf("missing sections: %s", got) } if !strings.Contains(got, "{{language}}") { t.Fatalf("missing language placeholder: %s", got) } } func TestAppendDescriptionFormulaSystemOverride(t *testing.T) { t.Parallel() desc := map[string]any{ "sections": []any{ map[string]any{"type": "h1", "instructions": "Heading"}, map[string]any{"type": "p", "instructions": "Body"}, }, } sys, user := resolveProductPromptTemplates(ProductInput{ EnhanceSystemTemplate: "Retail product copywriter.\n- description: 1-2 factual sentences", EnhanceUserTemplate: "Name: {{name}}", DescriptionTemplate: desc, }) // Structured description_template uses A1 GPT-predloga path (legacy generator.php). if !strings.Contains(user, "GPT predloga:") || !strings.Contains(user, "

{Heading}

") { t.Fatalf("missing A1 description predloga: %s", user) } if !strings.Contains(sys, "Si tekstopisec") { t.Fatalf("system should be A1 copywriter when formula set: %s", sys) } if AppendDescriptionFormulaSystemOverride("plain", nil) != "plain" { t.Fatal("empty formula must be no-op") } } func TestAppendTitleFormulaSystemOverride(t *testing.T) { t.Parallel() title := map[string]any{ "separator": " ", "elements": []any{ map[string]any{"type": "variable", "value": "brand"}, map[string]any{"type": "text", "value": "Pro"}, }, } sys, user := resolveProductPromptTemplates(ProductInput{ EnhanceSystemTemplate: "Retail product copywriter.\n- name: short retail title", EnhanceUserTemplate: "Name: {{name}}", TitleTemplate: title, }) if !strings.Contains(user, "GPT predloga:") || !strings.Contains(user, "{") { t.Fatalf("missing A1 title predloga: %s", user) } if !strings.Contains(user, "brand") { t.Fatalf("missing brand in title formula: %s", user) } if !strings.Contains(sys, "Si tekstopisec") { t.Fatalf("system should be A1 copywriter when title formula set: %s", sys) } if AppendTitleFormulaSystemOverride("plain", nil) != "plain" { t.Fatal("empty title formula must be no-op") } // Direct override helper stays idempotent for soft path. soft := "Retail product copywriter.\n- name: short retail title" with := AppendTitleFormulaSystemOverride(soft, title) dup := AppendTitleFormulaSystemOverride(with, title) if strings.Count(dup, `When the user message includes a "Title formula"`) != 1 { t.Fatalf("title override must be idempotent: %s", dup) } } func TestAppendCategoryEnhanceSystemOverlay(t *testing.T) { t.Parallel() if AppendCategoryEnhanceSystemOverlay("plain", "") != "plain" { t.Fatal("empty category prompt must be no-op") } got := AppendCategoryEnhanceSystemOverlay("Retail system.", "Focus on panel tech.") if !strings.Contains(got, "Retail system.") || !strings.Contains(got, `apply it to "name", "description", and "attrs"`) { t.Fatalf("expected category system overlay: %s", got) } if AppendCategoryEnhanceSystemOverlay(got, "again") != got { t.Fatal("category overlay must be idempotent") } legacy := AppendCategoryEnhanceSystemOverlay("Retail.", "x") // Force-inject legacy marker then upgrade. legacy = "Retail.\nWhen the user message includes category guidance, apply it to BOTH \"name\" and \"description\" (tone)." upgraded := AppendCategoryEnhanceSystemOverlay(legacy, "Focus") if !strings.Contains(upgraded, `apply it to "name", "description", and "attrs"`) { t.Fatalf("legacy overlay should upgrade to attrs: %s", upgraded) } } func TestFormatAttributeAllowlistConstraint(t *testing.T) { t.Parallel() allowed := map[string]struct{}{ "diagonala_zaslona": {}, "vrsta_panela": {}, } title := map[string]any{ "separator": " ", "elements": []any{ map[string]any{"type": "variable", "value": "brand"}, map[string]any{"type": "variable", "value": "product_model"}, }, } got := FormatAttributeAllowlistConstraint(allowed, title) if !strings.Contains(got, "Allowed attribute keys") { t.Fatalf("missing header: %s", got) } if !strings.Contains(got, "diagonala_zaslona") || !strings.Contains(got, "vrsta_panela") { t.Fatalf("missing category keys: %s", got) } if !strings.Contains(got, "brand") || !strings.Contains(got, "product_model") { t.Fatalf("missing formula keys: %s", got) } if FormatAttributeAllowlistConstraint(nil, nil) != "" { t.Fatal("empty allowlist+formula must be no-op") } appended := AppendAttributeConstraints("Attrs: {{attrs}}", allowed, title) if !strings.Contains(appended, "Allowed attribute keys") || !strings.HasPrefix(appended, "Attrs:") { t.Fatalf("append failed: %s", appended) } if AppendAttributeConstraints(appended, allowed, title) != appended { t.Fatal("attribute constraints must be idempotent") } } func TestResolveProductPromptTemplates_includesAttributeAllowlist(t *testing.T) { t.Parallel() _, user := resolveProductPromptTemplates(ProductInput{ EnhanceUserTemplate: "Name: {{name}}", CategoryUniqueID: "50", CategoryAttrKeys: map[string]map[string]struct{}{ "50": {"diagonala_zaslona": {}, "vrsta_panela": {}}, }, TitleTemplate: map[string]any{ "separator": " ", "elements": []any{ map[string]any{"type": "variable", "value": "brand"}, }, }, CategoryEnhancePrompt: "Focus on panel tech for monitors.", }) if !strings.Contains(user, "Allowed attribute keys") { t.Fatalf("missing attribute allowlist in user tpl: %s", user) } if !strings.Contains(user, "diagonala_zaslona") { t.Fatalf("missing category attr key: %s", user) } if !strings.Contains(user, "Focus on panel tech") { t.Fatalf("missing category overlay: %s", user) } } func TestDescriptionSatisfiesFormula(t *testing.T) { t.Parallel() tpl := map[string]any{ "sections": []any{ map[string]any{"type": "h1", "instructions": "Heading"}, map[string]any{"type": "p", "instructions": "Body"}, map[string]any{"type": "ul", "instructions": "Specs"}, }, } if descriptionSatisfiesFormula("", tpl) { t.Fatal("empty must fail") } if descriptionSatisfiesFormula("GIGABYTE GS27QC is a gaming monitor with a 61 cm curved panel for immersive play.", tpl) { t.Fatal("plain prose must fail multi-section formula") } if !descriptionSatisfiesFormula("

GIGABYTE GS27QC

Gaming monitor.

", tpl) { t.Fatal("full HTML skeleton must pass") } if !descriptionSatisfiesFormula("any copy", nil) { t.Fatal("nil formula always satisfied") } if descriptionNeedsEnhanceRepair( "GIGABYTE GS27QC is a gaming monitor with a 61 cm curved panel for immersive play.", tpl, "GIGABYTE GS27QC", ) != true { t.Fatal("non-weak prose that ignores formula must need repair") } } func TestAppendFormulaConstraints_injectedIntoResolve(t *testing.T) { t.Parallel() title := map[string]any{ "separator": "-", "elements": []any{ map[string]any{"type": "variable", "value": "brand"}, map[string]any{"type": "variable", "value": "product_model"}, }, } desc := map[string]any{ "sections": []any{ map[string]any{"type": "p", "instructions": "Factual summary"}, }, "metaTitle": "Include product name and main benefit (50-60 chars)", "metaDescription": "Write a short Meta Description spotlighting key specs (120-155 chars)", } sys, user := resolveProductPromptTemplates(ProductInput{ EnhanceUserTemplate: "Name: {{name}}\nAttrs: {{attrs}}", TitleTemplate: title, DescriptionTemplate: desc, }) if !strings.Contains(user, "GPT predloga:") { t.Fatalf("expected A1 GPT predloga: %s", user) } if !strings.Contains(user, "{") || !strings.Contains(user, "brand") { t.Fatalf("missing title formula in predloga: %s", user) } if !strings.Contains(user, "

{Factual summary}

") { t.Fatalf("missing description formula HTML: %s", user) } if !strings.Contains(user, "{") || !strings.Contains(user, "120-155") { t.Fatalf("missing SEO meta in predloga: %s", user) } if !strings.Contains(sys, "Si tekstopisec") { t.Fatalf("system missing A1 copywriter: %s", sys) } // Render substitutes {{language}} in formula blocks. _, rendered := RenderProductEnhancePrompts( sys, user, "50", "Old Name", "", "", "", "sl", map[string]any{"brand": "Vox", "product_model": "EHT6020"}, ) if !strings.Contains(rendered, "Slovenian") { t.Fatalf("formula language not rendered: %s", rendered) } if strings.Contains(rendered, "{{language}}") { t.Fatalf("unrendered language placeholder left: %s", rendered) } } func TestFormatMetaFormulaConstraint(t *testing.T) { t.Parallel() got := FormatMetaFormulaConstraint(map[string]any{ "metaTitle": "Create a concise Meta Title with product name", "metaDescription": "Write a short Meta Description for search results", "sections": []any{ map[string]any{"type": "p", "instructions": "Body"}, }, }) if !strings.Contains(got, "SEO meta formula") { t.Fatalf("missing header: %s", got) } if !strings.Contains(got, "meta_title (50-60 chars): Create a concise Meta Title") { t.Fatalf("missing meta_title: %s", got) } if !strings.Contains(got, "meta_description (120-155 chars): Write a short Meta Description") { t.Fatalf("missing meta_description: %s", got) } if FormatMetaFormulaConstraint(map[string]any{"sections": []any{}}) != "" { t.Fatal("sections-only template must not invent SEO meta formula") } } func TestCategoryFormulasFor_explicitWins(t *testing.T) { t.Parallel() in := ProductInput{ TitleTemplate: map[string]any{"elements": []any{map[string]any{"type": "text", "value": "X"}}}, CategoryFormulasByKey: map[string]CategoryFormulas{ "50": {TitleTemplate: map[string]any{"elements": []any{map[string]any{"type": "text", "value": "Y"}}}}, }, } title, _ := categoryFormulasFor(in, "50") m, _ := title.(map[string]any) els, _ := m["elements"].([]any) first, _ := els[0].(map[string]any) if first["value"] != "X" { t.Fatalf("explicit should win, got %#v", title) } in2 := ProductInput{ CategoryFormulasByKey: map[string]CategoryFormulas{ "50": {DescriptionTemplate: map[string]any{"sections": []any{map[string]any{"type": "p", "instructions": "Hi"}}}}, }, } _, desc := categoryFormulasFor(in2, "50") if FormatDescriptionFormulaConstraint(desc) == "" { t.Fatal("expected cache formula") } }