mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-10-07 16:47:13 +00:00
190 lines
6.0 KiB
Python
190 lines
6.0 KiB
Python
"""Fixtures available to every Paperless-ngx app.
|
|
|
|
Loaded automatically for every test path. Keep module-scope imports minimal:
|
|
this file is imported for every session, so anything heavy belongs inside
|
|
the fixture body that needs it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
import pytest
|
|
|
|
from paperless_testing import samples
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Generator
|
|
from pathlib import Path
|
|
|
|
from django.contrib.auth.models import User
|
|
from pytest_django.fixtures import Settings
|
|
from rest_framework.test import APIClient
|
|
|
|
from paperless_testing.dirs import PaperlessDirs
|
|
from paperless_testing.fakes.progress import FakeProgressManager
|
|
|
|
|
|
@pytest.fixture(scope="session", autouse=True)
|
|
def faker_session_locale() -> str:
|
|
"""Pin Faker's locale so generated data does not follow the host locale.
|
|
|
|
The seed itself is left to pytest-randomly, which derives one per run.
|
|
"""
|
|
return "en_US"
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _fast_password_hasher(settings: Settings) -> None:
|
|
"""Hash test passwords with MD5 instead of Django's default PBKDF2.
|
|
|
|
PBKDF2 is deliberately slow, and every ``admin_user`` or
|
|
``create_superuser`` call pays for it: about 600 ms each. No test depends
|
|
on the hash format, only on ``check_password`` and on the stored value
|
|
changing when the password does.
|
|
"""
|
|
settings.PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_content_type_caches() -> None:
|
|
"""Clear Django's ContentType cache and guardian's lru_cache before each test.
|
|
|
|
Tests that delete and reinsert ContentType/Permission rows (e.g. the
|
|
importer) corrupt both caches. Without this fixture a subsequent test on
|
|
the same xdist worker sees stale ContentType objects and guardian raises
|
|
MixedContentTypeError.
|
|
"""
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from guardian.shortcuts import clear_ct_cache
|
|
|
|
ContentType.objects.clear_cache()
|
|
clear_ct_cache()
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_django_caches() -> None:
|
|
"""Clear every configured cache before each test.
|
|
|
|
Cached values outlive the test that wrote them: the classifier keys its
|
|
vectorized content on a hash of the content itself, so a second test
|
|
generating the same fixture data takes the cache-hit path and never calls
|
|
the code it is asserting against.
|
|
"""
|
|
from django.core.cache import caches
|
|
|
|
for cache in caches.all(initialized_only=False):
|
|
cache.clear()
|
|
|
|
|
|
@pytest.fixture
|
|
def paperless_dirs(
|
|
tmp_path: Path,
|
|
settings: Settings,
|
|
) -> Generator[PaperlessDirs, None, None]:
|
|
"""The standard temp directory layout, applied to Django settings."""
|
|
from documents.search import reset_backend
|
|
from paperless_testing.dirs import build_paperless_dirs
|
|
from paperless_testing.dirs import dirs_settings
|
|
|
|
dirs = build_paperless_dirs(tmp_path)
|
|
for name, value in dirs_settings(dirs).items():
|
|
setattr(settings, name, value)
|
|
|
|
# Not directory settings, but they are needed alongside the layout by the
|
|
# sanity checker tests.
|
|
settings.IGNORABLE_FILES = {".DS_Store", "Thumbs.db", "desktop.ini"}
|
|
settings.APP_LOGO = ""
|
|
|
|
reset_backend()
|
|
yield dirs
|
|
reset_backend()
|
|
|
|
|
|
@pytest.fixture
|
|
def rest_api_client() -> APIClient:
|
|
"""The basic DRF APIClient, unauthenticated."""
|
|
from rest_framework.test import APIClient
|
|
|
|
return APIClient()
|
|
|
|
|
|
@pytest.fixture
|
|
def regular_user(db: None) -> User:
|
|
"""Unprivileged user for permission boundary tests."""
|
|
from paperless_testing.factories import UserFactory
|
|
|
|
return UserFactory(username="regular")
|
|
|
|
|
|
@pytest.fixture
|
|
def admin_client(rest_api_client: APIClient, admin_user: User) -> APIClient:
|
|
"""Admin client pre-authenticated and sending the v10 Accept header."""
|
|
rest_api_client.force_authenticate(user=admin_user)
|
|
rest_api_client.credentials(HTTP_ACCEPT="application/json; version=10")
|
|
return rest_api_client
|
|
|
|
|
|
@pytest.fixture
|
|
def v9_client(rest_api_client: APIClient, admin_user: User) -> APIClient:
|
|
"""Admin client pre-authenticated and sending the v9 Accept header."""
|
|
rest_api_client.force_authenticate(user=admin_user)
|
|
rest_api_client.credentials(HTTP_ACCEPT="application/json; version=9")
|
|
return rest_api_client
|
|
|
|
|
|
@pytest.fixture
|
|
def user_client(rest_api_client: APIClient, regular_user: User) -> APIClient:
|
|
"""Regular-user client pre-authenticated and sending the v10 Accept header."""
|
|
rest_api_client.force_authenticate(user=regular_user)
|
|
rest_api_client.credentials(HTTP_ACCEPT="application/json; version=10")
|
|
return rest_api_client
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_progress_manager(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> type[FakeProgressManager]:
|
|
"""Replace documents.tasks.ProgressManager with the fake, so consuming a file
|
|
in a test never tries to reach a broker."""
|
|
from paperless_testing.fakes.progress import FakeProgressManager
|
|
|
|
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
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def thumbnail_webp_file() -> Path:
|
|
"""Small WebP thumbnail."""
|
|
return samples.THUMBNAIL_WEBP
|