Ctrl K
ComunidadGuías…

src/conftest.py

Rama dev · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""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