Email the requester the export link, best-effort 🟥

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-14 09:28:16 +00:00
parent f774ca4424
commit e825b58c13

View file

@ -93,3 +93,65 @@ def test_builds_and_uploads_a_sheet_per_scenario_workbook(exports: S3Client) ->
assert result.export_s3_key in result.presigned_url
workbook = load_workbook(BytesIO(exports.get_object(result.export_s3_key)))
assert workbook.sheetnames == ["Fabric first", "Low carbon"]
def test_emails_the_requester_the_presigned_download_link(exports: S3Client) -> None:
# arrange
email = _RecordingEmail()
orchestrator = ScenarioExportOrchestrator(
rows=_FakeRows({5: [_row(1)]}),
scenarios=_FakeScenarioNames({5: "Fabric first"}),
exports=exports,
email=email,
url_ttl_seconds=3600,
)
# act
result = orchestrator.run(
portfolio_id=100,
scenario_ids=[5],
property_ids=[1],
recipient_email="user@example.com",
export_name="portfolio-100",
)
# assert — the requester is emailed the link (raw URL in the plain-text
# fallback, a clean button in the HTML alternative).
assert len(email.sent) == 1
to, _subject, body, html_body = email.sent[0]
assert to == "user@example.com"
assert result.presigned_url in body
assert html_body is not None
assert "Download" in html_body
class _FailingEmail:
def send(
self, *, to: str, subject: str, body: str, html_body: Optional[str] = None
) -> None:
raise RuntimeError("smtp down")
def test_an_email_failure_does_not_lose_the_built_export(exports: S3Client) -> None:
# arrange — the workbook uploads fine, but email delivery blows up.
orchestrator = ScenarioExportOrchestrator(
rows=_FakeRows({5: [_row(1)]}),
scenarios=_FakeScenarioNames({5: "Fabric first"}),
exports=exports,
email=_FailingEmail(),
url_ttl_seconds=3600,
)
# act — must not raise.
result = orchestrator.run(
portfolio_id=100,
scenario_ids=[5],
property_ids=[1],
recipient_email="user@example.com",
export_name="portfolio-100",
)
# assert — the export was still uploaded and its link returned (it also lands
# on sub_task.outputs, so the link isn't lost).
assert result.presigned_url
assert exports.get_object(result.export_s3_key)