feat: Implement the initial IPSExtractor project structure, including application logic, GUI, core utilities, and documentation.
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from application.service import InvoiceProcessingService
|
||||
from application.strategies import InvoiceExtractorStrategy
|
||||
import application.service as service_module
|
||||
|
||||
|
||||
class DummyExtractor(InvoiceExtractorStrategy):
|
||||
"""Extractor mínimo para pruebas que devuelve un JSON fijo."""
|
||||
def __init__(self, fail_first: bool = False) -> None:
|
||||
self.fail_first = fail_first
|
||||
self.calls = 0
|
||||
|
||||
def extract(self, images_b64: List[str], instruction: str) -> Dict[str, Any]:
|
||||
self.calls += 1
|
||||
if self.fail_first and self.calls == 1:
|
||||
raise RuntimeError("forced failure")
|
||||
return {
|
||||
"raw": '{"ok": true}',
|
||||
"usage": {},
|
||||
}
|
||||
|
||||
|
||||
def test_extract_full_from_pdf_propagates_tiff_page_range(monkeypatch, tmp_path) -> None:
|
||||
dummy_pdf = tmp_path / "doc.tif"
|
||||
dummy_pdf.write_bytes(b"fake")
|
||||
|
||||
captured: List[Dict[str, Any]] = []
|
||||
|
||||
def fake_document_to_images_bs64(path: str, **kwargs: Any) -> List[str]:
|
||||
captured.append({"path": path, **kwargs})
|
||||
return ["img"]
|
||||
|
||||
# Parchear el símbolo importado en el módulo de servicio
|
||||
monkeypatch.setattr(service_module, "document_to_images_bs64", fake_document_to_images_bs64)
|
||||
|
||||
service = InvoiceProcessingService(DummyExtractor())
|
||||
data, usage = service.extract_full_from_pdf(str(dummy_pdf), instruction="x", tiff_page_range=(2, 3))
|
||||
|
||||
assert data == {"ok": True}
|
||||
assert isinstance(usage, dict)
|
||||
assert captured, "Se esperaba al menos una llamada a document_to_images_bs64"
|
||||
# Primera llamada (flujo normal) debe respetar el rango solicitado
|
||||
assert captured[0].get("tiff_page_range") == (2, 3)
|
||||
|
||||
|
||||
def test_extract_full_from_pdf_fallback_ignores_tiff_page_range(monkeypatch, tmp_path) -> None:
|
||||
dummy_pdf = tmp_path / "doc2.tif"
|
||||
dummy_pdf.write_bytes(b"fake")
|
||||
|
||||
captured: List[Dict[str, Any]] = []
|
||||
|
||||
def fake_document_to_images_bs64(path: str, **kwargs: Any) -> List[str]:
|
||||
captured.append({"path": path, **kwargs})
|
||||
return ["img"]
|
||||
|
||||
monkeypatch.setattr(service_module, "document_to_images_bs64", fake_document_to_images_bs64)
|
||||
|
||||
service = InvoiceProcessingService(DummyExtractor(fail_first=True))
|
||||
data, usage = service.extract_full_from_pdf(str(dummy_pdf), instruction="x", tiff_page_range=(2, 3))
|
||||
|
||||
assert data == {"ok": True}
|
||||
assert isinstance(usage, dict)
|
||||
# Deben existir dos llamadas: una normal (que falla el extractor) y otra fallback
|
||||
assert len(captured) == 2
|
||||
assert captured[0].get("tiff_page_range") == (2, 3)
|
||||
# En fallback NO debe propagarse el rango explícito
|
||||
assert captured[1].get("tiff_page_range") is None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user