mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-10-03 14:50:31 +00:00
Test modules in paperless, paperless_mail and documents imported filesystem assertions, the migration test base, the retry helper and the streaming-response reader out of documents/tests/utils.py, which kept each app's tests coupled to another app's test package. They now live in paperless_testing, and the progress manager fake is renamed FakeProgressManager and now subclasses the real ProgressManager, overriding only the transport, so the payload it records is built by the production code. The twenty places that patched documents.tasks.ProgressManager by hand now use a fake_progress_manager fixture.
152 lines
5.0 KiB
Python
152 lines
5.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
|
|
|
|
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
|