319 lines
11 KiB
Go
319 lines
11 KiB
Go
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, "<hN>") {
|
|
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,
|
|
})
|
|
if !strings.Contains(user, "Description formula") {
|
|
t.Fatalf("missing user formula: %s", user)
|
|
}
|
|
if !strings.Contains(sys, "Description formula") || !strings.Contains(sys, "1-2 sentences") {
|
|
t.Fatalf("system should keep company text and append formula override: %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, "Title formula") || !strings.Contains(user, "attr [brand]") {
|
|
t.Fatalf("missing user title formula: %s", user)
|
|
}
|
|
if !strings.Contains(sys, `When the user message includes a "Title formula"`) || !strings.Contains(sys, "short retail title") {
|
|
t.Fatalf("system should keep company text and append title override: %s", sys)
|
|
}
|
|
if AppendTitleFormulaSystemOverride("plain", nil) != "plain" {
|
|
t.Fatal("empty title formula must be no-op")
|
|
}
|
|
dup := AppendTitleFormulaSystemOverride(sys, 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("<h1>GIGABYTE GS27QC</h1><p>Gaming monitor.</p><ul><li>61 cm</li></ul>", 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, "Name: {{name}}") {
|
|
t.Fatalf("lost base template: %s", user)
|
|
}
|
|
if !strings.Contains(user, "Title formula") || !strings.Contains(user, "attr [brand]") {
|
|
t.Fatalf("missing title formula: %s", user)
|
|
}
|
|
if !strings.Contains(user, "Description formula") || !strings.Contains(user, "- p: Factual summary") {
|
|
t.Fatalf("missing description formula: %s", user)
|
|
}
|
|
if !strings.Contains(user, "SEO meta formula") || !strings.Contains(user, "meta_title (50-60 chars)") {
|
|
t.Fatalf("missing SEO meta formula: %s", user)
|
|
}
|
|
if !strings.Contains(sys, "SEO meta formula") {
|
|
t.Fatalf("system missing meta override: %s", sys)
|
|
}
|
|
// Render substitutes {{language}} in formula blocks.
|
|
_, rendered := RenderProductEnhancePrompts(
|
|
"Write in {{language}}.", 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")
|
|
}
|
|
}
|