feat: add campaign copying scheduling and residual handling
This commit is contained in:
@@ -275,6 +275,92 @@ class CampaignAttachmentBuildTests(unittest.TestCase):
|
||||
self.assertEqual(archive.namelist(), ["matched.xlsx"])
|
||||
self.assertEqual(archive.read("matched.xlsx"), b"matched workbook")
|
||||
|
||||
def test_residual_files_become_a_separate_reviewed_report_or_attachment_message(self) -> None:
|
||||
for mode, expected_attachment_count in (("report", 0), ("attach", 1)):
|
||||
with self.subTest(mode=mode), tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
watched = root / "watched"
|
||||
watched.mkdir()
|
||||
(watched / "assigned.txt").write_text("assigned", encoding="utf-8")
|
||||
(watched / "residual.txt").write_text("residual", encoding="utf-8")
|
||||
campaign_file = root / "campaign.json"
|
||||
campaign_file.write_text("{}", encoding="utf-8")
|
||||
config = CampaignConfig.model_validate({
|
||||
"version": "1.0",
|
||||
"campaign": {"id": f"residual-{mode}", "name": "Monthly import", "mode": "test"},
|
||||
"fields": [],
|
||||
"global_values": {},
|
||||
"server": {
|
||||
"mail_profile_id": "profile-1",
|
||||
"profile_capabilities": {"smtp_available": True},
|
||||
},
|
||||
"recipients": {
|
||||
"from": {"email": "sender@example.org", "type": "to"},
|
||||
"allow_individual_to": True,
|
||||
},
|
||||
"template": {"subject": "Normal message", "text": "Normal body"},
|
||||
"attachments": {
|
||||
"base_paths": [{
|
||||
"id": "watched",
|
||||
"name": "Watched folder",
|
||||
"path": "watched",
|
||||
"unsent_warning": True,
|
||||
}],
|
||||
"global": [{
|
||||
"id": "assigned",
|
||||
"base_path_id": "watched",
|
||||
"base_dir": "watched",
|
||||
"file_filter": "assigned.txt",
|
||||
"required": True,
|
||||
}],
|
||||
"residual_files": {
|
||||
"mode": mode,
|
||||
"recipient": {"email": "operator@example.org", "name": "Operator"},
|
||||
"subject": "Residual files for {{local:campaign_name}}",
|
||||
"text": "{{local:residual_file_count}} file(s):\n{{local:residual_file_list}}",
|
||||
},
|
||||
},
|
||||
"entries": {"inline": [{
|
||||
"id": "recipient-1",
|
||||
"to": [{"email": "recipient@example.org", "type": "to"}],
|
||||
}]},
|
||||
"validation_policy": {
|
||||
"missing_email": "block",
|
||||
"template_error": "block",
|
||||
"unsent_attachment_files": "block",
|
||||
},
|
||||
"delivery": {"imap_append_sent": {"enabled": False}},
|
||||
})
|
||||
|
||||
result = build_campaign_messages(
|
||||
config,
|
||||
campaign_file=campaign_file,
|
||||
output_dir=root / "out",
|
||||
write_eml=True,
|
||||
)
|
||||
|
||||
self.assertEqual(len(result.report.messages), 2)
|
||||
normal, residual = result.report.messages
|
||||
self.assertEqual(normal.validation_status.value, "ready")
|
||||
self.assertEqual(residual.entry_id, "__residual_files__")
|
||||
self.assertEqual(residual.validation_status.value, "needs_review")
|
||||
self.assertEqual(residual.to[0].email, "operator@example.org")
|
||||
self.assertEqual(residual.subject, "Residual files for Monthly import")
|
||||
self.assertEqual(residual.attachment_count, expected_attachment_count)
|
||||
self.assertIn(
|
||||
"residual_attachment_disposition",
|
||||
{issue.code for issue in residual.issues},
|
||||
)
|
||||
self.assertNotIn(
|
||||
"unsent_attachment_files",
|
||||
{issue.code for message in result.report.messages for issue in message.issues},
|
||||
)
|
||||
mime = result.built_messages[1].mime
|
||||
self.assertIsNotNone(mime)
|
||||
self.assertIn("residual.txt", mime.get_body(preferencelist=("plain",)).get_content())
|
||||
filenames = [part.get_filename() for part in mime.iter_attachments()]
|
||||
self.assertEqual(filenames, ["residual.txt"] if mode == "attach" else [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user