Files
audita/internal/framework/validators/vocabulary_test.go

123 lines
6.4 KiB
Go

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)
}
}
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)
}
})
}
}
func TestValidationContextIncludesEachSharedDefinition(t *testing.T) {
entries := []glossary.Entry{
{Collection: "spells", ID: "shield", Name: "Shield", Category: "spell"},
{Collection: "items", ID: "shield", Name: "Shield", Category: "item"},
{Collection: "items", ID: "sword", Name: "Sword", Category: "item"},
}
segments := []schema.Segment{{ID: 1, Text: "Shield"}}
_, selected := validationContext(segments, 0, entries, NewProtectedVocabulary(glossary.Vocabulary(entries, nil)), "Barrier")
if len(selected) != 2 || selected[0].Category != "spell" || selected[1].ID != "shield" || selected[1].Collection != "items" {
t.Fatalf("selected=%+v", selected)
}
}