Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
3
fetchmail_attach_from_folder/tests/__init__.py
Executable file
3
fetchmail_attach_from_folder/tests/__init__.py
Executable file
@@ -0,0 +1,3 @@
|
||||
# Copyright - 2015-2018 Therp BV <https://therp.nl>.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from . import test_match_algorithms
|
||||
371
fetchmail_attach_from_folder/tests/test_match_algorithms.py
Executable file
371
fetchmail_attach_from_folder/tests/test_match_algorithms.py
Executable file
@@ -0,0 +1,371 @@
|
||||
# Copyright - 2015-2018 Therp BV <https://acme.com>.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
from odoo.addons.mail.models.fetchmail import FetchmailServer
|
||||
|
||||
from ..match_algorithm import email_domain
|
||||
|
||||
TEST_EMAIL = "reynaert@dutchsagas.nl"
|
||||
TEST_SUBJECT = "Test subject"
|
||||
MSG_BODY = [
|
||||
(
|
||||
"1 (RFC822 {1149}",
|
||||
"Return-Path: <ronald@acme.com>\r\n"
|
||||
"Delivered-To: demo@yourcompany.example.com\r\n"
|
||||
"Received: from localhost (localhost [127.0.0.1])\r\n"
|
||||
"\tby vanaheim.acme.com (Postfix) with ESMTP id 14A3183163\r\n"
|
||||
"\tfor <demo@yourcompany.example.com>;"
|
||||
" Mon, 26 Mar 2018 16:03:52 +0200 (CEST)\r\n"
|
||||
"To: Test User <nonexistingemail@yourcompany.example.com>\r\n"
|
||||
f"From: Reynaert de Vos <{TEST_EMAIL}>\r\n"
|
||||
f"Subject: {TEST_SUBJECT}\r\n"
|
||||
"Message-ID: <485a8041-d560-a981-5afc-d31c1f136748@acme.com>\r\n"
|
||||
"Date: Mon, 26 Mar 2018 16:03:51 +0200\r\n"
|
||||
"User-Agent: Mock Test\r\n"
|
||||
"MIME-Version: 1.0\r\n"
|
||||
"Content-Type: text/plain; charset=utf-8\r\n"
|
||||
"Content-Language: en-US\r\n"
|
||||
"Content-Transfer-Encoding: 7bit\r\n\r\n"
|
||||
"Hallo Wereld!\r\n",
|
||||
)
|
||||
]
|
||||
MAIL_MESSAGE = {"subject": TEST_SUBJECT, "to": "demo@yourcompany.example.com"}
|
||||
|
||||
|
||||
class MockConnection:
|
||||
def select(self, path=None):
|
||||
return ("OK",)
|
||||
|
||||
def store(self, msgid, msg_item, value):
|
||||
"""Mock store command."""
|
||||
return "OK"
|
||||
|
||||
def fetch(self, msgid, parts):
|
||||
"""Return RFC822 formatted message."""
|
||||
return ("OK", MSG_BODY)
|
||||
|
||||
def search(self, charset, criteria):
|
||||
"""Return some msgid's."""
|
||||
return ("OK", ["123 456"])
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestMatchAlgorithms(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.partner_model = cls.env["res.partner"]
|
||||
cls.test_partner = cls.partner_model.with_context(tracking_disable=True).create(
|
||||
{
|
||||
"name": "Reynaert de Vos",
|
||||
"email": TEST_EMAIL,
|
||||
"is_company": False,
|
||||
"category_id": [
|
||||
(6, 0, []),
|
||||
],
|
||||
}
|
||||
)
|
||||
cls.server_model = cls.env["fetchmail.server"]
|
||||
cls.folder_model = cls.env["fetchmail.server.folder"]
|
||||
cls.server = cls.server_model.create(
|
||||
{
|
||||
"name": "Test Fetchmail Server",
|
||||
"server": "imap.example.com",
|
||||
"server_type": "imap",
|
||||
"active": True,
|
||||
"state": "done",
|
||||
}
|
||||
)
|
||||
cls.folder = cls.folder_model.create(
|
||||
{
|
||||
"server_id": cls.server.id,
|
||||
"sequence": 5,
|
||||
"path": "INBOX",
|
||||
"model_id": cls.env.ref("base.model_res_partner").id,
|
||||
"model_field": "email",
|
||||
"match_algorithm": "email_exact",
|
||||
# The intention is to link email to sender partner object.
|
||||
"mail_field": "from",
|
||||
}
|
||||
)
|
||||
cls.partner_ir_model = cls.env["ir.model"].search(
|
||||
[
|
||||
("model", "=", cls.partner_model._name),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
cls.partner_category = cls.env.ref("base.res_partner_category_12")
|
||||
cls.server_action = cls.env["ir.actions.server"].create(
|
||||
{
|
||||
"name": "Action Set Active Partner",
|
||||
"state": "object_write",
|
||||
"update_path": "category_id",
|
||||
"evaluation_type": "value",
|
||||
"value": str(cls.partner_category.id),
|
||||
"model_id": cls.partner_ir_model.id,
|
||||
}
|
||||
)
|
||||
|
||||
def test_email_exact(self):
|
||||
"""A message to ronald@acme.com should be linked to partner with that email."""
|
||||
MAIL_MESSAGE["from"] = TEST_EMAIL
|
||||
self._test_search_matches(email_domain.EmailDomain)
|
||||
self._test_apply_matching(email_domain.EmailDomain)
|
||||
|
||||
def test_email_domain(self):
|
||||
"""Test with email in same domain, but different mailbox."""
|
||||
ALTERNATE_EMAIL = TEST_EMAIL.replace("reynaert@", "mariken@")
|
||||
MAIL_MESSAGE["from"] = ALTERNATE_EMAIL
|
||||
self.folder.match_algorithm = "email_domain"
|
||||
self.folder.match_first = True
|
||||
self._test_search_matches(email_domain.EmailDomain)
|
||||
self._test_apply_matching(email_domain.EmailDomain)
|
||||
|
||||
def _test_search_matches(self, match_algorithm):
|
||||
matcher = match_algorithm()
|
||||
matches = matcher.search_matches(self.folder, MAIL_MESSAGE)
|
||||
# matches should be a record set with length 1.
|
||||
self.assertEqual(matches.email, self.test_partner.email)
|
||||
self.assertEqual(matches, self.test_partner)
|
||||
|
||||
def _test_apply_matching(self, match_algorithm):
|
||||
connection = MockConnection()
|
||||
thread_id = self.folder.apply_matching(connection, "1")
|
||||
self.assertEqual(thread_id, self.test_partner.id)
|
||||
self.assertEqual(self.test_partner.message_ids[-1].subject, TEST_SUBJECT)
|
||||
|
||||
def test_apply_matching_exact(self):
|
||||
folder = self.folder
|
||||
folder.match_algorithm = "email_exact"
|
||||
connection = MockConnection()
|
||||
msgid = "<485a8041-d560-a981-5afc-d31c1f136748@acme.com>"
|
||||
folder.apply_matching(connection, msgid)
|
||||
|
||||
def test_retrieve_imap_folder_domain(self):
|
||||
folder = self.folder
|
||||
folder.match_algorithm = "email_domain"
|
||||
connection = MockConnection()
|
||||
folder.retrieve_imap_folder(connection)
|
||||
|
||||
def test_non_action(self):
|
||||
connection = MockConnection()
|
||||
self.folder.action_id = False
|
||||
self.folder.apply_matching(connection, "1")
|
||||
self.assertFalse(self.test_partner.category_id)
|
||||
|
||||
def test_action(self):
|
||||
connection = MockConnection()
|
||||
self.folder.action_id = self.server_action
|
||||
self.folder.apply_matching(connection, "1")
|
||||
self.assertEqual(self.partner_category, self.test_partner.category_id)
|
||||
|
||||
def test_button_confirm_folder(self):
|
||||
"""Test the button_confirm_folder method."""
|
||||
folder = self.folder
|
||||
with patch.object(
|
||||
self.server.__class__, "connect", return_value=MockConnection()
|
||||
):
|
||||
folder.active = False
|
||||
folder.button_confirm_folder()
|
||||
self.assertEqual(folder.state, "draft")
|
||||
|
||||
folder.active = True
|
||||
folder.button_confirm_folder()
|
||||
self.assertEqual(folder.state, "done")
|
||||
|
||||
def test_set_draft(self):
|
||||
"""Test the set_draft method."""
|
||||
folder = self.folder
|
||||
res = folder.set_draft()
|
||||
self.assertEqual(res, True)
|
||||
self.assertEqual(folder.state, "draft")
|
||||
|
||||
|
||||
class TestAttachMailManually(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.Wizard = cls.env["fetchmail.attach.mail.manually"]
|
||||
cls.Folder = cls.env["fetchmail.server.folder"]
|
||||
cls.Partner = cls.env["res.partner"]
|
||||
|
||||
cls.server = cls.env["fetchmail.server"].create(
|
||||
{
|
||||
"name": "Test IMAP",
|
||||
"server": "imap.example.com",
|
||||
"server_type": "imap",
|
||||
"user": "test@example.com",
|
||||
"password": "secret",
|
||||
"state": "done",
|
||||
}
|
||||
)
|
||||
|
||||
cls.folder = cls.Folder.create(
|
||||
{
|
||||
"server_id": cls.server.id,
|
||||
"sequence": 5,
|
||||
"path": "INBOX",
|
||||
"model_id": cls.env.ref("base.model_res_partner").id,
|
||||
"model_field": "email",
|
||||
"match_algorithm": "email_exact",
|
||||
# The intention is to link email to sender partner object.
|
||||
"mail_field": "from",
|
||||
}
|
||||
)
|
||||
|
||||
cls.partner = cls.Partner.create(
|
||||
{"name": "Test Partner", "email": "test@example.com"}
|
||||
)
|
||||
|
||||
def _mock_connection(self):
|
||||
mock_conn = MagicMock()
|
||||
mock_conn.select.return_value = ("OK",)
|
||||
mock_conn.search.return_value = ("OK", [b"1"])
|
||||
mock_conn.fetch.return_value = (
|
||||
"OK",
|
||||
[(b"1 (RFC822 {123}", b"Mocked raw email content")],
|
||||
)
|
||||
mock_conn.list.return_value = (
|
||||
"OK",
|
||||
[b'(\\HasNoChildren) "." "INBOX"', b'(\\HasChildren) "." "Sent"'],
|
||||
)
|
||||
return mock_conn
|
||||
|
||||
def _mock_fetch_msg(self, connection, msgid):
|
||||
"""Return a tuple like the real fetch_msg: (dict, bytes)"""
|
||||
mail_message = {
|
||||
"subject": "Test",
|
||||
"date": "2025-07-23 12:00:00",
|
||||
"from": "test@example.com",
|
||||
"body": "<p>Body</p>",
|
||||
}
|
||||
raw_message = b"Raw MIME message here"
|
||||
return mail_message, raw_message
|
||||
|
||||
@patch.object(FetchmailServer, "connect")
|
||||
def test_default_get_populates_mail_ids(self, mock_connect):
|
||||
"""Test that default_get loads emails into wizard."""
|
||||
mock_conn = self._mock_connection()
|
||||
mock_connect.return_value = mock_conn
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
self.folder.__class__, "fetch_msg", side_effect=self._mock_fetch_msg
|
||||
),
|
||||
patch.object(self.folder.__class__, "get_msgids", return_value=[b"1"]),
|
||||
patch.object(self.folder.__class__, "get_criteria", return_value="ALL"),
|
||||
):
|
||||
wizard = self.Wizard.with_context(folder_id=self.folder.id).create({})
|
||||
self.assertEqual(len(wizard.mail_ids), 1)
|
||||
self.assertEqual(wizard.mail_ids[0].subject, "Test")
|
||||
|
||||
@patch.object(FetchmailServer, "connect")
|
||||
def test_attach_mails_only_with_object_id(self, mock_connect):
|
||||
"""Only mails with object_id should be attached."""
|
||||
mock_conn = self._mock_connection()
|
||||
mock_connect.return_value = mock_conn
|
||||
with patch.object(
|
||||
self.folder.__class__,
|
||||
"fetch_msg",
|
||||
side_effect=lambda conn, msgid: (
|
||||
{
|
||||
"subject": "With Object",
|
||||
"date": "2025-07-23",
|
||||
"from": "test@example.com",
|
||||
"body": "<p>Body</p>",
|
||||
},
|
||||
b"raw_message",
|
||||
),
|
||||
):
|
||||
wizard = self.Wizard.create(
|
||||
{
|
||||
"folder_id": self.folder.id,
|
||||
"mail_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"msgid": "1",
|
||||
"subject": "No Object",
|
||||
"object_id": False,
|
||||
},
|
||||
),
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"msgid": "2",
|
||||
"subject": "With Object",
|
||||
"object_id": f"res.partner,{self.partner.id}",
|
||||
},
|
||||
),
|
||||
],
|
||||
}
|
||||
)
|
||||
with patch.object(self.folder.__class__, "attach_mail") as mock_attach:
|
||||
with patch.object(self.folder.__class__, "update_msg"):
|
||||
wizard.attach_mails()
|
||||
mock_attach.assert_called_once()
|
||||
args, _ = mock_attach.call_args
|
||||
self.assertEqual(args[0], self.partner)
|
||||
|
||||
def test_prepare_mail_returns_expected_dict(self):
|
||||
"""Test _prepare_mail returns correct structure."""
|
||||
folder = self.folder
|
||||
msgid = "123"
|
||||
mail_message = {
|
||||
"subject": "Test",
|
||||
"date": "2025-07-23",
|
||||
"from": "test@example.com",
|
||||
"body": "<p>Body</p>",
|
||||
}
|
||||
|
||||
result = self.Wizard._prepare_mail(folder, msgid, mail_message)
|
||||
expected = {
|
||||
"msgid": "123",
|
||||
"subject": "Test",
|
||||
"date": "2025-07-23",
|
||||
"body": "<p>Body</p>",
|
||||
"email_from": "test@example.com",
|
||||
"object_id": "res.partner,-1",
|
||||
}
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_wizard_name_is_translated(self):
|
||||
"""Test that default name is translated."""
|
||||
with (
|
||||
patch.object(FetchmailServer, "connect", return_value=MagicMock()),
|
||||
patch.object(self.folder.__class__, "fetch_msg", return_value=({}, b"raw")),
|
||||
patch.object(self.folder.__class__, "get_msgids", return_value=[b"1"]),
|
||||
patch.object(self.folder.__class__, "get_criteria", return_value="ALL"),
|
||||
):
|
||||
wizard = self.Wizard.with_context(folder_id=self.folder.id).create({})
|
||||
self.assertEqual(wizard.name, "Attach emails manually")
|
||||
|
||||
def test_compute_folders_available_success(self):
|
||||
"""Debe devolver las carpetas disponibles."""
|
||||
with patch.object(
|
||||
self.server.__class__, "connect", return_value=self._mock_connection()
|
||||
):
|
||||
result = self.server.folders_available
|
||||
self.assertEqual(result, "INBOX\nSent")
|
||||
|
||||
def test_compute_folders_available_not_done(self):
|
||||
"""Si el servidor no está confirmado, debe advertir."""
|
||||
self.server.state = "draft"
|
||||
result = self.server.folders_available
|
||||
self.assertEqual(result, "Confirm connection first.")
|
||||
|
||||
def test_compute_folders_available_list_error(self):
|
||||
"""Si list() falla, debe mostrar mensaje de error."""
|
||||
mock_conn = MagicMock()
|
||||
mock_conn.list.return_value = ("NO", [])
|
||||
with patch.object(self.server.__class__, "connect", return_value=mock_conn):
|
||||
result = self.server.folders_available
|
||||
self.assertEqual(result, "Unable to retrieve folders.")
|
||||
Reference in New Issue
Block a user