| """ |
| tests/test_validator.py — Tests for post-generation citation validator |
| """ |
| import sys |
| from pathlib import Path |
| import pytest |
|
|
| sys.path.insert(0, str(Path(__file__).parent.parent)) |
|
|
| from models import EvidenceChunk |
| from generation.validator import CitationValidator, ValidationReport |
|
|
|
|
| @pytest.fixture |
| def validator(): |
| return CitationValidator() |
|
|
|
|
| @pytest.fixture |
| def evidence(): |
| return [ |
| EvidenceChunk(chunk_id="aabb001100000000", text="Contract text.", source_file="f.pdf", score=0.9), |
| EvidenceChunk(chunk_id="ccdd002200000000", text="Payment terms.", source_file="f.pdf", score=0.8), |
| ] |
|
|
|
|
| class TestValidator: |
| def test_fully_cited_draft(self, validator, evidence): |
| draft = ( |
| "## 1. Document Overview\n" |
| "The contract was signed in January. [CHUNK-aabb0011]\n" |
| "## 2. Parties and Roles\n" |
| "Payment is $25,000 monthly. [CHUNK-ccdd0022]\n" |
| "## 3. Key Facts and Dates\n" |
| "The term is 12 months. [CHUNK-aabb0011]\n" |
| "## 4. Obligations and Terms\n" |
| "Late fees apply at 1.5%. [CHUNK-ccdd0022]\n" |
| "## 5. Red Flags and Issues\n" |
| "Limitation clause may be unenforceable. [CHUNK-aabb0011]\n" |
| "## 6. Analyst Notes\n" |
| "Recommend further review of liability cap. [CHUNK-ccdd0022]\n" |
| ) |
| report = validator.validate(draft, evidence) |
| assert report.uncited_ratio < 0.10 |
| assert report.is_clean() |
| assert report.severity() == "green" |
|
|
| def test_uncited_sentences_detected(self, validator, evidence): |
| draft = ( |
| "## 1. Document Overview\n" |
| "The contract was signed in January. [CHUNK-aabb0011]\n" |
| "This sentence has no citation and is long enough to count.\n" |
| "Another uncited claim that should be flagged by the validator.\n" |
| ) |
| report = validator.validate(draft, evidence) |
| assert len(report.uncited_sentences) == 2 |
| assert report.uncited_ratio > 0 |
|
|
| def test_invalid_citation_detected(self, validator, evidence): |
| draft = "The contract mentions something. [CHUNK-deadbeef]\n" |
| report = validator.validate(draft, evidence) |
| assert len(report.invalid_citations) == 1 |
|
|
| def test_not_evidenced_marker(self, validator, evidence): |
| draft = "The jurisdiction is not stated. [NOT EVIDENCED]\n" |
| report = validator.validate(draft, evidence) |
| assert report.not_evidenced_count == 1 |
| assert len(report.uncited_sentences) == 0 |
|
|
| def test_section_completeness(self, validator, evidence): |
| draft = "## 1. Document Overview\nSome text. [CHUNK-aabb0011]\n" |
| report = validator.validate(draft, evidence) |
| assert len(report.missing_sections) > 0 |
| assert report.section_completeness < 1.0 |
|
|
| def test_empty_draft(self, validator, evidence): |
| report = validator.validate("", evidence) |
| assert report.total_sentences == 0 |
|
|
|
|
| class TestValidationReport: |
| def test_severity_green(self): |
| r = ValidationReport(uncited_ratio=0.05) |
| assert r.severity() == "green" |
|
|
| def test_severity_yellow(self): |
| r = ValidationReport(uncited_ratio=0.15) |
| assert r.severity() == "yellow" |
|
|
| def test_severity_red(self): |
| r = ValidationReport(uncited_ratio=0.30) |
| assert r.severity() == "red" |
|
|
| def test_to_dict(self): |
| r = ValidationReport(total_sentences=10, cited_sentences=8) |
| d = r.to_dict() |
| assert "total_sentences" in d |
| assert "severity" in d |
|
|