Handles pytest-django

This commit is contained in:
Trenton Holmes
2026-09-05 08:46:25 -07:00
parent 98389a6b2d
commit 59ba416d65
18 changed files with 78 additions and 77 deletions
+3 -3
View File
@@ -10,7 +10,7 @@ import pytest
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from guardian.shortcuts import clear_ct_cache
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from rest_framework.test import APIClient
from documents.tests.factories import DocumentFactory
@@ -100,7 +100,7 @@ def sample_doc(
@pytest.fixture()
def _search_index(
tmp_path: Path,
settings: SettingsWrapper,
settings: Settings,
) -> Generator[None, None, None]:
"""Create a temp index directory and point INDEX_DIR at it.
@@ -118,7 +118,7 @@ def _search_index(
@pytest.fixture()
def settings_timezone(settings: SettingsWrapper) -> zoneinfo.ZoneInfo:
def settings_timezone(settings: Settings) -> zoneinfo.ZoneInfo:
return zoneinfo.ZoneInfo(settings.TIME_ZONE)
+1 -1
View File
@@ -70,7 +70,7 @@ def clear_lru_cache() -> Generator[None, None, None]:
@pytest.fixture
def mock_date_parser_settings(settings: pytest_django.fixtures.SettingsWrapper) -> Any:
def mock_date_parser_settings(settings: pytest_django.fixtures.Settings) -> Any:
"""
Override Django settings for the duration of date parser tests.
"""
+3 -3
View File
@@ -6,7 +6,7 @@ from pathlib import Path
import pytest
import pytest_mock
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from documents.export.sinks import DirectoryExportSink
from documents.export.sinks import ExportSink
@@ -242,7 +242,7 @@ class TestZipExportSink:
self,
tmp_path: Path,
source_file: Path,
settings: SettingsWrapper,
settings: Settings,
) -> None:
scratch_dir = tmp_path / "scratch"
settings.SCRATCH_DIR = scratch_dir
@@ -261,7 +261,7 @@ class TestZipExportSink:
def test_abort_after_manifest_written_cleans_up_pending_tmp(
self,
tmp_path: Path,
settings: SettingsWrapper,
settings: Settings,
) -> None:
scratch_dir = tmp_path / "scratch"
settings.SCRATCH_DIR = scratch_dir
+2 -2
View File
@@ -15,11 +15,11 @@ if TYPE_CHECKING:
from collections.abc import Generator
from pathlib import Path
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
@pytest.fixture
def index_dir(tmp_path: Path, settings: SettingsWrapper) -> Path:
def index_dir(tmp_path: Path, settings: Settings) -> Path:
path = tmp_path / "index"
path.mkdir()
settings.INDEX_DIR = path
+7 -6
View File
@@ -11,7 +11,8 @@ from documents.search._schema import needs_rebuild
if TYPE_CHECKING:
from pathlib import Path
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
pytestmark = pytest.mark.search
@@ -25,7 +26,7 @@ class TestNeedsRebuild:
def test_returns_false_when_version_and_language_match(
self,
index_dir: Path,
settings: SettingsWrapper,
settings: Settings,
) -> None:
settings.SEARCH_LANGUAGE = "en"
(index_dir / ".index_settings.json").write_text(
@@ -36,7 +37,7 @@ class TestNeedsRebuild:
def test_returns_true_on_schema_version_mismatch(
self,
index_dir: Path,
settings: SettingsWrapper,
settings: Settings,
) -> None:
settings.SEARCH_LANGUAGE = None
(index_dir / ".index_settings.json").write_text(
@@ -47,7 +48,7 @@ class TestNeedsRebuild:
def test_returns_true_when_version_is_not_an_integer(
self,
index_dir: Path,
settings: SettingsWrapper,
settings: Settings,
) -> None:
settings.SEARCH_LANGUAGE = None
(index_dir / ".index_settings.json").write_text(
@@ -58,7 +59,7 @@ class TestNeedsRebuild:
def test_returns_true_when_language_key_missing(
self,
index_dir: Path,
settings: SettingsWrapper,
settings: Settings,
) -> None:
settings.SEARCH_LANGUAGE = "en"
(index_dir / ".index_settings.json").write_text(
@@ -69,7 +70,7 @@ class TestNeedsRebuild:
def test_returns_true_when_language_differs(
self,
index_dir: Path,
settings: SettingsWrapper,
settings: Settings,
) -> None:
settings.SEARCH_LANGUAGE = "de"
(index_dir / ".index_settings.json").write_text(
+2 -2
View File
@@ -1,7 +1,7 @@
import pytest
from django.core.checks import Error
from django.core.checks import Warning
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from pytest_mock import MockerFixture
from documents.checks import filename_format_check
@@ -47,7 +47,7 @@ class TestFilenameFormatCheck:
)
def test_warns_on_old_style_format(
self,
settings: SettingsWrapper,
settings: Settings,
filename_format: str,
expected_hint: str,
) -> None:
@@ -43,7 +43,7 @@ if TYPE_CHECKING:
from collections.abc import Generator
from unittest.mock import MagicMock
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from pytest_mock import MockerFixture
@@ -605,7 +605,7 @@ class TestCommandValidation:
def test_raises_for_missing_consumption_dir(
self,
settings: SettingsWrapper,
settings: Settings,
) -> None:
"""Test command raises error when directory is not provided."""
settings.CONSUMPTION_DIR = None
@@ -639,7 +639,7 @@ class TestCommandOneshot:
scratch_dir: Path,
sample_pdf: Path,
mock_consume_file_delay: MagicMock,
settings: SettingsWrapper,
settings: Settings,
) -> None:
"""Test oneshot mode processes existing files."""
target = consumption_dir / "document.pdf"
@@ -659,7 +659,7 @@ class TestCommandOneshot:
scratch_dir: Path,
sample_pdf: Path,
mock_consume_file_delay: MagicMock,
settings: SettingsWrapper,
settings: Settings,
) -> None:
"""Test oneshot mode processes files recursively."""
subdir = consumption_dir / "subdir"
@@ -681,7 +681,7 @@ class TestCommandOneshot:
consumption_dir: Path,
scratch_dir: Path,
mock_consume_file_delay: MagicMock,
settings: SettingsWrapper,
settings: Settings,
) -> None:
"""Test oneshot mode ignores unsupported file extensions."""
target = consumption_dir / "document.xyz"
@@ -1256,7 +1256,7 @@ class TestProcessExistingFilesQueued:
consumption_dir: Path,
sample_pdf: Path,
mock_consume_file_delay: MagicMock,
settings: SettingsWrapper,
settings: Settings,
) -> None:
"""The set returned seeds the rescan's queued set, avoiding re-queue."""
target = consumption_dir / "document.pdf"
+2 -2
View File
@@ -1,7 +1,7 @@
from collections.abc import Generator
import pytest
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from documents.parsers import get_default_file_extension
from documents.parsers import get_supported_file_extensions
@@ -14,7 +14,7 @@ from paperless.parsers.tika import TikaDocumentParser
@pytest.fixture()
def _tika_registry(settings: SettingsWrapper) -> Generator[None, None, None]:
def _tika_registry(settings: Settings) -> Generator[None, None, None]:
"""
Rebuild the parser registry with Tika enabled for the duration of the
test, then reset on exit so other tests see the default (Tika-disabled)
+2 -2
View File
@@ -23,6 +23,7 @@ from guardian.shortcuts import get_users_with_perms
from httpx import ConnectError
from httpx import HTTPError
from httpx import HTTPStatusError
from pytest_django.fixtures import Settings
from pytest_httpx import HTTPXMock
from rest_framework.test import APIClient
from rest_framework.test import APITestCase
@@ -38,7 +39,6 @@ from paperless_ai.exceptions import LLMTimeoutError
if TYPE_CHECKING:
from django.db.models import QuerySet
from pytest_django.fixtures import SettingsWrapper
from documents import tasks
from documents.data_models import ConsumableDocument
@@ -5356,7 +5356,7 @@ class TestDateWorkflowLocalization(
def test_document_consumption_workflow_localization(
self,
tmp_path: Path,
settings: SettingsWrapper,
settings: Settings,
title_template: str,
expected_title: str,
) -> None:
+7 -7
View File
@@ -24,7 +24,7 @@ if TYPE_CHECKING:
from pathlib import Path
from unittest.mock import MagicMock
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from pytest_mock import MockerFixture
#: Type for the ``make_tesseract_parser`` fixture factory.
@@ -131,9 +131,9 @@ def empty_remote_ocr_app_config(mocker: MockerFixture) -> MagicMock:
@pytest.fixture()
def azure_settings(
settings: SettingsWrapper,
settings: Settings,
empty_remote_ocr_app_config: MagicMock,
) -> SettingsWrapper:
) -> Settings:
"""Configure Django settings for a valid Azure AI OCR engine.
Sets ``REMOTE_OCR_ENGINE``, ``REMOTE_OCR_API_KEY``, and
@@ -142,7 +142,7 @@ def azure_settings(
Returns
-------
SettingsWrapper
Settings
The modified settings object (for chaining further overrides).
"""
settings.REMOTE_OCR_ENGINE = "azureai"
@@ -153,14 +153,14 @@ def azure_settings(
@pytest.fixture()
def no_engine_settings(
settings: SettingsWrapper,
settings: Settings,
empty_remote_ocr_app_config: MagicMock,
) -> SettingsWrapper:
) -> Settings:
"""Configure Django settings with no remote engine configured.
Returns
-------
SettingsWrapper
Settings
The modified settings object.
"""
settings.REMOTE_OCR_ENGINE = None
@@ -7,7 +7,7 @@ import httpx
import pytest
from django.test.html import parse_html
from django.utils import timezone
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from pytest_httpx import HTTPXMock
from pytest_mock import MockerFixture
@@ -428,7 +428,7 @@ class TestTikaHtmlParse:
def test_tika_parse_unreachable(
self,
settings: SettingsWrapper,
settings: Settings,
mail_parser: MailDocumentParser,
) -> None:
"""
@@ -30,7 +30,7 @@ if TYPE_CHECKING:
from collections.abc import Callable
from pathlib import Path
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from pytest_mock import MockerFixture
@@ -67,7 +67,7 @@ def make_azure_mock() -> Callable[[str], Mock]:
@pytest.fixture()
def azure_client(
azure_settings: SettingsWrapper,
azure_settings: Settings,
make_azure_mock: Callable[[str], Mock],
mocker: MockerFixture,
) -> Mock:
@@ -83,7 +83,7 @@ def azure_client(
@pytest.fixture()
def failing_azure_client(
azure_settings: SettingsWrapper,
azure_settings: Settings,
mocker: MockerFixture,
) -> Mock:
"""Patch the Azure DI client to raise RuntimeError on every call.
@@ -199,7 +199,7 @@ class TestRemoteParserScore:
def test_score_returns_none_when_api_key_missing(
self,
no_engine_settings: SettingsWrapper,
no_engine_settings: Settings,
) -> None:
no_engine_settings.REMOTE_OCR_ENGINE = "azureai"
no_engine_settings.REMOTE_OCR_ENDPOINT = (
@@ -210,7 +210,7 @@ class TestRemoteParserScore:
def test_score_returns_none_when_endpoint_missing(
self,
no_engine_settings: SettingsWrapper,
no_engine_settings: Settings,
) -> None:
no_engine_settings.REMOTE_OCR_ENGINE = "azureai"
no_engine_settings.REMOTE_OCR_API_KEY = "key"
@@ -231,7 +231,7 @@ class TestRemoteParserScore:
@pytest.mark.django_db
def test_score_uses_app_config_when_env_unset(
self,
settings: SettingsWrapper,
settings: Settings,
) -> None:
"""The app config alone is enough to activate the parser."""
settings.REMOTE_OCR_ENGINE = None
@@ -5,7 +5,7 @@ from pathlib import Path
import pytest
from httpx import codes
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from pytest_httpx import HTTPXMock
from documents.parsers import ParseError
@@ -27,7 +27,7 @@ class TestTikaParserRegistryInterface:
def test_score_returns_none_when_tika_disabled(
self,
settings: SettingsWrapper,
settings: Settings,
) -> None:
settings.TIKA_ENABLED = False
result = TikaDocumentParser.score(
@@ -38,7 +38,7 @@ class TestTikaParserRegistryInterface:
def test_score_returns_int_when_tika_enabled(
self,
settings: SettingsWrapper,
settings: Settings,
) -> None:
settings.TIKA_ENABLED = True
result = TikaDocumentParser.score(
@@ -49,7 +49,7 @@ class TestTikaParserRegistryInterface:
def test_score_returns_none_for_unsupported_mime(
self,
settings: SettingsWrapper,
settings: Settings,
) -> None:
settings.TIKA_ENABLED = True
result = TikaDocumentParser.score("application/pdf", "doc.pdf")
@@ -90,7 +90,7 @@ class TestTikaParser:
def test_parse(
self,
httpx_mock: HTTPXMock,
settings: SettingsWrapper,
settings: Settings,
tika_parser: TikaDocumentParser,
sample_odt_file: Path,
) -> None:
@@ -179,7 +179,7 @@ class TestTikaParser:
setting_value: str,
expected_form_value: str,
httpx_mock: HTTPXMock,
settings: SettingsWrapper,
settings: Settings,
sample_odt_file: Path,
) -> None:
"""
+8 -8
View File
@@ -10,7 +10,7 @@ from django.contrib.auth.models import User
from django.forms import ValidationError
from django.http import HttpRequest
from django.urls import reverse
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from pytest_mock import MockerFixture
from rest_framework.authtoken.models import Token
@@ -19,7 +19,7 @@ from paperless.adapter import DrfTokenStrategy
@pytest.mark.django_db
class TestCustomAccountAdapter:
def test_is_open_for_signup(self, settings: SettingsWrapper) -> None:
def test_is_open_for_signup(self, settings: Settings) -> None:
adapter = get_adapter()
# With no accounts, signups should be allowed
@@ -33,7 +33,7 @@ class TestCustomAccountAdapter:
settings.ACCOUNT_ALLOW_SIGNUPS = False
assert not adapter.is_open_for_signup(None)
def test_is_safe_url(self, settings: SettingsWrapper) -> None:
def test_is_safe_url(self, settings: Settings) -> None:
request = HttpRequest()
request.get_host = lambda: "example.com"
with context.request_context(request):
@@ -55,7 +55,7 @@ class TestCustomAccountAdapter:
def test_pre_authenticate(
self,
settings: SettingsWrapper,
settings: Settings,
mocker: MockerFixture,
) -> None:
mocker.patch("allauth.core.internal.ratelimit.consume", return_value=True)
@@ -70,7 +70,7 @@ class TestCustomAccountAdapter:
with pytest.raises(ValidationError):
adapter.pre_authenticate(request)
def test_get_reset_password_from_key_url(self, settings: SettingsWrapper) -> None:
def test_get_reset_password_from_key_url(self, settings: Settings) -> None:
request = HttpRequest()
request.get_host = lambda: "foo.org"
with context.request_context(request):
@@ -87,7 +87,7 @@ class TestCustomAccountAdapter:
def test_save_user_adds_groups(
self,
settings: SettingsWrapper,
settings: Settings,
mocker: MockerFixture,
) -> None:
settings.ACCOUNT_DEFAULT_GROUPS = ["group1", "group2"]
@@ -130,7 +130,7 @@ class TestCustomAccountAdapter:
class TestCustomSocialAccountAdapter:
@pytest.mark.django_db
def test_is_open_for_signup(self, settings: SettingsWrapper) -> None:
def test_is_open_for_signup(self, settings: Settings) -> None:
adapter = get_social_adapter()
settings.SOCIALACCOUNT_ALLOW_SIGNUPS = True
@@ -146,7 +146,7 @@ class TestCustomSocialAccountAdapter:
@pytest.mark.django_db
def test_save_user_adds_groups(
self,
settings: SettingsWrapper,
settings: Settings,
mocker: MockerFixture,
) -> None:
settings.SOCIAL_ACCOUNT_DEFAULT_GROUPS = ["group1", "group2"]
+14 -14
View File
@@ -8,7 +8,7 @@ import pytest
from django.core.checks import ERROR
from django.core.checks import Error
from django.core.checks import Warning
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from pytest_mock import MockerFixture
from paperless.checks import audit_log_check
@@ -31,7 +31,7 @@ class PaperlessTestDirs:
# TODO: consolidate with documents/tests/conftest.py PaperlessDirs/paperless_dirs
# once the paperless and documents test suites are ready to share fixtures.
@pytest.fixture()
def directories(tmp_path: Path, settings: SettingsWrapper) -> PaperlessTestDirs:
def directories(tmp_path: Path, settings: Settings) -> PaperlessTestDirs:
data_dir = tmp_path / "data"
media_dir = tmp_path / "media"
consumption_dir = tmp_path / "consumption"
@@ -54,7 +54,7 @@ class TestChecks:
def test_binaries(self) -> None:
assert binaries_check(None) == []
def test_binaries_fail(self, settings: SettingsWrapper) -> None:
def test_binaries_fail(self, settings: Settings) -> None:
settings.CONVERT_BINARY = "uuuhh"
assert len(binaries_check(None)) == 1
@@ -62,7 +62,7 @@ class TestChecks:
def test_paths_check(self) -> None:
assert paths_check(None) == []
def test_paths_check_dont_exist(self, settings: SettingsWrapper) -> None:
def test_paths_check_dont_exist(self, settings: Settings) -> None:
settings.MEDIA_ROOT = Path("uuh")
settings.DATA_DIR = Path("whatever")
settings.CONSUMPTION_DIR = Path("idontcare")
@@ -89,11 +89,11 @@ class TestChecks:
for msg in msgs:
assert msg.msg.endswith("is not writeable")
def test_debug_disabled(self, settings: SettingsWrapper) -> None:
def test_debug_disabled(self, settings: Settings) -> None:
settings.DEBUG = False
assert debug_mode_check(None) == []
def test_debug_enabled(self, settings: SettingsWrapper) -> None:
def test_debug_enabled(self, settings: Settings) -> None:
settings.DEBUG = True
assert len(debug_mode_check(None)) == 1
@@ -150,7 +150,7 @@ class TestOcrSettingsChecks:
)
def test_invalid_setting_produces_one_error(
self,
settings: SettingsWrapper,
settings: Settings,
setting: str,
value: str,
expected_msg: str,
@@ -173,7 +173,7 @@ class TestOcrSettingsChecks:
class TestTimezoneSettingsChecks:
def test_invalid_timezone(self, settings: SettingsWrapper) -> None:
def test_invalid_timezone(self, settings: Settings) -> None:
"""
GIVEN:
- Default settings
@@ -192,7 +192,7 @@ class TestTimezoneSettingsChecks:
class TestEmailCertSettingsChecks:
def test_not_valid_file(self, settings: SettingsWrapper) -> None:
def test_not_valid_file(self, settings: Settings) -> None:
"""
GIVEN:
- Default settings
@@ -215,7 +215,7 @@ class TestEmailCertSettingsChecks:
class TestAuditLogChecks:
def test_was_enabled_once(
self,
settings: SettingsWrapper,
settings: Settings,
mocker: MockerFixture,
) -> None:
"""
@@ -634,7 +634,7 @@ class TestTesseractChecks:
def test_default_language(self) -> None:
check_default_language_available(None)
def test_no_language(self, settings: SettingsWrapper) -> None:
def test_no_language(self, settings: Settings) -> None:
settings.OCR_LANGUAGE = ""
@@ -649,7 +649,7 @@ class TestTesseractChecks:
def test_invalid_language(
self,
settings: SettingsWrapper,
settings: Settings,
mocker: MockerFixture,
) -> None:
@@ -668,7 +668,7 @@ class TestTesseractChecks:
def test_multi_part_language(
self,
settings: SettingsWrapper,
settings: Settings,
mocker: MockerFixture,
) -> None:
"""
@@ -692,7 +692,7 @@ class TestTesseractChecks:
def test_multi_part_language_bad_format(
self,
settings: SettingsWrapper,
settings: Settings,
mocker: MockerFixture,
) -> None:
"""
+3 -3
View File
@@ -1,13 +1,13 @@
from pathlib import Path
from django.test import Client
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
def test_favicon_view(
client: Client,
tmp_path: Path,
settings: SettingsWrapper,
settings: Settings,
) -> None:
favicon_path = tmp_path / "paperless" / "img" / "favicon.ico"
favicon_path.parent.mkdir(parents=True)
@@ -24,7 +24,7 @@ def test_favicon_view(
def test_favicon_view_missing_file(
client: Client,
tmp_path: Path,
settings: SettingsWrapper,
settings: Settings,
) -> None:
settings.STATIC_ROOT = tmp_path
response = client.get("/favicon.ico")
+2 -2
View File
@@ -3,11 +3,11 @@ from pathlib import Path
import pytest
import pytest_mock
from llama_index.core.base.embeddings.base import BaseEmbedding
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
@pytest.fixture
def temp_llm_index_dir(tmp_path: Path, settings: SettingsWrapper) -> Path:
def temp_llm_index_dir(tmp_path: Path, settings: Settings) -> Path:
settings.LLM_INDEX_DIR = tmp_path
settings.LLM_INDEX_LOCK = tmp_path / "index.lock"
settings.LLM_INDEX_RWLOCK = tmp_path / "llmindex.rwlock.db"
+2 -2
View File
@@ -8,7 +8,7 @@ import pytest
from django.conf import settings
from filelock import ReadWriteLock
from llama_index.core.schema import TextNode
from pytest_django.fixtures import SettingsWrapper
from pytest_django.fixtures import Settings
from paperless_ai import indexing
from paperless_ai.vector_store import PaperlessSqliteVecVectorStore
@@ -69,7 +69,7 @@ class TestCompactionLock:
def test_compaction_skips_when_a_reader_holds_the_lock(
self,
temp_llm_index_dir: Path,
settings: SettingsWrapper,
settings: Settings,
caplog: pytest.LogCaptureFixture,
) -> None:
_seed_bloated_index(temp_llm_index_dir)