from __future__ import annotations from datetime import UTC, datetime from types import SimpleNamespace import unittest from unittest.mock import patch from govoplan_campaign.backend.reports.campaigns import ( _job_evidence_row, _latest_by_job_id, _load_delivery_info, generate_campaign_report, ) def _dt() -> datetime: return datetime(2026, 7, 8, 12, 30, tzinfo=UTC) def test_job_evidence_row_contains_transport_and_message_evidence() -> None: job = SimpleNamespace( id="job-1", campaign_id="campaign-1", campaign_version_id="version-1", entry_index=0, entry_id="recipient-1", recipient_email="recipient@govoplan.test", subject="Evidence test", message_id_header="", eml_storage_key="eml/job-1.eml", eml_local_path="/tmp/job-1.eml", eml_size_bytes=1234, eml_sha256="abc123", build_status="built", validation_status="ready", queue_status="queued", send_status="smtp_accepted", imap_status="appended", attempt_count=2, queued_at=_dt(), claimed_at=None, smtp_started_at=_dt(), outcome_unknown_at=None, sent_at=_dt(), last_error=None, resolved_recipients={ "from": [{"name": "Sender", "email": "sender@govoplan.test"}], "to": [{"name": "Recipient", "email": "recipient@govoplan.test"}], "cc": [], "bcc": [], "reply_to": [{"email": "reply@govoplan.test"}], }, resolved_attachments=[ { "matches": [{"filename": "notice.pdf"}, "/tmp/evidence.xlsx"], "zip_filename": "bundle.zip", "message_filenames": ["notice.pdf"], } ], issues_snapshot=[], created_at=_dt(), updated_at=_dt(), ) smtp = SimpleNamespace( job_id="job-1", attempt_number=2, status="success", smtp_status_code=250, smtp_response="2.0.0 queued", error_type=None, error_message=None, started_at=_dt(), finished_at=_dt(), ) imap = SimpleNamespace( job_id="job-1", attempt_number=1, status="appended", folder="Sent", error_message=None, created_at=_dt(), updated_at=_dt(), ) row = _job_evidence_row(job, latest_smtp=smtp, latest_imap=imap) assert row["campaign_id"] == "campaign-1" assert row["campaign_version_id"] == "version-1" assert row["message_id_header"] == "" assert row["from"] == "Sender " assert row["to"] == "Recipient " assert row["reply_to"] == "reply@govoplan.test" assert "bundle.zip" in row["attachment_names"] assert "notice.pdf" in row["attachment_names"] assert row["latest_smtp_status_code"] == 250 assert "latest_smtp_response" not in row assert "latest_smtp_error_type" not in row assert "latest_smtp_error_message" not in row assert row["latest_imap_status"] == "appended" assert row["latest_imap_folder"] == "Sent" assert "latest_imap_error_message" not in row assert "eml_storage_key" not in row assert "eml_local_path" not in row def test_latest_by_job_id_keeps_highest_attempt_number() -> None: attempts = [ SimpleNamespace(job_id="job-1", attempt_number=1), SimpleNamespace(job_id="job-1", attempt_number=3), SimpleNamespace(job_id="job-2", attempt_number=2), ] latest = _latest_by_job_id(attempts) assert latest["job-1"].attempt_number == 3 assert latest["job-2"].attempt_number == 2 def test_invalid_legacy_snapshot_never_echoes_validation_details() -> None: version = SimpleNamespace( execution_snapshot={ "snapshot_version": "legacy", "smtp": {"host": "smtp.internal.example", "password": "provider-secret"}, }, execution_snapshot_hash=None, execution_snapshot_at=None, ) delivery = _load_delivery_info(version, []) assert delivery["load_error"] == "Execution snapshot is invalid; rebuild the selected version before delivery." assert "provider-secret" not in repr(delivery) assert "smtp.internal.example" not in repr(delivery) assert "background_workers_enabled" not in delivery assert "smtp_transport_revision" not in delivery operator_delivery = _load_delivery_info(version, [], include_diagnostics=True) assert "background_workers_enabled" in operator_delivery assert "smtp_transport_revision" in operator_delivery def test_aggregate_report_omits_recipient_level_failures_by_default() -> None: campaign = SimpleNamespace(id="campaign-1") version = SimpleNamespace(id="version-1") with ( patch("govoplan_campaign.backend.reports.campaigns._get_campaign", return_value=campaign), patch("govoplan_campaign.backend.reports.campaigns._selected_version", return_value=version), patch("govoplan_campaign.backend.reports.campaigns._report_jobs", return_value=[]), patch( "govoplan_campaign.backend.reports.campaigns._campaign_report_payload", return_value={"cards": {}}, ) as payload, ): report = generate_campaign_report( object(), # type: ignore[arg-type] tenant_id="tenant-1", campaign_id="campaign-1", ) assert report == {"cards": {}} assert payload.call_args.kwargs["include_recent_failures"] is False class CampaignReportProjectionTests(unittest.TestCase): def test_evidence_projection(self) -> None: test_job_evidence_row_contains_transport_and_message_evidence() def test_latest_attempt_projection(self) -> None: test_latest_by_job_id_keeps_highest_attempt_number() if __name__ == "__main__": unittest.main()