lexora / apps /api /tests /test_verify.py
Abdr007's picture
Lexora — deployed tree
547ce6e
Raw
History Blame Contribute Delete
4.47 kB
"""Citation verifier — the last line of defence against a fabricated reference."""
from __future__ import annotations
import pytest
from app.core.models import CitationStatus
from app.rag.verify import extract_citations, find_uncited_claims, verify_answer
from tests.conftest import make_chunk, make_scored
@pytest.fixture
def evidence():
return [
make_scored(make_chunk(article_no=51, law_label="Labour Law")),
make_scored(make_chunk(article_no=30, law_label="Labour Law")),
make_scored(make_chunk(article_no=25, law_label="Tenancy Law", law_id="dubai-tenancy-law")),
]
class TestExtractCitations:
@pytest.mark.parametrize(
("text", "expected"),
[
("x [Labour Law, Article 51] y", ("Labour Law", 51)),
("x [Labour Law, Art. 51] y", ("Labour Law", 51)),
("x [Labour Law, Art 51] y", ("Labour Law", 51)),
("x [labour law , article (51) ] y", ("labour law", 51)),
("x [Tenancy Law, Article 25] y", ("Tenancy Law", 25)),
],
)
def test_accepts_the_tolerated_forms(self, text: str, expected: tuple[str, int]):
"""A verifier that only recognises perfect formatting silently passes bad output."""
found = extract_citations(text)
assert len(found) == 1
assert (found[0][1], found[0][2]) == expected
def test_ignores_prose_that_is_not_a_citation(self):
assert extract_citations("Article 51 says something without brackets") == []
assert extract_citations("[see the annex]") == []
class TestVerifyAnswer:
def test_citation_present_in_context_is_verified(self, evidence):
report = verify_answer("Gratuity is payable [Labour Law, Article 51].", evidence)
assert report.passed
assert report.verified_count == 1
assert report.unsupported_count == 0
assert report.citations[0].chunk_id is not None
def test_fabricated_article_is_flagged_not_dropped(self, evidence):
"""Article 99 was never retrieved. The answer is flagged, and the citation kept.
Silently deleting it would hand the reader a redacted answer with no way to know
something was removed.
"""
report = verify_answer("It says X [Labour Law, Article 99].", evidence)
assert not report.passed
assert report.unsupported_count == 1
assert report.citations[0].status is CitationStatus.UNSUPPORTED
assert report.citations[0].chunk_id is None
def test_right_article_wrong_law_is_unsupported(self, evidence):
"""Article 51 exists — in the Labour Law, not the Tenancy Law."""
report = verify_answer("See [Tenancy Law, Article 51].", evidence)
assert not report.passed
assert report.unsupported_count == 1
def test_label_matching_ignores_case_and_punctuation(self, evidence):
report = verify_answer("See [labour-law, Article 51].", evidence)
assert report.passed
def test_matches_at_article_level_not_chunk_level(self):
"""A model given chunk 2 of Article 30 and citing Article 30 has cited correctly."""
chunk = make_chunk(article_no=30, text="Labour Law - Article 30 (2/2)\ntail of the article")
report = verify_answer("See [Labour Law, Article 30].", [make_scored(chunk)])
assert report.passed
def test_no_citations_at_all_still_passes_but_reports_uncited(self, evidence):
"""Zero citations is not a fabrication; it is reported as unverified prose."""
report = verify_answer("The law is generally quite protective of workers here.", evidence)
assert report.passed
assert report.verified_count == 0
assert report.uncited_sentences
def test_empty_evidence_makes_every_citation_unsupported(self):
report = verify_answer("See [Labour Law, Article 51].", [])
assert not report.passed
assert report.unsupported_count == 1
class TestUncitedClaims:
def test_short_fragments_are_not_claims(self):
assert find_uncited_claims("Yes. No. Maybe.") == []
def test_cited_sentences_are_not_reported(self):
text = "A worker gets thirty days of annual leave each year [Labour Law, Article 29]."
assert find_uncited_claims(text) == []
def test_uncited_assertion_is_reported(self):
text = "The employer must always pay double wages on public holidays without exception."
assert len(find_uncited_claims(text)) == 1