Spaces:
Sleeping
Sleeping
| """Tests for the readiness validator.""" | |
| from __future__ import annotations | |
| from src.app.domain.models import ( | |
| AltoReadiness, | |
| CanonicalDocument, | |
| EvidenceType, | |
| Geometry, | |
| GeometryStatus, | |
| Page, | |
| PageXmlReadiness, | |
| Provenance, | |
| ReadinessLevel, | |
| Source, | |
| TextLine, | |
| TextRegion, | |
| Word, | |
| ) | |
| from src.app.domain.models.status import InputType, MissingCapability | |
| from src.app.validators.readiness_validator import ( | |
| compute_document_readiness, | |
| compute_page_alto_readiness, | |
| compute_page_pagexml_readiness, | |
| ) | |
| def _prov() -> Provenance: | |
| return Provenance( | |
| provider="test", adapter="v1", source_ref="$", | |
| evidence_type=EvidenceType.PROVIDER_NATIVE, | |
| ) | |
| def _geo(status: GeometryStatus = GeometryStatus.EXACT) -> Geometry: | |
| return Geometry(bbox=(10, 10, 100, 30), status=status) | |
| def _complete_page() -> Page: | |
| """A fully complete page with all data.""" | |
| return Page( | |
| id="p1", page_index=0, width=2480, height=3508, | |
| alto_readiness=AltoReadiness(level=ReadinessLevel.FULL), | |
| page_readiness=PageXmlReadiness(level=ReadinessLevel.FULL), | |
| reading_order=["tb1"], | |
| text_regions=[ | |
| TextRegion( | |
| id="tb1", geometry=_geo(), provenance=_prov(), lang="fra", | |
| lines=[ | |
| TextLine( | |
| id="tl1", geometry=_geo(), provenance=_prov(), | |
| words=[ | |
| Word(id="w1", text="Hello", geometry=_geo(), | |
| provenance=_prov(), confidence=0.95), | |
| ], | |
| ), | |
| ], | |
| ), | |
| ], | |
| ) | |
| class TestAltoReadiness: | |
| def test_complete_page_is_full(self) -> None: | |
| page = _complete_page() | |
| r = compute_page_alto_readiness(page) | |
| assert r.level == ReadinessLevel.FULL | |
| assert r.missing == [] | |
| def test_missing_word_geometry_is_none(self) -> None: | |
| page = Page( | |
| id="p1", page_index=0, width=2480, height=3508, | |
| text_regions=[ | |
| TextRegion( | |
| id="tb1", geometry=_geo(), provenance=_prov(), | |
| lines=[TextLine( | |
| id="tl1", geometry=_geo(), provenance=_prov(), | |
| words=[Word( | |
| id="w1", text="Hello", | |
| geometry=_geo(GeometryStatus.UNKNOWN), | |
| provenance=_prov(), | |
| )], | |
| )], | |
| ), | |
| ], | |
| ) | |
| r = compute_page_alto_readiness(page) | |
| assert r.level == ReadinessLevel.NONE | |
| assert MissingCapability.WORD_GEOMETRY in r.missing | |
| def test_missing_confidence_is_partial(self) -> None: | |
| page = Page( | |
| id="p1", page_index=0, width=2480, height=3508, | |
| reading_order=["tb1"], | |
| text_regions=[ | |
| TextRegion( | |
| id="tb1", geometry=_geo(), provenance=_prov(), | |
| lines=[TextLine( | |
| id="tl1", geometry=_geo(), provenance=_prov(), | |
| words=[Word( | |
| id="w1", text="Hello", geometry=_geo(), | |
| provenance=_prov(), confidence=None, | |
| )], | |
| )], | |
| ), | |
| ], | |
| ) | |
| r = compute_page_alto_readiness(page) | |
| assert r.level == ReadinessLevel.PARTIAL | |
| assert MissingCapability.CONFIDENCE in r.missing | |
| def test_no_reading_order_is_partial(self) -> None: | |
| page = Page( | |
| id="p1", page_index=0, width=2480, height=3508, | |
| reading_order=[], | |
| text_regions=[ | |
| TextRegion( | |
| id="tb1", geometry=_geo(), provenance=_prov(), | |
| lines=[TextLine( | |
| id="tl1", geometry=_geo(), provenance=_prov(), | |
| words=[Word( | |
| id="w1", text="Hello", geometry=_geo(), | |
| provenance=_prov(), confidence=0.9, | |
| )], | |
| )], | |
| ), | |
| ], | |
| ) | |
| r = compute_page_alto_readiness(page) | |
| assert r.level == ReadinessLevel.PARTIAL | |
| assert MissingCapability.READING_ORDER in r.missing | |
| def test_empty_page_is_none(self) -> None: | |
| page = Page(id="p1", page_index=0, width=2480, height=3508) | |
| r = compute_page_alto_readiness(page) | |
| assert r.level == ReadinessLevel.NONE | |
| class TestPageXmlReadiness: | |
| def test_complete_page_is_full(self) -> None: | |
| page = _complete_page() | |
| r = compute_page_pagexml_readiness(page) | |
| assert r.level == ReadinessLevel.FULL | |
| def test_no_regions_is_none(self) -> None: | |
| page = Page(id="p1", page_index=0, width=2480, height=3508) | |
| r = compute_page_pagexml_readiness(page) | |
| assert r.level == ReadinessLevel.NONE | |
| def test_regions_without_word_geo_still_ok(self) -> None: | |
| """PAGE XML is more lenient — word geometry is not critical.""" | |
| page = Page( | |
| id="p1", page_index=0, width=2480, height=3508, | |
| reading_order=["tb1"], | |
| text_regions=[ | |
| TextRegion( | |
| id="tb1", geometry=_geo(), provenance=_prov(), | |
| lines=[TextLine( | |
| id="tl1", geometry=_geo(), provenance=_prov(), | |
| words=[Word( | |
| id="w1", text="Hello", | |
| geometry=_geo(GeometryStatus.UNKNOWN), | |
| provenance=_prov(), | |
| )], | |
| )], | |
| ), | |
| ], | |
| ) | |
| r = compute_page_pagexml_readiness(page) | |
| # PAGE doesn't require word geometry — should still be achievable | |
| assert r.level in (ReadinessLevel.FULL, ReadinessLevel.PARTIAL) | |
| class TestDocumentReadiness: | |
| def test_single_full_page(self) -> None: | |
| doc = CanonicalDocument( | |
| document_id="test", | |
| source=Source(input_type=InputType.IMAGE), | |
| pages=[_complete_page()], | |
| ) | |
| dr = compute_document_readiness(doc) | |
| assert dr.level == ReadinessLevel.FULL | |
| def test_mixed_pages(self) -> None: | |
| full_page = _complete_page() | |
| empty_page = Page(id="p2", page_index=1, width=2480, height=3508) | |
| doc = CanonicalDocument( | |
| document_id="test", | |
| source=Source(input_type=InputType.IMAGE), | |
| pages=[full_page, empty_page], | |
| ) | |
| dr = compute_document_readiness(doc) | |
| assert dr.level == ReadinessLevel.DEGRADED | |
| assert len(dr.page_readiness) == 2 | |