from __future__ import annotations from datetime import UTC, datetime from types import SimpleNamespace import unittest from sqlalchemy import create_engine from sqlalchemy.orm import Session from govoplan_core.auth import ApiPrincipal from govoplan_core.core.access import PrincipalRef from govoplan_core.core.events import EventObjectRef, EventTenantRef, PlatformEvent from govoplan_core.core.search import ( SearchAuthorizationRequest, SearchBackfillRequest, SearchResourceReference, ) from govoplan_decisions.backend.db.models import FormalDecisionRevision from govoplan_decisions.backend.search_source import DecisionsSearchSource, PROVIDER_ID NOW = datetime(2026, 8, 6, 11, 0, tzinfo=UTC) class DecisionsSearchSourceTests(unittest.TestCase): def setUp(self) -> None: self.engine = create_engine("sqlite+pysqlite:///:memory:") FormalDecisionRevision.__table__.create(self.engine) self.session = Session(self.engine) self.session.add( FormalDecisionRevision( tenant_id="tenant-1", decision_id="decision-1", revision="1", decision_type="permit", state="decided", valid_from=NOW, recorded_at=NOW, payload={ "operative_result": "PROTECTED-RESULT", "reasoning": "PROTECTED-REASONING", "conditions": ["PROTECTED-CONDITION"], "subject_refs": [ {"kind": "party", "owner_module": "parties", "object_id": "applicant-1"} ], "legal_bases": [ {"authority": "Example", "reference": "law:3", "version": "2026"} ], "authority_context": { "case_ref": {"object_id": "case-1"} }, "delivery_refs": ["postbox:delivery-1"], "remedy_refs": ["remedy:appeal"], "review_refs": [], }, created_by="account-1", ) ) self.session.commit() self.source = DecisionsSearchSource() def tearDown(self) -> None: self.session.close() self.engine.dispose() def test_search_projection_excludes_protected_decision_content(self) -> None: page = self.source.backfill( self.session, request=SearchBackfillRequest( tenant_id="tenant-1", provider_id=PROVIDER_ID, resource_type="formal_decision", rebuild_id="rebuild-1", ), ) document = page.documents[0] serialized = repr( (document.title, document.summary, document.body, document.keywords, document.metadata) ) self.assertNotIn("PROTECTED-", serialized) self.assertEqual("/cases/case-1", document.url) self.assertFalse(document.metadata["protected_content_indexed"]) reference = SearchResourceReference( tenant_id="tenant-1", module_id="decisions", resource_type="formal_decision", resource_id="decision-1", ) request = SearchAuthorizationRequest(reference=reference, source_revision="1") self.assertTrue( self.source.authorize( self.session, _principal({"decisions:decision:read"}), requests=(request,), )[reference.key] ) self.assertFalse( self.source.authorize( self.session, _principal(set()), requests=(request,), )[reference.key] ) changes = self.source.index_changes_for_event( self.session, event=PlatformEvent( type="decisions.decision.recorded", module_id="decisions", tenant=EventTenantRef(id="tenant-1"), resource=EventObjectRef(type="formal_decision", id="decision-1"), ), delivery_key="delivery-1", ) self.assertEqual("upsert", changes[0].kind) def _principal(scopes: set[str]) -> ApiPrincipal: return ApiPrincipal( principal=PrincipalRef( account_id="account-1", membership_id="membership-1", tenant_id="tenant-1", scopes=frozenset(scopes), ), account=SimpleNamespace(id="account-1"), user=SimpleNamespace(id="membership-1"), ) if __name__ == "__main__": unittest.main()