From a3e1174d0d1b9290e0eb99ab133ea54471e24d54 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Mon, 27 Jul 2026 11:24:49 +0000 Subject: [PATCH] =?UTF-8?q?Complete=20the=20tenant=20sync=20without=20cont?= =?UTF-8?q?acts=20when=20the=20property=20is=20void=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) --- orchestration/abri_orchestrator.py | 15 ++++++++++++- ...test_abri_orchestrator_tenant_data_sync.py | 22 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/orchestration/abri_orchestrator.py b/orchestration/abri_orchestrator.py index 9441fdd66..bf833ac42 100644 --- a/orchestration/abri_orchestrator.py +++ b/orchestration/abri_orchestrator.py @@ -65,7 +65,20 @@ class TenantDataSyncSummary: vulnerable_contact_count: int -TenantDataSyncResult = Union[TenantDataSyncSummary, AbriRequestRejected] +@dataclass(frozen=True) +class PropertyReportedVoid: + """Abri reported no live tenancy: no contacts to create for the deal. + + A normal outcome, not a failure — an empty property has no signatories to + sync, so the flow completes rather than failing the task. + """ + + place_ref: PlaceRef + + +TenantDataSyncResult = Union[ + TenantDataSyncSummary, PropertyReportedVoid, AbriRequestRejected +] class TenantDataSyncError(Exception): diff --git a/tests/orchestration/test_abri_orchestrator_tenant_data_sync.py b/tests/orchestration/test_abri_orchestrator_tenant_data_sync.py index b5bd17728..a7368ecb2 100644 --- a/tests/orchestration/test_abri_orchestrator_tenant_data_sync.py +++ b/tests/orchestration/test_abri_orchestrator_tenant_data_sync.py @@ -20,6 +20,7 @@ from infrastructure.abri.errors import AbriResponseParseError, AbriTransportErro from orchestration.abri_orchestrator import ( TenantDataSyncError, AbriOrchestrator, + PropertyReportedVoid, TenantDataSyncSummary, ) @@ -539,3 +540,24 @@ def test_sync_tenant_data_retries_a_rate_limited_hubspot_call_before_succeeding( vulnerable_contact_count=0, ) assert sdk_client.crm.contacts.basic_api.create.call_count == 2 + + +# --- void property: no tenancy to sync, and not a failure --- + + +def test_sync_tenant_data_reports_the_property_void_and_creates_no_contacts( + orchestrator: AbriOrchestrator, + mock_session: MagicMock, + sdk_client: MagicMock, +) -> None: + # Arrange: Abri's agreed signal that the place is known to be empty. + mock_session.post.return_value.content = _load_fixture( + "abri_relay_getscstenantdata_notenancyfound_response.xml" + ) + + # Act + result = orchestrator.sync_tenant_data(PLACE_REF, deal_id=DEAL_ID) + + # Assert + assert result == PropertyReportedVoid(place_ref=PLACE_REF) + assert sdk_client.crm.contacts.mock_calls == []