Complete the tenant sync without contacts when the property is void 🟥

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Roth 2026-07-27 11:24:49 +00:00
parent 6ee560af29
commit a3e1174d0d
2 changed files with 36 additions and 1 deletions

View file

@ -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):

View file

@ -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 == []