From a0639f4830b20facb63201441a8dc073bfc1192f Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:59:11 -0700 Subject: [PATCH] Backend tests --- src/documents/tests/search/test_backend.py | 71 +++++++++++++++++++ src/documents/tests/test_api_search.py | 79 ++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/src/documents/tests/search/test_backend.py b/src/documents/tests/search/test_backend.py index 5c92da447..1d32f0cc6 100644 --- a/src/documents/tests/search/test_backend.py +++ b/src/documents/tests/search/test_backend.py @@ -5,6 +5,7 @@ from documents.models import CustomField from documents.models import CustomFieldInstance from documents.models import Document from documents.models import Note +from documents.search._backend import SearchMode from documents.search._backend import TantivyBackend from documents.search._backend import get_backend from documents.search._backend import reset_backend @@ -46,6 +47,76 @@ class TestWriteBatch: class TestSearch: """Test search functionality.""" + def test_text_mode_limits_default_search_to_title_and_content( + self, + backend: TantivyBackend, + ): + """Simple text mode must not match metadata-only fields.""" + doc = Document.objects.create( + title="Invoice document", + content="monthly statement", + checksum="TXT1", + pk=9, + ) + backend.add_or_update(doc) + + metadata_only = backend.search( + "document_type:invoice", + user=None, + page=1, + page_size=10, + sort_field=None, + sort_reverse=False, + search_mode=SearchMode.TEXT, + ) + assert metadata_only.total == 0 + + content_match = backend.search( + "monthly", + user=None, + page=1, + page_size=10, + sort_field=None, + sort_reverse=False, + search_mode=SearchMode.TEXT, + ) + assert content_match.total == 1 + + def test_title_mode_limits_default_search_to_title_only( + self, + backend: TantivyBackend, + ): + """Title mode must not match content-only terms.""" + doc = Document.objects.create( + title="Invoice document", + content="monthly statement", + checksum="TXT2", + pk=10, + ) + backend.add_or_update(doc) + + content_only = backend.search( + "monthly", + user=None, + page=1, + page_size=10, + sort_field=None, + sort_reverse=False, + search_mode=SearchMode.TITLE, + ) + assert content_only.total == 0 + + title_match = backend.search( + "invoice", + user=None, + page=1, + page_size=10, + sort_field=None, + sort_reverse=False, + search_mode=SearchMode.TITLE, + ) + assert title_match.total == 1 + def test_scores_normalised_top_hit_is_one(self, backend: TantivyBackend): """Search scores must be normalized so top hit has score 1.0 for UI consistency.""" for i, title in enumerate(["bank invoice", "bank statement", "bank receipt"]): diff --git a/src/documents/tests/test_api_search.py b/src/documents/tests/test_api_search.py index 69bd65198..149c478c6 100644 --- a/src/documents/tests/test_api_search.py +++ b/src/documents/tests/test_api_search.py @@ -91,6 +91,60 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): self.assertEqual(response.data["count"], 0) self.assertEqual(len(results), 0) + def test_simple_text_search(self) -> None: + tagged = Tag.objects.create(name="invoice") + matching_doc = Document.objects.create( + title="Quarterly summary", + content="Monthly bank report", + checksum="T1", + pk=11, + ) + matching_doc.tags.add(tagged) + + metadata_only_doc = Document.objects.create( + title="Completely unrelated", + content="No matching terms here", + checksum="T2", + pk=12, + ) + metadata_only_doc.tags.add(tagged) + + backend = get_backend() + backend.add_or_update(matching_doc) + backend.add_or_update(metadata_only_doc) + + response = self.client.get("/api/documents/?text=monthly") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["count"], 1) + self.assertEqual(response.data["results"][0]["id"], matching_doc.id) + + response = self.client.get("/api/documents/?text=tag:invoice") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["count"], 0) + + def test_simple_title_search(self) -> None: + title_match = Document.objects.create( + title="Quarterly summary", + content="No matching content here", + checksum="T3", + pk=13, + ) + content_only = Document.objects.create( + title="Completely unrelated", + content="Quarterly summary appears only in content", + checksum="T4", + pk=14, + ) + + backend = get_backend() + backend.add_or_update(title_match) + backend.add_or_update(content_only) + + response = self.client.get("/api/documents/?title_search=quarterly") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["count"], 1) + self.assertEqual(response.data["results"][0]["id"], title_match.id) + def test_search_returns_all_for_api_version_9(self) -> None: d1 = Document.objects.create( title="invoice", @@ -1493,6 +1547,31 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): self.assertEqual(results["custom_fields"][0]["id"], custom_field1.id) self.assertEqual(results["workflows"][0]["id"], workflow1.id) + def test_global_search_db_only_limits_documents_to_title_matches(self) -> None: + title_match = Document.objects.create( + title="bank statement", + content="no additional terms", + checksum="GS1", + pk=21, + ) + content_only = Document.objects.create( + title="not a title match", + content="bank appears only in content", + checksum="GS2", + pk=22, + ) + + backend = get_backend() + backend.add_or_update(title_match) + backend.add_or_update(content_only) + + self.client.force_authenticate(self.user) + + response = self.client.get("/api/search/?query=bank&db_only=true") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data["documents"]), 1) + self.assertEqual(response.data["documents"][0]["id"], title_match.id) + 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")