mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-13 22:15:10 +00:00
Fixes batch 2
This commit is contained in:
1 parent
c17caf24ec
commit
4e14a79923
16 files changed
+278
-31
No files matched your search
@@ -13,6 +13,7 @@ Three behaviours matter and are asserted separately:
|
||||
import importlib.machinery
|
||||
import sys
|
||||
import types
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -319,9 +320,7 @@ def test_init_parser_imports_for_real_not_just_find_spec(monkeypatch):
|
||||
|
||||
# --- PDF trust check + tableize wiring (PR 3) -----------------------------------
|
||||
|
||||
from pathlib import Path as _Path
|
||||
|
||||
FIXTURES = _Path(__file__).parent / "fixtures"
|
||||
FIXTURES = Path(__file__).parent / "fixtures"
|
||||
CID_PDF = FIXTURES / "nda_en_zh_cid_font.pdf"
|
||||
|
||||
|
||||
|
||||
@@ -158,8 +158,12 @@ class TestOcrEngineSelection:
|
||||
|
||||
def test_build_tesseract_reads_ocr_langs(self, settings, monkeypatch):
|
||||
pytest.importorskip("docling")
|
||||
import application.parser.file.ocr_parser as op
|
||||
from application.parser.file.docling_parser import _build_ocr_options
|
||||
|
||||
# Pack inventory unknown: the resolved list is passed through untouched
|
||||
# (a host with tesseract but no chi_sim pack would otherwise drop it).
|
||||
monkeypatch.setattr(op, "tesseract_languages", lambda: None)
|
||||
monkeypatch.setattr(settings, "OCR_LANGS", "eng+chi_sim")
|
||||
options = _build_ocr_options("tesseract", None, True)
|
||||
|
||||
|
||||
@@ -964,3 +964,38 @@ def test_delegate_parse_lets_setup_errors_through():
|
||||
|
||||
with pytest.raises(ImportError, match="docling is required"):
|
||||
delegate_parse(_NeedsLib(), Path("x.pdf"), "ignore")
|
||||
|
||||
|
||||
class TestTextLayerDelegate:
|
||||
"""``text_layer_delegate`` mirrors ``parse_file``'s delegation decision."""
|
||||
|
||||
def test_every_page_with_text_returns_the_text_parser(self, monkeypatch):
|
||||
import pypdfium2 as pdfium
|
||||
|
||||
class _Pdf:
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
monkeypatch.setattr(pdfium, "PdfDocument", lambda path: _Pdf())
|
||||
text_parser = object()
|
||||
parser = op.NativeOcrPdfParser(text_parser=text_parser, min_text_chars=20)
|
||||
monkeypatch.setattr(parser, "_text_layer_counts", lambda pdf: [50, 60])
|
||||
assert parser.text_layer_delegate(Path("x.pdf")) is text_parser
|
||||
|
||||
def test_a_scanned_page_keeps_the_native_path(self, monkeypatch):
|
||||
import pypdfium2 as pdfium
|
||||
|
||||
class _Pdf:
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
monkeypatch.setattr(pdfium, "PdfDocument", lambda path: _Pdf())
|
||||
parser = op.NativeOcrPdfParser(text_parser=object(), min_text_chars=20)
|
||||
monkeypatch.setattr(parser, "_text_layer_counts", lambda pdf: [50, 3])
|
||||
assert parser.text_layer_delegate(Path("x.pdf")) is None
|
||||
|
||||
def test_no_text_parser_or_unreadable_file_is_none(self, tmp_path):
|
||||
assert op.NativeOcrPdfParser().text_layer_delegate(Path("x.pdf")) is None
|
||||
broken = tmp_path / "broken.pdf"
|
||||
broken.write_bytes(b"not a pdf")
|
||||
assert op.NativeOcrPdfParser(text_parser=object()).text_layer_delegate(broken) is None
|
||||
@@ -182,6 +182,36 @@ class TestStreamWalk:
|
||||
assert result["type0_no_tounicode"] == 1
|
||||
|
||||
|
||||
class TestObjectWalk:
|
||||
def test_bodies_match_the_regex_reference(self):
|
||||
"""The forward walk yields exactly what ``\\d+\\s+\\d+\\s+obj(.*?)endobj`` did."""
|
||||
import re
|
||||
|
||||
from application.parser.file.pdf_trust import _object_bodies
|
||||
|
||||
reference = re.compile(rb"\d+\s+\d+\s+obj(.*?)endobj", re.DOTALL)
|
||||
data = (
|
||||
TYPE0_BARE
|
||||
+ b"7 0 obj no terminator here " # orphan header: body runs to the next endobj
|
||||
+ SIMPLE_FONT
|
||||
+ b"junk 12 0 obj<</A/B>>endobj"
|
||||
+ _stream(b"x")
|
||||
+ b"99 0 obj trailing orphan"
|
||||
)
|
||||
assert list(_object_bodies(data)) == [m.group(1) for m in reference.finditer(data)]
|
||||
|
||||
def test_orphan_headers_are_linear(self):
|
||||
"""Headers with no ``endobj`` after them: the old regex rescanned to EOF for
|
||||
each one (2000 in 1 MB took 13.5 s), pinning an ingest worker per upload."""
|
||||
import time
|
||||
|
||||
data = TYPE0_BARE + b"1 0 obj\n" * 20_000 + b"\x00" * 500_000
|
||||
started = time.perf_counter()
|
||||
result = check_pdf_fonts(data)
|
||||
assert time.perf_counter() - started < 1.0
|
||||
assert result["type0_no_tounicode"] == 1
|
||||
|
||||
|
||||
# --- the CJK cross-check is confirmed against pdfium's text layer --------------
|
||||
|
||||
|
||||
|
||||
@@ -724,6 +724,31 @@ def test_fast_engine_covers_the_whole_legacy_map():
|
||||
assert type(dr._legacy_parser_for(suffix)).__name__ == name
|
||||
|
||||
|
||||
@pytest.mark.parametrize("suffix", [".odt", ".doc", ".rtf", ".xls", ".ods", ".odp"])
|
||||
def test_fast_engine_is_terminal_for_anydoc_only_suffixes(monkeypatch, suffix):
|
||||
"""A suffix only anydoc reads has no legacy parser; `fast` must report that
|
||||
rather than silently running the configured engine the caller opted out of."""
|
||||
|
||||
def _boom(*args, **kwargs):
|
||||
raise AssertionError("fast fell through to the configured engine map")
|
||||
|
||||
monkeypatch.setattr(dr, "get_default_file_extractor", _boom)
|
||||
monkeypatch.setattr(dr, "_zip_bomb_reason", lambda *a, **k: None)
|
||||
|
||||
assert dr._pick_parser(suffix, ocr_enabled=False, engine="fast") is None
|
||||
out = parse_document_bytes(b"\xd0\xcf\x11\xe0 bytes", f"doc{suffix}", output="text", engine="fast")
|
||||
assert "fast engine" in out["error"] and suffix in out["error"]
|
||||
|
||||
|
||||
def test_fast_engine_still_falls_through_for_images_and_audio(monkeypatch):
|
||||
"""Formats with no legacy parser and no anydoc alternative keep the configured map."""
|
||||
seen = []
|
||||
monkeypatch.setattr(dr, "get_default_file_extractor", lambda **kwargs: seen.append(kwargs) or {})
|
||||
for suffix in (".png", ".jpg", ".mp3", ".txt"):
|
||||
dr._pick_parser(suffix, ocr_enabled=True, engine="fast")
|
||||
assert len(seen) == 4
|
||||
|
||||
|
||||
def test_tables_are_not_collected_when_the_pdf_parser_is_not_docling(monkeypatch):
|
||||
"""Under OCR_BACKEND=native the docling engine hands PDFs to the native OCR
|
||||
parser; a vanilla DocumentConverter table pass would OCR the scan again."""
|
||||
@@ -746,6 +771,46 @@ def test_tables_are_not_collected_when_the_pdf_parser_is_not_docling(monkeypatch
|
||||
assert calls == []
|
||||
|
||||
|
||||
def test_tables_survive_native_ocr_delegation_for_text_only_pdfs(monkeypatch):
|
||||
"""OCR_BACKEND=native under the docling engine wraps a DoclingParser as the
|
||||
native parser's text_parser. A PDF with a text layer on every page is handed
|
||||
to that parser, so tables must ride that single conversion instead of being
|
||||
dropped by the scan exclusion."""
|
||||
|
||||
from application.parser.file.docling_parser import DoclingParser
|
||||
from application.parser.file.ocr_parser import NativeOcrPdfParser
|
||||
|
||||
class _Docling(DoclingParser):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._parser_config = {"ready": True}
|
||||
|
||||
docling = _Docling()
|
||||
native = NativeOcrPdfParser(text_parser=docling)
|
||||
native._parser_config = {}
|
||||
monkeypatch.setattr(native, "text_layer_delegate", lambda path: docling)
|
||||
monkeypatch.setattr(
|
||||
native, "parse_file", lambda path, errors="ignore": pytest.fail("native OCR ran on a text-only PDF")
|
||||
)
|
||||
|
||||
calls = []
|
||||
|
||||
def _fake_structured(path, *, ocr_enabled, include_tables, parser=None):
|
||||
calls.append(parser)
|
||||
return {"markdown": "# text pdf", "tables": [{"markdown": "| t |"}], "structured": {}, "page_count": 1}
|
||||
|
||||
monkeypatch.setattr(dr, "_pick_parser", lambda *a, **k: native)
|
||||
monkeypatch.setattr(dr, "_effective_engine", lambda engine: "docling")
|
||||
monkeypatch.setattr(dr, "_docling_structured", _fake_structured)
|
||||
monkeypatch.setattr(dr, "_zip_bomb_reason", lambda *a, **k: None)
|
||||
|
||||
out = parse_document_bytes(b"%PDF-1.4", "text.pdf", engine="docling", include_tables=True)
|
||||
|
||||
assert out["content"] == "# text pdf"
|
||||
assert out["tables"] == [{"markdown": "| t |"}]
|
||||
assert calls == [docling]
|
||||
|
||||
|
||||
def test_binary_office_suffix_without_anydoc_is_an_error_not_text(monkeypatch):
|
||||
monkeypatch.setattr(dr, "_pick_parser", lambda *a, **k: None)
|
||||
monkeypatch.setattr(dr, "_zip_bomb_reason", lambda *a, **k: None)
|
||||
|
||||
Reference in new issue
Block a user