2026-11-10 17:25:08 +00:00
package validators
import (
"testing"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/glossary"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/proposals"
)
func TestIndependentProtectionForSelectedGlossary ( t * testing . T ) {
active := [ ] glossary . Entry { { Collection : "spells" , ID : "shield" , Name : "Shield" } }
entries := append ( append ( [ ] glossary . Entry ( nil ) , active ... ) , glossary . Entry { Collection : "items" , ID : "shield" , Name : "Shield" , Aliases : [ ] string { "Aegis" } } )
cases := [ ] struct {
name string
terms [ ] glossary . Term
before , after string
approved bool
} {
{ "shared spelling exempt" , glossary . Vocabulary ( entries , active ) , "Shield" , "Barrier" , true } ,
{ "other alias protected" , glossary . Vocabulary ( entries , active ) , "Aegis" , "Armor" , false } ,
{ "full protection retained" , glossary . Vocabulary ( entries , nil ) , "Shield" , "Barrier" , false } ,
{ "empty protection" , nil , "Aegis" , "Armor" , true } ,
}
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
result , err := ValidateProtectedTerms ( Request { ProtectedTerms : tc . terms , CandidateProposal : [ ] proposals . EnrichedCorrectionProposal { { CorrectionProposal : proposals . CorrectionProposal { OriginalText : tc . before , CorrectedText : tc . after } } } } )
if err != nil || len ( result . Decisions ) != 1 || result . Decisions [ 0 ] . Approved != tc . approved {
t . Fatalf ( "result=%+v err=%v" , result , err )
}
} )
}
}
func TestSharedAliasRetainsAllIdentities ( t * testing . T ) {
entries := [ ] glossary . Entry {
{ Collection : "a" , ID : "first" , Name : "First" , Aliases : [ ] string { "Shared" } } ,
{ Collection : "b" , ID : "second" , Name : "Second" , Aliases : [ ] string { "Shared" } } ,
}
vocabulary := NewProtectedVocabulary ( glossary . Vocabulary ( entries , nil ) )
identities := vocabulary . occurrencesByIdentity ( "Shared" )
if len ( identities ) != 2 || len ( identities [ glossary . Identity { Collection : "a" , ID : "first" } ] ) != 1 || len ( identities [ glossary . Identity { Collection : "b" , ID : "second" } ] ) != 1 {
t . Fatalf ( "identities=%v" , identities )
}
}
func TestApplicationChecksCurrentTextWithSelectedProtection ( t * testing . T ) {
active := [ ] glossary . Entry { { Collection : "spells" , ID : "spell" , Name : "Shield" } }
protected := append ( append ( [ ] glossary . Entry ( nil ) , active ... ) , glossary . Entry { Collection : "items" , ID : "item" , Name : "Shield" , Aliases : [ ] string { "Aegis" } } )
vocabulary := NewProtectedVocabulary ( glossary . Vocabulary ( protected , active ) )
transcript := & schema . Transcript { Segments : [ ] schema . Segment { { ID : 1 , Text : "Shield and Aegis" } } }
candidates := [ ] proposals . EnrichedCorrectionProposal {
{ CorrectionProposal : proposals . CorrectionProposal { TargetSegmentID : 1 , OriginalText : "Shield" , CorrectedText : "Barrier" , Confidence : 1 } , ProposalMetadata : proposals . ProposalMetadata { ProposalIndex : 0 } } ,
{ CorrectionProposal : proposals . CorrectionProposal { TargetSegmentID : 1 , OriginalText : "egis" , CorrectedText : "rmor" , Confidence : 1 } , ProposalMetadata : proposals . ProposalMetadata { ProposalIndex : 1 } } ,
}
result := proposals . ApplyProposals ( transcript , candidates , proposals . ReplacementPolicyRequireUnique , func ( before , after string ) bool {
return vocabulary . AllowsChange ( before , after , false )
} )
if result . Transcript . Segments [ 0 ] . Text != "Barrier and Aegis" || len ( result . Applied ) != 1 || len ( result . Skipped ) != 1 || result . Skipped [ 0 ] . SkipReason != proposals . SkipReasonProtectedTerm {
t . Fatalf ( "unexpected application: %+v" , result )
}
}
2026-11-10 20:33:24 +00:00
func TestExemptGlossarySpellingKeepsLongestMatchBoundary ( t * testing . T ) {
active := [ ] glossary . Entry {
{ ID : "greater-mage" , Name : "Greater Mage Armor" } ,
{ ID : "greater-magus" , Name : "Greater Magus Armor" } ,
}
protected := [ ] glossary . Entry {
active [ 0 ] , active [ 1 ] ,
{ ID : "short" , Name : "Mage Armor" } ,
{ ID : "long" , Name : "Superior Greater Mage Armor" } ,
}
for _ , tc := range [ ] struct {
name , module , before , original , replacement string
exempt , allowed bool
} {
{ "full active name" , "glossary" , "Cast Greater Mage Armor." , "Greater Mage Armor" , "Greater Magus Armor" , true , true } ,
{ "span within active name" , "glossary" , "Cast Greater Mage Armor." , "Mage Armor" , "Magus Armor" , true , true } ,
{ "standalone protected name" , "glossary" , "Cast Mage Armor." , "Mage Armor" , "Magus Armor" , true , false } ,
{ "longer protected name" , "glossary" , "Cast Superior Greater Mage Armor." , "Greater Mage Armor" , "Greater Magus Armor" , true , false } ,
{ "same name in a grammar pass" , "grammar" , "Cast Greater Mage Armor." , "Greater Mage Armor" , "Greater Magus Armor" , false , false } ,
} {
t . Run ( tc . name , func ( t * testing . T ) {
var exclusions [ ] glossary . Entry
if tc . exempt {
exclusions = active
}
terms := glossary . Vocabulary ( protected , exclusions )
transcript := & schema . Transcript { Segments : [ ] schema . Segment { { ID : 1 , Text : tc . before } } }
candidate := proposals . EnrichedCorrectionProposal { CorrectionProposal : proposals . CorrectionProposal {
TargetSegmentID : 1 , OriginalText : tc . original , CorrectedText : tc . replacement , Confidence : 1 ,
} }
policy := proposals . ReplacementPolicyRequireUnique
decision , err := ValidateProtectedTerms ( Request { ModuleKey : tc . module , WorkingTranscript : transcript , ReplacementPolicy : policy ,
ProtectedTerms : terms , CandidateProposal : [ ] proposals . EnrichedCorrectionProposal { candidate } } )
if err != nil || len ( decision . Decisions ) != 1 || decision . Decisions [ 0 ] . Approved != tc . allowed {
t . Fatalf ( "decision=%+v err=%v" , decision , err )
}
vocabulary := NewProtectedVocabulary ( terms )
result := proposals . ApplyProposals ( transcript , [ ] proposals . EnrichedCorrectionProposal { candidate } , policy , func ( before , after string ) bool {
return vocabulary . AllowsChange ( before , after , false )
} )
if ( len ( result . Applied ) == 1 ) != tc . allowed {
t . Fatalf ( "apply-time protection disagrees with validation: %+v" , result )
}
} )
}
}
2026-11-10 17:25:08 +00:00
func TestValidationContextIncludesEachSharedDefinition ( t * testing . T ) {
entries := [ ] glossary . Entry {
2026-11-10 20:42:28 +00:00
{ Collection : "items" , ID : "sword" , Name : "Sword" , Category : "item" } ,
2026-11-10 17:25:08 +00:00
{ Collection : "spells" , ID : "shield" , Name : "Shield" , Category : "spell" } ,
{ Collection : "items" , ID : "shield" , Name : "Shield" , Category : "item" } ,
2026-11-10 20:42:28 +00:00
{ Collection : "items" , ID : "bow" , Name : "Bow" , Category : "item" } ,
2026-11-10 17:25:08 +00:00
}
2026-11-10 20:42:28 +00:00
segments := [ ] schema . Segment { { ID : 1 , Text : "Shield Shield" } }
_ , selected := prepareGlossaryContext ( entries ) . validationContext ( segments , 0 , "Sword" )
if len ( selected ) != 3 || selected [ 0 ] . ID != "sword" || selected [ 1 ] . Category != "spell" || selected [ 2 ] . ID != "shield" || selected [ 2 ] . Collection != "items" {
2026-11-10 17:25:08 +00:00
t . Fatalf ( "selected=%+v" , selected )
}
}