Refactor validators into package-owned components

This commit is contained in:
2026-05-14 00:28:28 +00:00
parent 3b160cf05b
commit 52ffe42e73
26 changed files with 775 additions and 98 deletions

View File

@@ -0,0 +1,44 @@
package protected_terms
import (
"context"
"gitea.maximumdirect.net/eric/audita/internal/framework/contracts"
frameworkvalidators "gitea.maximumdirect.net/eric/audita/internal/framework/validators"
validatormetadata "gitea.maximumdirect.net/eric/audita/internal/validators/metadata"
)
const Key = "protected_terms"
func New() (contracts.Validator, error) {
return validatormetadata.Wrap(validator{}, validatormetadata.ExecutionClassDeterministic), nil
}
func NewGlossaryStage() (contracts.Validator, error) {
return validatormetadata.Wrap(validator{glossaryStage: true}, validatormetadata.ExecutionClassDeterministic), nil
}
type validator struct {
glossaryStage bool
}
func (v validator) Name() string {
return Key
}
func (v validator) Validate(_ context.Context, req contracts.ValidationRequest) (frameworkvalidators.Result, error) {
if !v.glossaryStage && req.ModuleKey == "glossary" {
decisions := make([]frameworkvalidators.Decision, 0, len(req.CandidateProposal))
for _, c := range req.CandidateProposal {
decisions = append(decisions, frameworkvalidators.Decision{
ProposalIndex: c.ProposalIndex,
Approved: true,
ReasonCode: frameworkvalidators.ReasonApproved,
Message: "approved",
})
}
return frameworkvalidators.Result{ValidatorName: Key, Decisions: decisions}, nil
}
return frameworkvalidators.ValidateProtectedTerms(req, v.glossaryStage)
}

View File

@@ -0,0 +1,164 @@
package protected_terms
import (
"context"
"testing"
"gitea.maximumdirect.net/eric/audita/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/internal/framework/contracts"
"gitea.maximumdirect.net/eric/audita/internal/framework/proposals"
frameworkvalidators "gitea.maximumdirect.net/eric/audita/internal/framework/validators"
validatormetadata "gitea.maximumdirect.net/eric/audita/internal/validators/metadata"
)
func proposal(index int, moduleKey, original, corrected string) proposals.EnrichedCorrectionProposal {
return proposals.EnrichedCorrectionProposal{
CorrectionProposal: proposals.CorrectionProposal{
TargetSegmentID: 1,
OriginalText: original,
CorrectedText: corrected,
Confidence: 0.95,
},
ProposalMetadata: proposals.ProposalMetadata{
ProposalIndex: index,
ModuleKey: moduleKey,
ModuleInstance: moduleKey,
},
}
}
func tinyGlossary() *schema.Glossary {
return &schema.Glossary{
Entries: []schema.GlossaryEntry{
{Name: "Jesters", Category: "faction", Summary: "Faction"},
},
}
}
func TestNewReportsStableKeyAndDeterministicClass(t *testing.T) {
v, err := New()
if err != nil {
t.Fatalf("New: %v", err)
}
if v.Name() != Key {
t.Fatalf("expected key %q, got %q", Key, v.Name())
}
if got := validatormetadata.ClassOf(v); got != validatormetadata.ExecutionClassDeterministic {
t.Fatalf("expected deterministic class, got %q", got)
}
}
func TestNewUsesGeneralBehaviorForGlossaryModule(t *testing.T) {
v, err := New()
if err != nil {
t.Fatalf("New: %v", err)
}
req := contracts.ValidationRequest{
ModuleKey: "glossary",
Glossary: tinyGlossary(),
CandidateProposal: []proposals.EnrichedCorrectionProposal{
proposal(0, "glossary", "Jesters", "Gestures"),
},
}
result, err := v.Validate(context.Background(), req)
if err != nil {
t.Fatalf("Validate: %v", err)
}
if len(result.Decisions) != 1 {
t.Fatalf("expected one decision, got %+v", result.Decisions)
}
if !result.Decisions[0].Approved {
t.Fatalf("expected glossary-module approval in general variant, got %+v", result.Decisions[0])
}
if result.Decisions[0].ReasonCode != frameworkvalidators.ReasonApproved {
t.Fatalf("expected reason %q, got %+v", frameworkvalidators.ReasonApproved, result.Decisions[0])
}
}
func TestNewRejectsProtectedTermChangeOutsideGlossaryStage(t *testing.T) {
v, err := New()
if err != nil {
t.Fatalf("New: %v", err)
}
req := contracts.ValidationRequest{
ModuleKey: "homophones",
Glossary: tinyGlossary(),
CandidateProposal: []proposals.EnrichedCorrectionProposal{
proposal(0, "homophones", "Jesters", "Gestures"),
},
}
result, err := v.Validate(context.Background(), req)
if err != nil {
t.Fatalf("Validate: %v", err)
}
if len(result.Decisions) != 1 {
t.Fatalf("expected one decision, got %+v", result.Decisions)
}
if result.Decisions[0].Approved {
t.Fatalf("expected protected_terms rejection, got %+v", result.Decisions[0])
}
if result.Decisions[0].ReasonCode != frameworkvalidators.ReasonProtectedGlossaryTerm {
t.Fatalf("expected reason %q, got %+v", frameworkvalidators.ReasonProtectedGlossaryTerm, result.Decisions[0])
}
}
func TestNewGlossaryStageRejectsProtectedTermChange(t *testing.T) {
v, err := NewGlossaryStage()
if err != nil {
t.Fatalf("NewGlossaryStage: %v", err)
}
if v.Name() != Key {
t.Fatalf("expected key %q, got %q", Key, v.Name())
}
if got := validatormetadata.ClassOf(v); got != validatormetadata.ExecutionClassDeterministic {
t.Fatalf("expected deterministic class, got %q", got)
}
req := contracts.ValidationRequest{
ModuleKey: "glossary",
Glossary: tinyGlossary(),
CandidateProposal: []proposals.EnrichedCorrectionProposal{
proposal(0, "glossary", "Jesters", "Gestures"),
},
}
result, err := v.Validate(context.Background(), req)
if err != nil {
t.Fatalf("Validate: %v", err)
}
if len(result.Decisions) != 1 {
t.Fatalf("expected one decision, got %+v", result.Decisions)
}
if result.Decisions[0].Approved {
t.Fatalf("expected glossary-stage protected_terms rejection, got %+v", result.Decisions[0])
}
if result.Decisions[0].ReasonCode != frameworkvalidators.ReasonProtectedGlossaryTerm {
t.Fatalf("expected reason %q, got %+v", frameworkvalidators.ReasonProtectedGlossaryTerm, result.Decisions[0])
}
}
func TestNewGlossaryStageAllowsProtectedTermInsertion(t *testing.T) {
v, err := NewGlossaryStage()
if err != nil {
t.Fatalf("NewGlossaryStage: %v", err)
}
req := contracts.ValidationRequest{
ModuleKey: "glossary",
Glossary: tinyGlossary(),
CandidateProposal: []proposals.EnrichedCorrectionProposal{
proposal(0, "glossary", "gestures", "Jesters"),
},
}
result, err := v.Validate(context.Background(), req)
if err != nil {
t.Fatalf("Validate: %v", err)
}
if len(result.Decisions) != 1 {
t.Fatalf("expected one decision, got %+v", result.Decisions)
}
if !result.Decisions[0].Approved {
t.Fatalf("expected glossary-stage approval for protected-term insertion, got %+v", result.Decisions[0])
}
}