]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Enhancement: check for mail destination directory, log post-consume errors (#7808)
authorMartin Richtarsky <s@martinien.de>
Thu, 3 Oct 2024 03:21:35 +0000 (05:21 +0200)
committerGitHub <noreply@github.com>
Thu, 3 Oct 2024 03:21:35 +0000 (20:21 -0700)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
src/paperless_mail/mail.py
src/paperless_mail/tests/test_mail.py

index 84f97b742d6efa36eae324f2401ff2cd8711a3fc..77d293ea04acd65b4bc0c1a79d1cc33b53e212f3 100644 (file)
@@ -28,6 +28,7 @@ from imap_tools import MailboxFolderSelectError
 from imap_tools import MailBoxUnencrypted
 from imap_tools import MailMessage
 from imap_tools import MailMessageFlags
+from imap_tools import errors
 from imap_tools.mailbox import MailBoxTls
 from imap_tools.query import LogicOperator
 
@@ -266,7 +267,14 @@ def apply_mail_action(
             M.folder.set(rule.folder)
 
             action = get_rule_action(rule, supports_gmail_labels)
-            action.post_consume(M, message_uid, rule.action_parameter)
+            try:
+                action.post_consume(M, message_uid, rule.action_parameter)
+            except errors.ImapToolsError:
+                logger = logging.getLogger("paperless_mail")
+                logger.exception(
+                    "Error while processing mail action during post_consume",
+                )
+                raise
 
         ProcessedMail.objects.create(
             owner=rule.owner,
@@ -570,13 +578,17 @@ class MailAccountHandler(LoggingMixin):
         rule: MailRule,
         supports_gmail_labels: bool,
     ):
-        self.log.debug(f"Rule {rule}: Selecting folder {rule.folder}")
-
+        folders = [rule.folder]
+        # In case of MOVE, make sure also the destination exists
+        if rule.action == MailRule.MailAction.MOVE:
+            folders.insert(0, rule.action_parameter)
         try:
-            M.folder.set(rule.folder)
+            for folder in folders:
+                self.log.debug(f"Rule {rule}: Selecting folder {folder}")
+                M.folder.set(folder)
         except MailboxFolderSelectError as err:
             self.log.error(
-                f"Unable to access folder {rule.folder}, attempting folder listing",
+                f"Unable to access folder {folder}, attempting folder listing",
             )
             try:
                 for folder_info in M.folder.list():
@@ -588,7 +600,7 @@ class MailAccountHandler(LoggingMixin):
                 )
 
             raise MailError(
-                f"Rule {rule}: Folder {rule.folder} "
+                f"Rule {rule}: Folder {folder} "
                 f"does not exist in account {rule.account}",
             ) from err
 
index 9078335a6b957faadbc71e686d7196c0ac72c1ad..b1e3ff06e78ea506ae756f6c168b863ec4811c44 100644 (file)
@@ -10,6 +10,7 @@ import pytest
 from django.core.management import call_command
 from django.db import DatabaseError
 from django.test import TestCase
+from django.utils import timezone
 from imap_tools import NOT
 from imap_tools import EmailAddress
 from imap_tools import FolderInfo
@@ -17,6 +18,7 @@ from imap_tools import MailboxFolderSelectError
 from imap_tools import MailboxLoginError
 from imap_tools import MailMessage
 from imap_tools import MailMessageFlags
+from imap_tools import errors
 
 from documents.models import Correspondent
 from documents.tests.utils import DirectoriesMixin
@@ -28,6 +30,7 @@ from paperless_mail.mail import TagMailAction
 from paperless_mail.mail import apply_mail_action
 from paperless_mail.models import MailAccount
 from paperless_mail.models import MailRule
+from paperless_mail.models import ProcessedMail
 
 
 @dataclasses.dataclass
@@ -1424,6 +1427,95 @@ class TestMail(
         )  # still 2
 
 
+class TestPostConsumeAction(TestCase):
+    def setUp(self):
+        self.account = MailAccount.objects.create(
+            name="test",
+            imap_server="imap.test.com",
+            imap_port=993,
+            imap_security=MailAccount.ImapSecurity.SSL,
+            username="testuser",
+            password="password",
+        )
+        self.rule = MailRule.objects.create(
+            name="testrule",
+            account=self.account,
+            action=MailRule.MailAction.MARK_READ,
+            action_parameter="",
+            folder="INBOX",
+        )
+        self.message_uid = "12345"
+        self.message_subject = "Test Subject"
+        self.message_date = timezone.make_aware(timezone.datetime(2023, 1, 1, 12, 0, 0))
+
+    @mock.patch("paperless_mail.mail.get_mailbox")
+    @mock.patch("paperless_mail.mail.mailbox_login")
+    @mock.patch("paperless_mail.mail.get_rule_action")
+    def test_post_consume_success(
+        self,
+        mock_get_rule_action,
+        mock_mailbox_login,
+        mock_get_mailbox,
+    ):
+        mock_mailbox = mock.MagicMock()
+        mock_get_mailbox.return_value.__enter__.return_value = mock_mailbox
+        mock_action = mock.MagicMock()
+        mock_get_rule_action.return_value = mock_action
+
+        apply_mail_action(
+            result=[],
+            rule_id=self.rule.pk,
+            message_uid=self.message_uid,
+            message_subject=self.message_subject,
+            message_date=self.message_date,
+        )
+
+        mock_mailbox_login.assert_called_once_with(mock_mailbox, self.account)
+        mock_mailbox.folder.set.assert_called_once_with(self.rule.folder)
+        mock_action.post_consume.assert_called_once_with(
+            mock_mailbox,
+            self.message_uid,
+            self.rule.action_parameter,
+        )
+
+        processed_mail = ProcessedMail.objects.get(uid=self.message_uid)
+        self.assertEqual(processed_mail.status, "SUCCESS")
+
+    @mock.patch("paperless_mail.mail.get_mailbox")
+    @mock.patch("paperless_mail.mail.mailbox_login")
+    @mock.patch("paperless_mail.mail.get_rule_action")
+    def test_post_consume_failure(
+        self,
+        mock_get_rule_action,
+        mock_mailbox_login,
+        mock_get_mailbox,
+    ):
+        mock_mailbox = mock.MagicMock()
+        mock_get_mailbox.return_value.__enter__.return_value = mock_mailbox
+        mock_action = mock.MagicMock()
+        mock_get_rule_action.return_value = mock_action
+        mock_action.post_consume.side_effect = errors.ImapToolsError("Test Exception")
+
+        with (
+            self.assertRaises(errors.ImapToolsError),
+            self.assertLogs("paperless.mail", level="ERROR") as cm,
+        ):
+            apply_mail_action(
+                result=[],
+                rule_id=self.rule.pk,
+                message_uid=self.message_uid,
+                message_subject=self.message_subject,
+                message_date=self.message_date,
+            )
+            error_str = cm.output[0]
+            expected_str = "Error while processing mail action during post_consume"
+            self.assertIn(expected_str, error_str)
+
+        processed_mail = ProcessedMail.objects.get(uid=self.message_uid)
+        self.assertEqual(processed_mail.status, "FAILED")
+        self.assertIn("Test Exception", processed_mail.error)
+
+
 class TestManagementCommand(TestCase):
     @mock.patch(
         "paperless_mail.management.commands.mail_fetcher.tasks.process_mail_accounts",