diff --git a/infrastructure/abri/abri_client.py b/infrastructure/abri/abri_client.py index 19054d200..66e094eab 100644 --- a/infrastructure/abri/abri_client.py +++ b/infrastructure/abri/abri_client.py @@ -78,14 +78,21 @@ class AbriClient: return self._parse_job_logged(outcome) def amend_job(self, request: AmendJobRequest) -> AmendJobResult: + # OpenHousing silently drops the appointment unless a bookable resource + # is sent, so fall back to the configured surveyor when the request + # carries no explicit override (mirrors log_job). + resource = ( + request.resource + if request.resource is not None + else self._config.default_resource + ) parameters = [ ("job_no", request.job_no), ("action", "amend"), ("appointment_date", _format_appointment_date(request.appointment_date)), ("appointment_time", request.appointment_time), + ("resource", resource), ] - if request.resource is not None: - parameters.append(("resource", request.resource)) outcome = self._exchange( request_type="amendoptiappt", diff --git a/infrastructure/abri/config.py b/infrastructure/abri/config.py index f2da18a77..f29e3f10c 100644 --- a/infrastructure/abri/config.py +++ b/infrastructure/abri/config.py @@ -11,9 +11,16 @@ class AbriConfig: @classmethod def from_env(cls, env: Mapping[str, str]) -> "AbriConfig": + # A blank default_resource does not error at the relay — OpenHousing + # logs the job but silently drops the appointment — so reject it here + # rather than let the misconfiguration ship an empty resource. + default_resource = env["ABRI_RELAY_DEFAULT_RESOURCE"] + if not default_resource.strip(): + raise ValueError("ABRI_RELAY_DEFAULT_RESOURCE must not be blank") + return cls( endpoint_url=env["ABRI_RELAY_URL"], username=env["ABRI_RELAY_USERNAME"], password=env["ABRI_RELAY_PASSWORD"], - default_resource=env["ABRI_RELAY_DEFAULT_RESOURCE"], + default_resource=default_resource, ) diff --git a/tests/infrastructure/abri/test_abri_client.py b/tests/infrastructure/abri/test_abri_client.py index e891e4283..42ede432a 100644 --- a/tests/infrastructure/abri/test_abri_client.py +++ b/tests/infrastructure/abri/test_abri_client.py @@ -410,10 +410,12 @@ def test_amend_job_sends_the_recorded_amendoptiappt_envelope_to_the_relay_endpoi ) -def test_amend_job_omits_the_resource_parameter_when_no_surveyor_is_provided( +def test_amend_job_defaults_the_resource_to_the_configured_surveyor( client: AbriClient, mock_session: MagicMock ) -> None: - # Arrange + # Arrange: OpenHousing silently drops the appointment unless a bookable + # resource is sent, so an amend without an explicit surveyor falls back to + # the configured default rather than omitting the parameter. mock_session.post.return_value.content = _load_fixture( "abri_relay_amendoptiappt_success_response.xml" ) @@ -423,11 +425,12 @@ def test_amend_job_omits_the_resource_parameter_when_no_surveyor_is_provided( # Assert sent_body: bytes = mock_session.post.call_args.kwargs["data"] - sent_attributes = [ - parameter.get("attribute") + resources = [ + parameter.get("attribute_value") for parameter in ET.fromstring(sent_body).findall(".//Parameters") + if parameter.get("attribute") == "resource" ] - assert "resource" not in sent_attributes + assert resources == [CONFIG.default_resource] # --- amend_job: rejection --- diff --git a/tests/infrastructure/abri/test_abri_config.py b/tests/infrastructure/abri/test_abri_config.py index 4e1d8c87d..0884a14a3 100644 --- a/tests/infrastructure/abri/test_abri_config.py +++ b/tests/infrastructure/abri/test_abri_config.py @@ -1,3 +1,5 @@ +import pytest + from infrastructure.abri.config import AbriConfig @@ -20,3 +22,18 @@ def test_config_hydrates_relay_credentials_and_defaults_from_environment() -> No password="secret", default_resource="CBRYAN", ) + + +@pytest.mark.parametrize("blank", ["", " "]) +def test_config_rejects_a_blank_default_resource(blank: str) -> None: + # A blank resource does not error at the relay: OpenHousing logs the job + # but silently drops the appointment. Fail loudly at config load instead. + env = { + "ABRI_RELAY_URL": "https://relay.example.test/api/DomnaRelay?code=key", + "ABRI_RELAY_USERNAME": "DomnaWeb", + "ABRI_RELAY_PASSWORD": "secret", + "ABRI_RELAY_DEFAULT_RESOURCE": blank, + } + + with pytest.raises(ValueError, match="ABRI_RELAY_DEFAULT_RESOURCE"): + AbriConfig.from_env(env) diff --git a/tests/orchestration/test_abri_orchestrator_amend_job.py b/tests/orchestration/test_abri_orchestrator_amend_job.py index f5ba20e57..1a3386a5b 100644 --- a/tests/orchestration/test_abri_orchestrator_amend_job.py +++ b/tests/orchestration/test_abri_orchestrator_amend_job.py @@ -114,6 +114,9 @@ def test_amend_job_sends_an_amendoptiappt_envelope_for_the_deals_recorded_job( "action": "amend", "appointment_date": "24/06/2026", "appointment_time": "PM", + # OpenHousing drops the appointment without a bookable resource, so the + # amend defaults to the configured surveyor when none is overridden. + "resource": CONFIG.default_resource, }