package proposals import ( "encoding/json" "testing" ) func TestCorrectionProposalValidate_Valid(t *testing.T) { proposal := CorrectionProposal{ TargetSegmentID: 42, OriginalText: "gestures", CorrectedText: "Jesters", Confidence: 0.95, } if err := proposal.Validate(); err != nil { t.Fatalf("expected valid proposal, got error: %v", err) } } func TestCorrectionProposalValidate_InvalidEmptyOriginalText(t *testing.T) { proposal := CorrectionProposal{ TargetSegmentID: 42, OriginalText: "", CorrectedText: "Jesters", Confidence: 0.95, } err := proposal.Validate() if err == nil { t.Fatal("expected validation error, got nil") } if err.Error() != "proposal original_text must not be empty" { t.Fatalf("unexpected error: %v", err) } } func TestCorrectionProposalValidate_AllowsEmptyCorrectedText(t *testing.T) { proposal := CorrectionProposal{ TargetSegmentID: 42, OriginalText: "gestures", CorrectedText: "", Confidence: 0.95, } if err := proposal.Validate(); err != nil { t.Fatalf("expected empty corrected_text to be allowed, got %v", err) } } func TestCorrectionProposalValidate_InvalidConfidence(t *testing.T) { proposal := CorrectionProposal{ TargetSegmentID: 42, OriginalText: "gestures", CorrectedText: "Jesters", Confidence: 1.1, } err := proposal.Validate() if err == nil { t.Fatal("expected validation error, got nil") } if err.Error() != "proposal confidence must be between 0.0 and 1.0" { t.Fatalf("unexpected error: %v", err) } } func TestCorrectionProposalValidate_InvalidSegmentID(t *testing.T) { proposal := CorrectionProposal{ TargetSegmentID: 0, OriginalText: "gestures", CorrectedText: "Jesters", Confidence: 0.95, } err := proposal.Validate() if err == nil { t.Fatal("expected validation error, got nil") } if err.Error() != "proposal id must be positive" { t.Fatalf("unexpected error: %v", err) } } func TestParseReplacementPolicy(t *testing.T) { policy, err := ParseReplacementPolicy("require_unique") if err != nil { t.Fatalf("unexpected parse error: %v", err) } if policy != ReplacementPolicyRequireUnique { t.Fatalf("unexpected policy: %q", policy) } policy, err = ParseReplacementPolicy("replace_all") if err != nil { t.Fatalf("unexpected parse error: %v", err) } if policy != ReplacementPolicyReplaceAll { t.Fatalf("unexpected policy: %q", policy) } _, err = ParseReplacementPolicy("unknown") if err == nil { t.Fatal("expected parse error for unknown policy") } } func TestReplacementPolicyJSON(t *testing.T) { type payload struct { Policy ReplacementPolicy `json:"replacement_policy"` } raw := []byte(`{"replacement_policy":"replace_all"}`) var p payload if err := json.Unmarshal(raw, &p); err != nil { t.Fatalf("unexpected unmarshal error: %v", err) } if p.Policy != ReplacementPolicyReplaceAll { t.Fatalf("unexpected policy: %q", p.Policy) } encoded, err := json.Marshal(p) if err != nil { t.Fatalf("unexpected marshal error: %v", err) } if string(encoded) != `{"replacement_policy":"replace_all"}` { t.Fatalf("unexpected json: %s", string(encoded)) } invalidRaw := []byte(`{"replacement_policy":"invalid"}`) if err := json.Unmarshal(invalidRaw, &p); err == nil { t.Fatal("expected unmarshal error for invalid replacement policy") } } func TestEnrichedCorrectionProposalJSONIncludesMetadata(t *testing.T) { sectionIndex := 3 proposal := EnrichedCorrectionProposal{ CorrectionProposal: CorrectionProposal{ TargetSegmentID: 7, OriginalText: "teh", CorrectedText: "the", Confidence: 0.8, }, ProposalMetadata: ProposalMetadata{ ProposalIndex: 1, ModuleKey: "grammar", ModuleInstance: "grammar_1", SectionIndex: §ionIndex, }, } b, err := json.Marshal(proposal) if err != nil { t.Fatalf("marshal failed: %v", err) } const expected = `{"id":7,"original_text":"teh","corrected_text":"the","confidence":0.8,"proposal_index":1,"module_key":"grammar","module_instance":"grammar_1","section_index":3}` if string(b) != expected { t.Fatalf("unexpected json\nexpected: %s\nactual: %s", expected, string(b)) } }