package processing import ( "context" "encoding/json" "strings" "testing" ) func TestAttrsFromEnhanceObj_andMerge(t *testing.T) { t.Parallel() obj := map[string]any{ "name": "Monitor", "attrs": map[string]any{ "diagonala_zaslona": "27\"", "zavora": "junk", "brand": "Acme", }, } got := attrsFromEnhanceObj(obj) if got["brand"] != "Acme" || got["diagonala_zaslona"] != "27\"" { t.Fatalf("attrsFromEnhanceObj=%v", got) } allowed := map[string]struct{}{ "diagonala_zaslona": {}, "vrsta_panela": {}, } merged := mergeEnhanceAttrsInto(map[string]any{ "brand": "Acme", "product_model": "X1", }, got, allowed) if merged["diagonala_zaslona"] != "27\"" { t.Fatalf("expected remapped/validated diagonala, got %v", merged) } if _, ok := merged["zavora"]; ok { t.Fatalf("zavora must be dropped by allowlist: %v", merged) } if merged["brand"] != "Acme" || merged["product_model"] != "X1" { t.Fatalf("core/base attrs must remain: %v", merged) } if mergeEnhanceAttrsInto(map[string]any{"a": 1}, nil, allowed) != nil { t.Fatal("empty llm attrs must yield nil (no change)") } } func TestEnhanceAttrsFromRaw_roundTrip(t *testing.T) { t.Parallel() meta := map[string]any{"status": "ok"} attachEnhanceAttrs(meta, map[string]any{"brand": "Bosch", "nosilnost": "40 kg"}) got := enhanceAttrsFromRaw(meta) if got["brand"] != "Bosch" || got["nosilnost"] != "40 kg" { t.Fatalf("round-trip attrs=%v", got) } } func TestRunSteps_enhanceMergesValidatedAttrs(t *testing.T) { t.Parallel() payload := mustJSON(map[string]any{ "name": "Acme UltraView 27", "description": "

Acme UltraView 27

27 inch IPS monitor for desk work with clear specs.

", "meta_title": "Acme UltraView 27 | IPS", "meta_description": "Shop Acme UltraView 27 IPS monitor.", "attrs": map[string]any{ "diagonala_zaslona": "27\"", "vrsta_panela": "IPS", "zavora": "must-drop", }, }) e := &Engine{ Completer: stubCompleter{fn: func(system, user string) (Completion, error) { return Completion{Text: payload, TotalTokens: 12, Raw: map[string]any{"ok": true}}, nil }}, Vector: NoopVectorCategorizer{}, } out, err := e.RunSteps(context.Background(), "co", ProductInput{ Name: "UltraView 27 Gaming Monitor", Description: "A solid IPS desk monitor with factory specs.", Mapped: map[string]any{ "category_unique_id": "28", "name": "UltraView 27 Gaming Monitor", "attributes": map[string]any{ "brand": "Acme", }, }, CategoryNamesByUID: map[string]string{"28": "Monitorji"}, CategoryUniqueID: "28", CategoryAttrKeys: map[string]map[string]struct{}{ "28": {"diagonala_zaslona": {}, "vrsta_panela": {}}, }, CategoryEnhancePrompt: "Focus on panel technology for monitors.", Language: "en", }, "full", nil, StepPolicy{AllowAI: true}) if err != nil { t.Fatal(err) } pa := out.ProcessedAttributes if pa["diagonala_zaslona"] != "27\"" || pa["vrsta_panela"] != "IPS" { t.Fatalf("expected LLM attrs merged: %v notes=%v gpt=%v", pa, out.Notes, out.GPTResponse) } if _, ok := pa["zavora"]; ok { t.Fatalf("zavora must not survive allowlist: %v", pa) } if pa["brand"] != "Acme" { t.Fatalf("feed brand must remain: %v", pa) } if out.FieldSources["attributes"] != "ai_enhance" { t.Fatalf("field source=%v", out.FieldSources["attributes"]) } if !strings.Contains(out.ProcessedName, "UltraView") { t.Fatalf("name=%q", out.ProcessedName) } } func mustJSON(v any) string { b, err := json.Marshal(v) if err != nil { panic(err) } return string(b) }