From 28255668b9299516ccd5d2bba549b5bcbf8ad69a Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Sat, 3 Oct 2026 15:17:19 -0700 Subject: [PATCH] Chore: Move the four duplicated parser sample PDFs into a shared paperless_testing location Co-Authored-By: Claude Sonnet 5.5 --- pyproject.toml | 4 +- src/conftest.py | 32 ++++++ src/paperless/tests/parsers/conftest.py | 24 ----- .../tests/parsers/test_tesseract_parser.py | 93 +++++++++--------- src/paperless/tests/test_parser_utils.py | 10 +- .../sample_files}/multi-page-digital.pdf | Bin .../sample_files}/multi-page-images.pdf | Bin .../sample_files}/simple-digital.pdf | Bin .../sample_files}/with-form.pdf | Bin src/paperless_testing/samples.py | 14 +++ 10 files changed, 99 insertions(+), 78 deletions(-) rename src/{paperless/tests/samples/tesseract => paperless_testing/sample_files}/multi-page-digital.pdf (100%) rename src/{paperless/tests/samples/tesseract => paperless_testing/sample_files}/multi-page-images.pdf (100%) rename src/{paperless/tests/samples/tesseract => paperless_testing/sample_files}/simple-digital.pdf (100%) rename src/{paperless/tests/samples/tesseract => paperless_testing/sample_files}/with-form.pdf (100%) create mode 100644 src/paperless_testing/samples.py diff --git a/pyproject.toml b/pyproject.toml index fbd4593d8..ebf808271 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -256,8 +256,8 @@ isort.force-single-line = true [tool.codespell] ignore-words-list = "criterias,afterall,valeu,ureue,equest,ure,assertIn,Oktober,commitish,NIN,nin,reprot" skip = """\ - src-ui/src/locale/*,src-ui/pnpm-lock.yaml,src-ui/e2e/*,src/paperless_mail/tests/samples/*,src/paperless/tests/samples\ - /mail/*,src/documents/tests/samples/*,*.po,*.json\ + src-ui/src/locale/*,src-ui/pnpm-lock.yaml,src-ui/e2e/*,src/paperless/tests/samples/mail/*,src/documents/tests/samples\ + /*,src/paperless_testing/sample_files/*,*.po,*.json\ """ write-changes = true diff --git a/src/conftest.py b/src/conftest.py index 781cdff61..8cedbcc56 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -11,6 +11,8 @@ from typing import TYPE_CHECKING import pytest +from paperless_testing import samples + if TYPE_CHECKING: from collections.abc import Generator from pathlib import Path @@ -149,3 +151,33 @@ def fake_progress_manager( monkeypatch.setattr("documents.tasks.ProgressManager", FakeProgressManager) return FakeProgressManager + + +@pytest.fixture(scope="session") +def shared_samples_dir() -> Path: + """Directory of sample files used by more than one app's tests.""" + return samples.SHARED_SAMPLES_DIR + + +@pytest.fixture(scope="session") +def simple_digital_pdf_file() -> Path: + """One-page PDF with a text layer.""" + return samples.SIMPLE_DIGITAL_PDF + + +@pytest.fixture(scope="session") +def multi_page_digital_pdf_file() -> Path: + """Three-page PDF with a text layer.""" + return samples.MULTI_PAGE_DIGITAL_PDF + + +@pytest.fixture(scope="session") +def with_form_pdf_file() -> Path: + """PDF containing a fillable form.""" + return samples.WITH_FORM_PDF + + +@pytest.fixture(scope="session") +def multi_page_images_pdf_file() -> Path: + """Multi-page PDF of scanned images, no text layer.""" + return samples.MULTI_PAGE_IMAGES_PDF diff --git a/src/paperless/tests/parsers/conftest.py b/src/paperless/tests/parsers/conftest.py index 343e9ea8a..2175e3667 100644 --- a/src/paperless/tests/parsers/conftest.py +++ b/src/paperless/tests/parsers/conftest.py @@ -432,30 +432,6 @@ def tesseract_samples_dir(parser_samples_dir: Path) -> Path: return parser_samples_dir / "tesseract" -@pytest.fixture(scope="session") -def multi_page_images_pdf_file(tesseract_samples_dir: Path) -> Path: - """Path to a multi-page PDF with images. - - Returns - ------- - Path - Absolute path to ``tesseract/multi-page-images.pdf``. - """ - return tesseract_samples_dir / "multi-page-images.pdf" - - -@pytest.fixture(scope="session") -def simple_digital_pdf_file(tesseract_samples_dir: Path) -> Path: - """Path to a simple digital PDF sample file. - - Returns - ------- - Path - Absolute path to ``tesseract/simple-digital.pdf``. - """ - return tesseract_samples_dir / "simple-digital.pdf" - - @pytest.fixture(scope="session") def simple_no_dpi_png_file(tesseract_samples_dir: Path) -> Path: """Path to a simple PNG without DPI information. diff --git a/src/paperless/tests/parsers/test_tesseract_parser.py b/src/paperless/tests/parsers/test_tesseract_parser.py index f057ad086..ff451dbeb 100644 --- a/src/paperless/tests/parsers/test_tesseract_parser.py +++ b/src/paperless/tests/parsers/test_tesseract_parser.py @@ -161,11 +161,11 @@ class TestGetPageCount: def test_single_page_pdf( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, ) -> None: assert ( tesseract_parser.get_page_count( - tesseract_samples_dir / "simple-digital.pdf", + simple_digital_pdf_file, "application/pdf", ) == 1 @@ -188,7 +188,7 @@ class TestGetPageCount: self, mocker: MockerFixture, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, caplog, ) -> None: """ @@ -204,7 +204,7 @@ class TestGetPageCount: with caplog.at_level(logging.WARNING): page_count = tesseract_parser.get_page_count( - tesseract_samples_dir / "simple-digital.pdf", + simple_digital_pdf_file, "application/pdf", ) assert page_count is None @@ -273,10 +273,10 @@ class TestGetThumbnail: def test_thumbnail_is_file( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, ) -> None: thumb = tesseract_parser.get_thumbnail( - tesseract_samples_dir / "simple-digital.pdf", + simple_digital_pdf_file, "application/pdf", ) assert thumb.is_file() @@ -316,7 +316,7 @@ class TestGetThumbnail: self, mocker: MockerFixture, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, ) -> None: """ GIVEN: @@ -326,7 +326,7 @@ class TestGetThumbnail: THEN: - The PDF is repaired with qpdf and a real thumbnail is rendered """ - original = tesseract_samples_dir / "simple-digital.pdf" + original = simple_digital_pdf_file def _fail_on_original(in_path: Path, out_path: Path, **kwargs) -> None: if in_path == original: @@ -368,11 +368,11 @@ class TestExtractText: def test_extract_text_from_digital_pdf( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, ) -> None: text = tesseract_parser.extract_text( None, - tesseract_samples_dir / "simple-digital.pdf", + simple_digital_pdf_file, ) assert text is not None assert "This is a test document." in text.strip() @@ -387,7 +387,7 @@ class TestParsePdf: def test_simple_digital_creates_archive( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + multi_page_digital_pdf_file: Path, ) -> None: """ GIVEN: @@ -401,7 +401,7 @@ class TestParsePdf: - Text is extracted """ tesseract_parser.parse( - tesseract_samples_dir / "multi-page-digital.pdf", + multi_page_digital_pdf_file, "application/pdf", ) assert tesseract_parser.archive_path is not None @@ -416,10 +416,10 @@ class TestParsePdf: def test_with_form_default( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + with_form_pdf_file: Path, ) -> None: tesseract_parser.parse( - tesseract_samples_dir / "with-form.pdf", + with_form_pdf_file, "application/pdf", ) assert tesseract_parser.archive_path is not None @@ -432,11 +432,11 @@ class TestParsePdf: def test_with_form_redo_no_archive_when_not_requested( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + with_form_pdf_file: Path, ) -> None: tesseract_parser.settings.mode = ModeChoices.REDO tesseract_parser.parse( - tesseract_samples_dir / "with-form.pdf", + with_form_pdf_file, "application/pdf", produce_archive=False, ) @@ -449,11 +449,11 @@ class TestParsePdf: def test_with_form_force( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + with_form_pdf_file: Path, ) -> None: tesseract_parser.settings.mode = ModeChoices.FORCE tesseract_parser.parse( - tesseract_samples_dir / "with-form.pdf", + with_form_pdf_file, "application/pdf", ) assert_ordered_substrings( @@ -494,7 +494,7 @@ class TestParsePdf: self, mocker: MockerFixture, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, ) -> None: mocker.patch( "ocrmypdf.ocr", @@ -502,7 +502,7 @@ class TestParsePdf: ) with pytest.raises(ParseError): tesseract_parser.parse( - tesseract_samples_dir / "simple-digital.pdf", + simple_digital_pdf_file, "application/pdf", ) @@ -578,10 +578,10 @@ class TestParseMultiPage: def test_multi_page_digital( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + multi_page_digital_pdf_file: Path, ) -> None: tesseract_parser.parse( - tesseract_samples_dir / "multi-page-digital.pdf", + multi_page_digital_pdf_file, "application/pdf", ) assert tesseract_parser.archive_path is not None @@ -605,12 +605,12 @@ class TestParseMultiPage: self, mode: str, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + multi_page_digital_pdf_file: Path, ) -> None: tesseract_parser.settings.pages = 2 tesseract_parser.settings.mode = mode tesseract_parser.parse( - tesseract_samples_dir / "multi-page-digital.pdf", + multi_page_digital_pdf_file, "application/pdf", ) assert tesseract_parser.archive_path is not None @@ -624,11 +624,11 @@ class TestParseMultiPage: def test_multi_page_images_skip( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + multi_page_images_pdf_file: Path, ) -> None: tesseract_parser.settings.mode = ModeChoices.AUTO tesseract_parser.parse( - tesseract_samples_dir / "multi-page-images.pdf", + multi_page_images_pdf_file, "application/pdf", ) assert tesseract_parser.archive_path is not None @@ -642,7 +642,7 @@ class TestParseMultiPage: def test_multi_page_images_redo_pages_2( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + multi_page_images_pdf_file: Path, ) -> None: """ GIVEN: @@ -657,7 +657,7 @@ class TestParseMultiPage: tesseract_parser.settings.pages = 2 tesseract_parser.settings.mode = ModeChoices.REDO tesseract_parser.parse( - tesseract_samples_dir / "multi-page-images.pdf", + multi_page_images_pdf_file, "application/pdf", ) assert tesseract_parser.archive_path is not None @@ -670,7 +670,7 @@ class TestParseMultiPage: def test_multi_page_images_force_page_1( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + multi_page_images_pdf_file: Path, ) -> None: """ GIVEN: @@ -685,7 +685,7 @@ class TestParseMultiPage: tesseract_parser.settings.pages = 1 tesseract_parser.settings.mode = ModeChoices.FORCE tesseract_parser.parse( - tesseract_samples_dir / "multi-page-images.pdf", + multi_page_images_pdf_file, "application/pdf", ) assert tesseract_parser.archive_path is not None @@ -781,7 +781,7 @@ class TestSkipArchive: def test_skip_noarchive_with_text_layer( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + multi_page_digital_pdf_file: Path, ) -> None: """ GIVEN: @@ -795,7 +795,7 @@ class TestSkipArchive: """ tesseract_parser.settings.mode = ModeChoices.AUTO tesseract_parser.parse( - tesseract_samples_dir / "multi-page-digital.pdf", + multi_page_digital_pdf_file, "application/pdf", produce_archive=False, ) @@ -810,7 +810,7 @@ class TestSkipArchive: def test_skip_noarchive_image_only_creates_archive( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + multi_page_images_pdf_file: Path, ) -> None: """ GIVEN: @@ -823,7 +823,7 @@ class TestSkipArchive: """ tesseract_parser.settings.mode = ModeChoices.AUTO tesseract_parser.parse( - tesseract_samples_dir / "multi-page-images.pdf", + multi_page_images_pdf_file, "application/pdf", ) assert tesseract_parser.archive_path is not None @@ -835,29 +835,29 @@ class TestSkipArchive: ) @pytest.mark.parametrize( - ("produce_archive", "filename", "expect_archive"), + ("produce_archive", "sample_fixture", "expect_archive"), [ pytest.param( True, - "multi-page-digital.pdf", + "multi_page_digital_pdf_file", True, id="produce-archive-with-text", ), pytest.param( True, - "multi-page-images.pdf", + "multi_page_images_pdf_file", True, id="produce-archive-no-text", ), pytest.param( False, - "multi-page-digital.pdf", + "multi_page_digital_pdf_file", False, id="no-archive-with-text-layer", ), pytest.param( False, - "multi-page-images.pdf", + "multi_page_images_pdf_file", False, id="no-archive-no-text-layer", ), @@ -866,10 +866,10 @@ class TestSkipArchive: def test_produce_archive_flag( self, produce_archive: bool, # noqa: FBT001 - filename: str, + sample_fixture: str, expect_archive: bool, # noqa: FBT001 tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + request: pytest.FixtureRequest, ) -> None: """ GIVEN: @@ -882,8 +882,9 @@ class TestSkipArchive: - Text is always extracted """ tesseract_parser.settings.mode = ModeChoices.AUTO + sample = request.getfixturevalue(sample_fixture) tesseract_parser.parse( - tesseract_samples_dir / filename, + sample, "application/pdf", produce_archive=produce_archive, ) @@ -900,7 +901,7 @@ class TestSkipArchive: self, mocker: MockerFixture, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, ) -> None: """ GIVEN: @@ -916,7 +917,7 @@ class TestSkipArchive: tesseract_parser.settings.mode = ModeChoices.AUTO mock_ocr = mocker.patch("ocrmypdf.ocr") tesseract_parser.parse( - tesseract_samples_dir / "simple-digital.pdf", + simple_digital_pdf_file, "application/pdf", produce_archive=False, ) @@ -955,7 +956,7 @@ class TestSkipArchive: def test_tagged_pdf_produces_pdfa_archive_without_ocr( self, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, ) -> None: """ GIVEN: @@ -970,7 +971,7 @@ class TestSkipArchive: """ tesseract_parser.settings.mode = ModeChoices.AUTO tesseract_parser.parse( - tesseract_samples_dir / "simple-digital.pdf", + simple_digital_pdf_file, "application/pdf", produce_archive=True, ) diff --git a/src/paperless/tests/test_parser_utils.py b/src/paperless/tests/test_parser_utils.py index 7bae1f78d..a56d0ac9e 100644 --- a/src/paperless/tests/test_parser_utils.py +++ b/src/paperless/tests/test_parser_utils.py @@ -18,8 +18,6 @@ from paperless.parsers.utils import read_file_handle_unicode_errors if TYPE_CHECKING: from pytest_mock import MockerFixture -SAMPLES = Path(__file__).parent / "samples" / "tesseract" - class TestReadFileHandleUnicodeErrors: def test_plain_utf8(self, tmp_path: Path) -> None: @@ -57,11 +55,11 @@ class TestReadFileHandleUnicodeErrors: class TestIsTaggedPdf: - def test_tagged_pdf_returns_true(self) -> None: - assert is_tagged_pdf(SAMPLES / "simple-digital.pdf") is True + def test_tagged_pdf_returns_true(self, simple_digital_pdf_file: Path) -> None: + assert is_tagged_pdf(simple_digital_pdf_file) is True - def test_untagged_pdf_returns_false(self) -> None: - assert is_tagged_pdf(SAMPLES / "multi-page-images.pdf") is False + def test_untagged_pdf_returns_false(self, multi_page_images_pdf_file: Path) -> None: + assert is_tagged_pdf(multi_page_images_pdf_file) is False def test_nonexistent_path_returns_false(self) -> None: assert is_tagged_pdf(Path("/nonexistent/file.pdf")) is False diff --git a/src/paperless/tests/samples/tesseract/multi-page-digital.pdf b/src/paperless_testing/sample_files/multi-page-digital.pdf similarity index 100% rename from src/paperless/tests/samples/tesseract/multi-page-digital.pdf rename to src/paperless_testing/sample_files/multi-page-digital.pdf diff --git a/src/paperless/tests/samples/tesseract/multi-page-images.pdf b/src/paperless_testing/sample_files/multi-page-images.pdf similarity index 100% rename from src/paperless/tests/samples/tesseract/multi-page-images.pdf rename to src/paperless_testing/sample_files/multi-page-images.pdf diff --git a/src/paperless/tests/samples/tesseract/simple-digital.pdf b/src/paperless_testing/sample_files/simple-digital.pdf similarity index 100% rename from src/paperless/tests/samples/tesseract/simple-digital.pdf rename to src/paperless_testing/sample_files/simple-digital.pdf diff --git a/src/paperless/tests/samples/tesseract/with-form.pdf b/src/paperless_testing/sample_files/with-form.pdf similarity index 100% rename from src/paperless/tests/samples/tesseract/with-form.pdf rename to src/paperless_testing/sample_files/with-form.pdf diff --git a/src/paperless_testing/samples.py b/src/paperless_testing/samples.py new file mode 100644 index 000000000..a34602b53 --- /dev/null +++ b/src/paperless_testing/samples.py @@ -0,0 +1,14 @@ +"""Test sample files that more than one app's tests need. + +A sample used by a single app stays in that app's ``tests/samples`` tree. +Only a file that two or more apps need lives here, exactly once. +""" + +from pathlib import Path + +SHARED_SAMPLES_DIR = (Path(__file__).parent / "sample_files").resolve() + +SIMPLE_DIGITAL_PDF = SHARED_SAMPLES_DIR / "simple-digital.pdf" +MULTI_PAGE_DIGITAL_PDF = SHARED_SAMPLES_DIR / "multi-page-digital.pdf" +WITH_FORM_PDF = SHARED_SAMPLES_DIR / "with-form.pdf" +MULTI_PAGE_IMAGES_PDF = SHARED_SAMPLES_DIR / "multi-page-images.pdf"