Always send a bookable resource on Abri amend_job

OpenHousing logs a job but silently drops the appointment when no bookable
resource is sent, so the survey date never lands. log_job already sends the
configured default_resource; amend_job omitted the parameter unless the
request carried an explicit surveyor. Default amend's resource to the
configured surveyor too, and fail loudly at config load on a blank
ABRI_RELAY_DEFAULT_RESOURCE so the misconfiguration can't ship an empty
resource again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Daniel Roth 2026-07-10 10:37:35 +00:00
parent 7997d88375
commit f8b9d82200
5 changed files with 45 additions and 8 deletions

View file

@ -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",

View file

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

View file

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

View file

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

View file

@ -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,
}