Model/tests/applications/abri/test_abri_trigger_request.py
Daniel Roth 84d0e1e0d5 Date an abandonment to the deal's own dates, not now() 🟩
date_abandoned now resolves to the third failed attempt's confirmed
survey date, falling back to its last submission date, rather than the
processing date. This dates the OpenHousing cancellation to when the
job actually lapsed even if the trigger message lags or redelivers.

- domain: abandonment_date() encodes the confirmed-survey -> last-
  submission fallback
- DealAbandonment carries both dates; abandon_job raises
  AbandonmentDateUnknownError when a deal has neither
- last_submission_date now flows through the trigger message and request
- the injected clock is dropped: nothing reads now() any more

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:51:39 +00:00

143 lines
4 KiB
Python

from datetime import date
from typing import Any, Dict
import pytest
from pydantic import ValidationError
from applications.abri.abri_trigger_request import AbriTriggerRequest
def _full_message() -> Dict[str, Any]:
return {
"hubspot_deal_id": "9876543210",
"flows": ["amend_job", "log_job", "sync_tenant_data"],
"place_ref": "1007165",
"deal_name": "49 Admers Crescent",
"confirmed_survey_date": "2026-06-24",
"confirmed_survey_time": "14:30",
}
def test_a_full_message_parses_into_a_typed_request() -> None:
# Act
request = AbriTriggerRequest.model_validate(_full_message())
# Assert
assert request.hubspot_deal_id == "9876543210"
assert request.flows == ["amend_job", "log_job", "sync_tenant_data"]
assert request.place_ref == "1007165"
assert request.deal_name == "49 Admers Crescent"
assert request.confirmed_survey_date == date(2026, 6, 24)
assert request.confirmed_survey_time == "14:30"
@pytest.mark.parametrize(
("flow", "missing_field"),
[
("log_job", "place_ref"),
("log_job", "deal_name"),
("log_job", "confirmed_survey_date"),
("amend_job", "confirmed_survey_date"),
("sync_tenant_data", "place_ref"),
],
)
def test_a_message_missing_a_field_its_flow_needs_fails_validation(
flow: str, missing_field: str
) -> None:
# Arrange
message = _full_message()
message["flows"] = [flow]
del message[missing_field]
# Act / Assert
with pytest.raises(ValidationError, match=missing_field):
AbriTriggerRequest.model_validate(message)
def test_a_message_missing_only_fields_of_unfired_flows_parses() -> None:
# Arrange: tenant sync needs only the place_ref (and the deal id).
message = {
"hubspot_deal_id": "9876543210",
"flows": ["sync_tenant_data"],
"place_ref": "1007165",
}
# Act
request = AbriTriggerRequest.model_validate(message)
# Assert
assert request.flows == ["sync_tenant_data"]
assert request.confirmed_survey_date is None
def test_a_message_without_a_confirmed_time_parses_for_logging() -> None:
# Arrange: the time slot is optional — no time means an all-day slot.
message = _full_message()
message["flows"] = ["log_job"]
del message["confirmed_survey_time"]
# Act
request = AbriTriggerRequest.model_validate(message)
# Assert
assert request.confirmed_survey_time is None
def test_a_message_naming_abandon_job_validates_with_only_the_deal_id() -> None:
# Arrange: abandonment needs no deal field beyond the deal id.
message = {
"hubspot_deal_id": "9876543210",
"flows": ["abandon_job"],
}
# Act
request = AbriTriggerRequest.model_validate(message)
# Assert
assert request.flows == ["abandon_job"]
def test_a_message_carries_the_outcome_for_the_abandonment_reason_mapping() -> None:
# Arrange
message = _full_message()
message["flows"] = ["abandon_job"]
message["outcome"] = "no answer"
# Act
request = AbriTriggerRequest.model_validate(message)
# Assert
assert request.outcome == "no answer"
def test_a_message_carries_the_last_submission_date_for_abandonment_dating() -> None:
# Arrange
message = _full_message()
message["flows"] = ["abandon_job"]
message["last_submission_date"] = "2026-07-01"
# Act
request = AbriTriggerRequest.model_validate(message)
# Assert
assert request.last_submission_date == date(2026, 7, 1)
def test_a_message_naming_an_unknown_flow_fails_validation() -> None:
# Arrange
message = _full_message()
message["flows"] = ["cancel_job"]
# Act / Assert
with pytest.raises(ValidationError):
AbriTriggerRequest.model_validate(message)
def test_a_message_naming_no_flows_fails_validation() -> None:
# Arrange
message = _full_message()
message["flows"] = []
# Act / Assert
with pytest.raises(ValidationError):
AbriTriggerRequest.model_validate(message)