From 532e3728961f7ef36b2c1594fa9ca1002897def6 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 5347fe7de..abc47794e 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 bb6a85031..1d321b0be 100644 --- a/src/paperless/tests/parsers/test_tesseract_parser.py +++ b/src/paperless/tests/parsers/test_tesseract_parser.py @@ -160,11 +160,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 @@ -187,7 +187,7 @@ class TestGetPageCount: self, mocker: MockerFixture, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, caplog, ) -> None: """ @@ -203,7 +203,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 @@ -272,10 +272,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() @@ -284,7 +284,7 @@ class TestGetThumbnail: self, mocker: MockerFixture, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, ) -> None: def _raise_on_pdf(input_file, output_file, **kwargs) -> None: if ".pdf" in str(input_file): @@ -294,7 +294,7 @@ class TestGetThumbnail: mocker.patch("documents.parsers.run_convert", side_effect=_raise_on_pdf) thumb = tesseract_parser.get_thumbnail( - tesseract_samples_dir / "simple-digital.pdf", + simple_digital_pdf_file, "application/pdf", ) assert thumb.is_file() @@ -320,11 +320,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() @@ -339,7 +339,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: @@ -353,7 +353,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 @@ -368,10 +368,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 @@ -384,11 +384,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, ) @@ -401,11 +401,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( @@ -446,7 +446,7 @@ class TestParsePdf: self, mocker: MockerFixture, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, ) -> None: mocker.patch( "ocrmypdf.ocr", @@ -454,7 +454,7 @@ class TestParsePdf: ) with pytest.raises(ParseError): tesseract_parser.parse( - tesseract_samples_dir / "simple-digital.pdf", + simple_digital_pdf_file, "application/pdf", ) @@ -530,10 +530,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 @@ -557,12 +557,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 @@ -576,11 +576,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 @@ -594,7 +594,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: @@ -609,7 +609,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 @@ -622,7 +622,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: @@ -637,7 +637,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 @@ -733,7 +733,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: @@ -747,7 +747,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, ) @@ -762,7 +762,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: @@ -775,7 +775,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 @@ -787,29 +787,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", ), @@ -818,10 +818,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: @@ -834,8 +834,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, ) @@ -852,7 +853,7 @@ class TestSkipArchive: self, mocker: MockerFixture, tesseract_parser: RasterisedDocumentParser, - tesseract_samples_dir: Path, + simple_digital_pdf_file: Path, ) -> None: """ GIVEN: @@ -868,7 +869,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, ) @@ -907,7 +908,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: @@ -922,7 +923,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 f0a63ea44..3af49fea7 100644 --- a/src/paperless/tests/test_parser_utils.py +++ b/src/paperless/tests/test_parser_utils.py @@ -16,8 +16,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: @@ -55,11 +53,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"