feat(mail): map standard IMAP folders per profile

This commit is contained in:
2026-08-19 21:27:42 +02:00
parent 393331574a
commit e0e00d7000
13 changed files with 243 additions and 33 deletions
+41
View File
@@ -8,6 +8,7 @@ from govoplan_mail.backend.config import ImapConfig
from govoplan_mail.backend.sending.imap import (
ImapAppendError,
ImapConfigurationError,
_detect_standard_folder_mappings,
_detect_sent_folder,
_extract_mailbox_name,
_fetch_message_by_uid,
@@ -18,6 +19,7 @@ from govoplan_mail.backend.sending.imap import (
_select_readonly,
_sequence_set,
append_message_to_sent,
list_imap_folders,
list_imap_messages,
list_imap_uids_since,
)
@@ -79,6 +81,45 @@ class ImapFolderParserTests(unittest.TestCase):
"Gesendet",
)
def test_detects_all_standard_folder_roles_by_flag_then_name(self):
self.assertEqual(
{
"inbox": "INBOX",
"sent": "Gesendete Elemente",
"drafts": "Entwürfe",
"trash": "Deleted",
"archive": "All Mail",
"junk": "Spam",
},
_detect_standard_folder_mappings(
[
("INBOX", set()),
("Gesendete Elemente", set()),
("Entwürfe", set()),
("Deleted", {"\\trash"}),
("All Mail", {"\\all"}),
("Spam", {"\\junk"}),
]
),
)
def test_mock_folder_listing_exposes_detected_standard_mappings(self):
result = list_imap_folders(
imap_config=ImapConfig(host="mock.imap.local"), include_status=False
)
self.assertEqual(
{
"inbox": "INBOX",
"sent": "Sent",
"drafts": "Drafts",
"trash": "Trash",
"archive": "Archive",
},
result.detected_folder_mappings,
)
self.assertEqual("Sent", result.detected_sent_folder)
class ImapMessagePaginationTests(unittest.TestCase):
def test_mock_message_cursor_preserves_order_and_resets_when_stale(self):
+45
View File
@@ -290,6 +290,51 @@ class MailProfileTransportHelperTests(unittest.TestCase):
self.assertEqual(audit.call_args.kwargs["details"]["protocol"], "imap")
self.assertNotIn("imap-secret", repr(audit.call_args.kwargs))
def test_apply_transport_update_persists_standard_folder_mappings(self):
profile = SimpleNamespace(
id="profile-folders",
tenant_id="tenant-1",
scope_type="tenant",
scope_id="tenant-1",
smtp_config={"host": "smtp.example.org"},
smtp_username=None,
smtp_password_encrypted=None,
smtp_transport_revision="smtp-before",
imap_config={"host": "imap.example.org", "sent_folder": "Legacy Sent"},
imap_username=None,
imap_password_encrypted=None,
imap_transport_revision="imap-before",
)
session = SimpleNamespace(flush=lambda: None)
with patch("govoplan_mail.backend.mail_profiles.clear_mailbox_index"):
_apply_profile_transport_update(
session, # type: ignore[arg-type]
profile,
user_id="user-1",
api_key_id=None,
smtp=None,
imap=ImapConfig(
host="imap.example.org",
folder_mappings={
"inbox": "INBOX",
"sent": "Sent Items",
"drafts": "Drafts",
},
),
clear_imap=False,
)
self.assertEqual(profile.imap_config["sent_folder"], "Sent Items")
self.assertEqual(
profile.imap_config["folder_mappings"],
{
"inbox": "INBOX",
"sent": "Sent Items",
"drafts": "Drafts",
},
)
def test_imap_password_replacement_clears_cache_without_rotating_identity_revision(self):
profile = SimpleNamespace(
id="profile-1",