diff --git a/tests/orchestration/test_scenario_export_orchestrator.py b/tests/orchestration/test_scenario_export_orchestrator.py index fc918d0e8..17e933f81 100644 --- a/tests/orchestration/test_scenario_export_orchestrator.py +++ b/tests/orchestration/test_scenario_export_orchestrator.py @@ -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)