package validators import ( "encoding/json" "strings" "testing" "gitea.maximumdirect.net/eric/audita/internal/core/chunking" "gitea.maximumdirect.net/eric/audita/internal/core/schema" "gitea.maximumdirect.net/eric/audita/internal/framework/proposals" ) func TestValidationContextCarriesAdjacentEvidenceAndRelevantDefinitions(t *testing.T) { req := makeReq([]proposals.EnrichedCorrectionProposal{mk(7, "gestures", "Jesters")}) req.WorkingTranscript = &schema.Transcript{Segments: []schema.Segment{ {ID: 1, Text: "The performers arrived."}, {ID: 2, Text: "Those gestures are back."}, {ID: 3, Text: "Lyra recognized the troupe."}, {ID: 4, Text: "Elsewhere."}, }} req.CandidateProposal[0].TargetSegmentID = 2 req.Glossary = &schema.Glossary{Entries: []schema.GlossaryEntry{ {Name: "Jesters", Summary: "A traveling troupe"}, {Name: "Lyra", Summary: "The host"}, {Name: "Unrelated", Summary: "Not mentioned"}, }} payload, immediate := BuildLLMValidationRequest("spoken_form_plausibility", LLMValidatorTypeSpokenFormPlausibility, req) if len(immediate) != 0 || len(payload.Items) != 1 { t.Fatalf("payload: %+v immediate: %+v", payload, immediate) } item := payload.Items[0] if len(item.NeighboringSegments) != 2 || item.NeighboringSegments[0].Position != "preceding" || item.NeighboringSegments[1].Position != "following" || len(item.GlossaryEntries) != 2 || item.GlossaryEntries[0].Name != "Jesters" || item.GlossaryEntries[1].Name != "Lyra" { t.Fatalf("context: %+v", item) } variables, err := BuildValidationVariables(payload.Items, "") if err != nil { t.Fatal(err) } var rendered []LLMValidationItem if err := json.Unmarshal([]byte(variables["PayloadJSON"]), &rendered); err != nil { t.Fatal(err) } if strings.Contains(variables["PayloadJSON"], `"id":`) || rendered[0].CorrectionIndex != 1 || payload.Items[0].CorrectionIndex != 7 { t.Fatalf("leaked internal identity or mutated request: %s", variables["PayloadJSON"]) } if len(rendered[0].NeighboringSegments) != 2 || rendered[0].GlossaryEntries[0].Summary != "A traveling troupe" { t.Fatalf("rendered context: %+v", rendered) } // The context-free endpoint must not reach beyond the current chunk. req.WorkingTranscript.Segments = req.WorkingTranscript.Segments[1:2] req.Glossary = nil isolated, _ := BuildLLMValidationRequest("editorial_review", LLMValidatorTypeEditorialReview, req) if len(isolated.Items[0].NeighboringSegments) != 0 || len(isolated.Items[0].GlossaryEntries) != 0 { t.Fatalf("unexpected external context: %+v", isolated) } } func TestValidationContextFitsBudgetWithoutDiscardingCandidate(t *testing.T) { estimator := chunking.NewSimpleTokenEstimator() base := LLMValidationItem{CorrectionIndex: 7, SegmentID: 1, OriginalText: "teh", CorrectedText: "the", OriginalSegmentText: "teh cat", CorrectedSegmentText: "the cat"} required, err := estimateBatchTokens(estimator, []LLMValidationItem{base}) if err != nil { t.Fatal(err) } enriched := base enriched.NeighboringSegments = []NeighboringSegment{{Position: "following", Text: strings.Repeat("long context ", 300)}} enriched.GlossaryEntries = []schema.GlossaryEntry{{Name: "cat", Summary: strings.Repeat("long definition ", 300)}} fitted := fitValidationContext(enriched, required, estimator) tokens, err := estimateBatchTokens(estimator, []LLMValidationItem{fitted}) if err != nil || tokens > required || fitted.OriginalSegmentText != base.OriginalSegmentText || fitted.CorrectedSegmentText != base.CorrectedSegmentText { t.Fatalf("fitted: %+v tokens=%d err=%v", fitted, tokens, err) } full, _ := estimateBatchTokens(estimator, []LLMValidationItem{enriched}) batches, err := ChunkLLMValidationItems([]LLMValidationItem{enriched, enriched}, full, estimator) if err != nil || len(batches) != 2 { t.Fatalf("context must count toward batch budget: %+v %v", batches, err) } } func TestValidationContextSelectsGlossaryForEachReplacement(t *testing.T) { req := makeReq([]proposals.EnrichedCorrectionProposal{mk(7, "gestures", "Jesters"), mk(11, "gestures", "Actors")}) req.WorkingTranscript = &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "Those gestures are back."}}} req.Glossary = &schema.Glossary{Entries: []schema.GlossaryEntry{{Name: "Actors"}, {Name: "Jesters"}}} payload, immediate := BuildLLMValidationRequest("editorial_review", LLMValidatorTypeEditorialReview, req) if len(immediate) != 0 || len(payload.Items) != 2 { t.Fatalf("payload: %+v immediate: %+v", payload, immediate) } for i, name := range []string{"Jesters", "Actors"} { entries := payload.Items[i].GlossaryEntries if len(entries) != 1 || entries[0].Name != name { t.Fatalf("candidate %d context: %+v", i, entries) } } }