2026-08-09 22:47:43 +02:00
package processing
import (
"context"
"strings"
"testing"
2026-08-16 16:57:36 +02:00
"github.com/descrybe/descrybe-v2/apps/api/internal/company"
2026-08-09 22:47:43 +02:00
)
func TestHashEnhanceInput_stableAndSensitive ( t * testing . T ) {
attrs := map [ string ] any { "brand" : "Acme" , "color" : "Red" }
a := HashEnhanceInput ( "Shoes" , "Runner" , "A shoe" , "" , "" , "" , "" , attrs )
b := HashEnhanceInput ( "Shoes" , "Runner" , "A shoe" , "" , "" , "" , "" , attrs )
if a == "" || a != b {
t . Fatalf ( "expected stable hash, got %q vs %q" , a , b )
}
if HashEnhanceInput ( "Shoes" , "Runner X" , "A shoe" , "" , "" , "" , "" , attrs ) == a {
t . Fatal ( "name change must change hash" )
}
if HashEnhanceInput ( "Shoes" , "Runner" , "A shoe" , "Brand:\n- tone: bold" , "" , "" , "" , attrs ) == a {
t . Fatal ( "brand prompt must change hash" )
}
if HashEnhanceInput ( "Shoes" , "Runner" , "A shoe" , "" , "" , "sys-a" , "user-a" , attrs ) == a {
t . Fatal ( "prompt template change must change hash" )
}
if HashEnhanceInput ( "Shoes" , "Runner" , "A shoe" , "" , "fr" , "" , "" , attrs ) == a {
t . Fatal ( "language change must change hash" )
}
// Attr key order must not matter (CompactAttrs + json map sort).
attrs2 := map [ string ] any { "color" : "Red" , "brand" : "Acme" }
if HashEnhanceInput ( "Shoes" , "Runner" , "A shoe" , "" , "" , "" , "" , attrs2 ) != a {
t . Fatal ( "attr key order must not change hash" )
}
}
func TestRunSteps_skipsEnhanceWhenInputHashUnchanged ( t * testing . T ) {
calls := 0
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
calls ++
return Completion { Text : `{"name":"ShouldNotRun","description":"Nope"}` , TotalTokens : 9 }, nil
}},
Vector : NoopVectorCategorizer {},
}
2026-08-16 16:57:36 +02:00
priorDesc := "Cached description with enough retail detail for listing copy."
2026-08-09 22:47:43 +02:00
in := ProductInput {
Name : "Widget" ,
2026-08-16 16:57:36 +02:00
Description : "A widget with enough mapped detail for hashing." ,
Mapped : map [ string ] any { "name" : "Widget" , "description" : "A widget with enough mapped detail for hashing." },
2026-08-09 22:47:43 +02:00
PriorProcessedName : "Cached Widget" ,
2026-08-16 16:57:36 +02:00
PriorProcessedDescription : priorDesc ,
2026-08-09 22:47:43 +02:00
}
// First pass without prior hash to compute the hash shape via enhance path is awkward;
// compute the same hash RunSteps will see after normalize (name/desc from mapped).
// enhance_only: normalize then enhance with out.Name from normalized.
normName := "Widget"
2026-08-16 16:57:36 +02:00
normDesc := "A widget with enough mapped detail for hashing."
2026-08-09 22:47:43 +02:00
sysTpl , userTpl := resolveProductPromptTemplates ( ProductInput {})
in . PriorEnhanceHash = HashEnhanceInput ( "" , normName , normDesc , "" , "" , sysTpl , userTpl , map [ string ] any {})
out , err := e . RunSteps ( context . Background (), "co" , in , "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if calls != 0 {
t . Fatalf ( "expected LLM skip, calls=%d" , calls )
}
if out . ProcessedName != "Cached Widget" {
t . Fatalf ( "name=%q" , out . ProcessedName )
}
2026-08-16 16:57:36 +02:00
if out . ProcessedDescription != priorDesc {
2026-08-09 22:47:43 +02:00
t . Fatalf ( "desc=%q" , out . ProcessedDescription )
}
if out . TotalTokens != 0 {
t . Fatalf ( "tokens=%d want 0" , out . TotalTokens )
}
if out . FieldSources [ FieldEnhanceInputHash ] != in . PriorEnhanceHash {
t . Fatalf ( "hash field=%v" , out . FieldSources [ FieldEnhanceInputHash ])
}
if src , _ := out . FieldSources [ "name" ].( string ); src != "ai_enhance_unchanged" {
t . Fatalf ( "name source=%v" , out . FieldSources [ "name" ])
}
if ! out . SkipCreditDebit {
t . Fatal ( "expected SkipCreditDebit when enhance hash unchanged" )
}
if shouldDebitProductProcessing ( false , out ) {
t . Fatal ( "processOne must not debit when enhance unchanged" )
}
2026-08-16 16:57:36 +02:00
// Hash-skip (0 tokens) must still persist known engine mode, not "unknown".
if out . AIProviderMode != AIProviderInternal {
t . Fatalf ( "ai_provider_mode=%q want %q on hash-skip" , out . AIProviderMode , AIProviderInternal )
}
// processOne treats unknown as empty and prefers job modeLabel.
got := preferKnownProviderMode ( AIProviderUnknown , AIProviderInternal )
if got != AIProviderInternal {
t . Fatalf ( "preferKnownProviderMode(unknown, internal)=%q" , got )
}
2026-08-09 22:47:43 +02:00
joined := strings . Join ( out . Notes , ";" )
2026-08-16 21:35:48 +02:00
if ! strings . Contains ( joined , "ai_enhance_unchanged" ) {
2026-08-09 22:47:43 +02:00
t . Fatalf ( "notes=%v" , out . Notes )
}
}
func TestRunSteps_callsEnhanceWhenInputHashDiffers ( t * testing . T ) {
calls := 0
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
calls ++
2026-08-16 19:21:49 +02:00
return Completion { Text : `{"name":"Fresh","description":"Fresh retail copy with enough detail for a quality enhance hash."}` , TotalTokens : 3 }, nil
2026-08-09 22:47:43 +02:00
}},
Vector : NoopVectorCategorizer {},
}
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Mapped : map [ string ] any { "name" : "Widget" , "description" : "A widget" },
PriorEnhanceHash : "deadbeef" ,
PriorProcessedName : "Old" ,
PriorProcessedDescription : "Old desc" ,
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if calls != 1 {
t . Fatalf ( "calls=%d" , calls )
}
if out . ProcessedName != "Fresh" {
t . Fatalf ( "name=%q" , out . ProcessedName )
}
if out . TotalTokens != 3 {
t . Fatalf ( "tokens=%d" , out . TotalTokens )
}
if out . SkipCreditDebit {
t . Fatal ( "hash miss must still debit" )
}
if ! shouldDebitProductProcessing ( false , out ) {
t . Fatal ( "expected debit when enhance ran" )
}
h , _ := out . FieldSources [ FieldEnhanceInputHash ].( string )
if h == "" || h == "deadbeef" {
t . Fatalf ( "expected new hash in field_sources, got %q" , h )
}
}
2026-08-16 16:57:36 +02:00
func TestRunSteps_reenhancesWhenPriorDescEqualsTitle ( t * testing . T ) {
calls := 0
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
calls ++
return Completion { Text : `{"name":"Widget Pro","description":"A durable retail widget for everyday use with clear specs."}` , TotalTokens : 5 }, nil
}},
Vector : NoopVectorCategorizer {},
}
in := ProductInput {
Name : "Widget" ,
Description : "Widget" ,
Mapped : map [ string ] any { "name" : "Widget" , "description" : "Widget" },
PriorProcessedName : "Widget" ,
PriorProcessedDescription : "Widget" ,
}
sysTpl , userTpl := resolveProductPromptTemplates ( ProductInput {})
in . PriorEnhanceHash = HashEnhanceInput ( "" , "Widget" , "Widget" , "" , "" , sysTpl , userTpl , map [ string ] any {})
out , err := e . RunSteps ( context . Background (), "co" , in , "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if calls != 1 {
t . Fatalf ( "expected LLM re-enhance when prior desc equals title, calls=%d" , calls )
}
if out . ProcessedDescription == "Widget" || out . ProcessedDescription == "" {
t . Fatalf ( "desc=%q" , out . ProcessedDescription )
}
if out . SkipCreditDebit {
t . Fatal ( "title-echo prior must still debit" )
}
}
func TestPreferredProductDescription_skipsTitleEcho ( t * testing . T ) {
title := "Acme Widget Pro"
if got := preferredProductDescription ( title , title , "Acme Widget Pro." , "A durable retail widget for everyday use." ); got != "A durable retail widget for everyday use." {
t . Fatalf ( "got %q" , got )
}
if got := preferredProductDescription ( title , title , strings . ToUpper ( title )); got != "" {
t . Fatalf ( "expected empty when all candidates echo title, got %q" , got )
}
if got := preferredProductDescription ( title , "" , "<nil>" , "Category:" ); got != "" {
t . Fatalf ( "expected empty for unusable candidates, got %q" , got )
}
}
func TestRunSteps_emptyDescriptionGetsNonemptyProcessedDesc ( t * testing . T ) {
calls := 0
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
calls ++
return Completion { Text : `{"name":"Gigabyte Monitor","description":""}` , TotalTokens : 4 }, nil
}},
Vector : NoopVectorCategorizer {},
}
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Name : "Gigabyte Monitor" ,
Mapped : map [ string ] any { "name" : "Gigabyte Monitor" , "brand" : "Gigabyte" },
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if calls != 1 {
t . Fatalf ( "expected enhance call, calls=%d" , calls )
}
if strings . TrimSpace ( out . ProcessedDescription ) == "" {
t . Fatal ( "expected nonempty ProcessedDescription when title set" )
}
if descriptionEchoesTitle ( out . ProcessedDescription , out . ProcessedName ) {
t . Fatalf ( "ProcessedDescription must not echo title: name=%q desc=%q" , out . ProcessedName , out . ProcessedDescription )
}
if descriptionEchoesTitle ( out . ProcessedDescription , "Gigabyte Monitor" ) {
t . Fatalf ( "ProcessedDescription must not echo mapped title: desc=%q" , out . ProcessedDescription )
}
}
func TestRunSteps_thinOKDoesNotPersistEnhanceHash ( t * testing . T ) {
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
return Completion { Text : `{"name":"Monitor","description":"ok"}` , TotalTokens : 2 }, nil
}},
Vector : NoopVectorCategorizer {},
}
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Name : "Monitor" ,
Description : "Monitor" ,
Mapped : map [ string ] any { "name" : "Monitor" , "description" : "Monitor" },
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if h , _ := out . FieldSources [ FieldEnhanceInputHash ].( string ); h != "" && isWeakPriorEnhanceDescription ( out . ProcessedDescription , out . ProcessedName ) {
t . Fatalf ( "must not persist hash with weak desc: hash=%q desc=%q" , h , out . ProcessedDescription )
}
for _ , lf := range out . LocalizedContent {
if lf . EnhanceInputHash != "" && isWeakPriorEnhanceDescription ( lf . ProcessedDescription , lf . ProcessedName ) {
t . Fatalf ( "localized hash poisoned with weak desc: %+v" , lf )
}
}
}
2026-08-16 19:21:49 +02:00
func TestRunSteps_synthesizedDescDoesNotPersistEnhanceHash ( t * testing . T ) {
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
// Empty description forces synthesizeProductDescription.
return Completion { Text : `{"name":"GIGABYTE GS27QC","description":""}` , TotalTokens : 3 }, nil
}},
Vector : NoopVectorCategorizer {},
}
descTpl := map [ string ] any {
"sections" : [] any {
map [ string ] any { "type" : "h1" , "instructions" : "Heading" },
map [ string ] any { "type" : "p" , "instructions" : "Body" },
},
}
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Name : "GIGABYTE GS27QC" ,
Mapped : map [ string ] any { "name" : "GIGABYTE GS27QC" , "category" : "7" , "brand" : "GIGABYTE" },
DescriptionTemplate : descTpl ,
Language : "sl" ,
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if ! strings . Contains ( out . ProcessedDescription , "<h1>" ) {
t . Fatalf ( "expected formula synthesize HTML, got %q" , out . ProcessedDescription )
}
if h , _ := out . FieldSources [ FieldEnhanceInputHash ].( string ); h != "" {
t . Fatalf ( "synthesized description must not persist enhance hash (got %q)" , h )
}
for _ , lf := range out . LocalizedContent {
if lf . EnhanceInputHash != "" {
t . Fatalf ( "localized synthesized hash must be empty: %+v" , lf )
}
}
}
2026-08-16 21:35:48 +02:00
func TestRunSteps_formulaIgnoredByLLM_retriesThenSynthesizes ( t * testing . T ) {
calls := 0
var sawFormulaRetry bool
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
calls ++
if strings . Contains ( user , "INVALID DESCRIPTION" ) {
sawFormulaRetry = true
}
// Non-weak short prose that ignores multi-section HTML formula.
return Completion {
Text : `{"name":"GIGABYTE GS27QC","description":"GIGABYTE GS27QC is a gaming monitor with a 61 cm curved panel for immersive play."}` ,
TotalTokens : 4 ,
}, nil
}},
Vector : NoopVectorCategorizer {},
}
descTpl := 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" },
},
}
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Name : "GIGABYTE GS27QC" ,
Mapped : map [ string ] any { "name" : "GIGABYTE GS27QC" , "category" : "7" , "brand" : "GIGABYTE" , "width" : "61 cm" },
CategoryFormulasByKey : map [ string ] CategoryFormulas {
"7" : { DescriptionTemplate : descTpl },
},
Language : "en" ,
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if calls < 2 {
t . Fatalf ( "expected formula retry after ignore, calls=%d" , calls )
}
if ! sawFormulaRetry {
t . Fatal ( "expected INVALID DESCRIPTION retry user suffix" )
}
for _ , tag := range [] string { "<h1>" , "<p>" , "<ul>" } {
if ! strings . Contains ( out . ProcessedDescription , tag ) {
t . Fatalf ( "expected formula HTML with %s, got %q" , tag , out . ProcessedDescription )
}
}
if h , _ := out . FieldSources [ FieldEnhanceInputHash ].( string ); h != "" {
t . Fatalf ( "formula synthesize must not persist hash, got %q" , h )
}
}
func TestRunSteps_formulaHTMLFromLLM_persistsHash ( t * testing . T ) {
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
return Completion {
Text : `{"name":"GIGABYTE GS27QC","description":"<h1>GIGABYTE GS27QC</h1><p>Curved gaming monitor.</p><ul><li>width 61 cm</li></ul>"}` ,
TotalTokens : 6 ,
}, nil
}},
Vector : NoopVectorCategorizer {},
}
descTpl := 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" },
},
}
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Name : "GIGABYTE GS27QC" ,
Mapped : map [ string ] any { "name" : "GIGABYTE GS27QC" , "category" : "7" },
DescriptionTemplate : descTpl ,
Language : "en" ,
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if ! strings . Contains ( out . ProcessedDescription , "<ul>" ) {
t . Fatalf ( "expected LLM HTML kept, got %q" , out . ProcessedDescription )
}
if h , _ := out . FieldSources [ FieldEnhanceInputHash ].( string ); h == "" {
t . Fatal ( "compliant formula HTML should persist enhance hash" )
}
}
func TestRunSteps_synthPriorHashDoesNotSkipReprocess ( t * testing . T ) {
calls := 0
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
calls ++
return Completion { Text : `{"name":"Vogel WALL 3245","description":"Vogel's WALL 3245 is a sturdy TV mount with clear load ratings and install guidance for wall displays."}` , TotalTokens : 5 }, nil
}},
Vector : NoopVectorCategorizer {},
}
normName := "Vogel's WALL 3245 TV Wall Mount"
normDesc := "A mount with enough mapped detail for hashing inputs."
synthPrior := synthesizeDescriptionFromTitle ( normName , "TV Mounts" , "en" , map [ string ] any {
"brand" : "Vogel's" , "width" : "45 cm" , "max_load" : "40 kg" ,
})
if ! company . LooksLikeHeuristicSynthesize ( synthPrior ) {
t . Fatalf ( "expected invent synth prior, got %q" , synthPrior )
}
sysTpl , userTpl := resolveProductPromptTemplates ( ProductInput {})
priorHash := HashEnhanceInput ( "" , normName , normDesc , "" , "" , sysTpl , userTpl , map [ string ] any {})
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Name : normName ,
Description : normDesc ,
Mapped : map [ string ] any { "name" : normName , "description" : normDesc },
PriorEnhanceHash : priorHash ,
PriorProcessedName : normName ,
PriorProcessedDescription : synthPrior ,
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if calls != 1 {
t . Fatalf ( "synth prior must force completer, calls=%d" , calls )
}
if out . SkipCreditDebit {
t . Fatal ( "synth prior must not SkipCreditDebit" )
}
joined := strings . Join ( out . Notes , ";" )
if ! strings . Contains ( joined , "forced re-enhance" ) {
t . Fatalf ( "expected forced re-enhance note, notes=%v" , out . Notes )
}
if company . LooksLikeHeuristicSynthesize ( out . ProcessedDescription ) && calls == 1 {
// LLM stub returned non-synth copy — ok if still synth after outer repair.
}
if out . ProcessedDescription == synthPrior {
t . Fatalf ( "must not reuse synth prior: %q" , out . ProcessedDescription )
}
}
func TestRunSteps_formulaMismatchPriorHashDoesNotSkipReprocess ( t * testing . T ) {
calls := 0
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
calls ++
return Completion {
Text : `{"name":"GIGABYTE GS27QC","description":"<h1>GIGABYTE GS27QC</h1><p>Curved gaming monitor with vivid colors.</p><ul><li>width 61 cm</li></ul>"}` ,
TotalTokens : 6 ,
}, nil
}},
Vector : NoopVectorCategorizer {},
}
descTpl := 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" },
},
}
normName := "GIGABYTE GS27QC"
normDesc := "GIGABYTE GS27QC is a gaming monitor with a 61 cm curved panel for immersive play."
sysTpl , userTpl := resolveProductPromptTemplates ( ProductInput { DescriptionTemplate : descTpl })
priorHash := HashEnhanceInput ( "" , normName , normDesc , "" , "" , sysTpl , userTpl , map [ string ] any {})
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Name : normName ,
Description : normDesc ,
Mapped : map [ string ] any { "name" : normName , "description" : normDesc , "category" : "7" },
DescriptionTemplate : descTpl ,
PriorEnhanceHash : priorHash ,
PriorProcessedName : normName ,
PriorProcessedDescription : normDesc , // plain prose — formula mismatch
Language : "en" ,
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if calls < 1 {
t . Fatalf ( "formula-mismatch prior must force completer, calls=%d" , calls )
}
if out . SkipCreditDebit {
t . Fatal ( "formula-mismatch must not SkipCreditDebit" )
}
joined := strings . Join ( out . Notes , ";" )
if ! strings . Contains ( joined , "forced re-enhance" ) && ! strings . Contains ( joined , "formula" ) {
// forced note when hash matched; formula repair notes also acceptable
if ! strings . Contains ( joined , "synth" ) && calls >= 1 {
// still ok if LLM ran without note if reason path differed
}
}
for _ , tag := range [] string { "<h1>" , "<p>" , "<ul>" } {
if ! strings . Contains ( out . ProcessedDescription , tag ) {
t . Fatalf ( "expected formula HTML with %s, got %q" , tag , out . ProcessedDescription )
}
}
}
2026-08-16 16:57:36 +02:00
func TestRunSteps_weakPriorHashDoesNotSkipReprocess ( t * testing . T ) {
calls := 0
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
calls ++
return Completion { Text : `{"name":"Widget Pro","description":"A durable retail widget for everyday use with clear specs."}` , TotalTokens : 5 }, nil
}},
Vector : NoopVectorCategorizer {},
}
normName := "Widget"
normDesc := "Widget"
sysTpl , userTpl := resolveProductPromptTemplates ( ProductInput {})
priorHash := HashEnhanceInput ( "" , normName , normDesc , "" , "" , sysTpl , userTpl , map [ string ] any {})
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Name : "Widget" ,
Description : "Widget" ,
Mapped : map [ string ] any { "name" : "Widget" , "description" : "Widget" },
PriorEnhanceHash : priorHash ,
PriorProcessedName : "Widget" ,
PriorProcessedDescription : "ok" ,
PriorLocalized : company . LocalizedContent {
"en" : { ProcessedName : "Widget" , ProcessedDescription : "ok" , EnhanceInputHash : priorHash },
},
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if calls != 1 {
t . Fatalf ( "weak prior desc must force completer, calls=%d" , calls )
}
if out . SkipCreditDebit {
t . Fatal ( "weak prior must not SkipCreditDebit" )
}
if out . ProcessedDescription == "ok" || out . ProcessedDescription == "Widget" {
t . Fatalf ( "desc=%q" , out . ProcessedDescription )
}
}
func TestRunSteps_failedEnhanceDoesNotPoisonLocalizedHash ( t * testing . T ) {
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
return Completion {}, context . DeadlineExceeded
}},
Vector : NoopVectorCategorizer {},
}
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Name : "Widget" ,
Mapped : map [ string ] any { "name" : "Widget" , "description" : "A widget with enough mapped detail for hashing." },
PriorEnhanceHash : "should-not-survive" ,
PriorLocalized : company . LocalizedContent {
"en" : { ProcessedName : "Old" , ProcessedDescription : "Old desc that is long enough to look real." , EnhanceInputHash : "poison" },
},
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if _ , ok := out . FieldSources [ FieldEnhanceInputHash ]; ok {
t . Fatalf ( "failed enhance must not keep field_sources hash, got %v" , out . FieldSources [ FieldEnhanceInputHash ])
}
for lang , lf := range out . LocalizedContent {
if lf . EnhanceInputHash != "" {
t . Fatalf ( "lang %s: failed enhance must clear localized hash, got %q" , lang , lf . EnhanceInputHash )
}
}
}
2026-08-16 21:35:48 +02:00
func TestRunSteps_emptyLLMDescriptionDoesNotPersistHashViaOriginalFallback ( t * testing . T ) {
// Prod bug: empty LLM description preferred originals and stamped ok+hash.
richOriginal := "This durable retail mount includes mounting hardware, load ratings, and install guidance for wall displays."
e := & Engine {
Completer : stubCompleter { fn : func ( system , user string ) ( Completion , error ) {
return Completion { Text : `{"name":"Vogel WALL 3245","description":""}` , TotalTokens : 2 }, nil
}},
Vector : NoopVectorCategorizer {},
}
out , err := e . RunSteps ( context . Background (), "co" , ProductInput {
Name : "Vogel WALL 3245" ,
Description : richOriginal ,
Mapped : map [ string ] any {
"name" : "Vogel WALL 3245" ,
"description" : richOriginal ,
},
}, "enhance_only" , nil , StepPolicy { AllowAI : true , AllowEPREL : true })
if err != nil {
t . Fatal ( err )
}
if h , _ := out . FieldSources [ FieldEnhanceInputHash ].( string ); h != "" {
t . Fatalf ( "empty LLM description must not persist enhance hash (got %q); desc=%q" , h , out . ProcessedDescription )
}
for _ , lf := range out . LocalizedContent {
if lf . EnhanceInputHash != "" {
t . Fatalf ( "localized hash must stay empty on empty LLM desc: %+v" , lf )
}
}
joined := strings . Join ( out . Notes , ";" )
if ! strings . Contains ( joined , "refused" ) && ! strings . Contains ( joined , "synthesized" ) && ! strings . Contains ( joined , "empty_description" ) {
t . Fatalf ( "expected refuse/synthesize note, notes=%v" , out . Notes )
}
if strings . TrimSpace ( out . ProcessedDescription ) == "" {
t . Fatal ( "expected nonempty fallback/synth description" )
}
}
func TestLlmEnhanceHardRefuseReason ( t * testing . T ) {
t . Parallel ()
if got := llmEnhanceHardRefuseReason ( "" , "desc long enough" ); got != "empty_name" {
t . Fatalf ( "got %q" , got )
}
if got := llmEnhanceHardRefuseReason ( "Category:" , "desc long enough here for tests" ); got != "prompt_leakage_name" {
t . Fatalf ( "got %q" , got )
}
if got := llmEnhanceHardRefuseReason ( "Widget" , "" ); got != "empty_description" {
t . Fatalf ( "got %q" , got )
}
if got := llmEnhanceHardRefuseReason ( "Widget" , "A durable retail widget for everyday use with clear specs." ); got != "" {
t . Fatalf ( "got %q" , got )
}
}