mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-09-24 02:10:33 +00:00
The model factories lived in the documents test package, but three other apps needed them. The AI, mail and testing suites all reached across an app boundary to import from documents.tests.factories, which made a private test package into a shared dependency. The factories now live in the shared testing package, where cross-app use is the intended use.
236 lines
7.1 KiB
Python
236 lines
7.1 KiB
Python
import types
|
|
|
|
import pytest
|
|
import tantivy
|
|
from django.contrib.admin.sites import AdminSite
|
|
from django.contrib.auth.models import Permission
|
|
from django.contrib.auth.models import User
|
|
from django.test import Client
|
|
from django.test import TestCase
|
|
from pytest_mock import MockerFixture
|
|
from rest_framework import status
|
|
|
|
from documents.admin import DocumentAdmin
|
|
from documents.admin import TagAdmin
|
|
from documents.models import Document
|
|
from documents.models import Tag
|
|
from documents.search import get_backend
|
|
from documents.search import reset_backend
|
|
from documents.tests.utils import DirectoriesMixin
|
|
from paperless.admin import PaperlessUserAdmin
|
|
from paperless_testing.factories import DocumentFactory
|
|
from paperless_testing.factories import TagFactory
|
|
from paperless_testing.factories import UserFactory
|
|
|
|
|
|
@pytest.fixture
|
|
def tag_admin() -> TagAdmin:
|
|
return TagAdmin(model=Tag, admin_site=AdminSite())
|
|
|
|
|
|
@pytest.fixture
|
|
def user_admin() -> PaperlessUserAdmin:
|
|
return PaperlessUserAdmin(model=User, admin_site=AdminSite())
|
|
|
|
|
|
@pytest.fixture
|
|
def staff_user(db) -> User:
|
|
return UserFactory.create(username="staff", staff=True)
|
|
|
|
|
|
class TestDocumentAdmin(DirectoriesMixin, TestCase):
|
|
def get_document_from_index(self, doc):
|
|
backend = get_backend()
|
|
searcher = backend._index.searcher()
|
|
results = searcher.search(
|
|
tantivy.Query.term_query(backend._schema, "id", doc.pk),
|
|
limit=1,
|
|
)
|
|
if results.hits:
|
|
return searcher.doc(results.hits[0][1]).to_dict()
|
|
return None
|
|
|
|
def setUp(self) -> None:
|
|
super().setUp()
|
|
reset_backend()
|
|
self.doc_admin = DocumentAdmin(model=Document, admin_site=AdminSite())
|
|
|
|
def tearDown(self) -> None:
|
|
reset_backend()
|
|
super().tearDown()
|
|
|
|
def test_save_model(self) -> None:
|
|
doc = DocumentFactory.create(title="test")
|
|
|
|
doc.title = "new title"
|
|
self.doc_admin.save_model(None, doc, None, None)
|
|
|
|
self.assertEqual(self.get_document_from_index(doc)["id"], [doc.id])
|
|
|
|
def test_delete_model(self) -> None:
|
|
doc = DocumentFactory.create(title="test")
|
|
get_backend().add_or_update(doc)
|
|
self.assertIsNotNone(self.get_document_from_index(doc))
|
|
|
|
self.doc_admin.delete_model(None, doc)
|
|
|
|
self.assertIsNone(self.get_document_from_index(doc))
|
|
|
|
def test_delete_queryset(self) -> None:
|
|
docs = DocumentFactory.create_batch(
|
|
2,
|
|
title="Many documents with the same title",
|
|
)
|
|
for doc in docs:
|
|
get_backend().add_or_update(doc)
|
|
self.assertIsNotNone(self.get_document_from_index(doc))
|
|
|
|
self.doc_admin.delete_queryset(None, Document.objects.all())
|
|
|
|
for doc in docs:
|
|
self.assertIsNone(self.get_document_from_index(doc))
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestTagAdmin:
|
|
def test_parent_tags_get_added(
|
|
self,
|
|
tag_admin: TagAdmin,
|
|
mocker: MockerFixture,
|
|
) -> None:
|
|
mock_bulk_update = mocker.patch(
|
|
"documents.tasks.bulk_update_documents.apply_async",
|
|
)
|
|
document = DocumentFactory.create(title="test")
|
|
parent = TagFactory.create(name="parent")
|
|
child = TagFactory.create(name="child")
|
|
document.tags.add(child)
|
|
|
|
child.tn_parent = parent
|
|
tag_admin.save_model(None, child, None, change=True)
|
|
|
|
document.refresh_from_db()
|
|
assert parent in document.tags.all()
|
|
mock_bulk_update.assert_called_once()
|
|
assert mock_bulk_update.call_args.kwargs["kwargs"] == {
|
|
"document_ids": [document.id],
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestPaperlessAdmin:
|
|
def test_request_is_passed_to_form(
|
|
self,
|
|
user_admin: PaperlessUserAdmin,
|
|
) -> None:
|
|
user = UserFactory.create()
|
|
non_superuser = UserFactory.create()
|
|
request = types.SimpleNamespace(user=non_superuser)
|
|
form_type = user_admin.get_form(request)
|
|
form = form_type(data={}, instance=user)
|
|
assert form.request == request
|
|
|
|
def test_non_superuser_cannot_change_superuser_status(
|
|
self,
|
|
user_admin: PaperlessUserAdmin,
|
|
) -> None:
|
|
non_superuser = UserFactory.create()
|
|
user = UserFactory.create()
|
|
|
|
form = user_admin.form(
|
|
{"username": user.username, "is_superuser": True},
|
|
instance=user,
|
|
)
|
|
form.request = types.SimpleNamespace(user=non_superuser)
|
|
|
|
assert not form.is_valid()
|
|
assert form.errors.get("__all__") == [
|
|
"Superuser status can only be changed by a superuser",
|
|
]
|
|
|
|
def test_superuser_can_change_superuser_status(
|
|
self,
|
|
user_admin: PaperlessUserAdmin,
|
|
admin_user: User,
|
|
) -> None:
|
|
user = UserFactory.create()
|
|
|
|
form = user_admin.form(
|
|
{"username": user.username, "is_superuser": True},
|
|
instance=user,
|
|
)
|
|
form.request = types.SimpleNamespace(user=admin_user)
|
|
|
|
assert form.is_valid()
|
|
assert form.errors == {}
|
|
|
|
@pytest.mark.parametrize(
|
|
("method", "perm_codename", "expected_message"),
|
|
[
|
|
pytest.param(
|
|
"patch",
|
|
"change_user",
|
|
"Superusers can only be modified by other superusers",
|
|
id="modify",
|
|
),
|
|
pytest.param(
|
|
"delete",
|
|
"delete_user",
|
|
"Superusers can only be deleted by other superusers",
|
|
id="delete",
|
|
),
|
|
],
|
|
)
|
|
def test_non_superuser_cannot_mutate_superuser(
|
|
self,
|
|
client: Client,
|
|
admin_user: User,
|
|
staff_user: User,
|
|
method: str,
|
|
perm_codename: str,
|
|
expected_message: str,
|
|
) -> None:
|
|
staff_user.user_permissions.add(
|
|
Permission.objects.get(codename=perm_codename),
|
|
)
|
|
client.force_login(staff_user)
|
|
|
|
response = getattr(client, method)(
|
|
f"/api/users/{admin_user.pk}/",
|
|
{"first_name": "Updated"},
|
|
content_type="application/json",
|
|
)
|
|
|
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
|
assert response.content.decode() == expected_message
|
|
assert User.objects.filter(pk=admin_user.pk).exists()
|
|
|
|
def test_superuser_can_modify_superuser(
|
|
self,
|
|
client: Client,
|
|
admin_user: User,
|
|
) -> None:
|
|
client.force_login(admin_user)
|
|
response = client.patch(
|
|
f"/api/users/{admin_user.pk}/",
|
|
{"first_name": "Updated"},
|
|
content_type="application/json",
|
|
)
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
admin_user.refresh_from_db()
|
|
assert admin_user.first_name == "Updated"
|
|
|
|
def test_superuser_can_delete_superuser(
|
|
self,
|
|
client: Client,
|
|
admin_user: User,
|
|
) -> None:
|
|
target = UserFactory.create(superuser=True)
|
|
client.force_login(admin_user)
|
|
|
|
response = client.delete(f"/api/users/{target.pk}/")
|
|
|
|
assert response.status_code == status.HTTP_204_NO_CONTENT
|
|
assert not User.objects.filter(pk=target.pk).exists()
|