From f2f4d04ab666c50341de4d5f7ed0a179ff808496 Mon Sep 17 00:00:00 2001 From: Trenton Holmes <797416+stumpylog@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:50:43 -0700 Subject: [PATCH] test(search): hoist deferred imports, add _index() helper in test_acceptance.py User/DocumentType/StoragePath were imported inside individual test bodies despite the module already importing documents.models at top level - nothing here needed deferred import. Also add an _index() helper (Document.objects.create + backend.add_or_update in one call) for the many sites where nothing needs to happen between creating a document and indexing it; the two-step ceremony was outweighing the fixture data at every call site. Left as two explicit steps wherever a Note or CustomFieldInstance genuinely has to be attached before indexing. --- src/documents/tests/search/test_acceptance.py | 101 +++++++++--------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/src/documents/tests/search/test_acceptance.py b/src/documents/tests/search/test_acceptance.py index d71e6a055..625b7914f 100644 --- a/src/documents/tests/search/test_acceptance.py +++ b/src/documents/tests/search/test_acceptance.py @@ -14,11 +14,14 @@ 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 from documents.models import Document +from documents.models import DocumentType from documents.models import Note +from documents.models import StoragePath from documents.search._query import parse_user_query if TYPE_CHECKING: @@ -33,37 +36,48 @@ def _matched_ids(backend: TantivyBackend, query: str) -> set[int]: return set(backend.search_ids(query, user=None)) +def _index(backend: TantivyBackend, **kwargs: object) -> Document: + """Create a Document and index it in one step, for the common case + where nothing needs to happen between the two (no related Note/ + CustomFieldInstance to attach first).""" + doc = Document.objects.create(**kwargs) + backend.add_or_update(doc) + return doc + + @pytest.fixture def indexed_documents(backend: TantivyBackend) -> dict[str, int]: """Index a small fixture set, return {label: doc_id} for corpus queries.""" docs = { - "invoice_2020": Document.objects.create( + "invoice_2020": _index( + backend, title="Invoice 2020", content="invoice total due", checksum="acc-invoice-2020", archive_serial_number=100, ), - "invoice_2021": Document.objects.create( + "invoice_2021": _index( + backend, title="Invoice 2021", content="invoice total due", checksum="acc-invoice-2021", archive_serial_number=101, ), - "invoice_2023": Document.objects.create( + "invoice_2023": _index( + backend, title="Invoice 2023", content="invoice total due", checksum="acc-invoice-2023", archive_serial_number=102, ), - "receipt_2022": Document.objects.create( + "receipt_2022": _index( + backend, title="Receipt 2022", content="receipt total due", checksum="acc-receipt-2022", archive_serial_number=103, ), } - for doc in docs.values(): - backend.add_or_update(doc) return {label: doc.pk for label, doc in docs.items()} @@ -99,18 +113,18 @@ class TestFieldBoosts: self, backend: TantivyBackend, ) -> None: - title_match = Document.objects.create( + title_match = _index( + backend, title="urgent", content="nothing else relevant", checksum="acc-boost-title", ) - content_match = Document.objects.create( + _index( + backend, title="nothing", content="urgent matter here", checksum="acc-boost-content", ) - backend.add_or_update(title_match) - backend.add_or_update(content_match) query = parse_user_query(backend._index, "urgent", UTC) searcher = backend._index.searcher() results = searcher.search(query, limit=10) @@ -125,8 +139,6 @@ class TestJsonSubpaths: self, backend: TantivyBackend, ) -> None: - from django.contrib.auth.models import User - alice = User.objects.create_user(username="alice") doc_with_note = Document.objects.create( title="Has note", @@ -134,13 +146,8 @@ class TestJsonSubpaths: checksum="acc-note-with", ) Note.objects.create(document=doc_with_note, user=alice, note="reminder") - doc_without = Document.objects.create( - title="No note", - content="x", - checksum="acc-note-without", - ) backend.add_or_update(doc_with_note) - backend.add_or_update(doc_without) + _index(backend, title="No note", content="x", checksum="acc-note-without") matched = _matched_ids(backend, "notes.user:alice") assert matched == {doc_with_note.pk} @@ -166,6 +173,7 @@ class TestJsonSubpaths: field=field, value_text="policy", ) + backend.add_or_update(matching) non_matching = Document.objects.create( title="Non-matching", content="x", @@ -176,7 +184,6 @@ class TestJsonSubpaths: field=other_field, value_text="policy", ) - backend.add_or_update(matching) backend.add_or_update(non_matching) matched = _matched_ids( backend, @@ -215,13 +222,13 @@ class TestFuzzyBlendSurvivesWhooshGrammar: ) -> None: settings.ADVANCED_FUZZY_SEARCH_THRESHOLD = 0.5 with time_machine.travel(FROZEN_NOW, tick=False): - doc = Document.objects.create( + doc = _index( + backend, title="Receipt March", content="receipt total due", checksum="fuzzy-blend-1", archive_serial_number=900, ) - backend.add_or_update(doc) # Sanity: the exact spelling matches through the exact clause. assert doc.pk in _matched_ids(backend, "added:today receipt") # The regression: the misspelling (one transposition) only @@ -249,13 +256,13 @@ class TestFuzzyBlendSurvivesWhooshGrammar: # it even for a naive implementation.) settings.ADVANCED_FUZZY_SEARCH_THRESHOLD = 0.5 with time_machine.travel(FROZEN_NOW, tick=False): - receipt_only = Document.objects.create( + _index( + backend, title="Receipt Archive", content="receipt archived stack", checksum="fuzzy-blend-2", archive_serial_number=901, ) - backend.add_or_update(receipt_only) assert _matched_ids(backend, "added:today total NOT receipt") == set() @@ -269,22 +276,22 @@ class TestUnquotedDateKeywordPhrases: @pytest.fixture def period_documents(self, backend: TantivyBackend) -> dict[str, int]: with time_machine.travel(FROZEN_NOW, tick=False): - in_may = Document.objects.create( + in_may = _index( + backend, title="May Doc", content="statement", checksum="kw-may", archive_serial_number=910, added=datetime(2026, 5, 20, 12, 0, tzinfo=UTC), ) - in_june = Document.objects.create( + in_june = _index( + backend, title="June Doc", content="statement", checksum="kw-june", archive_serial_number=911, added=datetime(2026, 6, 10, 12, 0, tzinfo=UTC), ) - for doc in (in_may, in_june): - backend.add_or_update(doc) return {"in_may": in_may.pk, "in_june": in_june.pk} @pytest.mark.parametrize( @@ -337,13 +344,13 @@ class TestUnquotedDateKeywordPhrases: # text, not a date phrase: a title actually containing the words # matches, and the date-window documents do not. with time_machine.travel(FROZEN_NOW, tick=False): - wordy = Document.objects.create( + wordy = _index( + backend, title="Notes from the previous month", content="meeting notes", checksum="kw-text", archive_serial_number=912, ) - backend.add_or_update(wordy) assert _matched_ids(backend, "title:previous month") == {wordy.pk} @@ -357,8 +364,6 @@ class TestBareJsonFieldPrefixes: self, backend: TantivyBackend, ) -> None: - from django.contrib.auth.models import User - alice = User.objects.create_user(username="alice") with_note = Document.objects.create( title="Has note", @@ -366,15 +371,15 @@ class TestBareJsonFieldPrefixes: checksum="bare-notes-with", ) Note.objects.create(document=with_note, user=alice, note="crocodile") + backend.add_or_update(with_note) # This document's CONTENT contains the words a demoted text search # would match; it must NOT match once the prefix addresses notes. - decoy = Document.objects.create( + _index( + backend, title="Notes about things", content="notes crocodile mention", checksum="bare-notes-decoy", ) - backend.add_or_update(with_note) - backend.add_or_update(decoy) assert _matched_ids(backend, "notes:crocodile") == {with_note.pk} def test_bare_custom_fields_prefix_searches_values( @@ -395,21 +400,19 @@ class TestBareJsonFieldPrefixes: field=field, value_text="crocodile", ) - decoy = Document.objects.create( + backend.add_or_update(with_value) + _index( + backend, title="Custom things", content="custom fields crocodile", checksum="bare-cf-decoy", ) - backend.add_or_update(with_value) - backend.add_or_update(decoy) assert _matched_ids(backend, "custom_fields:crocodile") == {with_value.pk} def test_subpath_spellings_are_untouched( self, backend: TantivyBackend, ) -> None: - from django.contrib.auth.models import User - bob = User.objects.create_user(username="bob") doc = Document.objects.create( title="Bob note", @@ -431,8 +434,6 @@ class TestFieldAliases: self, backend: TantivyBackend, ) -> None: - from documents.models import DocumentType - invoice_type = DocumentType.objects.create(name="invoice") # Discriminating shape: document_type is itself a default search # field, so if alias resolution ever broke and "type:invoice" @@ -441,19 +442,19 @@ class TestFieldAliases: # word in content, so a demoted search matches BOTH documents and # the exact-set assertions fail. (The title avoids stemming to # "type": english stems Typed -> type.) - typed = Document.objects.create( + typed = _index( + backend, title="First", content="quarterly statement", checksum="alias-type-1", document_type=invoice_type, ) - decoy = Document.objects.create( + _index( + backend, title="Second", content="invoice mentioned in body", checksum="alias-type-2", ) - backend.add_or_update(typed) - backend.add_or_update(decoy) assert _matched_ids(backend, "type:invoice") == {typed.pk} assert _matched_ids(backend, "document_type:invoice") == {typed.pk} @@ -461,10 +462,9 @@ class TestFieldAliases: self, backend: TantivyBackend, ) -> None: - from documents.models import StoragePath - archive = StoragePath.objects.create(name="archive", path="archive/{title}") - stored = Document.objects.create( + stored = _index( + backend, title="Stored", content="quarterly statement", checksum="alias-path-1", @@ -473,12 +473,11 @@ class TestFieldAliases: # storage_path is NOT a default search field today, so a demoted # "path:archive" already matches nothing; the content decoy keeps # this test discriminating even if it ever joins the defaults. - loose = Document.objects.create( + _index( + backend, title="Loose", content="archive mentioned in body", checksum="alias-path-2", ) - backend.add_or_update(stored) - backend.add_or_update(loose) assert _matched_ids(backend, "path:archive") == {stored.pk} assert _matched_ids(backend, "storage_path:archive") == {stored.pk}