Release govoplan-search v0.1.20: centralize filtering and refresh behavior
Module Package Release / publish-packages (push) Successful in 11s

This commit is contained in:
2026-09-08 01:32:53 +02:00
parent 6c5545af66
commit 6ad7c6e0ef
20 changed files with 530 additions and 788 deletions
+21
View File
@@ -26,6 +26,27 @@ class SearchDocumentationTests(unittest.TestCase):
for topic in manifest.documentation:
self.assertEqual((), user_workflow_scope_condition_issues(topic))
def test_filter_workflow_documents_tri_state_context_and_stale_reads(self) -> None:
topic = next(
item
for item in manifest.documentation
if item.id == "search.global-and-contextual"
)
steps = " ".join(topic.metadata["steps"])
german = topic.structured_translations["de"]
german_steps = " ".join(german["steps"])
self.assertIn("Modules and Result types dropdowns", steps)
self.assertIn("Deselect all intentionally shows no results", steps)
self.assertIn("Clear filters keeps the selected context and query", steps)
self.assertIn("Escape closes the open dropdown first", steps)
self.assertIn("Alle abwählen zeigt bewusst keine Ergebnisse", german_steps)
self.assertIn("gewählten Kontext und Suchbegriff", german_steps)
self.assertIn("Escape schließt zuerst", german_steps)
for metadata in (topic.metadata, german):
self.assertIn("change_filters", metadata["operational_consequences"])
self.assertEqual(4, len(metadata["limitations"]))
self.assertEqual(5, len(metadata["verification"]))
if __name__ == "__main__":
unittest.main()
+57
View File
@@ -232,6 +232,63 @@ class SearchServiceTests(unittest.TestCase):
self.assertEqual("Cases", catalogue[0].label)
self.assertEqual(25, catalogue[0].order)
def test_legacy_empty_filters_remain_unrestricted_with_current_acl(self) -> None:
service = SearchIndexService(_Registry(active_modules=("search", "cases", "files", "mail")))
for module_id, resource_type, resource_id, title, allowed in (
("cases", "case", "case-1", "Permit A", True),
("files", "file", "file-1", "Permit B", True),
("mail", "message", "message-1", "Permit C", True),
("files", "folder", "folder-1", "Permit D", True),
("files", "file", "restricted", "Permit E", False),
):
service.upsert_document(self.session, self.principal, document=SearchDocument(
tenant_id="tenant-1", module_id=module_id, resource_type=resource_type,
resource_id=resource_id, title=title, url=f"/{module_id}/{resource_id}",
acl_tokens=("account:account-1" if allowed else "account:someone-else",),
))
self.session.flush()
results = service.search(self.session, self.principal, query=SearchQuery(
text="permit", tenant_id="tenant-1", module_ids=(), resource_types=(),
))
self.assertEqual(
{"case-1", "file-1", "message-1", "folder-1"},
{result.resource_id for result in results},
)
# The UI's explicit-none state must not be serialized as legacy empty
# tuples: empty tuples intentionally keep existing callers unrestricted.
narrowed = service.search(self.session, self.principal, query=SearchQuery(
text="permit", tenant_id="tenant-1",
module_ids=("cases", "files"), resource_types=("case", "file"),
))
self.assertEqual({"case-1", "file-1"}, {result.resource_id for result in narrowed})
before_limit = service.search(self.session, self.principal, query=SearchQuery(
text="permit", tenant_id="tenant-1", module_ids=("files",), resource_types=("file",), limit=1,
))
self.assertEqual(["file-1"], [result.resource_id for result in before_limit])
unknown = service.search(self.session, self.principal, query=SearchQuery(
text="permit", tenant_id="tenant-1", module_ids=("not-an-installed-module",),
))
self.assertEqual((), unknown)
def test_cursor_cannot_be_reused_after_filter_or_context_changes(self) -> None:
registry = _AggregateRegistry()
first = aggregate_search_page(registry, self.session, self.principal, query=SearchQuery(
text="permit", tenant_id="tenant-1", limit=2,
))
self.assertIsNotNone(first.next_cursor)
for changed in (
{"module_ids": ("cases",)},
{"resource_types": ("case",)},
{"context_kind": "module"},
{"context_id": "cases.selected-case"},
{"language": "german"},
):
with self.subTest(changed=changed), self.assertRaisesRegex(ValueError, "cursor"):
aggregate_search_page(registry, self.session, self.principal, query=SearchQuery(
text="permit", tenant_id="tenant-1", limit=2, cursor=first.next_cursor, **changed,
))
def test_upsert_replaces_acl_tokens_and_delete_is_idempotent(self) -> None:
document = SearchDocument(
tenant_id="tenant-1",