From 0973ead3b8383b1c977fca3babaf8e954a617739 Mon Sep 17 00:00:00 2001 From: stumpylog <797416+stumpylog@users.noreply.github.com> Date: Fri, 18 Sep 2026 11:40:43 -0700 Subject: [PATCH] Chore: Share the API client fixtures and build test users cheaply --- src/conftest.py | 42 +++++ src/documents/tests/conftest.py | 42 ----- src/documents/tests/search/test_acceptance.py | 4 +- src/documents/tests/search/test_backend.py | 9 +- .../tests/search/test_json_field_prefixes.py | 6 +- .../search/test_json_subpath_completeness.py | 4 +- .../search/test_permission_field_isolation.py | 9 +- src/documents/tests/test_api_app_config.py | 6 +- src/documents/tests/test_api_bulk_download.py | 6 +- src/documents/tests/test_api_bulk_edit.py | 15 +- src/documents/tests/test_api_chat.py | 4 +- src/documents/tests/test_api_custom_fields.py | 14 +- .../tests/test_api_document_versions.py | 30 ++-- src/documents/tests/test_api_documents.py | 87 +++++----- src/documents/tests/test_api_email.py | 8 +- .../tests/test_api_filter_by_custom_fields.py | 4 +- src/documents/tests/test_api_objects.py | 35 ++-- src/documents/tests/test_api_permissions.py | 57 +++--- src/documents/tests/test_api_profile.py | 6 +- src/documents/tests/test_api_search.py | 42 ++--- src/documents/tests/test_api_status.py | 10 +- src/documents/tests/test_api_tasks.py | 20 +-- src/documents/tests/test_api_trash.py | 12 +- src/documents/tests/test_api_uisettings.py | 8 +- src/documents/tests/test_api_workflows.py | 3 +- src/documents/tests/test_consumer.py | 3 +- src/documents/tests/test_file_handling.py | 4 +- .../tests/test_merge_documents_as_versions.py | 10 +- .../test_permission_filtering_security.py | 162 +++++++++--------- .../tests/test_permitted_objects_filter.py | 20 +-- .../tests/test_share_link_bundles.py | 7 +- src/documents/tests/test_tag_hierarchy.py | 8 +- src/documents/tests/test_task_signals.py | 5 +- src/documents/tests/test_views.py | 17 +- src/documents/tests/test_workflows.py | 23 +-- .../tests/settings/test_remote_user.py | 6 +- src/paperless/tests/test_adapter.py | 11 +- src/paperless/tests/test_api_auth.py | 8 +- src/paperless/tests/test_auth_middleware.py | 6 +- src/paperless/tests/test_signals.py | 86 +++------- src/paperless_mail/tests/test_api.py | 33 ++-- src/paperless_mail/tests/test_mail.py | 18 +- src/paperless_mail/tests/test_mail_oauth.py | 4 +- src/paperless_testing/factories.py | 5 +- 44 files changed, 442 insertions(+), 477 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 10293880a..a8fc6aa65 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -15,7 +15,9 @@ 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 @@ -82,3 +84,43 @@ def paperless_dirs( 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 diff --git a/src/documents/tests/conftest.py b/src/documents/tests/conftest.py index aae70c882..20ffdc890 100644 --- a/src/documents/tests/conftest.py +++ b/src/documents/tests/conftest.py @@ -5,14 +5,10 @@ from typing import TYPE_CHECKING import filelock import pytest -from django.contrib.auth import get_user_model from pytest_django.fixtures import Settings -from rest_framework.test import APIClient from paperless_testing.factories import DocumentFactory -UserModelT = get_user_model() - if TYPE_CHECKING: from documents.models import Document from paperless_testing.dirs import PaperlessDirs @@ -76,44 +72,6 @@ def _search_index( reset_backend() -@pytest.fixture -def rest_api_client(): - """ - The basic DRF ApiClient - """ - yield APIClient() - - -@pytest.fixture() -def regular_user(django_user_model: type[UserModelT]) -> UserModelT: - """Unprivileged authenticated user for permission boundary tests.""" - return django_user_model.objects.create_user(username="regular", password="regular") - - -@pytest.fixture() -def admin_client(rest_api_client: APIClient, admin_user: UserModelT) -> 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: UserModelT) -> 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: UserModelT) -> 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 indexed_document(_search_index: None) -> "Document": """One searchable document, for tests about what the search endpoint diff --git a/src/documents/tests/search/test_acceptance.py b/src/documents/tests/search/test_acceptance.py index 5ef09b8b0..d5dc8cfb8 100644 --- a/src/documents/tests/search/test_acceptance.py +++ b/src/documents/tests/search/test_acceptance.py @@ -14,7 +14,6 @@ from typing import TYPE_CHECKING import pytest import time_machine -from django.contrib.auth.models import User from documents.models import CustomField from documents.models import CustomFieldInstance @@ -23,6 +22,7 @@ from documents.models import Note from documents.models import StoragePath from documents.search._query import parse_user_query from paperless_testing.factories import DocumentFactory +from paperless_testing.factories import UserFactory if TYPE_CHECKING: from collections.abc import Callable @@ -148,7 +148,7 @@ class TestJsonSubpaths: THEN: - Only the document with alice's note matches """ - alice = User.objects.create_user(username="alice") + alice = UserFactory(username="alice") doc_with_note = DocumentFactory( title="Has note", content="x", diff --git a/src/documents/tests/search/test_backend.py b/src/documents/tests/search/test_backend.py index e5b87d436..d49e32baa 100644 --- a/src/documents/tests/search/test_backend.py +++ b/src/documents/tests/search/test_backend.py @@ -3,7 +3,6 @@ from pathlib import Path import pytest from django.contrib.auth.models import Group -from django.contrib.auth.models import User from django.db import connection from django.test.utils import CaptureQueriesContext from guardian.shortcuts import assign_perm @@ -763,8 +762,8 @@ class TestSearchIds: def test_respects_permission_filter(self, backend: TantivyBackend) -> None: """search_ids must respect user permission filtering.""" - owner = User.objects.create_user("ids_owner") - other = User.objects.create_user("ids_other") + owner = UserFactory(username="ids_owner") + other = UserFactory(username="ids_other") doc = Document.objects.create( title="private doc", content="secret keyword", @@ -1071,7 +1070,7 @@ class TestFieldHandling: def test_notes_include_user_information(self, backend: TantivyBackend) -> None: """Notes must be indexed with user information when available for structured queries.""" - user = User.objects.create_user("notewriter") + user = UserFactory(username="notewriter") doc = Document.objects.create( title="Doc with notes", content="test", @@ -1173,7 +1172,7 @@ class TestHighlightHits: notes.note: prefix so the query targets notes content directly, but the snippet is generated from notes_text which stores the same text. """ - user = User.objects.create_user("hl_noteuser") + user = UserFactory(username="hl_noteuser") doc = Document.objects.create( title="Doc with matching note", content="unrelated content", diff --git a/src/documents/tests/search/test_json_field_prefixes.py b/src/documents/tests/search/test_json_field_prefixes.py index 8468e2e65..039be7bc1 100644 --- a/src/documents/tests/search/test_json_field_prefixes.py +++ b/src/documents/tests/search/test_json_field_prefixes.py @@ -17,12 +17,12 @@ from __future__ import annotations from typing import TYPE_CHECKING import pytest -from django.contrib.auth.models import User from documents.models import CustomField from documents.models import CustomFieldInstance from documents.models import Note from paperless_testing.factories import DocumentFactory +from paperless_testing.factories import UserFactory if TYPE_CHECKING: from collections.abc import Callable @@ -53,7 +53,7 @@ class TestBareJsonFieldPrefixes: decoy's content match does not resurface through a demoted text search """ - alice = User.objects.create_user(username="alice") + alice = UserFactory(username="alice") with_note = DocumentFactory(title="Has note", content="x") Note.objects.create(document=with_note, user=alice, note="crocodile") backend.add_or_update(with_note) @@ -116,7 +116,7 @@ class TestBareJsonFieldPrefixes: document; the default-subpath resolution for the bare prefix does not interfere with explicit subpath addressing """ - bob = User.objects.create_user(username="bob") + bob = UserFactory(username="bob") doc = DocumentFactory(title="Bob note", content="x") Note.objects.create(document=doc, user=bob, note="remark") backend.add_or_update(doc) diff --git a/src/documents/tests/search/test_json_subpath_completeness.py b/src/documents/tests/search/test_json_subpath_completeness.py index a0245ca44..54f2ca8fb 100644 --- a/src/documents/tests/search/test_json_subpath_completeness.py +++ b/src/documents/tests/search/test_json_subpath_completeness.py @@ -20,7 +20,6 @@ from typing import TYPE_CHECKING import pytest import tantivy -from django.contrib.auth.models import User from whoosh_compat import FieldKind from documents.models import CustomField @@ -28,6 +27,7 @@ from documents.models import CustomFieldInstance from documents.models import Document from documents.models import Note from documents.search._fields import PUBLIC_FIELDS +from paperless_testing.factories import UserFactory if TYPE_CHECKING: from documents.search._backend import TantivyBackend @@ -49,7 +49,7 @@ class TestJsonSubpathsAreWrittenAtIndexTime: - Every subpath PUBLIC_FIELDS declares for notes/custom_fields is present as a key in the document's stored JSON payload """ - user = User.objects.create_user(username="completeness-user") + user = UserFactory(username="completeness-user") field = CustomField.objects.create( name="Completeness Field", data_type=CustomField.FieldDataType.STRING, diff --git a/src/documents/tests/search/test_permission_field_isolation.py b/src/documents/tests/search/test_permission_field_isolation.py index bfafac484..784abfaaa 100644 --- a/src/documents/tests/search/test_permission_field_isolation.py +++ b/src/documents/tests/search/test_permission_field_isolation.py @@ -25,6 +25,7 @@ from documents.models import Document from documents.models import DocumentType from documents.models import StoragePath from documents.models import Tag +from paperless_testing.factories import UserFactory if TYPE_CHECKING: from documents.search._backend import TantivyBackend @@ -34,22 +35,22 @@ pytestmark = [pytest.mark.search, pytest.mark.django_db] @pytest.fixture def owner() -> User: - return User.objects.create_user(username="owner") + return UserFactory(username="owner") @pytest.fixture def stranger() -> User: - return User.objects.create_user(username="stranger") + return UserFactory(username="stranger") @pytest.fixture def viewer() -> User: - return User.objects.create_user(username="viewer") + return UserFactory(username="viewer") @pytest.fixture def group_member() -> User: - user = User.objects.create_user(username="group_member") + user = UserFactory(username="group_member") user.groups.add(Group.objects.create(name="accounting")) return user diff --git a/src/documents/tests/test_api_app_config.py b/src/documents/tests/test_api_app_config.py index e6d3fc7af..a670c2375 100644 --- a/src/documents/tests/test_api_app_config.py +++ b/src/documents/tests/test_api_app_config.py @@ -3,7 +3,6 @@ from io import BytesIO from pathlib import Path from unittest.mock import patch -from django.contrib.auth.models import User from django.core.files.uploadedfile import SimpleUploadedFile from django.test import override_settings from PIL import Image @@ -15,6 +14,7 @@ from documents.tests.utils import read_streaming_response from paperless.models import ApplicationConfiguration from paperless.models import ColorConvertChoices from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestApiAppConfig(DirectoriesMixin, APITestCase): @@ -23,7 +23,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - user = User.objects.create_superuser(username="temp_admin") + user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=user) def test_api_get_config(self) -> None: @@ -267,7 +267,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase): THEN: - old app_logo file is deleted """ - admin = User.objects.create_superuser(username="admin") + admin = UserFactory(username="admin", superuser=True) self.client.force_login(user=admin) response = self.client.get("/logo/") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) diff --git a/src/documents/tests/test_api_bulk_download.py b/src/documents/tests/test_api_bulk_download.py index 912eefdc6..4eeabb887 100644 --- a/src/documents/tests/test_api_bulk_download.py +++ b/src/documents/tests/test_api_bulk_download.py @@ -5,7 +5,6 @@ import shutil import zipfile from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.test import override_settings from django.utils import timezone from rest_framework import status @@ -17,6 +16,7 @@ from documents.models import DocumentType from documents.tests.utils import SampleDirMixin from documents.tests.utils import read_streaming_response from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestBulkDownload(DirectoriesMixin, SampleDirMixin, APITestCase): @@ -25,7 +25,7 @@ class TestBulkDownload(DirectoriesMixin, SampleDirMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) self.doc1 = Document.objects.create(title="unrelated", checksum="A") @@ -326,7 +326,7 @@ class TestBulkDownload(DirectoriesMixin, SampleDirMixin, APITestCase): ) def test_download_insufficient_permissions(self) -> None: - user = User.objects.create_user(username="temp_user") + user = UserFactory(username="temp_user") user.user_permissions.add( Permission.objects.get(codename="view_document"), ) diff --git a/src/documents/tests/test_api_bulk_edit.py b/src/documents/tests/test_api_bulk_edit.py index 7e861e6e1..d5a0e92c3 100644 --- a/src/documents/tests/test_api_bulk_edit.py +++ b/src/documents/tests/test_api_bulk_edit.py @@ -16,13 +16,14 @@ from documents.models import DocumentType from documents.models import StoragePath from documents.models import Tag from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestBulkEditAPI(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - user = User.objects.create_superuser(username="temp_admin") + user = UserFactory(username="temp_admin", superuser=True) self.user = user self.client.force_authenticate(user=user) @@ -284,9 +285,9 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase): m, ) -> None: self.setup_mock(m, "modify_custom_fields") - user = User.objects.create_user(username="doc-owner") + user = UserFactory(username="doc-owner") user.user_permissions.add(Permission.objects.get(codename="change_document")) - other_user = User.objects.create_user(username="other-user") + other_user = UserFactory(username="other-user") source_doc = Document.objects.create( checksum="source", title="Source", @@ -787,7 +788,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase): @mock.patch("documents.serialisers.bulk_edit.set_storage_path") def test_api_bulk_edit_with_all_true_resolves_owned_duplicates(self, m) -> None: self.setup_mock(m, "set_storage_path") - user = User.objects.create_user(username="duplicate-owner") + user = UserFactory(username="duplicate-owner") user.user_permissions.add( Permission.objects.get(codename="change_document"), ) @@ -1919,7 +1920,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase): @mock.patch("documents.views.bulk_edit.merge") def test_merge_and_delete_requires_change_permission(self, m) -> None: self.setup_mock(m, "merge") - user = User.objects.create_user(username="no-change") + user = UserFactory(username="no-change") user.user_permissions.add( Permission.objects.get(codename="add_document"), Permission.objects.get(codename="delete_document"), @@ -2345,7 +2346,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase): @mock.patch("documents.views.bulk_edit.edit_pdf") def test_edit_pdf_update_requires_change_permission(self, m) -> None: self.setup_mock(m, "edit_pdf") - user = User.objects.create_user(username="no-change") + user = UserFactory(username="no-change") self.client.force_authenticate(user=user) response = self.client.post( @@ -2372,7 +2373,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase): ) -> None: self.setup_mock(edit_pdf_mock, "edit_pdf") self.setup_mock(remove_password_mock, "remove_password") - user = User.objects.create_user(username="no-delete") + user = UserFactory(username="no-delete") user.user_permissions.add( Permission.objects.get(codename="add_document"), Permission.objects.get(codename="change_document"), diff --git a/src/documents/tests/test_api_chat.py b/src/documents/tests/test_api_chat.py index 8098febfa..ccc0bb28f 100644 --- a/src/documents/tests/test_api_chat.py +++ b/src/documents/tests/test_api_chat.py @@ -10,6 +10,8 @@ from rest_framework import status from rest_framework.test import APIClient from rest_framework.test import APITestCase +from paperless_testing.factories import UserFactory + if TYPE_CHECKING: from pytest_mock import MockerFixture @@ -17,7 +19,7 @@ if TYPE_CHECKING: class TestChatStreamingViewInputValidation(APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) def _mock_ai_enabled(self) -> mock.MagicMock: diff --git a/src/documents/tests/test_api_custom_fields.py b/src/documents/tests/test_api_custom_fields.py index adcd41615..8d604e60a 100644 --- a/src/documents/tests/test_api_custom_fields.py +++ b/src/documents/tests/test_api_custom_fields.py @@ -4,7 +4,6 @@ from unittest import mock from unittest.mock import ANY from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.test import override_settings from guardian.shortcuts import assign_perm from rest_framework import status @@ -14,13 +13,14 @@ from documents.models import CustomField from documents.models import CustomFieldInstance from documents.models import Document from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestCustomFieldsAPI(DirectoriesMixin, APITestCase): ENDPOINT = "/api/custom_fields/" def setUp(self) -> None: - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) return super().setUp() @@ -1174,11 +1174,11 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase): def test_documentlink_patch_requires_change_permission_on_target_documents( self, ) -> None: - source_owner = User.objects.create_user(username="source-owner") + source_owner = UserFactory(username="source-owner") source_owner.user_permissions.add( Permission.objects.get(codename="change_document"), ) - other_user = User.objects.create_user(username="other-user") + other_user = UserFactory(username="other-user") source_doc = Document.objects.create( title="Source", @@ -1221,11 +1221,11 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase): def test_documentlink_patch_allowed_with_change_permission_on_target_documents( self, ) -> None: - source_owner = User.objects.create_user(username="source-owner") + source_owner = UserFactory(username="source-owner") source_owner.user_permissions.add( Permission.objects.get(codename="change_document"), ) - other_user = User.objects.create_user(username="other-user") + other_user = UserFactory(username="other-user") source_doc = Document.objects.create( title="Source", @@ -1337,7 +1337,7 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase): self.assertEqual(results[0]["document_count"], 1) # Test as user without access to the document - non_superuser = User.objects.create_user(username="non_superuser") + non_superuser = UserFactory(username="non_superuser") non_superuser.user_permissions.add( *Permission.objects.exclude(codename="view_global_statistics"), ) diff --git a/src/documents/tests/test_api_document_versions.py b/src/documents/tests/test_api_document_versions.py index d7241b54a..edbabeda5 100644 --- a/src/documents/tests/test_api_document_versions.py +++ b/src/documents/tests/test_api_document_versions.py @@ -6,7 +6,6 @@ from unittest import mock from auditlog.models import LogEntry # type: ignore[import-untyped] from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase as DjangoTestCase @@ -22,6 +21,7 @@ from documents.tests.utils import read_streaming_response from documents.versioning import annotate_effective_content from documents.views import DocumentSelectionMixin from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory if TYPE_CHECKING: from pathlib import Path @@ -31,7 +31,7 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) def _make_pdf_upload(self, name: str = "version.pdf") -> SimpleUploadedFile: @@ -89,8 +89,8 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) def test_root_endpoint_returns_403_when_user_lacks_permission(self) -> None: - owner = User.objects.create_user(username="owner") - viewer = User.objects.create_user(username="viewer") + owner = UserFactory(username="owner") + viewer = UserFactory(username="viewer") viewer.user_permissions.add( Permission.objects.get(codename="view_document"), ) @@ -283,8 +283,8 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): self.assertEqual(mock_backend.add_or_update.call_args[0][0].id, root.id) def test_delete_version_returns_403_without_permission(self) -> None: - owner = User.objects.create_user(username="owner") - other = User.objects.create_user(username="other") + owner = UserFactory(username="owner") + other = UserFactory(username="other") other.user_permissions.add( Permission.objects.get(codename="delete_document"), ) @@ -371,8 +371,8 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): self.assertTrue(resp.data["is_root"]) def test_update_version_label_returns_403_without_permission(self) -> None: - owner = User.objects.create_user(username="owner") - other = User.objects.create_user(username="other") + owner = UserFactory(username="owner") + other = UserFactory(username="other") other.user_permissions.add( Permission.objects.get(codename="change_document"), ) @@ -553,8 +553,8 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) def test_metadata_returns_403_when_user_lacks_permission(self) -> None: - owner = User.objects.create_user(username="owner") - other = User.objects.create_user(username="other") + owner = UserFactory(username="owner") + other = UserFactory(username="other") other.user_permissions.add( Permission.objects.get(codename="view_document"), ) @@ -653,8 +653,8 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): self.assertEqual(resp.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) def test_update_version_returns_403_without_permission(self) -> None: - owner = User.objects.create_user(username="owner") - other = User.objects.create_user(username="other") + owner = UserFactory(username="owner") + other = UserFactory(username="other") root = Document.objects.create( title="root", checksum="root", @@ -672,7 +672,7 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN) def test_update_version_requires_global_change_permission(self) -> None: - user = User.objects.create_user(username="add-only") + user = UserFactory(username="add-only") user.user_permissions.add(Permission.objects.get(codename="add_document")) root = Document.objects.create( title="root", @@ -978,7 +978,7 @@ class TestVersionAwareFilters(DjangoTestCase): superseded content -- selecting documents the list view, filtered by the same term, does not show. """ - user = User.objects.create_superuser(username="bulk_selection") + user = UserFactory(username="bulk_selection", superuser=True) selected = DocumentSelectionMixin()._resolve_document_ids( user=user, @@ -1005,7 +1005,7 @@ class TestBulkSelectionExcludesVersions(DjangoTestCase): "Select all matching" reconstructs the document list, which never contains version documents as rows of their own. """ - user = User.objects.create_superuser(username="bulk_versions") + user = UserFactory(username="bulk_versions", superuser=True) root = Document.objects.create( title="shared-title root", checksum="bulk-root", diff --git a/src/documents/tests/test_api_documents.py b/src/documents/tests/test_api_documents.py index 1b5dc48eb..e6dbf4cf9 100644 --- a/src/documents/tests/test_api_documents.py +++ b/src/documents/tests/test_api_documents.py @@ -53,13 +53,14 @@ from documents.tests.utils import read_streaming_response from paperless_testing.dirs import DirectoriesMixin from paperless_testing.factories import DocumentFactory from paperless_testing.factories import TagFactory +from paperless_testing.factories import UserFactory class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) cache.clear() @@ -357,8 +358,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): with Path(filename).open("wb") as f: f.write(content) - user1 = User.objects.create_user(username="test1") - user2 = User.objects.create_user(username="test2") + user1 = UserFactory(username="test1") + user2 = UserFactory(username="test2") user1.user_permissions.add(*Permission.objects.filter(codename="view_document")) user2.user_permissions.add(*Permission.objects.filter(codename="view_document")) @@ -760,7 +761,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): - History is returned """ # No auditlog permissions - user = User.objects.create_user(username="test") + user = UserFactory(username="test") user.user_permissions.add(*Permission.objects.filter(codename="view_document")) self.client.force_authenticate(user=user) doc = Document.objects.create( @@ -776,7 +777,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): # superuser user.is_superuser = True user.save() - user2 = User.objects.create_user(username="test2") + user2 = UserFactory(username="test2") doc2 = Document.objects.create( title="Second title", checksum="456", @@ -1073,8 +1074,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): self.assertEqual(response.data["duplicate_documents"], []) def test_has_duplicates_filter_respects_document_permissions(self) -> None: - owner = User.objects.create_user(username="duplicate-owner") - requester = User.objects.create_user(username="duplicate-requester") + owner = UserFactory(username="duplicate-owner") + requester = UserFactory(username="duplicate-requester") requester.user_permissions.add( Permission.objects.get(codename="view_document"), ) @@ -1317,8 +1318,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - Owner filters work correctly but still respect permissions """ - u1 = User.objects.create_user("user1") - u2 = User.objects.create_user("user2") + u1 = UserFactory(username="user1") + u2 = UserFactory(username="user2") u1.user_permissions.add(*Permission.objects.filter(codename="view_document")) u2.user_permissions.add(*Permission.objects.filter(codename="view_document")) @@ -1424,7 +1425,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): - The document is returned exactly once, not once per permission path (regression test for https://github.com/paperless-ngx/paperless-ngx/issues/13331) """ - user = User.objects.create_user("user1") + user = UserFactory(username="user1") user.user_permissions.add(*Permission.objects.filter(codename="view_document")) group = Group.objects.create(name="group1") user.groups.add(group) @@ -1452,8 +1453,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - The document does not appear in their results """ - owner = User.objects.create_user("owner1") - stranger = User.objects.create_user("stranger1") + owner = UserFactory(username="owner1") + stranger = UserFactory(username="stranger1") stranger.user_permissions.add( *Permission.objects.filter(codename="view_document"), ) @@ -1474,9 +1475,9 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - Only the group member sees the document """ - owner = User.objects.create_user("owner2") - member = User.objects.create_user("member1") - non_member = User.objects.create_user("nonmember1") + owner = UserFactory(username="owner2") + member = UserFactory(username="member1") + non_member = UserFactory(username="nonmember1") for u in (member, non_member): u.user_permissions.add(*Permission.objects.filter(codename="view_document")) @@ -1785,8 +1786,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - Statistics only include inbox counts for tags accessible by the user """ - u1 = User.objects.create_user("user1") - u2 = User.objects.create_user("user2") + u1 = UserFactory(username="user1") + u2 = UserFactory(username="user2") inbox_tag_u1 = Tag.objects.create(name="inbox_u1", is_inbox_tag=True, owner=u1) Tag.objects.create(name="inbox_u2", is_inbox_tag=True, owner=u2) doc_u1 = Document.objects.create( @@ -1816,8 +1817,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): self.assertEqual(response.data["documents_inbox"], 0) def test_statistics_with_statistics_permission(self) -> None: - owner = User.objects.create_user("owner") - stats_user = User.objects.create_user("stats-user") + owner = UserFactory(username="owner") + stats_user = UserFactory(username="stats-user") stats_user.user_permissions.add( Permission.objects.get(codename="view_global_statistics"), ) @@ -1986,7 +1987,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) def test_upload_insufficient_permissions(self) -> None: - self.client.force_authenticate(user=User.objects.create_user("testuser2")) + self.client.force_authenticate(user=UserFactory(username="testuser2")) with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f: response = self.client.post( @@ -2782,9 +2783,9 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): mock_get_date_parser.assert_not_called() def test_saved_views(self) -> None: - u1 = User.objects.create_user("user1") - u2 = User.objects.create_user("user2") - u3 = User.objects.create_user("user3") + u1 = UserFactory(username="user1") + u2 = UserFactory(username="user2") + u3 = UserFactory(username="user3") view_perm = Permission.objects.get(codename="view_savedview") change_perm = Permission.objects.get(codename="change_savedview") @@ -3064,7 +3065,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): self.assertListEqual(saved_view_settings["sidebar_views_visible_ids"], [v2.id]) def test_saved_view_create_update_patch(self) -> None: - User.objects.create_user("user1") + UserFactory(username="user1") view = { "name": "test", @@ -3127,7 +3128,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): - Display options are updated - Display fields are validated """ - User.objects.create_user("user1") + UserFactory(username="user1") view = { "name": "test", @@ -3568,11 +3569,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - Notes are neither created nor deleted """ - user1 = User.objects.create_user(username="test1") + user1 = UserFactory(username="test1") user1.user_permissions.add(*Permission.objects.all()) user1.save() - user2 = User.objects.create_user(username="test2") + user2 = UserFactory(username="test2") user2.save() doc = Document.objects.create( @@ -3616,7 +3617,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_notes_require_global_document_permissions(self) -> None: - user = User.objects.create_user(username="note_editor") + user = UserFactory(username="note_editor") user.user_permissions.add( *Permission.objects.filter( codename__in=["view_note", "add_note", "delete_note"], @@ -3797,11 +3798,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): - Unique items are created - Non-unique items are not allowed """ - user1 = User.objects.create_user(username="test1") + user1 = UserFactory(username="test1") user1.user_permissions.add(*Permission.objects.filter(codename="add_tag")) user1.save() - user2 = User.objects.create_user(username="test2") + user2 = UserFactory(username="test2") user2.user_permissions.add(*Permission.objects.filter(codename="add_tag")) user2.save() @@ -3857,11 +3858,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): - Unique items are created - Non-unique items are not allowed on update """ - user1 = User.objects.create_user(username="test1") + user1 = UserFactory(username="test1") user1.user_permissions.add(*Permission.objects.filter(codename="change_tag")) user1.save() - user2 = User.objects.create_user(username="test2") + user2 = UserFactory(username="test2") user2.user_permissions.add(*Permission.objects.filter(codename="change_tag")) user2.save() @@ -3993,11 +3994,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - Links only shown if user has permissions """ - user1 = User.objects.create_user(username="test1") + user1 = UserFactory(username="test1") user1.user_permissions.add(*Permission.objects.all()) user1.save() - user2 = User.objects.create_user(username="test2") + user2 = UserFactory(username="test2") user2.save() doc = Document.objects.create( @@ -4034,11 +4035,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - Share link creation is denied until view permission is granted """ - user1 = User.objects.create_user(username="test1") + user1 = UserFactory(username="test1") user1.user_permissions.add(*Permission.objects.filter(codename="add_sharelink")) user1.save() - user2 = User.objects.create_user(username="test2") + user2 = UserFactory(username="test2") user2.save() doc = Document.objects.create( @@ -4097,11 +4098,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - ASN +1 from user2's doc is returned for user1 """ - user1 = User.objects.create_user(username="test1") + user1 = UserFactory(username="test1") user1.user_permissions.add(*Permission.objects.all()) user1.save() - user2 = User.objects.create_user(username="test2") + user2 = UserFactory(username="test2") user2.save() doc1 = Document.objects.create( @@ -4141,7 +4142,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - ASN 1 is returned """ - user1 = User.objects.create_user(username="test1") + user1 = UserFactory(username="test1") user1.user_permissions.add(*Permission.objects.all()) user1.save() @@ -4170,7 +4171,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - Explicit error is returned """ - user1 = User.objects.create_superuser(username="test1") + user1 = UserFactory(username="test1", superuser=True) self.client.force_authenticate(user1) @@ -4348,7 +4349,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): THEN: - Error response is returned """ - user1 = User.objects.create_user(username="test1") + user1 = UserFactory(username="test1") user1.user_permissions.add(*Permission.objects.all()) user1.save() @@ -4454,7 +4455,7 @@ class TestDocumentApiTagColors(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) @@ -4534,7 +4535,7 @@ class TestDocumentApiCustomFieldsSorting(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) self.doc1 = Document.objects.create( diff --git a/src/documents/tests/test_api_email.py b/src/documents/tests/test_api_email.py index e404e3037..a34015ece 100644 --- a/src/documents/tests/test_api_email.py +++ b/src/documents/tests/test_api_email.py @@ -3,7 +3,6 @@ import shutil from unittest import mock from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.core import mail from django.test import override_settings from rest_framework import status @@ -12,6 +11,7 @@ from rest_framework.test import APITestCase from documents.models import Document from documents.tests.utils import SampleDirMixin from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase): @@ -20,7 +20,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) self.doc1 = Document.objects.create( @@ -303,7 +303,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase): THEN: - Forbidden response is returned """ - user1 = User.objects.create_user(username="test1") + user1 = UserFactory(username="test1") user1.user_permissions.add(*Permission.objects.filter(codename="view_document")) doc_owned = Document.objects.create( @@ -338,7 +338,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase): THEN: - Request succeeds """ - user1 = User.objects.create_user(username="test1") + user1 = UserFactory(username="test1") user1.user_permissions.add(*Permission.objects.filter(codename="view_document")) self.client.force_authenticate(user1) diff --git a/src/documents/tests/test_api_filter_by_custom_fields.py b/src/documents/tests/test_api_filter_by_custom_fields.py index 0c51169c1..3c0ace782 100644 --- a/src/documents/tests/test_api_filter_by_custom_fields.py +++ b/src/documents/tests/test_api_filter_by_custom_fields.py @@ -4,7 +4,6 @@ from collections.abc import Callable from datetime import date from urllib.parse import quote -from django.contrib.auth.models import User from rest_framework.test import APITestCase from documents.models import CustomField @@ -14,6 +13,7 @@ from documents.models import SavedView from documents.models import SavedViewFilterRule from documents.serialisers import DocumentSerializer from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class DocumentWrapper: @@ -35,7 +35,7 @@ class TestCustomFieldsSearch(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) # Create one custom field per type. The fields are called f"{type}_field". diff --git a/src/documents/tests/test_api_objects.py b/src/documents/tests/test_api_objects.py index 69264548e..e1e5b1fad 100644 --- a/src/documents/tests/test_api_objects.py +++ b/src/documents/tests/test_api_objects.py @@ -22,13 +22,14 @@ from documents.models import DocumentType from documents.models import StoragePath from documents.models import Tag from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestApiObjects(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - user = User.objects.create_superuser(username="temp_admin") + user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=user) self.tag1 = Tag.objects.create(name="t1", is_inbox_tag=True) @@ -153,7 +154,7 @@ class TestApiObjects(DirectoriesMixin, APITestCase): # A newer document owned by another user must not leak through the # aggregate for a non-superuser who cannot view it - other = User.objects.create_user(username="other") + other = UserFactory(username="other") Document.objects.create( mime_type="application/pdf", correspondent=self.c1, @@ -162,7 +163,7 @@ class TestApiObjects(DirectoriesMixin, APITestCase): owner=other, ) - user = User.objects.create_user(username="regular") + user = UserFactory(username="regular") user.user_permissions.add( Permission.objects.get(codename="view_correspondent"), ) @@ -200,7 +201,7 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - user = User.objects.create_superuser(username="temp_admin") + user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=user) self.sp1 = StoragePath.objects.create(name="sp1", path="Something/{checksum}") @@ -455,8 +456,8 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase): self.assertEqual(response.data, "folder/Something.pdf") def test_test_storage_path_requires_document_view_permission(self) -> None: - owner = User.objects.create_user(username="owner") - unprivileged = User.objects.create_user(username="unprivileged") + owner = UserFactory(username="owner") + unprivileged = UserFactory(username="unprivileged") unprivileged.user_permissions.add( Permission.objects.get(codename="view_document"), ) @@ -481,8 +482,8 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase): self.assertIn("document", response.data) def test_test_storage_path_allows_shared_document_view_permission(self) -> None: - owner = User.objects.create_user(username="owner") - viewer = User.objects.create_user(username="viewer") + owner = UserFactory(username="owner") + viewer = UserFactory(username="viewer") document = Document.objects.create( mime_type="application/pdf", owner=owner, @@ -545,7 +546,7 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase): def test_test_storage_path_exposes_basic_document_context_but_not_sensitive_owner_data( self, ) -> None: - owner = User.objects.create_user( + owner = UserFactory( username="owner", password="password", email="owner@example.com", @@ -614,8 +615,8 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase): def test_test_storage_path_includes_related_objects_for_visible_document( self, ) -> None: - owner = User.objects.create_user(username="owner") - viewer = User.objects.create_user(username="viewer") + owner = UserFactory(username="owner") + viewer = UserFactory(username="viewer") private_correspondent = Correspondent.objects.create( name="Private Correspondent", owner=owner, @@ -662,7 +663,7 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase): self.assertEqual(response.data, "Private Correspondent.pdf") def test_test_storage_path_superuser_can_view_private_related_objects(self) -> None: - owner = User.objects.create_user(username="owner") + owner = UserFactory(username="owner") private_correspondent = Correspondent.objects.create( name="Private Correspondent", owner=owner, @@ -693,8 +694,8 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase): def test_test_storage_path_includes_doc_type_storage_path_and_tags( self, ) -> None: - owner = User.objects.create_user(username="owner") - viewer = User.objects.create_user(username="viewer") + owner = UserFactory(username="owner") + viewer = UserFactory(username="viewer") private_document_type = DocumentType.objects.create( name="Private Type", owner=owner, @@ -756,8 +757,8 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase): def test_test_storage_path_includes_custom_fields_for_visible_document( self, ) -> None: - owner = User.objects.create_user(username="owner") - viewer = User.objects.create_user(username="viewer") + owner = UserFactory(username="owner") + viewer = UserFactory(username="viewer") document = Document.objects.create( mime_type="application/pdf", owner=owner, @@ -798,7 +799,7 @@ class TestBulkEditObjects(APITestCase): def setUp(self) -> None: super().setUp() - self.temp_admin = User.objects.create_superuser(username="temp_admin") + self.temp_admin = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.temp_admin) self.t1 = Tag.objects.create(name="t1") diff --git a/src/documents/tests/test_api_permissions.py b/src/documents/tests/test_api_permissions.py index 867cdefaa..e91eca1e7 100644 --- a/src/documents/tests/test_api_permissions.py +++ b/src/documents/tests/test_api_permissions.py @@ -20,6 +20,7 @@ from documents.models import MatchingModel from documents.models import StoragePath from documents.models import Tag from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestApiAuth(DirectoriesMixin, APITestCase): @@ -93,14 +94,14 @@ class TestApiAuth(DirectoriesMixin, APITestCase): self.assertNotIn("X-Version", response) def test_api_version_with_auth(self) -> None: - user = User.objects.create_superuser(username="test") + user = UserFactory(username="test", superuser=True) self.client.force_authenticate(user) response = self.client.get("/api/documents/") self.assertIn("X-Api-Version", response) self.assertIn("X-Version", response) def test_api_insufficient_permissions(self) -> None: - user = User.objects.create_user(username="test") + user = UserFactory(username="test") self.client.force_authenticate(user) Document.objects.create(title="Test") @@ -137,7 +138,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase): ) def test_api_sufficient_permissions(self) -> None: - user = User.objects.create_user(username="test") + user = UserFactory(username="test") user.user_permissions.add(*Permission.objects.all()) user.is_staff = True self.client.force_authenticate(user) @@ -166,8 +167,8 @@ class TestApiAuth(DirectoriesMixin, APITestCase): ) def test_api_get_object_permissions(self) -> None: - user1 = User.objects.create_user(username="test1") - user2 = User.objects.create_user(username="test2") + user1 = UserFactory(username="test1") + user2 = UserFactory(username="test2") user1.user_permissions.add(*Permission.objects.filter(codename="view_document")) self.client.force_authenticate(user1) @@ -205,7 +206,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase): THEN: - Object created with current user as owner """ - user1 = User.objects.create_superuser(username="user1") + user1 = UserFactory(username="user1", superuser=True) self.client.force_authenticate(user1) @@ -234,7 +235,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase): THEN: - Object created with no owner """ - user1 = User.objects.create_superuser(username="user1") + user1 = UserFactory(username="user1", superuser=True) self.client.force_authenticate(user1) @@ -265,7 +266,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase): THEN: - Object permissions are set appropriately """ - user1 = User.objects.create_superuser(username="user1") + user1 = UserFactory(username="user1", superuser=True) user2 = User.objects.create(username="user2") group1 = Group.objects.create(name="group1") @@ -313,7 +314,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase): THEN: - Object permissions are set appropriately """ - user1 = User.objects.create_superuser(username="user1") + user1 = UserFactory(username="user1", superuser=True) user2 = User.objects.create(username="user2") group1 = Group.objects.create(name="group1") @@ -363,7 +364,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase): mime_type="application/pdf", content="this is a document", ) - user1 = User.objects.create_superuser(username="user1") + user1 = UserFactory(username="user1", superuser=True) user2 = User.objects.create(username="user2") group1 = Group.objects.create(name="group1") @@ -413,7 +414,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase): mime_type="application/pdf", content="this is a document", ) - user1 = User.objects.create_superuser(username="user1") + user1 = UserFactory(username="user1", superuser=True) user2 = User.objects.create(username="user2") group1 = Group.objects.create(name="group1") doc.owner = user1 @@ -446,8 +447,8 @@ class TestApiAuth(DirectoriesMixin, APITestCase): self.assertIn("change_document", get_perms(group1, doc)) def test_document_permissions_change_requires_owner(self) -> None: - owner = User.objects.create_user(username="owner") - editor = User.objects.create_user(username="editor") + owner = UserFactory(username="owner") + editor = UserFactory(username="editor") editor.user_permissions.add( *Permission.objects.all(), ) @@ -499,9 +500,9 @@ class TestApiAuth(DirectoriesMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_dynamic_permissions_fields(self) -> None: - user1 = User.objects.create_user(username="user1") + user1 = UserFactory(username="user1") user1.user_permissions.add(*Permission.objects.filter(codename="view_document")) - user2 = User.objects.create_user(username="user2") + user2 = UserFactory(username="user2") Document.objects.create(title="Test", content="content 1", checksum="1") doc2 = Document.objects.create( @@ -574,8 +575,8 @@ class TestApiAuth(DirectoriesMixin, APITestCase): owned by someone else with no explicit guardian grant -- mirrors guardian's own ObjectPermissionChecker.has_perm() superuser shortcut. """ - superuser = User.objects.create_superuser(username="admin") - other_user = User.objects.create_user(username="user2") + superuser = UserFactory(username="admin", superuser=True) + other_user = UserFactory(username="user2") Document.objects.create( title="Test", content="content", @@ -602,7 +603,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase): THEN: - MFA required error is returned """ - user1 = User.objects.create_user(username="user1") + user1 = UserFactory(username="user1") user1.set_password("password") user1.save() @@ -626,7 +627,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase): THEN: - MFA code is required """ - user1 = User.objects.create_user(username="user1") + user1 = UserFactory(username="user1") user1.set_password("password") user1.save() @@ -688,7 +689,7 @@ class TestApiUser(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) def test_get_users(self) -> None: @@ -858,7 +859,7 @@ class TestApiUser(DirectoriesMixin, APITestCase): ) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - regular_user = User.objects.create_user(username="regular_user") + regular_user = UserFactory(username="regular_user") regular_user.user_permissions.add( *Permission.objects.all(), ) @@ -885,9 +886,9 @@ class TestApiUser(DirectoriesMixin, APITestCase): - Only superusers can change superuser status """ - user1 = User.objects.create_user(username="user1") + user1 = UserFactory(username="user1") user1.user_permissions.add(*Permission.objects.all()) - user2 = User.objects.create_superuser(username="user2") + user2 = UserFactory(username="user2", superuser=True) self.client.force_authenticate(user1) @@ -972,9 +973,9 @@ class TestApiUser(DirectoriesMixin, APITestCase): - Only superusers can change staff status """ - user1 = User.objects.create_user(username="user1") + user1 = UserFactory(username="user1") user1.user_permissions.add(*Permission.objects.all()) - user2 = User.objects.create_superuser(username="user2") + user2 = UserFactory(username="user2", superuser=True) self.client.force_authenticate(user1) @@ -1027,7 +1028,7 @@ class TestApiGroup(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) def test_get_groups(self) -> None: @@ -1128,7 +1129,7 @@ class TestBulkEditObjectPermissions(APITestCase): def setUp(self) -> None: super().setUp() - self.temp_admin = User.objects.create_superuser(username="temp_admin") + self.temp_admin = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.temp_admin) self.t1 = Tag.objects.create(name="t1") @@ -1680,7 +1681,7 @@ class TestFullPermissionsFlag(APITestCase): def setUp(self) -> None: super().setUp() - self.admin = User.objects.create_superuser(username="admin") + self.admin = UserFactory(username="admin", superuser=True) def test_full_perms_flag(self) -> None: """ diff --git a/src/documents/tests/test_api_profile.py b/src/documents/tests/test_api_profile.py index 7422e5426..24ddda5da 100644 --- a/src/documents/tests/test_api_profile.py +++ b/src/documents/tests/test_api_profile.py @@ -9,6 +9,7 @@ from rest_framework.authtoken.models import Token from rest_framework.test import APITestCase from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory # see allauth.socialaccount.providers.openid.provider.OpenIDProvider @@ -55,10 +56,11 @@ class TestApiProfile(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser( + self.user = UserFactory( username="temp_admin", first_name="firstname", last_name="surname", + superuser=True, ) self.client.force_authenticate(user=self.user) @@ -401,7 +403,7 @@ class TestApiTOTPViews(APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) def test_get_totp(self) -> None: diff --git a/src/documents/tests/test_api_search.py b/src/documents/tests/test_api_search.py index 18bcefc95..887068ea3 100644 --- a/src/documents/tests/test_api_search.py +++ b/src/documents/tests/test_api_search.py @@ -7,7 +7,6 @@ import time_machine from dateutil.relativedelta import relativedelta from django.contrib.auth.models import Group from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.test import override_settings from django.utils import timezone from guardian.shortcuts import assign_perm @@ -31,6 +30,7 @@ from paperless_mail.models import MailAccount from paperless_mail.models import MailRule from paperless_testing.dirs import DirectoriesMixin from paperless_testing.factories import DocumentFactory +from paperless_testing.factories import UserFactory pytestmark = pytest.mark.search @@ -40,7 +40,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): super().setUp() reset_backend() - self.user = User.objects.create_superuser(username="temp_admin") + self.user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=self.user) def tearDown(self) -> None: @@ -949,8 +949,8 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): THEN: - Terms only within docs user has access to are returned """ - u1 = User.objects.create_user("user1") - u2 = User.objects.create_user("user2") + u1 = UserFactory(username="user1") + u2 = UserFactory(username="user2") u1.user_permissions.add(Permission.objects.get(codename="view_document")) self.client.force_authenticate(user=u1) @@ -999,8 +999,8 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): self.assertEqual(response.data, ["applebaum", "apples", "appletini"]) def test_search_autocomplete_group_revocation_is_immediate(self) -> None: - user = User.objects.create_user("group-user") - owner = User.objects.create_user("document-owner") + user = UserFactory(username="group-user") + owner = UserFactory(username="document-owner") group = Group.objects.create(name="temporary-viewers") user.user_permissions.add(Permission.objects.get(codename="view_document")) user.groups.add(group) @@ -1091,8 +1091,8 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): self.assertIsNone(response.data["corrected_query"]) def test_search_spelling_suggestion_suppressed_for_private_terms(self) -> None: - owner = User.objects.create_user("owner") - attacker = User.objects.create_user("attacker") + owner = UserFactory(username="owner") + attacker = UserFactory(username="attacker") attacker.user_permissions.add( Permission.objects.get(codename="view_document"), ) @@ -1222,8 +1222,8 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): THEN: - The request is rejected """ - owner = User.objects.create_user("owner") - attacker = User.objects.create_user("attacker") + owner = UserFactory(username="owner") + attacker = UserFactory(username="attacker") attacker.user_permissions.add( Permission.objects.get(codename="view_document"), ) @@ -1534,9 +1534,9 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): - Only owned docs are returned for regular users - All docs are returned for superuser """ - superuser = User.objects.create_superuser("superuser") - u1 = User.objects.create_user("user1") - u2 = User.objects.create_user("user2") + superuser = UserFactory(username="superuser", superuser=True) + u1 = UserFactory(username="user1") + u2 = UserFactory(username="user2") u1.user_permissions.add(*Permission.objects.filter(codename="view_document")) u2.user_permissions.add(*Permission.objects.filter(codename="view_document")) @@ -1588,8 +1588,8 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): THEN: - Only docs with granted view permissions are returned """ - u1 = User.objects.create_user("user1") - u2 = User.objects.create_user("user2") + u1 = UserFactory(username="user1") + u2 = UserFactory(username="user2") u1.user_permissions.add(*Permission.objects.filter(codename="view_document")) u2.user_permissions.add(*Permission.objects.filter(codename="view_document")) @@ -1641,8 +1641,8 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): self.assertEqual(r.data["count"], 1) def test_search_sorting(self) -> None: - u1 = User.objects.create_user("user1") - u2 = User.objects.create_user("user2") + u1 = UserFactory(username="user1") + u2 = UserFactory(username="user2") c1 = Correspondent.objects.create(name="corres Ax") c2 = Correspondent.objects.create(name="corres Cx") c3 = Correspondent.objects.create(name="corres Bx") @@ -1892,8 +1892,8 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): pk=5, ) - user1 = User.objects.create_user("bank user1") - user2 = User.objects.create_superuser("user2") + user1 = UserFactory(username="bank user1") + user2 = UserFactory(username="user2", superuser=True) group1 = Group.objects.create(name="bank group1") Group.objects.create(name="group2") @@ -2018,8 +2018,8 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): self.assertEqual(returned.get(root.id), "latest content") def test_global_search_filters_owned_mail_objects(self) -> None: - user1 = User.objects.create_user("mail-search-user") - user2 = User.objects.create_user("other-mail-search-user") + user1 = UserFactory(username="mail-search-user") + user2 = UserFactory(username="other-mail-search-user") user1.user_permissions.add( Permission.objects.get(codename="view_mailaccount"), Permission.objects.get(codename="view_mailrule"), diff --git a/src/documents/tests/test_api_status.py b/src/documents/tests/test_api_status.py index ae21e5ffb..effee88f8 100644 --- a/src/documents/tests/test_api_status.py +++ b/src/documents/tests/test_api_status.py @@ -6,7 +6,6 @@ from pathlib import Path from unittest import mock from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.test import override_settings from django.utils import timezone from rest_framework import status @@ -16,6 +15,7 @@ from documents.models import PaperlessTask from documents.permissions import has_system_status_permission from paperless import version from paperless_testing.factories import PaperlessTaskFactory +from paperless_testing.factories import UserFactory class TestSystemStatus(APITestCase): @@ -23,9 +23,7 @@ class TestSystemStatus(APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser( - username="temp_admin", - ) + self.user = UserFactory(username="temp_admin", superuser=True) self.tmp_dir = Path(tempfile.mkdtemp()) self.override = override_settings(MEDIA_ROOT=self.tmp_dir) self.override.enable() @@ -96,7 +94,7 @@ class TestSystemStatus(APITestCase): response = self.client.get(self.ENDPOINT) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response["WWW-Authenticate"], "Token") - normal_user = User.objects.create_user(username="normal_user") + normal_user = UserFactory(username="normal_user") self.client.force_login(normal_user) response = self.client.get(self.ENDPOINT) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) @@ -107,7 +105,7 @@ class TestSystemStatus(APITestCase): response = self.client.get(self.ENDPOINT) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - user = User.objects.create_user(username="status_user") + user = UserFactory(username="status_user") user.user_permissions.add( Permission.objects.get(codename="view_system_monitoring"), ) diff --git a/src/documents/tests/test_api_tasks.py b/src/documents/tests/test_api_tasks.py index 8f8493417..71805961a 100644 --- a/src/documents/tests/test_api_tasks.py +++ b/src/documents/tests/test_api_tasks.py @@ -26,7 +26,6 @@ from paperless_testing.factories import PaperlessTaskFactory pytestmark = pytest.mark.api ENDPOINT = "/api/tasks/" -ACCEPT_V10 = "application/json; version=10" ACCEPT_V9 = "application/json; version=9" @@ -346,21 +345,18 @@ class TestGetTasksV10: self, admin_user: User, regular_user: User, + user_client: APIClient, ) -> None: """Regular users see their own tasks and unowned (system) tasks; other users' tasks are hidden.""" regular_user.user_permissions.add( Permission.objects.get(codename="view_paperlesstask"), ) - client = APIClient() - client.force_authenticate(user=regular_user) - client.credentials(HTTP_ACCEPT=ACCEPT_V10) - PaperlessTaskFactory(owner=admin_user) # other user — not visible unowned_task = PaperlessTaskFactory() # unowned (system task) — visible own_task = PaperlessTaskFactory(owner=regular_user) - response = client.get(ENDPOINT) + response = user_client.get(ENDPOINT) assert response.status_code == status.HTTP_200_OK assert response.data["count"] == 2 @@ -732,19 +728,19 @@ class TestAcknowledge: assert response.status_code == status.HTTP_403_FORBIDDEN - def test_succeeds_with_change_permission(self, regular_user: User) -> None: + def test_succeeds_with_change_permission( + self, + regular_user: User, + user_client: APIClient, + ) -> None: """Users granted change_paperlesstask permission can acknowledge tasks.""" regular_user.user_permissions.add( Permission.objects.get(codename="change_paperlesstask"), ) regular_user.save() - client = APIClient() - client.force_authenticate(user=regular_user) - client.credentials(HTTP_ACCEPT=ACCEPT_V10) - task = PaperlessTaskFactory() - response = client.post( + response = user_client.post( ENDPOINT + "acknowledge/", {"tasks": [task.id]}, format="json", diff --git a/src/documents/tests/test_api_trash.py b/src/documents/tests/test_api_trash.py index 052a7f76d..ac0e00ead 100644 --- a/src/documents/tests/test_api_trash.py +++ b/src/documents/tests/test_api_trash.py @@ -1,20 +1,20 @@ from datetime import date from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.core.cache import cache from rest_framework import status from rest_framework.test import APITestCase from documents.models import Document from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestTrashAPI(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_user(username="temp_admin") + self.user = UserFactory(username="temp_admin") self.user.user_permissions.add(*Permission.objects.all()) self.client.force_authenticate(user=self.user) cache.clear() @@ -70,7 +70,7 @@ class TestTrashAPI(DirectoriesMixin, APITestCase): self.assertEqual(Document.global_objects.count(), 0) def test_trash_list_requires_global_document_view_permission(self) -> None: - user = User.objects.create_user(username="trash_owner") + user = UserFactory(username="trash_owner") document = Document.objects.create(title="Owned", owner=user) document.delete() self.client.force_authenticate(user) @@ -140,7 +140,7 @@ class TestTrashAPI(DirectoriesMixin, APITestCase): created=date(2023, 1, 2), ) document_not_owned.delete() - user2 = User.objects.create_user(username="user2") + user2 = UserFactory(username="user2") document_u2 = Document.objects.create( title="Title3", content="content3", @@ -158,7 +158,7 @@ class TestTrashAPI(DirectoriesMixin, APITestCase): self.assertEqual(resp.data["results"][1]["id"], document_u1.pk) # superuser sees all documents - superuser = User.objects.create_superuser(username="superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) resp = self.client.get("/api/trash/") self.assertEqual(resp.status_code, status.HTTP_200_OK) @@ -174,7 +174,7 @@ class TestTrashAPI(DirectoriesMixin, APITestCase): - 403 Forbidden """ - user2 = User.objects.create_user(username="user2") + user2 = UserFactory(username="user2") document = Document.objects.create( title="Title", content="content", diff --git a/src/documents/tests/test_api_uisettings.py b/src/documents/tests/test_api_uisettings.py index e365ea0e0..7743626c2 100644 --- a/src/documents/tests/test_api_uisettings.py +++ b/src/documents/tests/test_api_uisettings.py @@ -1,13 +1,13 @@ import json from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.test import override_settings from rest_framework import status from rest_framework.test import APITestCase from paperless.version import __full_version_str__ from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestApiUiSettings(DirectoriesMixin, APITestCase): @@ -15,7 +15,7 @@ class TestApiUiSettings(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.test_user = User.objects.create_superuser(username="test") + self.test_user = UserFactory(username="test", superuser=True) self.test_user.first_name = "Test" self.test_user.last_name = "User" self.test_user.save() @@ -91,7 +91,7 @@ class TestApiUiSettings(DirectoriesMixin, APITestCase): ) def test_api_set_ui_settings_insufficient_global_permissions(self) -> None: - not_superuser = User.objects.create_user(username="test_not_superuser") + not_superuser = UserFactory(username="test_not_superuser") self.client.force_authenticate(user=not_superuser) settings = { @@ -111,7 +111,7 @@ class TestApiUiSettings(DirectoriesMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_api_set_ui_settings_sufficient_global_permissions(self) -> None: - not_superuser = User.objects.create_user(username="test_not_superuser") + not_superuser = UserFactory(username="test_not_superuser") not_superuser.user_permissions.add( *Permission.objects.filter(codename__contains="uisettings"), ) diff --git a/src/documents/tests/test_api_workflows.py b/src/documents/tests/test_api_workflows.py index 3dc08c32f..4b5e6eb48 100644 --- a/src/documents/tests/test_api_workflows.py +++ b/src/documents/tests/test_api_workflows.py @@ -15,6 +15,7 @@ from documents.models import Workflow from documents.models import WorkflowAction from documents.models import WorkflowTrigger from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestApiWorkflows(DirectoriesMixin, APITestCase): @@ -25,7 +26,7 @@ class TestApiWorkflows(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - user = User.objects.create_superuser(username="temp_admin") + user = UserFactory(username="temp_admin", superuser=True) self.client.force_authenticate(user=user) self.user2 = User.objects.create(username="user2") self.user3 = User.objects.create(username="user3") diff --git a/src/documents/tests/test_consumer.py b/src/documents/tests/test_consumer.py index 91c7701f4..e18c7e8a0 100644 --- a/src/documents/tests/test_consumer.py +++ b/src/documents/tests/test_consumer.py @@ -35,6 +35,7 @@ from documents.tests.utils import FileSystemAssertsMixin from documents.tests.utils import GetConsumerMixin from paperless_mail.models import MailRule from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class _BaseNewStyleParser: @@ -769,7 +770,7 @@ class TestConsumer( original_modified = timezone.now() - datetime.timedelta(days=1) Document.objects.filter(pk=root_doc.pk).update(modified=original_modified) - actor = User.objects.create_user( + actor = UserFactory( username="actor", email="actor@example.com", password="password", diff --git a/src/documents/tests/test_file_handling.py b/src/documents/tests/test_file_handling.py index 0e8434645..e7f8354b5 100644 --- a/src/documents/tests/test_file_handling.py +++ b/src/documents/tests/test_file_handling.py @@ -8,7 +8,6 @@ from unittest import mock import pytest from auditlog.context import disable_auditlog from django.conf import settings -from django.contrib.auth.models import User from django.db import DatabaseError from django.db import connection from django.test import TestCase @@ -33,6 +32,7 @@ from documents.tasks import empty_trash from documents.tests.utils import FileSystemAssertsMixin from paperless_testing.dirs import DirectoriesMixin from paperless_testing.factories import DocumentFactory +from paperless_testing.factories import UserFactory class TestFileHandling(DirectoriesMixin, FileSystemAssertsMixin, TestCase): @@ -1323,7 +1323,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase): - Document without owner returns "none" """ - u1 = User.objects.create_user("user1") + u1 = UserFactory(username="user1") owned_doc = Document.objects.create( title="The Title", diff --git a/src/documents/tests/test_merge_documents_as_versions.py b/src/documents/tests/test_merge_documents_as_versions.py index 8b9a7bf35..7e2457965 100644 --- a/src/documents/tests/test_merge_documents_as_versions.py +++ b/src/documents/tests/test_merge_documents_as_versions.py @@ -3,7 +3,6 @@ from unittest import mock from auditlog.models import LogEntry from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.test import TestCase from rest_framework import status @@ -12,6 +11,7 @@ from rest_framework.test import APITestCase from documents.bulk_edit import merge_as_versions from documents.models import Document from documents.serialisers import MergeDocumentsAsVersionsSerializer +from paperless_testing.factories import UserFactory class TestMergeDocumentsAsVersionsSerializer(TestCase): @@ -294,7 +294,7 @@ class TestMergeDocumentsAsVersions(TestCase): @mock.patch("documents.bulk_edit.bulk_update_documents.apply_async") @mock.patch("documents.search.get_backend") def test_writes_audit_log_entry(self, *_mocks) -> None: - user = User.objects.create_user(username="merger") + user = UserFactory(username="merger") root = Document.objects.create(checksum="A", title="Root") source = Document.objects.create(checksum="B", title="Source") LogEntry.objects.all().delete() @@ -335,7 +335,7 @@ class TestMergeDocumentsAsVersions(TestCase): class TestMergeDocumentsAsVersionsAPI(APITestCase): def setUp(self) -> None: - self.user = User.objects.create_user(username="user") + self.user = UserFactory(username="user") self.user.user_permissions.add( Permission.objects.get(codename="change_document"), Permission.objects.get(codename="view_document"), @@ -382,7 +382,7 @@ class TestMergeDocumentsAsVersionsAPI(APITestCase): @mock.patch("documents.views.bulk_edit.merge_as_versions") def test_requires_change_permission(self, merge_mock) -> None: merge_mock.__name__ = "merge_as_versions" - user = User.objects.create_user(username="no-change") + user = UserFactory(username="no-change") self.doc1.owner = user self.doc1.save() self.doc2.owner = user @@ -405,7 +405,7 @@ class TestMergeDocumentsAsVersionsAPI(APITestCase): def test_requires_delete_permission(self, merge_mock) -> None: merge_mock.__name__ = "merge_as_versions" # Owns them and may change them, but may not make them stop being documents - user = User.objects.create_user(username="no-delete") + user = UserFactory(username="no-delete") user.user_permissions.add( Permission.objects.get(codename="change_document"), Permission.objects.get(codename="view_document"), diff --git a/src/documents/tests/test_permission_filtering_security.py b/src/documents/tests/test_permission_filtering_security.py index 8d30c7c6c..d1852d89d 100644 --- a/src/documents/tests/test_permission_filtering_security.py +++ b/src/documents/tests/test_permission_filtering_security.py @@ -8,7 +8,6 @@ import pytest from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import Group from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.test import override_settings from guardian.shortcuts import assign_perm from rest_framework.test import APIClient @@ -30,6 +29,7 @@ from paperless_testing.factories import DocumentFactory from paperless_testing.factories import DocumentTypeFactory from paperless_testing.factories import StoragePathFactory from paperless_testing.factories import TagFactory +from paperless_testing.factories import UserFactory if TYPE_CHECKING: from paperless_testing.dirs import PaperlessDirs @@ -51,8 +51,8 @@ def assert_visible_document_ids(actual_ids, *, expected_visible, expected_hidden @pytest.mark.django_db class TestPermittedDocumentIdsSecurity: def test_owner_sees_own_document(self): - user = User.objects.create_user(username="alice") - stranger = User.objects.create_user(username="mallory") + user = UserFactory(username="alice") + stranger = UserFactory(username="mallory") owned = DocumentFactory(owner=user) strangers_doc = DocumentFactory(owner=stranger) @@ -65,7 +65,7 @@ class TestPermittedDocumentIdsSecurity: ) def test_unowned_document_visible_to_everyone(self): - user = User.objects.create_user(username="alice") + user = UserFactory(username="alice") unowned = DocumentFactory(owner=None) assert_visible_document_ids( @@ -75,9 +75,9 @@ class TestPermittedDocumentIdsSecurity: ) def test_explicit_user_permission_grants_visibility(self): - grantee = User.objects.create_user(username="alice") - stranger = User.objects.create_user(username="mallory") - owner = User.objects.create_user(username="owner") + grantee = UserFactory(username="alice") + stranger = UserFactory(username="mallory") + owner = UserFactory(username="owner") shared = DocumentFactory(owner=owner) not_shared = DocumentFactory(owner=owner) assign_perm("view_document", grantee, shared) @@ -94,9 +94,9 @@ class TestPermittedDocumentIdsSecurity: ) def test_explicit_group_permission_grants_visibility_to_members_only(self): - owner = User.objects.create_user(username="owner") - member = User.objects.create_user(username="member") - non_member = User.objects.create_user(username="non_member") + owner = UserFactory(username="owner") + member = UserFactory(username="member") + non_member = UserFactory(username="non_member") group = Group.objects.create(name="finance") member.groups.add(group) shared = DocumentFactory(owner=owner) @@ -114,7 +114,7 @@ class TestPermittedDocumentIdsSecurity: ) def test_soft_deleted_document_excluded_by_default(self): - owner = User.objects.create_user(username="owner") + owner = UserFactory(username="owner") doc = DocumentFactory(owner=owner) doc.delete() # soft delete doc.refresh_from_db() @@ -130,8 +130,8 @@ class TestPermittedDocumentIdsSecurity: ) def test_superuser_sees_everything_including_no_perm_documents(self): - superuser = User.objects.create_superuser(username="root") - owner = User.objects.create_user(username="owner") + superuser = UserFactory(username="root", superuser=True) + owner = UserFactory(username="owner") doc = DocumentFactory(owner=owner) assert_visible_document_ids( @@ -141,7 +141,7 @@ class TestPermittedDocumentIdsSecurity: ) def test_anonymous_user_sees_only_unowned_documents(self): - owner = User.objects.create_user(username="owner") + owner = UserFactory(username="owner") owned = DocumentFactory(owner=owner) unowned = DocumentFactory(owner=None) @@ -155,7 +155,7 @@ class TestPermittedDocumentIdsSecurity: @pytest.mark.django_db class TestPermittedDocumentIdsIncludeDeleted: def test_include_deleted_true_reveals_soft_deleted_owned_document(self): - owner = User.objects.create_user(username="owner") + owner = UserFactory(username="owner") doc = DocumentFactory(owner=owner) doc.delete() @@ -166,8 +166,8 @@ class TestPermittedDocumentIdsIncludeDeleted: ) def test_include_deleted_true_still_respects_permission_boundary(self): - owner = User.objects.create_user(username="owner") - stranger = User.objects.create_user(username="mallory") + owner = UserFactory(username="owner") + stranger = UserFactory(username="mallory") doc = DocumentFactory(owner=owner) doc.delete() @@ -195,8 +195,8 @@ class TestAiChatAllDocumentsPermissionBoundary: def test_chat_all_documents_excludes_unshared_document(self, mock_stream_chat): mock_stream_chat.return_value = iter([b"data"]) - owner = User.objects.create_user(username="owner") - asker = User.objects.create_user(username="asker") + owner = UserFactory(username="owner") + asker = UserFactory(username="asker") asker.user_permissions.add( *Permission.objects.filter(codename="view_document"), ) @@ -223,8 +223,8 @@ class TestAiChatAllDocumentsPermissionBoundary: @pytest.mark.django_db class TestDuplicateDocumentsPermissionBoundary: def test_get_viewable_duplicates_includes_soft_deleted_but_respects_perms(self): - owner = User.objects.create_user(username="owner") - stranger = User.objects.create_user(username="mallory") + owner = UserFactory(username="owner") + stranger = UserFactory(username="mallory") original = DocumentFactory(owner=owner, checksum="dupe-checksum") dup_visible = DocumentFactory(owner=owner, checksum="dupe-checksum") dup_hidden = DocumentFactory(owner=owner, checksum="dupe-checksum") @@ -241,9 +241,9 @@ class TestDuplicateDocumentsPermissionBoundary: @pytest.mark.django_db class TestPermittedDocumentIdsArbitraryPermission: def test_change_document_permission_is_distinct_from_view(self): - owner = User.objects.create_user(username="owner") - viewer_only = User.objects.create_user(username="viewer") - editor = User.objects.create_user(username="editor") + owner = UserFactory(username="owner") + viewer_only = UserFactory(username="viewer") + editor = UserFactory(username="editor") doc = DocumentFactory(owner=owner) assign_perm("view_document", viewer_only, doc) assign_perm("change_document", editor, doc) @@ -261,8 +261,8 @@ class TestPermittedDocumentIdsArbitraryPermission: ) def test_qualified_permission_string_is_normalized_to_codename(self): - owner = User.objects.create_user(username="owner") - editor = User.objects.create_user(username="editor") + owner = UserFactory(username="owner") + editor = UserFactory(username="editor") doc = DocumentFactory(owner=owner) assign_perm("change_document", editor, doc) @@ -273,9 +273,9 @@ class TestPermittedDocumentIdsArbitraryPermission: ) def test_delete_permission_with_include_deleted_for_trash_restore(self): - owner = User.objects.create_user(username="owner") - stranger = User.objects.create_user(username="mallory") - view_only = User.objects.create_user(username="viewer") + owner = UserFactory(username="owner") + stranger = UserFactory(username="mallory") + view_only = UserFactory(username="viewer") doc = DocumentFactory(owner=owner) assign_perm("view_document", view_only, doc) doc.delete() @@ -311,8 +311,8 @@ class TestEmailDocumentPermissionBoundary: self, rest_api_client, ): - owner = User.objects.create_user(username="owner") - requester = User.objects.create_user(username="requester") + owner = UserFactory(username="owner") + requester = UserFactory(username="requester") requester.user_permissions.add( Permission.objects.get(codename="view_document"), ) @@ -343,8 +343,8 @@ class TestBulkEditChangePermissionBoundary: # permitted document must not be partially applied just because it # was bundled with a forbidden one, proving the endpoint checks # every document in the batch rather than only the first/last. - owner = User.objects.create_user(username="owner") - requester = User.objects.create_user(username="requester") + owner = UserFactory(username="owner") + requester = UserFactory(username="requester") # grant the global change_document permission so the object-level # check (not the global has_perm check) is what's under test requester.user_permissions.add( @@ -376,8 +376,8 @@ class TestBulkDownloadPermissionChecksRootDocument: rest_api_client: APIClient, paperless_dirs: PaperlessDirs, ) -> None: - owner = User.objects.create_user(username="owner") - requester = User.objects.create_user(username="requester") + owner = UserFactory(username="owner") + requester = UserFactory(username="requester") root = DocumentFactory(owner=owner) root.source_path.write_bytes(b"%PDF-1.4 test") assign_perm("view_document", requester, root) @@ -396,8 +396,8 @@ class TestBulkDownloadPermissionChecksRootDocument: rest_api_client: APIClient, paperless_dirs: PaperlessDirs, ) -> None: - owner = User.objects.create_user(username="owner") - requester = User.objects.create_user(username="requester") + owner = UserFactory(username="owner") + requester = UserFactory(username="requester") requester.user_permissions.add( Permission.objects.get(codename="view_document"), ) @@ -424,7 +424,7 @@ class TestBulkDownloadPermissionChecksRootDocument: # root-or-version bug; a user with no grant at all (the old # `stranger` case) can't tell the two apart, since they're denied # either way. - version_only_grantee = User.objects.create_user(username="version_only_grantee") + version_only_grantee = UserFactory(username="version_only_grantee") version_only_grantee.user_permissions.add( Permission.objects.get(codename="view_document"), ) @@ -447,8 +447,8 @@ class TestTrashRestorePermissionBoundary: self, rest_api_client, ): - owner = User.objects.create_user(username="owner") - requester = User.objects.create_user(username="requester") + owner = UserFactory(username="owner") + requester = UserFactory(username="requester") requester.user_permissions.add( Permission.objects.get(codename="delete_document"), ) @@ -468,8 +468,8 @@ class TestTrashRestorePermissionBoundary: self, rest_api_client, ): - owner = User.objects.create_user(username="owner") - requester = User.objects.create_user(username="requester") + owner = UserFactory(username="owner") + requester = UserFactory(username="requester") requester.user_permissions.add( Permission.objects.get(codename="delete_document"), ) @@ -486,8 +486,8 @@ class TestTrashRestorePermissionBoundary: assert response.status_code == HTTPStatus.OK def test_restore_requires_global_delete_permission(self, rest_api_client): - owner = User.objects.create_user(username="owner") - requester = User.objects.create_user(username="requester") + owner = UserFactory(username="owner") + requester = UserFactory(username="requester") rest_api_client.force_authenticate(user=requester) doc = DocumentFactory(owner=owner) assign_perm("delete_document", requester, doc) @@ -515,8 +515,8 @@ class TestTrashViewExcludesExplicitlyGrantedDocuments: """ def test_explicit_grant_does_not_leak_trashed_document(self, rest_api_client): - owner = User.objects.create_user(username="trash_owner") - grantee = User.objects.create_user(username="trash_grantee") + owner = UserFactory(username="trash_owner") + grantee = UserFactory(username="trash_grantee") grantee.user_permissions.add( Permission.objects.get(codename="view_document"), ) @@ -544,8 +544,8 @@ class TestTrashViewExcludesExplicitlyGrantedDocuments: ) class TestPermittedObjectIdsGenericModels: def test_owner_sees_own_object(self, model, factory, perm): - owner = User.objects.create_user(username=f"owner_{model.__name__}") - stranger = User.objects.create_user(username=f"stranger_{model.__name__}") + owner = UserFactory(username=f"owner_{model.__name__}") + stranger = UserFactory(username=f"stranger_{model.__name__}") owned = factory(owner=owner) strangers = factory(owner=stranger) @@ -558,12 +558,12 @@ class TestPermittedObjectIdsGenericModels: @pytest.mark.parametrize("is_superuser", [False, True]) def test_inactive_user_sees_nothing(self, model, factory, perm, is_superuser): suffix = f"{model.__name__}_{is_superuser}" - user = User.objects.create_user( + user = UserFactory( username=f"inactive_{suffix}", is_active=False, is_superuser=is_superuser, ) - other = User.objects.create_user(username=f"other_{suffix}") + other = UserFactory(username=f"other_{suffix}") granted = factory(owner=other) assign_perm(perm, user, granted) @@ -578,7 +578,7 @@ class TestPermittedObjectIdsGenericModels: ) def test_unowned_object_visible_to_everyone(self, model, factory, perm): - user = User.objects.create_user(username=f"user_{model.__name__}") + user = UserFactory(username=f"user_{model.__name__}") unowned = factory(owner=None) assert_visible_document_ids( @@ -588,9 +588,9 @@ class TestPermittedObjectIdsGenericModels: ) def test_explicit_permission_grants_visibility(self, model, factory, perm): - owner = User.objects.create_user(username=f"owner2_{model.__name__}") - grantee = User.objects.create_user(username=f"grantee_{model.__name__}") - stranger = User.objects.create_user(username=f"stranger2_{model.__name__}") + owner = UserFactory(username=f"owner2_{model.__name__}") + grantee = UserFactory(username=f"grantee_{model.__name__}") + stranger = UserFactory(username=f"stranger2_{model.__name__}") shared = factory(owner=owner) not_shared = factory(owner=owner) assign_perm(perm, grantee, shared) @@ -612,9 +612,9 @@ class TestPermittedObjectIdsGenericModels: factory, perm, ): - owner = User.objects.create_user(username=f"owner3_{model.__name__}") - member = User.objects.create_user(username=f"member_{model.__name__}") - non_member = User.objects.create_user(username=f"nonmember_{model.__name__}") + owner = UserFactory(username=f"owner3_{model.__name__}") + member = UserFactory(username=f"member_{model.__name__}") + non_member = UserFactory(username=f"nonmember_{model.__name__}") group = Group.objects.create(name=f"group_{model.__name__}") member.groups.add(group) shared = factory(owner=owner) @@ -632,8 +632,8 @@ class TestPermittedObjectIdsGenericModels: ) def test_superuser_sees_everything(self, model, factory, perm): - superuser = User.objects.create_superuser(username=f"root_{model.__name__}") - owner = User.objects.create_user(username=f"owner4_{model.__name__}") + superuser = UserFactory(username=f"root_{model.__name__}", superuser=True) + owner = UserFactory(username=f"owner4_{model.__name__}") obj = factory(owner=owner) assert_visible_document_ids( @@ -646,8 +646,8 @@ class TestPermittedObjectIdsGenericModels: @pytest.mark.django_db class TestMatchingRespectsObjectPermissions: def test_match_tags_only_considers_tags_visible_to_user(self): - owner = User.objects.create_user(username="tag_owner") - classifying_user = User.objects.create_user(username="classifier_user") + owner = UserFactory(username="tag_owner") + classifying_user = UserFactory(username="classifier_user") visible_tag = TagFactory( owner=owner, match="invoice", @@ -667,8 +667,8 @@ class TestMatchingRespectsObjectPermissions: assert hidden_tag.pk not in matched_ids def test_match_correspondents_only_considers_correspondents_visible_to_user(self): - owner = User.objects.create_user(username="correspondent_owner") - classifying_user = User.objects.create_user(username="classifier_user2") + owner = UserFactory(username="correspondent_owner") + classifying_user = UserFactory(username="classifier_user2") visible_correspondent = CorrespondentFactory( owner=owner, match="invoice", @@ -688,8 +688,8 @@ class TestMatchingRespectsObjectPermissions: assert hidden_correspondent.pk not in matched_ids def test_match_document_types_only_considers_document_types_visible_to_user(self): - owner = User.objects.create_user(username="document_type_owner") - classifying_user = User.objects.create_user(username="classifier_user3") + owner = UserFactory(username="document_type_owner") + classifying_user = UserFactory(username="classifier_user3") visible_document_type = DocumentTypeFactory( owner=owner, match="invoice", @@ -709,8 +709,8 @@ class TestMatchingRespectsObjectPermissions: assert hidden_document_type.pk not in matched_ids def test_match_storage_paths_only_considers_storage_paths_visible_to_user(self): - owner = User.objects.create_user(username="storage_path_owner") - classifying_user = User.objects.create_user(username="classifier_user4") + owner = UserFactory(username="storage_path_owner") + classifying_user = UserFactory(username="classifier_user4") visible_storage_path = StoragePathFactory( owner=owner, match="invoice", @@ -733,9 +733,9 @@ class TestMatchingRespectsObjectPermissions: @pytest.mark.django_db class TestBulkEditObjectsApplyToAllPermissionBoundary: def test_apply_to_all_tags_excludes_unpermitted_tag(self, rest_api_client): - owner = User.objects.create_user(username="tags_owner") - requester = User.objects.create_user(username="tags_requester") - new_owner = User.objects.create_user(username="tags_new_owner") + owner = UserFactory(username="tags_owner") + requester = UserFactory(username="tags_requester") + new_owner = UserFactory(username="tags_new_owner") # grant the global change_tag permission so the object-level # filtering (not the global has_perm check) is what's under test requester.user_permissions.add( @@ -773,8 +773,8 @@ class TestBulkEditObjectsApplyToAllPermissionBoundary: request rather than being silently skipped. Editing permissions is limited to the owner, same as documents. """ - owner = User.objects.create_user(username="shared_tags_owner") - requester = User.objects.create_user(username="shared_tags_requester") + owner = UserFactory(username="shared_tags_owner") + requester = UserFactory(username="shared_tags_requester") requester.user_permissions.add( Permission.objects.get(codename="change_tag"), ) @@ -833,9 +833,9 @@ class TestBulkEditObjectsTagDescendantPartialPermission: would pass/fail based on FK cascade behavior, not on whether the descendant-expansion logic itself respected per-object permissions. """ - owner = User.objects.create_user(username="tag_hierarchy_owner") - requester = User.objects.create_user(username="tag_hierarchy_requester") - new_owner = User.objects.create_user(username="tag_hierarchy_new_owner") + owner = UserFactory(username="tag_hierarchy_owner") + requester = UserFactory(username="tag_hierarchy_requester") + new_owner = UserFactory(username="tag_hierarchy_new_owner") # global change_tag permission so the has_perm() gate passes and the # object-level permitted_object_ids filtering is what's under test requester.user_permissions.add( @@ -892,7 +892,7 @@ class TestRestrictQuerysetToVisible: - The queryset is returned unfiltered, rather than permitted_object_ids(None, ...)'s narrower "unowned rows only" """ - owner = User.objects.create_user(username="vis_none_owner") + owner = UserFactory(username="vis_none_owner") tag = TagFactory(owner=owner) visible = restrict_queryset_to_visible(Tag.objects.all(), None, "view_tag") @@ -909,8 +909,8 @@ class TestRestrictQuerysetToVisible: - The queryset is returned unfiltered, skipping the permission lookup entirely """ - superuser = User.objects.create_superuser(username="vis_active_super") - owner = User.objects.create_user(username="vis_active_super_owner") + superuser = UserFactory(username="vis_active_super", superuser=True) + owner = UserFactory(username="vis_active_super_owner") tag = TagFactory(owner=owner) visible = restrict_queryset_to_visible( @@ -932,7 +932,7 @@ class TestRestrictQuerysetToVisible: deactivation has to win over the superuser shortcut, matching permitted_object_ids's own ordering """ - user = User.objects.create_user( + user = UserFactory( username="vis_inactive_super", is_active=False, is_superuser=True, @@ -953,8 +953,8 @@ class TestRestrictQuerysetToVisible: THEN: - Only the rows permitted_object_ids() reports are visible """ - user = User.objects.create_user(username="vis_regular") - other = User.objects.create_user(username="vis_regular_other") + user = UserFactory(username="vis_regular") + other = UserFactory(username="vis_regular_other") own = TagFactory(owner=user) hidden = TagFactory(owner=other) diff --git a/src/documents/tests/test_permitted_objects_filter.py b/src/documents/tests/test_permitted_objects_filter.py index 5200cd94e..1f1348629 100644 --- a/src/documents/tests/test_permitted_objects_filter.py +++ b/src/documents/tests/test_permitted_objects_filter.py @@ -1,11 +1,11 @@ import pytest -from django.contrib.auth.models import User from guardian.shortcuts import assign_perm from rest_framework.test import APIRequestFactory from documents.filters import PermittedObjectsFilter from documents.models import Tag from paperless_testing.factories import TagFactory +from paperless_testing.factories import UserFactory class _DummyView: @@ -15,8 +15,8 @@ class _DummyView: @pytest.mark.django_db class TestPermittedObjectsFilter: def test_superuser_bypasses_filtering_entirely(self): - superuser = User.objects.create_superuser(username="root") - owner = User.objects.create_user(username="owner") + superuser = UserFactory(username="root", superuser=True) + owner = UserFactory(username="owner") TagFactory(owner=owner) request = APIRequestFactory().get("/") request.user = superuser @@ -29,8 +29,8 @@ class TestPermittedObjectsFilter: assert result.count() == Tag.objects.count() def test_non_superuser_sees_only_owned_unowned_and_granted(self): - owner = User.objects.create_user(username="owner") - grantee = User.objects.create_user(username="grantee") + owner = UserFactory(username="owner") + grantee = UserFactory(username="grantee") owned = TagFactory(owner=grantee) unowned = TagFactory(owner=None) granted = TagFactory(owner=owner) @@ -49,8 +49,8 @@ class TestPermittedObjectsFilter: assert hidden.pk not in visible_ids def test_include_granted_false_excludes_explicitly_shared_objects(self): - owner = User.objects.create_user(username="owner2") - grantee = User.objects.create_user(username="grantee2") + owner = UserFactory(username="owner2") + grantee = UserFactory(username="grantee2") owned = TagFactory(owner=grantee) granted = TagFactory(owner=owner) assign_perm("view_tag", grantee, granted) @@ -74,14 +74,14 @@ class TestPermittedObjectsFilter: [("inactive", False), ("inactive_super", True)], ) def test_inactive_user_sees_nothing(self, username: str, *, is_superuser: bool): - user = User.objects.create_user( + user = UserFactory( username=username, is_active=False, is_superuser=is_superuser, ) TagFactory(owner=None) TagFactory(owner=user) - granted = TagFactory(owner=User.objects.create_user(username=f"o_{username}")) + granted = TagFactory(owner=UserFactory(username=f"o_{username}")) assign_perm("view_tag", user, granted) request = APIRequestFactory().get("/") request.user = user @@ -94,7 +94,7 @@ class TestPermittedObjectsFilter: assert result.count() == 0 def test_inactive_user_sees_nothing_with_include_granted_false(self): - user = User.objects.create_user(username="inactive_owner", is_active=False) + user = UserFactory(username="inactive_owner", is_active=False) TagFactory(owner=user) TagFactory(owner=None) request = APIRequestFactory().get("/") diff --git a/src/documents/tests/test_share_link_bundles.py b/src/documents/tests/test_share_link_bundles.py index ce57a0b32..96e35415a 100644 --- a/src/documents/tests/test_share_link_bundles.py +++ b/src/documents/tests/test_share_link_bundles.py @@ -22,6 +22,7 @@ from documents.tasks import build_share_link_bundle from documents.tasks import cleanup_expired_share_link_bundles from paperless_testing.dirs import DirectoriesMixin from paperless_testing.factories import DocumentFactory +from paperless_testing.factories import UserFactory class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase): @@ -29,7 +30,7 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="bundle_admin") + self.user = UserFactory(username="bundle_admin", superuser=True) self.client.force_authenticate(self.user) self.document = DocumentFactory.create() @@ -55,8 +56,8 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase): self, delay_mock, ) -> None: - owner = User.objects.create_user(username="document_owner") - requester = User.objects.create_user(username="bundle_creator") + owner = UserFactory(username="document_owner") + requester = UserFactory(username="bundle_creator") requester.user_permissions.add( Permission.objects.get(codename="add_sharelinkbundle"), ) diff --git a/src/documents/tests/test_tag_hierarchy.py b/src/documents/tests/test_tag_hierarchy.py index 2b678010f..9b876b982 100644 --- a/src/documents/tests/test_tag_hierarchy.py +++ b/src/documents/tests/test_tag_hierarchy.py @@ -1,7 +1,6 @@ from unittest import mock from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from rest_framework import status from rest_framework.test import APITestCase @@ -14,12 +13,13 @@ from documents.models import WorkflowTrigger from documents.serialisers import TagSerializer from documents.signals.handlers import run_workflows from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestTagHierarchyPermissions(APITestCase): def test_children_only_include_visible_tags(self) -> None: - owner = User.objects.create_user(username="owner") - requester = User.objects.create_user(username="requester") + owner = UserFactory(username="owner") + requester = UserFactory(username="requester") requester.user_permissions.add( Permission.objects.get(codename="view_tag"), ) @@ -49,7 +49,7 @@ class TestTagHierarchyPermissions(APITestCase): class TestTagHierarchy(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser(username="admin") + self.user = UserFactory(username="admin", superuser=True) self.client.force_authenticate(user=self.user) self.parent = Tag.objects.create(name="Parent") diff --git a/src/documents/tests/test_task_signals.py b/src/documents/tests/test_task_signals.py index 43e519d1a..70de18ae5 100644 --- a/src/documents/tests/test_task_signals.py +++ b/src/documents/tests/test_task_signals.py @@ -18,6 +18,7 @@ from documents.signals.handlers import task_postrun_handler from documents.signals.handlers import task_prerun_handler from documents.signals.handlers import task_revoked_handler from paperless_testing.factories import PaperlessTaskFactory +from paperless_testing.factories import UserFactory @pytest.fixture @@ -34,8 +35,8 @@ def consume_input_doc(): @pytest.fixture -def consume_overrides(django_user_model): - user = django_user_model.objects.create_user(username="testuser") +def consume_overrides(): + user = UserFactory(username="testuser") overrides = mock.MagicMock(spec=DocumentMetadataOverrides) overrides.owner_id = user.id return overrides diff --git a/src/documents/tests/test_views.py b/src/documents/tests/test_views.py index 97dc6f66d..90b3a578c 100644 --- a/src/documents/tests/test_views.py +++ b/src/documents/tests/test_views.py @@ -34,6 +34,7 @@ from paperless.models import ApplicationConfiguration from paperless_ai.exceptions import LLMProviderError from paperless_ai.exceptions import LLMTimeoutError from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestViews(DirectoriesMixin, TestCase): @@ -43,7 +44,7 @@ class TestViews(DirectoriesMixin, TestCase): ApplicationConfiguration.objects.get_or_create() def setUp(self) -> None: - self.user = User.objects.create_user("testuser") + self.user = UserFactory(username="testuser") super().setUp() def test_login_redirect(self) -> None: @@ -338,7 +339,7 @@ class TestViews(DirectoriesMixin, TestCase): class TestAISuggestions(DirectoriesMixin, TestCase): def setUp(self) -> None: - self.user = User.objects.create_superuser(username="testuser") + self.user = UserFactory(username="testuser", superuser=True) self.document = Document.objects.create( title="Test Document", filename="test.pdf", @@ -425,9 +426,9 @@ class TestAISuggestions(DirectoriesMixin, TestCase): requester; the invisible tag id does not leak into either the matched or suggested tags """ - tag_owner = User.objects.create_user(username="cache_tag_owner") + tag_owner = UserFactory(username="cache_tag_owner") invisible_tag = Tag.objects.create(name="cache_restricted", owner=tag_owner) - requester = User.objects.create_user(username="cache_requester") + requester = UserFactory(username="cache_requester") requester.user_permissions.add( *Permission.objects.filter( codename__in=["view_document", "change_document", "view_tag"], @@ -637,7 +638,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase): - The classification runs with the second user's visibility context without evicting the first user's result """ - second_user = User.objects.create_superuser(username="second_user") + second_user = UserFactory(username="second_user", superuser=True) empty_choices = { "tags": {"existing_ids": [], "new_names": []}, "correspondents": {"existing_ids": [], "new_names": []}, @@ -875,9 +876,9 @@ class TestAISuggestions(DirectoriesMixin, TestCase): permission filtering survives the full request path - it does not appear in either the matched or suggested tags """ - tag_owner = User.objects.create_user(username="tagowner") + tag_owner = UserFactory(username="tagowner") invisible_tag = Tag.objects.create(name="restricted", owner=tag_owner) - requester = User.objects.create_user(username="requester") + requester = UserFactory(username="requester") requester.user_permissions.add( *Permission.objects.filter( codename__in=["view_document", "change_document", "view_tag"], @@ -956,7 +957,7 @@ class TestAIChatStreamingView(DirectoriesMixin, TestCase): ENDPOINT = "/api/documents/chat/" def setUp(self) -> None: - self.user = User.objects.create_user(username="testuser", password="pass") + self.user = UserFactory(username="testuser", password="pass") self.client.force_login(user=self.user) self.document = Document.objects.create( title="Test Document", diff --git a/src/documents/tests/test_workflows.py b/src/documents/tests/test_workflows.py index f79383323..6c0640746 100644 --- a/src/documents/tests/test_workflows.py +++ b/src/documents/tests/test_workflows.py @@ -71,6 +71,7 @@ from documents.workflows.actions import execute_password_removal_action from paperless_mail.models import MailAccount from paperless_mail.models import MailRule from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestWorkflows( @@ -1952,7 +1953,7 @@ class TestWorkflows( original_filename="sample.pdf", ) - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) self.client.patch( @@ -1982,7 +1983,7 @@ class TestWorkflows( w.actions.add(action) w.save() - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) test_file = shutil.copy( self.SAMPLE_DIR / "simple.pdf", @@ -2040,7 +2041,7 @@ class TestWorkflows( value_text="existing value", ) - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) self.client.patch( @@ -2084,7 +2085,7 @@ class TestWorkflows( ) CustomFieldInstance.objects.create(document=doc, field=self.cf1) - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) self.client.patch( @@ -2134,7 +2135,7 @@ class TestWorkflows( assign_perm("documents.view_document", self.group1, doc) assign_perm("documents.change_document", self.group1, doc) - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) self.client.patch( @@ -2905,7 +2906,7 @@ class TestWorkflows( assign_perm("documents.view_document", self.group1, doc) assign_perm("documents.change_document", self.group1, doc) - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) self.client.patch( @@ -2977,7 +2978,7 @@ class TestWorkflows( doc.refresh_from_db() doc.tags.set([self.t1, self.t2]) - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) self.client.patch( f"/api/documents/{doc.id}/", @@ -3041,7 +3042,7 @@ class TestWorkflows( doc.refresh_from_db() doc.tags.set([self.t1]) - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) self.client.patch( f"/api/documents/{doc.id}/", @@ -3102,7 +3103,7 @@ class TestWorkflows( assign_perm("documents.view_document", self.group1, doc) assign_perm("documents.change_document", self.group1, doc) - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) self.client.patch( @@ -3395,7 +3396,7 @@ class TestWorkflows( original_filename="sample.pdf", ) - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) self.client.force_authenticate(user=superuser) self.client.patch( @@ -5357,7 +5358,7 @@ class TestDateWorkflowLocalization( c = Correspondent.objects.create(name="Correspondent Name") client = APIClient() - superuser = User.objects.create_superuser("superuser") + superuser = UserFactory(username="superuser", superuser=True) client.force_authenticate(user=superuser) trigger = WorkflowTrigger.objects.create( diff --git a/src/paperless/tests/settings/test_remote_user.py b/src/paperless/tests/settings/test_remote_user.py index cc8b507c6..c9d62844c 100644 --- a/src/paperless/tests/settings/test_remote_user.py +++ b/src/paperless/tests/settings/test_remote_user.py @@ -2,22 +2,20 @@ import os from unittest import mock from django.conf import settings -from django.contrib.auth.models import User from django.test import override_settings from rest_framework import status from rest_framework.test import APITestCase from paperless.settings import _parse_remote_user_settings from paperless_testing.dirs import DirectoriesMixin +from paperless_testing.factories import UserFactory class TestRemoteUser(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_superuser( - username="temp_admin", - ) + self.user = UserFactory(username="temp_admin", superuser=True) def test_remote_user(self) -> None: """ diff --git a/src/paperless/tests/test_adapter.py b/src/paperless/tests/test_adapter.py index e8b2bc1a6..b7c330332 100644 --- a/src/paperless/tests/test_adapter.py +++ b/src/paperless/tests/test_adapter.py @@ -15,6 +15,7 @@ from pytest_mock import MockerFixture from rest_framework.authtoken.models import Token from paperless.adapter import DrfTokenStrategy +from paperless_testing.factories import UserFactory @pytest.mark.django_db @@ -25,7 +26,7 @@ class TestCustomAccountAdapter: # With no accounts, signups should be allowed assert adapter.is_open_for_signup(None) - User.objects.create_user("testuser") + UserFactory(username="testuser") settings.ACCOUNT_ALLOW_SIGNUPS = True assert adapter.is_open_for_signup(None) @@ -92,7 +93,7 @@ class TestCustomAccountAdapter: ) -> None: settings.ACCOUNT_DEFAULT_GROUPS = ["group1", "group2"] Group.objects.create(name="group1") - user = User.objects.create_user("testuser") + user = UserFactory(username="testuser") adapter = get_adapter() form = mocker.MagicMock( cleaned_data={ @@ -152,7 +153,7 @@ class TestCustomSocialAccountAdapter: settings.SOCIAL_ACCOUNT_DEFAULT_GROUPS = ["group1", "group2"] Group.objects.create(name="group1") adapter = get_social_adapter() - user = User.objects.create_user("testuser") + user = UserFactory(username="testuser") sociallogin = mocker.MagicMock(user=user) user = adapter.save_user(HttpRequest(), sociallogin, None) @@ -187,7 +188,7 @@ class TestDrfTokenStrategy: THEN: - A new token is created and its key is returned """ - user = User.objects.create_user("testuser") + user = UserFactory(username="testuser") request = HttpRequest() request.user = user @@ -207,7 +208,7 @@ class TestDrfTokenStrategy: THEN: - The same token key is returned (no new token created) """ - user = User.objects.create_user("testuser") + user = UserFactory(username="testuser") existing_token = Token.objects.create(user=user) request = HttpRequest() diff --git a/src/paperless/tests/test_api_auth.py b/src/paperless/tests/test_api_auth.py index d55b4bdb2..f0a98dc7e 100644 --- a/src/paperless/tests/test_api_auth.py +++ b/src/paperless/tests/test_api_auth.py @@ -1,12 +1,13 @@ import uuid -from django.contrib.auth.models import User from django.test import TestCase from django.test import override_settings from django.urls import resolve from django.urls import reverse from rest_framework import status +from paperless_testing.factories import UserFactory + class TestApiAuthViews(TestCase): def test_api_auth_login_uses_allauth_login_view(self): @@ -24,10 +25,7 @@ class TestApiAuthViews(TestCase): @override_settings(DISABLE_REGULAR_LOGIN=True) def test_api_auth_login_respects_disable_regular_login(self): username = f"testuser-{uuid.uuid4().hex}" - User.objects.create_user( - username=username, - password="testpassword", - ) + UserFactory(username=username, password="testpassword") response = self.client.post( reverse("rest_framework:login"), diff --git a/src/paperless/tests/test_auth_middleware.py b/src/paperless/tests/test_auth_middleware.py index fec355d7d..a6b6a783a 100644 --- a/src/paperless/tests/test_auth_middleware.py +++ b/src/paperless/tests/test_auth_middleware.py @@ -1,10 +1,10 @@ from django.contrib.auth.models import AnonymousUser -from django.contrib.auth.models import User from django.test import RequestFactory from django.test import TestCase from django.test import override_settings from paperless.auth import AutoLoginMiddleware +from paperless_testing.factories import UserFactory @override_settings(AUTO_LOGIN_USERNAME="autologin") @@ -29,7 +29,7 @@ class TestAutoLoginMiddleware(TestCase): THEN: - That user is attached to the request """ - user = User.objects.create_user(username="autologin") + user = UserFactory(username="autologin") request = self._process(self.factory.get("/")) @@ -44,7 +44,7 @@ class TestAutoLoginMiddleware(TestCase): THEN: - The request is left anonymous rather than authenticated as them """ - User.objects.create_user(username="autologin", is_active=False) + UserFactory(username="autologin", is_active=False) request = self.factory.get("/") request.user = AnonymousUser() diff --git a/src/paperless/tests/test_signals.py b/src/paperless/tests/test_signals.py index cf6ec19c5..383746d94 100644 --- a/src/paperless/tests/test_signals.py +++ b/src/paperless/tests/test_signals.py @@ -1,7 +1,6 @@ from unittest.mock import Mock from django.contrib.auth.models import Group -from django.contrib.auth.models import User from django.http import HttpRequest from django.test import TestCase from django.test import override_settings @@ -9,6 +8,7 @@ from django.test import override_settings from documents.models import UiSettings from paperless.signals import handle_failed_login from paperless.signals import handle_social_account_updated +from paperless_testing.factories import UserFactory class TestFailedLoginLogging(TestCase): @@ -120,7 +120,7 @@ class TestSyncSocialLoginGroups(TestCase): - The user's groups are updated to match the social login's groups """ group = Group.objects.create(name="group1") - user = User.objects.create_user(username="testuser") + user = UserFactory(username="testuser") sociallogin = Mock( user=user, account=Mock( @@ -147,7 +147,7 @@ class TestSyncSocialLoginGroups(TestCase): - The user's groups are not updated """ Group.objects.create(name="group1") - user = User.objects.create_user(username="testuser") + user = UserFactory(username="testuser") sociallogin = Mock( user=user, account=Mock( @@ -180,7 +180,7 @@ class TestSyncSocialLoginGroups(TestCase): would be rejected for a deactivated user anyway """ Group.objects.create(name="admin-group") - user = User.objects.create_user( + user = UserFactory( username="inactive_user", is_active=False, is_superuser=False, @@ -215,7 +215,7 @@ class TestSyncSocialLoginGroups(TestCase): - The user's groups are cleared to match the social login's groups """ group = Group.objects.create(name="group1") - user = User.objects.create_user(username="testuser") + user = UserFactory(username="testuser") user.groups.add(group) user.save() sociallogin = Mock( @@ -244,7 +244,7 @@ class TestSyncSocialLoginGroups(TestCase): - The user's groups are updated using `userinfo.groups` """ group = Group.objects.create(name="group1") - user = User.objects.create_user(username="testuser") + user = UserFactory(username="testuser") sociallogin = Mock( user=user, account=Mock( @@ -275,7 +275,7 @@ class TestSyncSocialLoginGroups(TestCase): - The user's groups are updated using `id_token.groups` """ group = Group.objects.create(name="group1") - user = User.objects.create_user(username="testuser") + user = UserFactory(username="testuser") sociallogin = Mock( user=user, account=Mock( @@ -310,11 +310,7 @@ class TestSyncSocialLoginGroups(TestCase): THEN: - User is not promoted, since only an exact group match counts """ - user = User.objects.create_user( - username="testuser", - is_superuser=False, - is_staff=False, - ) + user = UserFactory(username="testuser", is_superuser=False, is_staff=False) sociallogin = Mock( user=user, account=Mock( @@ -345,11 +341,7 @@ class TestSyncSocialLoginGroups(TestCase): THEN: - User becomes superuser and staff """ - user = User.objects.create_user( - username="testuser_s_e", - is_superuser=False, - is_staff=False, - ) + user = UserFactory(username="testuser_s_e", is_superuser=False, is_staff=False) sociallogin = Mock( user=user, account=Mock( @@ -380,11 +372,7 @@ class TestSyncSocialLoginGroups(TestCase): THEN: - User loses superuser status but preserves staff status if they had it """ - user = User.objects.create_user( - username="testuser_s_d", - is_superuser=True, - is_staff=True, - ) + user = UserFactory(username="testuser_s_d", is_superuser=True, is_staff=True) sociallogin = Mock( user=user, account=Mock( @@ -415,11 +403,7 @@ class TestSyncSocialLoginGroups(TestCase): THEN: - User becomes staff """ - user = User.objects.create_user( - username="testuser_st_e", - is_superuser=False, - is_staff=False, - ) + user = UserFactory(username="testuser_st_e", is_superuser=False, is_staff=False) sociallogin = Mock( user=user, account=Mock( @@ -450,11 +434,7 @@ class TestSyncSocialLoginGroups(TestCase): THEN: - User loses staff status """ - user = User.objects.create_user( - username="testuser_st_d", - is_superuser=False, - is_staff=True, - ) + user = UserFactory(username="testuser_st_d", is_superuser=False, is_staff=True) sociallogin = Mock( user=user, account=Mock( @@ -485,11 +465,7 @@ class TestSyncSocialLoginGroups(TestCase): - Roles are correctly assigned/revoked according to groups """ # Case 1: has both - user = User.objects.create_user( - username="testuser_b_1", - is_superuser=False, - is_staff=False, - ) + user = UserFactory(username="testuser_b_1", is_superuser=False, is_staff=False) sociallogin = Mock( user=user, account=Mock(extra_data={"groups": ["admin-group", "staff-group"]}), @@ -504,11 +480,7 @@ class TestSyncSocialLoginGroups(TestCase): self.assertTrue(user.is_staff) # Case 2: has only staff - user2 = User.objects.create_user( - username="testuser_b_2", - is_superuser=True, - is_staff=True, - ) + user2 = UserFactory(username="testuser_b_2", is_superuser=True, is_staff=True) sociallogin2 = Mock( user=user2, account=Mock(extra_data={"groups": ["staff-group"]}), @@ -523,11 +495,7 @@ class TestSyncSocialLoginGroups(TestCase): self.assertTrue(user2.is_staff) # Case 3: has neither - user3 = User.objects.create_user( - username="testuser_b_3", - is_superuser=True, - is_staff=True, - ) + user3 = UserFactory(username="testuser_b_3", is_superuser=True, is_staff=True) sociallogin3 = Mock( user=user3, account=Mock(extra_data={"groups": ["other-group"]}), @@ -554,11 +522,7 @@ class TestSyncSocialLoginGroups(TestCase): THEN: - Existing roles are not modified """ - user = User.objects.create_user( - username="testuser_n_s", - is_superuser=True, - is_staff=True, - ) + user = UserFactory(username="testuser_n_s", is_superuser=True, is_staff=True) sociallogin = Mock( user=user, account=Mock(extra_data={"groups": ["admin-group", "staff-group"]}), @@ -586,7 +550,7 @@ class TestSyncSocialLoginGroups(TestCase): THEN: - User's superuser status is demoted, matching the group claim exactly """ - user = User.objects.create_user( + user = UserFactory( username="local_admin", password="password123", is_superuser=True, @@ -618,11 +582,7 @@ class TestSyncSocialLoginGroups(TestCase): THEN: - User's superuser status is demoted, even though they are the last admin """ - user = User.objects.create_user( - username="last_admin", - is_superuser=True, - is_staff=True, - ) + user = UserFactory(username="last_admin", is_superuser=True, is_staff=True) user.set_unusable_password() user.save() @@ -652,7 +612,7 @@ class TestSyncSocialLoginGroups(TestCase): THEN: - User's staff status is demoted, matching the group claim exactly """ - user = User.objects.create_user( + user = UserFactory( username="local_staff", password="password123", is_superuser=False, @@ -688,8 +648,8 @@ class TestUserGroupDeletionCleanup(TestCase): THEN: - References in ui_settings are cleaned up """ - user = User.objects.create_user(username="testuser") - user2 = User.objects.create_user(username="testuser2") + user = UserFactory(username="testuser") + user2 = UserFactory(username="testuser2") group = Group.objects.create(name="testgroup") ui_settings = UiSettings.objects.create( @@ -727,8 +687,8 @@ class TestUserGroupDeletionCleanup(TestCase): THEN: - Error is logged and the system remains stable """ - user = User.objects.create_user(username="testuser") - user2 = User.objects.create_user(username="testuser2") + user = UserFactory(username="testuser") + user2 = UserFactory(username="testuser2") user2_id = user2.id Group.objects.create(name="testgroup") diff --git a/src/paperless_mail/tests/test_api.py b/src/paperless_mail/tests/test_api.py index a6c3c81bb..ca331624b 100644 --- a/src/paperless_mail/tests/test_api.py +++ b/src/paperless_mail/tests/test_api.py @@ -18,6 +18,7 @@ from paperless_testing.dirs import DirectoriesMixin from paperless_testing.factories import CorrespondentFactory from paperless_testing.factories import DocumentTypeFactory from paperless_testing.factories import TagFactory +from paperless_testing.factories import UserFactory class TestAPIMailAccounts(DirectoriesMixin, APITestCase): @@ -33,7 +34,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase): super().setUp() - self.user = User.objects.create_user(username="temp_admin") + self.user = UserFactory(username="temp_admin") self.user.user_permissions.add(*Permission.objects.all()) self.user.save() self.client.force_authenticate(user=self.user) @@ -291,7 +292,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase): imap_port=443, owner=None, ) - user = User.objects.create_user(username="no_perms") + user = UserFactory(username="no_perms") self.client.force_authenticate(user=user) response = self.client.post( @@ -321,7 +322,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase): THEN: - API returns forbidden """ - owner = User.objects.create_user(username="account_owner") + owner = UserFactory(username="account_owner") account = MailAccountFactory( username="admin", password="secret", @@ -329,7 +330,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase): imap_port=443, owner=owner, ) - user = User.objects.create_user(username="object_perms_only") + user = UserFactory(username="object_perms_only") assign_perm("change_mailaccount", user, account) self.client.force_authenticate(user=user) @@ -377,7 +378,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase): - Only unowned, owned by user or granted accounts are provided """ - user2 = User.objects.create_user(username="temp_admin2") + user2 = UserFactory(username="temp_admin2") account1 = MailAccountFactory(name="Email1") account2 = MailAccountFactory(name="Email2", owner=self.user) @@ -400,7 +401,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_user(username="temp_admin") + self.user = UserFactory(username="temp_admin") self.user.user_permissions.add(*Permission.objects.all()) self.user.save() self.client.force_authenticate(user=self.user) @@ -590,7 +591,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase): self.assertEqual(returned_rule1.action, MailRule.MailAction.DELETE) def test_create_mail_rule_scopes_accounts(self) -> None: - other_user = User.objects.create_user(username="mail-owner") + other_user = UserFactory(username="mail-owner") foreign_account = MailAccountFactory(name="ForeignEmail", owner=other_user) response = self.client.post( @@ -633,7 +634,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase): def test_create_mail_rule_allowed_for_granted_account_change_permission( self, ) -> None: - other_user = User.objects.create_user(username="mail-owner") + other_user = UserFactory(username="mail-owner") foreign_account = MailAccountFactory(name="ForeignEmail", owner=other_user) assign_perm("change_mailaccount", self.user, foreign_account) @@ -658,7 +659,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase): def test_update_mail_rule_forbidden_for_unpermitted_account(self) -> None: own_account = MailAccountFactory() - other_user = User.objects.create_user(username="mail-owner") + other_user = UserFactory(username="mail-owner") foreign_account = MailAccountFactory(owner=other_user) rule1 = MailRuleFactory(account=own_account) @@ -681,7 +682,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase): - Only unowned, owned by user or granted mail rules are provided """ - user2 = User.objects.create_user(username="temp_admin2") + user2 = UserFactory(username="temp_admin2") account1 = MailAccountFactory() rule1 = MailRuleFactory(account=account1, order=0) rule2 = MailRuleFactory(account=account1, order=1, owner=self.user) @@ -737,7 +738,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase): def setUp(self) -> None: super().setUp() - self.user = User.objects.create_user(username="temp_admin") + self.user = UserFactory(username="temp_admin") self.user.user_permissions.add(*Permission.objects.all()) self.user.save() self.client.force_authenticate(user=self.user) @@ -751,7 +752,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase): THEN: - Only unowned, owned by user or granted processed mails are provided """ - user2 = User.objects.create_user(username="temp_admin2") + user2 = UserFactory(username="temp_admin2") rule = MailRuleFactory() pm1 = ProcessedMailFactory(rule=rule) pm2 = ProcessedMailFactory( @@ -802,7 +803,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase): THEN: - Only the specified processed mails are deleted, respecting ownership and permissions """ - user2 = User.objects.create_user(username="temp_admin2") + user2 = UserFactory(username="temp_admin2") rule = MailRuleFactory() # unowned, owned by self, and one with explicit object perm pm_unowned = ProcessedMailFactory(rule=rule) @@ -855,8 +856,8 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_bulk_delete_requires_global_delete_permission(self) -> None: - owner = User.objects.create_user(username="mail_owner") - requester = User.objects.create_user(username="mail_deleter") + owner = UserFactory(username="mail_owner") + requester = UserFactory(username="mail_deleter") requester.user_permissions.add( Permission.objects.get(codename="add_processedmail"), ) @@ -893,7 +894,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase): THEN: - The request is rejected and neither mail is deleted """ - user2 = User.objects.create_user(username="temp_admin2") + user2 = UserFactory(username="temp_admin2") rule = MailRuleFactory() # Created first so it sorts ahead of the forbidden mail, i.e. the # permission check has to cover the whole batch before deleting rather diff --git a/src/paperless_mail/tests/test_mail.py b/src/paperless_mail/tests/test_mail.py index 3dca9fc7c..14882a6a1 100644 --- a/src/paperless_mail/tests/test_mail.py +++ b/src/paperless_mail/tests/test_mail.py @@ -9,7 +9,6 @@ from unittest import mock import pytest from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.core.management import call_command from django.db import DatabaseError from django.test import TestCase @@ -43,6 +42,7 @@ from paperless_mail.tests.factories import MailAccountFactory from paperless_mail.tests.factories import MailRuleFactory from paperless_testing.dirs import DirectoriesMixin from paperless_testing.factories import CorrespondentFactory +from paperless_testing.factories import UserFactory @dataclasses.dataclass @@ -2263,10 +2263,7 @@ class TestMailAccountTestView(APITestCase): self.mailMocker = MailMocker() self.mailMocker.setUp() self.addCleanup(self.mailMocker.doCleanups) - self.user = User.objects.create_user( - username="testuser", - password="testpassword", - ) + self.user = UserFactory(username="testuser", password="testpassword") self.user.user_permissions.add( *Permission.objects.filter( codename__in=["add_mailaccount", "change_mailaccount"], @@ -2409,10 +2406,7 @@ class TestMailAccountTestView(APITestCase): self.assertIn(expected_str, error_str) def test_mail_account_test_view_existing_forbidden_for_other_owner(self) -> None: - other_user = User.objects.create_user( - username="otheruser", - password="testpassword", - ) + other_user = UserFactory(username="otheruser", password="testpassword") existing_account = MailAccount.objects.create( name="Owned account", imap_server="imap.example.com", @@ -2535,9 +2529,10 @@ class TestMailAccountProcess(APITestCase): self.mailMocker = MailMocker() self.mailMocker.setUp() self.addCleanup(self.mailMocker.doCleanups) - self.user = User.objects.create_superuser( + self.user = UserFactory( username="testuser", password="testpassword", + superuser=True, ) self.client.force_authenticate(user=self.user) self.account = MailAccount.objects.create( @@ -2560,9 +2555,10 @@ class TestMailAccountProcess(APITestCase): class TestMailRuleAPI(APITestCase): def setUp(self) -> None: - self.user = User.objects.create_superuser( + self.user = UserFactory( username="testuser", password="testpassword", + superuser=True, ) self.client.force_authenticate(user=self.user) self.account = MailAccountFactory(owner=self.user) diff --git a/src/paperless_mail/tests/test_mail_oauth.py b/src/paperless_mail/tests/test_mail_oauth.py index 52b96ca83..26765b7da 100644 --- a/src/paperless_mail/tests/test_mail_oauth.py +++ b/src/paperless_mail/tests/test_mail_oauth.py @@ -2,7 +2,6 @@ from datetime import timedelta from unittest import mock from django.contrib.auth.models import Permission -from django.contrib.auth.models import User from django.test import TestCase from django.test import override_settings from django.utils import timezone @@ -14,6 +13,7 @@ from paperless_mail.mail import MailAccountHandler from paperless_mail.models import MailAccount from paperless_mail.oauth import PaperlessMailOAuth2Manager from paperless_mail.tests.factories import MailAccountFactory +from paperless_testing.factories import UserFactory @override_settings( @@ -27,7 +27,7 @@ class TestMailOAuth( TestCase, ): def setUp(self) -> None: - self.user = User.objects.create_user("testuser") + self.user = UserFactory(username="testuser") self.user.user_permissions.add( *Permission.objects.filter( codename__in=[ diff --git a/src/paperless_testing/factories.py b/src/paperless_testing/factories.py index a92791c77..4e2e2c507 100644 --- a/src/paperless_testing/factories.py +++ b/src/paperless_testing/factories.py @@ -78,7 +78,10 @@ class UserFactory(DjangoModelFactory[UserModelT]): username = factory.Sequence(lambda n: f"user{n}") is_staff = False is_superuser = False - password = factory.django.Password("test") + # Hashing a real password costs about half a second per user, and no test + # authenticates with its password. None gives the unusable password + # create_user() gives. + password = factory.django.Password(None) class Params: superuser = factory.Trait(is_staff=True, is_superuser=True)