diff --git a/tests/orchestration/test_tenant_data_sync_orchestrator.py b/tests/orchestration/test_tenant_data_sync_orchestrator.py
index 82aeac847..798b82d74 100644
--- a/tests/orchestration/test_tenant_data_sync_orchestrator.py
+++ b/tests/orchestration/test_tenant_data_sync_orchestrator.py
@@ -3,13 +3,15 @@ from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
+import requests
from hubspot.crm.associations.v4 import AssociationSpec # type: ignore[reportMissingTypeStubs]
from hubspot.crm.contacts import SimplePublicObjectInputForCreate # type: ignore[reportMissingTypeStubs]
-from domain.abri.models import PlaceRef
+from domain.abri.models import AbriRequestRejected, PlaceRef
from etl.hubspot.deal_contacts_client import HubspotDealContactsClient
from infrastructure.abri.abri_client import AbriClient
from infrastructure.abri.config import AbriConfig
+from infrastructure.abri.errors import AbriResponseParseError, AbriTransportError
from orchestration.tenant_data_sync_orchestrator import (
TenantDataSyncOrchestrator,
TenantDataSyncSummary,
@@ -330,3 +332,91 @@ def test_run_still_creates_a_contact_for_an_all_blank_signatory(
properties={"is_vulnerable": "false"}
)
)
+
+
+# --- unusual but valid outcomes ---
+
+
+def test_run_treats_a_tenancy_with_no_signatories_as_a_valid_zero_contact_outcome(
+ orchestrator: TenantDataSyncOrchestrator,
+ mock_session: MagicMock,
+ sdk_client: MagicMock,
+) -> None:
+ # Arrange
+ mock_session.post.return_value.content = _tenancy_response("")
+
+ # Act
+ result = orchestrator.run(PLACE_REF, deal_id=DEAL_ID)
+
+ # Assert
+ assert result == TenantDataSyncSummary(
+ tenancy_reference="10042020051017",
+ contact_ids=(),
+ vulnerable_contact_count=0,
+ )
+ assert sdk_client.mock_calls == []
+
+
+# --- rejection passthrough ---
+
+
+def test_run_returns_the_abri_rejection_verbatim_and_makes_no_hubspot_calls(
+ orchestrator: TenantDataSyncOrchestrator,
+ mock_session: MagicMock,
+ sdk_client: MagicMock,
+) -> None:
+ # Arrange
+ mock_session.post.return_value.content = _load_fixture(
+ "abri_relay_getscstenantdata_relayerror_response.xml"
+ )
+
+ # Act
+ result = orchestrator.run(PLACE_REF, deal_id=DEAL_ID)
+
+ # Assert
+ assert isinstance(result, AbriRequestRejected)
+ assert result.code == "RelayError"
+ assert result.message.startswith("No Tenancy Found for Query")
+ assert sdk_client.mock_calls == []
+
+
+# --- ambiguous responses are retriable, never success or rejection ---
+
+
+@pytest.mark.parametrize(
+ "body",
+ [
+ b"not xml at all <<<",
+ b"", # no Tenancy element
+ b"A1B2", # multiple
+ b'',
+ b"",
+ ],
+)
+def test_run_raises_a_retriable_parse_error_on_an_unexpectedly_shaped_response(
+ orchestrator: TenantDataSyncOrchestrator,
+ mock_session: MagicMock,
+ sdk_client: MagicMock,
+ body: bytes,
+) -> None:
+ # Arrange
+ mock_session.post.return_value.content = body
+
+ # Act / Assert
+ with pytest.raises(AbriResponseParseError):
+ orchestrator.run(PLACE_REF, deal_id=DEAL_ID)
+ assert sdk_client.mock_calls == []
+
+
+def test_run_raises_a_transport_error_when_the_relay_is_unreachable(
+ orchestrator: TenantDataSyncOrchestrator,
+ mock_session: MagicMock,
+ sdk_client: MagicMock,
+) -> None:
+ # Arrange
+ mock_session.post.side_effect = requests.ConnectionError("connection refused")
+
+ # Act / Assert
+ with pytest.raises(AbriTransportError):
+ orchestrator.run(PLACE_REF, deal_id=DEAL_ID)
+ assert sdk_client.mock_calls == []