"""Adversarial regression cases for `rag.grounding.verify`. Each case reproduces a defect found by Codex's 2026-08-06 code review (`coordination/CODEX_RAG_CODE_REVIEW_2026-08-06.md`, F-01) against the old implementation, which pooled every evidence number into one global set and never required a citation at all. `verify` must now bind a claim's numbers to only the evidence block(s) its own citation group names, and must reject a claim with no citation regardless of whether it contains a number. """ from rag import grounding def test_number_from_the_wrong_evidence_block_is_rejected(): # Reproduces `so_sai_nguon`: 500 mg is real, but only in evidence 2 — # citing [1] for it must fail, not pass because 500 exists *somewhere*. report = grounding.verify( "Liều 500 mg [1].", ("Không dùng khi suy thận.", "Liều 500 mg mỗi ngày."), ) assert not report.grounded assert report.unsupported_numbers == ("500",) assert report.reason == "ungrounded_number" def test_citing_the_correct_block_for_the_number_is_grounded(): report = grounding.verify( "Liều 500 mg [2].", ("Không dùng khi suy thận.", "Liều 500 mg mỗi ngày."), ) assert report.grounded assert report.cited_indices == (2,) def test_answer_with_no_citation_at_all_is_rejected(): # Reproduces `khong_citation`: a number that is genuinely in the evidence # still must not pass when the answer never cites anything. report = grounding.verify("Liều 500 mg.", ("Liều 500 mg mỗi ngày.",)) assert not report.grounded assert report.uncited_claim assert report.unsupported_numbers == ("500",) assert report.reason == "ungrounded_number" def test_nonnumeric_claim_with_no_citation_is_rejected(): report = grounding.verify( "Chống chỉ định với suy gan nặng.", ("Chống chỉ định: suy gan nặng.",) ) assert not report.grounded assert report.uncited_claim assert report.reason == "uncited_claim" def test_trailing_text_after_the_last_citation_needs_its_own_citation(): report = grounding.verify( "Liều 500 mg [1]. Không dùng khi suy thận.", ("Liều 500 mg mỗi ngày.",), ) assert not report.grounded assert report.uncited_claim def test_out_of_range_citation_is_invalid_and_leaves_its_claim_unsupported(): report = grounding.verify("Liều 500 mg [3].", ("Liều 500 mg mỗi ngày.",)) assert not report.grounded assert report.invalid_citations == (3,) assert report.unsupported_numbers == ("500",) assert report.reason == "ungrounded_number" def test_two_claims_each_binding_correctly_to_their_own_source_is_grounded(): report = grounding.verify( "Người lớn 500 mg [1]. Trẻ em 250 mg [2].", ("Liều người lớn 500 mg.", "Liều trẻ em 250 mg."), ) assert report.grounded assert report.cited_indices == (1, 2) def test_second_claim_citing_the_first_blocks_source_number_is_rejected(): # The child dose (250) is real, but only in evidence 2; citing [1] for it # is the same defect as `so_sai_nguon`, just in a second sentence. report = grounding.verify( "Người lớn 500 mg [1]. Trẻ em 250 mg [1].", ("Liều người lớn 500 mg.", "Liều trẻ em 250 mg."), ) assert not report.grounded assert report.unsupported_numbers == ("250",) def test_multiple_markers_on_one_claim_check_against_their_union(): report = grounding.verify( "Liều 500 mg [1][2].", ("Liều người lớn 500 mg.", "Liều mỗi ngày."), ) assert report.grounded assert report.cited_indices == (1, 2) def test_extractive_quoting_format_is_still_grounded(): # The service's extractive fallback formats each block as `text [n]`. report = grounding.verify( "Liều được ghi trong nguồn. [1]", ("Liều được ghi trong nguồn.",) ) assert report.grounded def test_decimal_separator_is_compared_verbatim_not_normalised(): report = grounding.verify("Liều 7.5 mg [1].", ("Liều 7,5 mg.",)) assert not report.grounded assert report.unsupported_numbers == ("7.5",) def test_known_gap_fabricated_nonnumeric_claim_with_a_valid_citation_still_passes(): # Documents the residual gap `grounding.verify` cannot close on its own # (see module docstring): a citation-bearing claim whose content the # cited block does not actually support. Closed by a separate LLM # entailment pass in `rag/answer.py`, not by this regex-only check. report = grounding.verify( "Metformin chữa ung thư [1].", ("Metformin dùng điều trị đái tháo đường.",), ) assert report.grounded