mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-04-01 13:52:43 +00:00
- Replace all `from documents import index` + Whoosh writer usage across admin.py, bulk_edit.py, tasks.py, views.py, signals/handlers.py with `get_backend().add_or_update/remove/batch_update` - Add `effective_content` param to `_build_tantivy_doc` / `add_or_update` (used by signal handler to re-index root doc with version's OCR text) - Add `wipe_index()` (renamed from `_wipe_index`) to public API; use from `document_index --recreate` flag - `index_optimize()` replaced with deprecation log message; Tantivy manages segment merging automatically - `index_reindex()` now calls `get_backend().rebuild()` + `reset_backend()` with select_related/prefetch_related for efficiency - `document_index` management command: add `--recreate` flag - Status view: use `get_backend()` + dir mtime scan instead of Whoosh `ix.last_modified()` - Delete `documents/index.py`, `test_index.py`, `test_delayedquery.py` - Update all tests: patch `documents.search.get_backend` (lazy imports); `DirectoriesMixin` calls `reset_backend()` in setUp/tearDown; `TestDocumentConsumptionFinishedSignal` likewise - `test_api_search.py`: fix order-independent assertions for date-range queries; fix `_rewrite_8digit_date` to be field-aware and timezone-correct for DateTimeField vs DateField Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
182 lines
6.4 KiB
Python
182 lines
6.4 KiB
Python
import types
|
|
from unittest.mock import patch
|
|
|
|
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 TestCase
|
|
from django.utils import timezone
|
|
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
|
|
|
|
|
|
class TestDocumentAdmin(DirectoriesMixin, TestCase):
|
|
def get_document_from_index(self, doc):
|
|
backend = get_backend()
|
|
searcher = backend._index.searcher()
|
|
results = searcher.search(
|
|
tantivy.Query.range_query(
|
|
backend._schema,
|
|
"id",
|
|
tantivy.FieldType.Unsigned,
|
|
doc.pk,
|
|
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 = Document.objects.create(title="test")
|
|
|
|
doc.title = "new title"
|
|
self.doc_admin.save_model(None, doc, None, None)
|
|
self.assertEqual(Document.objects.get(id=doc.id).title, "new title")
|
|
self.assertEqual(self.get_document_from_index(doc)["id"], [doc.id])
|
|
|
|
def test_delete_model(self) -> None:
|
|
doc = Document.objects.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.assertRaises(Document.DoesNotExist, Document.objects.get, id=doc.id)
|
|
self.assertIsNone(self.get_document_from_index(doc))
|
|
|
|
def test_delete_queryset(self) -> None:
|
|
docs = []
|
|
for i in range(42):
|
|
doc = Document.objects.create(
|
|
title="Many documents with the same title",
|
|
checksum=f"{i:02}",
|
|
)
|
|
docs.append(doc)
|
|
get_backend().add_or_update(doc)
|
|
|
|
self.assertEqual(Document.objects.count(), 42)
|
|
|
|
for doc in docs:
|
|
self.assertIsNotNone(self.get_document_from_index(doc))
|
|
|
|
self.doc_admin.delete_queryset(None, Document.objects.all())
|
|
|
|
self.assertEqual(Document.objects.count(), 0)
|
|
|
|
for doc in docs:
|
|
self.assertIsNone(self.get_document_from_index(doc))
|
|
|
|
def test_created(self) -> None:
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
created=timezone.make_aware(timezone.datetime(2020, 4, 12)),
|
|
)
|
|
self.assertEqual(self.doc_admin.created_(doc), "2020-04-12")
|
|
|
|
|
|
class TestTagAdmin(DirectoriesMixin, TestCase):
|
|
def setUp(self) -> None:
|
|
super().setUp()
|
|
self.tag_admin = TagAdmin(model=Tag, admin_site=AdminSite())
|
|
|
|
@patch("documents.tasks.bulk_update_documents")
|
|
def test_parent_tags_get_added(self, mock_bulk_update):
|
|
document = Document.objects.create(title="test")
|
|
parent = Tag.objects.create(name="parent")
|
|
child = Tag.objects.create(name="child")
|
|
document.tags.add(child)
|
|
|
|
child.tn_parent = parent
|
|
self.tag_admin.save_model(None, child, None, change=True)
|
|
document.refresh_from_db()
|
|
self.assertIn(parent, document.tags.all())
|
|
|
|
|
|
class TestPaperlessAdmin(DirectoriesMixin, TestCase):
|
|
def setUp(self) -> None:
|
|
super().setUp()
|
|
self.user_admin = PaperlessUserAdmin(model=User, admin_site=AdminSite())
|
|
|
|
def test_request_is_passed_to_form(self) -> None:
|
|
user = User.objects.create(username="test", is_superuser=False)
|
|
non_superuser = User.objects.create(username="requestuser")
|
|
request = types.SimpleNamespace(user=non_superuser)
|
|
formType = self.user_admin.get_form(request)
|
|
form = formType(data={}, instance=user)
|
|
self.assertEqual(form.request, request)
|
|
|
|
def test_only_superuser_can_change_superuser(self) -> None:
|
|
superuser = User.objects.create_superuser(username="superuser", password="test")
|
|
non_superuser = User.objects.create(username="requestuser")
|
|
user = User.objects.create(username="test", is_superuser=False)
|
|
|
|
data = {
|
|
"username": "test",
|
|
"is_superuser": True,
|
|
}
|
|
form = self.user_admin.form(data, instance=user)
|
|
form.request = types.SimpleNamespace(user=non_superuser)
|
|
self.assertFalse(form.is_valid())
|
|
self.assertEqual(
|
|
form.errors.get("__all__"),
|
|
["Superuser status can only be changed by a superuser"],
|
|
)
|
|
|
|
form = self.user_admin.form(data, instance=user)
|
|
form.request = types.SimpleNamespace(user=superuser)
|
|
self.assertTrue(form.is_valid())
|
|
self.assertEqual({}, form.errors)
|
|
|
|
def test_superuser_can_only_be_modified_by_superuser(self) -> None:
|
|
superuser = User.objects.create_superuser(username="superuser", password="test")
|
|
user = User.objects.create(
|
|
username="test",
|
|
is_superuser=False,
|
|
is_staff=True,
|
|
)
|
|
change_user_perm = Permission.objects.get(codename="change_user")
|
|
user.user_permissions.add(change_user_perm)
|
|
|
|
self.client.force_login(user)
|
|
response = self.client.patch(
|
|
f"/api/users/{superuser.pk}/",
|
|
{"first_name": "Updated"},
|
|
content_type="application/json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
|
self.assertEqual(
|
|
response.content.decode(),
|
|
"Superusers can only be modified by other superusers",
|
|
)
|
|
|
|
self.client.logout()
|
|
self.client.force_login(superuser)
|
|
response = self.client.patch(
|
|
f"/api/users/{superuser.pk}/",
|
|
{"first_name": "Updated"},
|
|
content_type="application/json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
superuser.refresh_from_db()
|
|
self.assertEqual(superuser.first_name, "Updated")
|