""" Unit tests for ReportAgent's similarity-highlight span builder (plan.md ยง8.4, Phase 5). The builder turns a SimilarityReport dict into mapper input spans so the single combined PdfHighlightMapper call can render both AI-detection and similarity overlays from one invocation. """ from agents.report_agent import similarity_highlight_spans def _report(status="complete", spans_per_source=1, source_count=2): return { "status": status, "sources": [ { "source_index": i + 1, "percent": 10 * (i + 1), "spans": [ { "excerpt": f"shared passage number {j} of source {i + 1}", "doc_char_start": j, "doc_char_end": j + 10, } for j in range(spans_per_source) ], } for i in range(source_count) ], } def test_builds_spans_with_kind_and_source_index(): spans = similarity_highlight_spans(_report(spans_per_source=2, source_count=2)) assert len(spans) == 4 assert all(s["kind"] == "similarity" for s in spans) assert spans[0]["meta"] == {"source_index": 1} assert spans[2]["meta"] == {"source_index": 2} assert "shared passage" in spans[0]["text"] def test_unavailable_report_contributes_nothing(): assert similarity_highlight_spans(_report(status="unavailable")) == [] assert similarity_highlight_spans({"status": "partial", "sources": []}) == [] def test_empty_excerpts_are_dropped(): report = _report(spans_per_source=2, source_count=2) report["sources"][0]["spans"] = [ {"excerpt": " ", "doc_char_start": 0, "doc_char_end": 1} ] spans = similarity_highlight_spans(report) assert all(s["text"] for s in spans) assert len(spans) == 2 # empty excerpt of source 1 dropped, source 2 kept def test_none_report_returns_empty(): assert similarity_highlight_spans(None) == []