diff --git a/addons/account/tests/test_account_move_send.py b/addons/account/tests/test_account_move_send.py
index 32d489b4140..cafe09c5fe6 100644
--- a/addons/account/tests/test_account_move_send.py
+++ b/addons/account/tests/test_account_move_send.py
@@ -29,7 +29,7 @@ class TestAccountComposerPerformance(AccountTestInvoicingCommon, MailCommon):
'body_html': '
TemplateBody for
',
'description': 'Sent to customers with their invoices in attachment',
'email_from': "{{ (object.invoice_user_id.email_formatted or user.email_formatted) }}",
- 'mail_server_id': cls.mail_server_global.id,
+ 'mail_server_id': cls.mail_server_default.id,
'model_id': cls.env['ir.model']._get_id('account.move'),
'name': "Invoice: Test Sending",
'partner_to': "{{ object.partner_id.id }}",
@@ -124,9 +124,6 @@ class TestAccountComposerPerformance(AccountTestInvoicingCommon, MailCommon):
def setUp(self):
super().setUp()
- # setup mail gateway to simulate complete reply-to computation
- self._init_mail_gateway()
-
# patch registry to simulate a ready environment
self.patch(self.env.registry, 'ready', True)
self.flush_tracking()
@@ -200,7 +197,7 @@ class TestAccountComposerPerformance(AccountTestInvoicingCommon, MailCommon):
'auto_delete': True,
'email_from': self.user_account_other.email_formatted,
'is_notification': True, # should keep logs by default
- 'mail_server_id': self.mail_server_global,
+ 'mail_server_id': self.mail_server_default,
'subject': _exp_subject,
'reply_to': formataddr((
f'{move.company_id.name} {_exp_move_name}',
@@ -286,7 +283,7 @@ class TestAccountComposerPerformance(AccountTestInvoicingCommon, MailCommon):
'auto_delete': True,
'email_from': self.user_account_other.email_formatted,
'is_notification': True, # should keep logs by default
- 'mail_server_id': self.mail_server_global,
+ 'mail_server_id': self.mail_server_default,
'subject': f'{self.env.user.company_id.name} Invoice (Ref {test_move.name})',
'reply_to': formataddr((
f'{test_move.company_id.name} {test_move.display_name}',
@@ -373,7 +370,7 @@ class TestAccountComposerPerformance(AccountTestInvoicingCommon, MailCommon):
'auto_delete': True,
'email_from': self.user_account_other.email_formatted,
'is_notification': True, # should keep logs by default
- 'mail_server_id': self.mail_server_global,
+ 'mail_server_id': self.mail_server_default,
'subject': f'SpanishSubject for {test_move.name}', # translated version
'reply_to': formataddr((
f'{test_move.company_id.name} {test_move.display_name}',
diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py
index 48f01f3cf88..2be12eb3a25 100644
--- a/addons/mail/tests/common.py
+++ b/addons/mail/tests/common.py
@@ -52,10 +52,15 @@ class MockEmail(common.BaseCase, MockSmtplibCase):
self.mail_unlink_sent = mail_unlink_sent
self._init_mail_mock()
- def _ir_mail_server_build_email(model, *args, **kwargs):
- self._mails.append(kwargs)
- self._mails_args.append(args)
- return build_email_origin(model, *args, **kwargs)
+ def _ir_mail_server_build_email(model, email_from, email_to, subject, body, **kwargs):
+ self._mails.append({
+ 'email_from': email_from,
+ 'email_to': email_to,
+ 'subject': subject,
+ 'body': body,
+ **kwargs,
+ })
+ return build_email_origin(model, email_from, email_to, subject, body, **kwargs)
def _mail_mail_create(model, *args, **kwargs):
res = mail_create_origin(model, *args, **kwargs)
@@ -79,40 +84,25 @@ class MockEmail(common.BaseCase, MockSmtplibCase):
def _init_mail_mock(self):
self._mails = []
- self._mails_args = []
self._new_mails = self.env['mail.mail'].sudo()
@classmethod
def _init_mail_gateway(cls):
+ super()._init_mail_gateway()
cls.alias_domain = 'test.mycompany.com'
cls.alias_catchall = 'catchall.test'
cls.alias_bounce = 'bounce.test'
- cls.default_from = 'notifications'
+ cls.default_from = 'notifications.test'
+ cls.default_from_filter = False
cls.env['ir.config_parameter'].set_param('mail.bounce.alias', cls.alias_bounce)
cls.env['ir.config_parameter'].set_param('mail.catchall.domain', cls.alias_domain)
cls.env['ir.config_parameter'].set_param('mail.catchall.alias', cls.alias_catchall)
cls.env['ir.config_parameter'].set_param('mail.default.from', cls.default_from)
+ cls.env['ir.config_parameter'].sudo().set_param('mail.default.from_filter', cls.default_from_filter)
# mailer daemon email preformatting
cls.mailer_daemon_email = formataddr(('MAILER-DAEMON', '%s@%s' % (cls.alias_bounce, cls.alias_domain)))
- @classmethod
- def _init_outgoing_gateway(cls):
- cls.env['ir.mail_server'].search([]).unlink()
- cls.mail_server_domain, cls.mail_server_global = cls.env['ir.mail_server'].create([
- {'from_filter': cls.alias_domain,
- 'name': 'Domain Based Server',
- 'smtp_encryption': 'none',
- 'smtp_host': 'smtp_host',
- },
- {'from_filter': False,
- 'name': 'No FromFilter Server',
- 'smtp_encryption': 'none',
- 'smtp_host': 'smtp_host',
- }
- ])
- cls.mail_servers = cls.mail_server_domain + cls.mail_server_global
-
# ------------------------------------------------------------
# GATEWAY TOOLS
# ------------------------------------------------------------
@@ -1106,7 +1096,7 @@ class MailCommon(common.TransactionCase, MailCase):
# give default values for all email aliases and domain
cls._init_mail_gateway()
- cls._init_outgoing_gateway()
+ cls._init_mail_servers()
# by default avoid rendering restriction complexity
cls.env['ir.config_parameter'].set_param('mail.restrict.template.rendering', False)
diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py
index e2a3917cc94..ee654188e71 100644
--- a/addons/test_mail/tests/test_mail_composer.py
+++ b/addons/test_mail/tests/test_mail_composer.py
@@ -768,14 +768,14 @@ class TestComposerInternals(TestMailComposer):
# 1. check without template + template update
composer = self.env['mail.compose.message'].with_context(ctx).create({
'body': 'Test Body />
')
- self.assertEqual(composer.mail_server_id, self.mail_server_global)
+ self.assertEqual(composer.mail_server_id, self.mail_server_default)
if composition_mode == 'comment' and not batch:
self.assertEqual(composer.record_name, self.test_record.name)
else:
@@ -804,13 +804,13 @@ class TestComposerInternals(TestMailComposer):
# manual values is kept over template
composer.write({
'body': 'Back to my amazing body />
')
- self.assertEqual(composer.mail_server_id, self.mail_server_global)
+ self.assertEqual(composer.mail_server_id, self.mail_server_default)
self.assertEqual(composer.record_name, 'Manual update')
self.assertEqual(composer.scheduled_date, '{{ datetime.datetime(2023, 1, 10, 10, 0, 0) }}')
self.assertEqual(composer.subject, 'Back to my amazing subject for {{ record.name }}')
@@ -821,13 +821,13 @@ class TestComposerInternals(TestMailComposer):
if composition_mode == 'comment' and not batch:
self.assertEqual(composer.body, 'Back to my amazing body />
')
- self.assertEqual(composer.mail_server_id, self.mail_server_global)
+ self.assertEqual(composer.mail_server_id, self.mail_server_default)
self.assertEqual(composer.record_name, 'Manual update')
self.assertEqual(composer.scheduled_date, '{{ datetime.datetime(2023, 1, 10, 10, 0, 0) }}')
self.assertEqual(composer.subject, 'Back to my amazing subject for {{ record.name }}')
diff --git a/addons/test_mail/tests/test_mail_mail.py b/addons/test_mail/tests/test_mail_mail.py
index f9740306bd2..a7f729f0c15 100644
--- a/addons/test_mail/tests/test_mail_mail.py
+++ b/addons/test_mail/tests/test_mail_mail.py
@@ -26,7 +26,6 @@ class TestMailMail(MailCommon):
@classmethod
def setUpClass(cls):
super(TestMailMail, cls).setUpClass()
- cls._init_mail_servers()
cls.test_record = cls.env['mail.test.gateway'].with_context(cls._test_context).create({
'name': 'Test',
@@ -359,9 +358,6 @@ class TestMailMail(MailCommon):
managed and stored at mail and notification level. """
mail, notification = self.test_mail, self.test_notification
- self.env['ir.config_parameter'].set_param('mail.catchall.domain', self.alias_domain)
- self.env['ir.config_parameter'].set_param('mail.default.from', self.default_from)
-
# MailServer.send_email(): _prepare_email_message: missing To
for email_to in self.emails_falsy:
self._reset_data()
@@ -660,9 +656,8 @@ class TestMailMailServer(MailCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
- cls._init_mail_servers()
- cls.server_domain_2 = cls.env['ir.mail_server'].create({
+ cls.mail_server_domain_2 = cls.env['ir.mail_server'].create({
'from_filter': 'test_2.com',
'name': 'Server 2',
'smtp_host': 'test_2.com',
@@ -676,7 +671,7 @@ class TestMailMailServer(MailCommon):
"""
self.assertEqual(
self.env['ir.mail_server']._get_default_from_address(),
- f'notifications@{self.alias_domain}'
+ f'{self.default_from}@{self.alias_domain}'
)
mail_values = {
@@ -696,19 +691,19 @@ class TestMailMailServer(MailCommon):
# Should use the test_2 mail server
# Once with "user_1@test_2.com" as login
# Once with "user_2@test_2.com" as login
- mails |= self.env['mail.mail'].create([{
+ mails += self.env['mail.mail'].create([{
**mail_values,
'email_from': 'user_1@test_2.com',
- } for _ in range(5)]) | self.env['mail.mail'].create([{
+ } for _ in range(5)]) + self.env['mail.mail'].create([{
**mail_values,
'email_from': 'user_2@test_2.com',
} for _ in range(5)])
# Mail server is forced
- mails |= self.env['mail.mail'].create([{
+ mails += self.env['mail.mail'].create([{
**mail_values,
'email_from': 'user_1@test_2.com',
- 'mail_server_id': self.server_domain.id,
+ 'mail_server_id': self.mail_server_domain.id,
} for _ in range(5)])
with self.mock_smtplib_connection():
@@ -722,21 +717,21 @@ class TestMailMailServer(MailCommon):
self.assertEqual(self.connect_mocked.call_count, 4, 'Must be called once per batch which share the same mail server and the same smtp from')
self.connect_mocked.assert_has_calls(
calls=[
- call(smtp_from=f'notifications@{self.alias_domain}', mail_server_id=self.server_notification.id),
- call(smtp_from='user_1@test_2.com', mail_server_id=self.server_domain_2.id),
- call(smtp_from='user_2@test_2.com', mail_server_id=self.server_domain_2.id),
- call(smtp_from='user_1@test_2.com', mail_server_id=self.server_domain.id),
+ call(smtp_from=f'{self.default_from}@{self.alias_domain}', mail_server_id=self.mail_server_notification.id),
+ call(smtp_from='user_1@test_2.com', mail_server_id=self.mail_server_domain_2.id),
+ call(smtp_from='user_2@test_2.com', mail_server_id=self.mail_server_domain_2.id),
+ call(smtp_from='user_1@test_2.com', mail_server_id=self.mail_server_domain.id),
],
any_order=True,
)
- self.assertSMTPEmailsSent(message_from=f'"test" ',
- emails_count=5, from_filter=self.server_notification.from_filter)
- self.assertSMTPEmailsSent(message_from=f'"test_2" ',
- emails_count=5, from_filter=self.server_notification.from_filter)
- self.assertSMTPEmailsSent(message_from='user_1@test_2.com', emails_count=5, from_filter=self.server_domain_2.from_filter)
- self.assertSMTPEmailsSent(message_from='user_2@test_2.com', emails_count=5, from_filter=self.server_domain_2.from_filter)
- self.assertSMTPEmailsSent(message_from='user_1@test_2.com', emails_count=5, from_filter=self.server_domain.from_filter)
+ self.assertSMTPEmailsSent(message_from=f'"test" <{self.default_from}@{self.alias_domain}>',
+ emails_count=5, from_filter=self.mail_server_notification.from_filter)
+ self.assertSMTPEmailsSent(message_from=f'"test_2" <{self.default_from}@{self.alias_domain}>',
+ emails_count=5, from_filter=self.mail_server_notification.from_filter)
+ self.assertSMTPEmailsSent(message_from='user_1@test_2.com', emails_count=5, mail_server=self.mail_server_domain_2)
+ self.assertSMTPEmailsSent(message_from='user_2@test_2.com', emails_count=5, mail_server=self.mail_server_domain_2)
+ self.assertSMTPEmailsSent(message_from='user_1@test_2.com', emails_count=5, mail_server=self.mail_server_domain)
@tagged('mail_mail')
diff --git a/addons/test_mass_mailing/tests/test_mailing_server.py b/addons/test_mass_mailing/tests/test_mailing_server.py
index b533d26db9e..fda438bf015 100644
--- a/addons/test_mass_mailing/tests/test_mailing_server.py
+++ b/addons/test_mass_mailing/tests/test_mailing_server.py
@@ -14,7 +14,6 @@ class TestMassMailingServer(TestMassMailCommon):
@classmethod
def setUpClass(cls):
super(TestMassMailingServer, cls).setUpClass()
- cls._init_mail_servers()
cls.recipients = cls._create_mailing_test_records(model='mailing.test.optout', count=8)
def test_mass_mailing_server_archived_usage_protection(self):
@@ -98,23 +97,23 @@ class TestMassMailingServer(TestMassMailCommon):
'body_html': 'Body for ',
'email_from': 'unknow_email@unknow_domain.com',
'mailing_model_id': self.env['ir.model']._get_id('mailing.test.optout'),
- 'mail_server_id': self.server_notification.id,
+ 'mail_server_id': self.mail_server_notification.id,
}])
with self.mock_smtplib_connection():
mailings.action_send_mail()
self.assertEqual(self.find_mail_server_mocked.call_count, 3, 'Must be called only once per mail from except when forced')
- for (expected_smtp_from, expected_msg_from, expected_from_filter) in [
- ('specific_user@test.mycompany.com', 'specific_user@test.mycompany.com', self.server_user.from_filter),
- (f'{self.alias_bounce}@{self.alias_domain}', 'unknown_name@test.mycompany.com', self.server_domain.from_filter),
+ for (expected_smtp_from, expected_msg_from, expected_mail_server) in [
+ ('specific_user@test.mycompany.com', 'specific_user@test.mycompany.com', self.mail_server_user),
+ (f'{self.alias_bounce}@{self.alias_domain}', 'unknown_name@test.mycompany.com', self.mail_server_domain),
# We do not have a mail server for this address email, so fall back to the "notifications@domain" email.
- ('notifications@test.mycompany.com', '"Testing" ', self.server_notification.from_filter),
+ (f'{self.default_from}@{self.alias_domain}', f'"Testing" <{self.default_from}@{self.alias_domain}>', self.mail_server_notification),
# forced sever
- ('unknow_email@unknow_domain.com', 'unknow_email@unknow_domain.com', self.server_notification.from_filter),
+ ('unknow_email@unknow_domain.com', 'unknow_email@unknow_domain.com', self.mail_server_notification),
]:
self.assertSMTPEmailsSent(
smtp_from=expected_smtp_from,
message_from=expected_msg_from,
- from_filter=expected_from_filter,
+ mail_server=expected_mail_server,
emails_count=8,
)
diff --git a/odoo/addons/base/tests/common.py b/odoo/addons/base/tests/common.py
index b2411e0eda4..ffe3649861f 100644
--- a/odoo/addons/base/tests/common.py
+++ b/odoo/addons/base/tests/common.py
@@ -341,11 +341,11 @@ class MockSmtplibCase:
def _build_email(self, mail_from, return_path=None, **kwargs):
return self.env['ir.mail_server'].build_email(
- body=kwargs.pop('body', 'body'),
- email_from=mail_from,
- email_to='dest@example-é.com',
+ mail_from,
+ kwargs.pop('email_to', 'dest@example-é.com'),
+ kwargs.pop('subject', 'subject'),
+ kwargs.pop('body', 'body'),
headers={'Return-Path': return_path} if return_path else None,
- subject='subject',
**kwargs,
)
@@ -354,19 +354,26 @@ class MockSmtplibCase:
self.env['ir.mail_server'].send_email(msg, smtp_session=smtp_session)
return smtp_session.messages.pop()
- def assertSMTPEmailsSent(self, smtp_from=None, smtp_to_list=None, message_from=None, from_filter=None, emails_count=1):
- """Check that the given email has been sent.
-
- If one of the parameter is None, it's just ignored and not used to retrieve the email.
+ def assertSMTPEmailsSent(self, smtp_from=None, smtp_to_list=None, message_from=None,
+ mail_server=None, from_filter=None,
+ emails_count=1):
+ """Check that the given email has been sent. If one of the parameter is
+ None it is just ignored and not used to retrieve the email.
:param smtp_from: FROM used for the authentication to the mail server
:param smtp_to_list: List of destination email address
:param message_from: FROM used in the SMTP headers
- :param from_filter: from_filter of the used to send the email
- Can use a lambda to check the value
+ :arap mail_server: used to compare the 'from_filter' as an alternative
+ to using the from_filter parameter
+ :param from_filter: from_filter of the used to send the
+ email. False means 'match everything';'
:param emails_count: the number of emails which should match the condition
:return: True if at least one email has been found with those parameters
"""
+ if from_filter is not None and mail_server:
+ raise ValueError('Invalid usage: use either from_filter either mail_server')
+ if from_filter is None and mail_server is not None:
+ from_filter = mail_server.from_filter
matching_emails = filter(
lambda email:
(smtp_from is None or smtp_from == email['smtp_from'])
@@ -391,10 +398,10 @@ class MockSmtplibCase:
)
@classmethod
- def _init_mail_config(cls):
+ def _init_mail_gateway(cls):
cls.alias_bounce = 'bounce.test'
cls.alias_domain = 'test.mycompany.com'
- cls.default_from = 'notifications'
+ cls.default_from = 'notifications.test'
cls.default_from_filter = False
cls.env['ir.config_parameter'].sudo().set_param('mail.catchall.domain', cls.alias_domain)
cls.env['ir.config_parameter'].sudo().set_param('mail.default.from', cls.default_from)
@@ -409,12 +416,7 @@ class MockSmtplibCase:
'smtp_host': 'smtp_host',
'smtp_encryption': 'none',
}
- (
- cls.server_domain,
- cls.server_user,
- cls.server_notification,
- cls.server_default,
- ) = cls.env['ir.mail_server'].create([
+ cls.mail_servers = cls.env['ir.mail_server'].create([
{
'name': 'Domain based server',
'from_filter': 'test.mycompany.com',
@@ -425,7 +427,7 @@ class MockSmtplibCase:
** ir_mail_server_values,
}, {
'name': 'Server Notifications',
- 'from_filter': 'notifications@test.mycompany.com',
+ 'from_filter': 'notifications.test@test.mycompany.com',
** ir_mail_server_values,
}, {
'name': 'Server No From Filter',
@@ -433,3 +435,7 @@ class MockSmtplibCase:
** ir_mail_server_values,
},
])
+ (
+ cls.mail_server_domain, cls.mail_server_user,
+ cls.mail_server_notification, cls.mail_server_default
+ ) = cls.mail_servers
diff --git a/odoo/addons/base/tests/test_ir_mail_server.py b/odoo/addons/base/tests/test_ir_mail_server.py
index 9d97f6e640b..ad822e4ae1b 100644
--- a/odoo/addons/base/tests/test_ir_mail_server.py
+++ b/odoo/addons/base/tests/test_ir_mail_server.py
@@ -3,14 +3,13 @@
import email.message
import email.policy
-import itertools
from unittest.mock import patch
from odoo import tools
from odoo.addons.base.tests import test_mail_examples
from odoo.addons.base.tests.common import MockSmtplibCase
-from odoo.tests import tagged
+from odoo.tests import tagged, users
from odoo.tests.common import TransactionCase
from odoo.tools import mute_logger
from odoo.tools import config
@@ -43,10 +42,14 @@ class EmailConfigCase(TransactionCase):
for (icp_from, icp_domain), expected_from in zip(
[
('icp', 'test.mycompany.com'),
+ ('icp', False),
('icp@another.company.com', 'test.mycompany.com'),
+ ('icp@another.company.com', False),
(False, 'test.mycompany.com'),
], [
"icp@test.mycompany.com",
+ "settings@example.com",
+ "icp@another.company.com",
"icp@another.company.com",
"settings@example.com",
],
@@ -67,7 +70,7 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
- cls._init_mail_config()
+ cls._init_mail_gateway()
cls._init_mail_servers()
cls.default_bounce_address = f'{cls.alias_bounce}@{cls.alias_domain}'
@@ -124,35 +127,7 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
"There should be 3 headers MIME-Version: one on the enveloppe, "
"one on the html part, one on the text part")
- def test_mail_server_match_from_filter(self):
- """ Test the from_filter field on the "ir.mail_server". """
- # Should match
- tests = [
- ('admin@mail.example.com', 'mail.example.com'),
- ('admin@mail.example.com', 'mail.EXAMPLE.com'),
- ('admin@mail.example.com', 'admin@mail.example.com'),
- ('admin@mail.example.com', False),
- ('"fake@test.mycompany.com" ', 'mail.example.com'),
- ('"fake@test.mycompany.com" ', 'mail.example.com'),
- ('"fake@test.mycompany.com" ', 'test.mycompany.com, mail.example.com, test2.com'),
- ]
- for email, from_filter in tests:
- self.assertTrue(self.env['ir.mail_server']._match_from_filter(email, from_filter))
-
- # Should not match
- tests = [
- ('admin@mail.example.com', 'test@mail.example.com'),
- ('admin@mail.example.com', 'test.mycompany.com'),
- ('admin@mail.example.com', 'mail.éxample.com'),
- ('admin@mmail.example.com', 'mail.example.com'),
- ('admin@mail.example.com', 'mmail.example.com'),
- ('"admin@mail.example.com" ', 'mail.example.com'),
- ('"fake@test.mycompany.com" ', 'test.mycompany.com, wrong.mail.example.com, test3.com'),
- ]
- for email, from_filter in tests:
- self.assertFalse(self.env['ir.mail_server']._match_from_filter(email, from_filter))
-
- def test_mail_body(self):
+ def test_content_mail_body(self):
bodies = [
'content',
'content
',
@@ -187,6 +162,34 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
body_alternative = body_alternative.strip('\n')
self.assertEqual(body_alternative, expected)
+ def test_mail_server_match_from_filter(self):
+ """ Test the from_filter field on the "ir.mail_server". """
+ # Should match
+ tests = [
+ ('admin@mail.example.com', 'mail.example.com'),
+ ('admin@mail.example.com', 'mail.EXAMPLE.com'),
+ ('admin@mail.example.com', 'admin@mail.example.com'),
+ ('admin@mail.example.com', False),
+ ('"fake@test.mycompany.com" ', 'mail.example.com'),
+ ('"fake@test.mycompany.com" ', 'mail.example.com'),
+ ('"fake@test.mycompany.com" ', 'test.mycompany.com, mail.example.com, test2.com'),
+ ]
+ for email, from_filter in tests:
+ self.assertTrue(self.env['ir.mail_server']._match_from_filter(email, from_filter))
+
+ # Should not match
+ tests = [
+ ('admin@mail.example.com', 'test@mail.example.com'),
+ ('admin@mail.example.com', 'test.mycompany.com'),
+ ('admin@mail.example.com', 'mail.éxample.com'),
+ ('admin@mmail.example.com', 'mail.example.com'),
+ ('admin@mail.example.com', 'mmail.example.com'),
+ ('"admin@mail.example.com" ', 'mail.example.com'),
+ ('"fake@test.mycompany.com" ', 'test.mycompany.com, wrong.mail.example.com, test3.com'),
+ ]
+ for email, from_filter in tests:
+ self.assertFalse(self.env['ir.mail_server']._match_from_filter(email, from_filter))
+
@mute_logger('odoo.models.unlink')
def test_mail_server_priorities(self):
""" Test if we choose the right mail server to send an email.
@@ -207,41 +210,40 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
self.assertTrue(self.env['ir.mail_server']._get_default_from_address(), 'Notifications email must be set for testing')
self.assertTrue(self.env['ir.mail_server']._get_default_bounce_address(), 'Bounce email must be set for testing')
# this mail server can not be used for a specific email address and 2 domain names
- self.server_user.from_filter = "domain1.com, specific_user@test.mycompany.com, domain2.com"
+ self.mail_server_user.from_filter = "domain1.com, specific_user@test.mycompany.com, domain2.com"
for email_from, (expected_mail_server, expected_email_from) in zip(
[
+ # matches user-specific server
'specific_user@test.mycompany.com',
+ # matches user-specific server (with formatting') -> should extract
+ # email from full name, must keep the given email_from
'"Name name@strange.name" ',
- 'specific_user@test.mycompany.com',
+ # case check
+ 'SPECIFIC_USER@test.mycompany.com',
+ 'specific_user@test.MYCOMPANY.com',
+ # matches domain-based server: domain is case insensitive
'unknown_email@test.mycompany.com',
'unknown_email@TEST.MYCOMPANY.COM',
+ '"Unknown" ',
+ # fallback on notification email
'"Test" ',
- # server_user multiple from filter check
- '"Test" ',
+ # mail_server_user multiple from_filter check: can be used for a
+ # specific email and 2 domain names -> check other domains in filter
'"Example" ',
'"Example" ',
- '"Test" ',
], [
- (self.server_user, 'specific_user@test.mycompany.com'),
- # must extract email from full name, must keep the given email from
- (self.server_user, '"Name name@strange.name" '),
- # should not be case sensitive
- (self.server_user, 'specific_user@test.mycompany.com'),
- (self.server_domain, 'unknown_email@test.mycompany.com'),
- # cover a different condition that the "email case insensitive" test: domain is case insensitive
- (self.server_domain, 'unknown_email@TEST.MYCOMPANY.COM'),
- # should take the notification email
- (self.server_notification, 'notifications@test.mycompany.com'),
- # server_user multipl from_filter check: can be used for a sepcific emial and 2 domain names
- # 1. entire email matches, server should be used
- (self.server_user, '"Test" '),
- # 2. domain matches, server should be used
- (self.server_user, '"Example" '),
- # 3. other domain matches, server shold be used
- (self.server_user, '"Example" '),
- # check that we take the domain server and not the "user specific" server
- (self.server_domain, '"Test" '),
+ (self.mail_server_user, 'specific_user@test.mycompany.com'),
+ (self.mail_server_user, '"Name name@strange.name" '),
+ (self.mail_server_user, 'SPECIFIC_USER@test.mycompany.com'),
+ (self.mail_server_user, 'specific_user@test.MYCOMPANY.com'),
+ (self.mail_server_domain, 'unknown_email@test.mycompany.com'),
+ (self.mail_server_domain, 'unknown_email@TEST.MYCOMPANY.COM'),
+ (self.mail_server_domain, '"Unknown" '),
+ (self.mail_server_notification, 'notifications.test@test.mycompany.com'),
+ # mail_server_user multiple from_filter check
+ (self.mail_server_user, '"Example" '),
+ (self.mail_server_user, '"Example" '),
],
):
with self.subTest(email_from=email_from):
@@ -256,70 +258,76 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
the FROM otherwise we cannot send any email. """
self.env['ir.config_parameter'].sudo().set_param('mail.catchall.domain', False)
self.env['ir.config_parameter'].sudo().set_param('mail.default.from', False)
- for email_from, (expected_mail_server, expected_from_filter, expected_email_from) in zip(
+
+ for email_from, (expected_mail_server, expected_email_from) in zip(
[
- 'test@unknown_domain.com',
+ 'specific_user@test.mycompany.com',
+ 'unknown_email@test.mycompany.com',
+ # no notification set, must be forced to spoof the FROM
+ '"Test" ',
], [
- (self.server_default, False, 'test@unknown_domain.com'),
+ (self.mail_server_user, 'specific_user@test.mycompany.com'),
+ (self.mail_server_domain, 'unknown_email@test.mycompany.com'),
+ (self.mail_server_default, '"Test" '),
],
):
with self.subTest(email_from=email_from):
- mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='test@unknown_domain.com')
- self.assertEqual(mail_server.from_filter, expected_from_filter, 'No notifications email set, must be forced to spoof the FROM')
+ mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from=email_from)
self.assertEqual(mail_server, expected_mail_server)
self.assertEqual(mail_from, expected_email_from)
@mute_logger('odoo.models.unlink')
def test_mail_server_send_email(self):
+ """ Test main 'send_email' usage: check mail_server choice based on from
+ filters, encapsulation, spoofing. """
IrMailServer = self.env['ir.mail_server']
- for (mail_from, (expected_smtp_from, expected_msg_from, expected_from_filter)), provide_smtp in itertools.product(
- zip(
- [
- 'specific_user@test.mycompany.com',
- '"Name" ',
- 'test@unknown_domain.com',
- 'unknown_name@test.mycompany.com'
- ], [
- # A mail server is configured for the email
- ('specific_user@test.mycompany.com', 'specific_user@test.mycompany.com', 'specific_user@test.mycompany.com'),
- # No mail server are configured for the email address, so it will use the
- # notifications email instead and encapsulate the old email
- ('notifications@test.mycompany.com', '"Name" ', 'notifications@test.mycompany.com'),
- # same situation, but the original email has no name part
- ('notifications@test.mycompany.com', '"test" ', 'notifications@test.mycompany.com'),
- # A mail server is configured for the entire domain name, so we can use the bounce
- # email address because the mail server supports it
- (self.default_bounce_address, 'unknown_name@test.mycompany.com', 'test.mycompany.com'),
- ],
- ),
- [False, True], # provide SMTP session or not: should not impact sending
+ for mail_from, (expected_smtp_from, expected_msg_from, expected_mail_server) in zip(
+ [
+ 'specific_user@test.mycompany.com',
+ '"Name" ',
+ 'test@unknown_domain.com',
+ '"Name" '
+ ], [
+ # A mail server is configured for the email
+ ('specific_user@test.mycompany.com', 'specific_user@test.mycompany.com', self.mail_server_user),
+ # No mail server are configured for the email address, so it will use the
+ # notifications email instead and encapsulate the old email
+ ('notifications.test@test.mycompany.com', '"Name" ', self.mail_server_notification),
+ # same situation, but the original email has no name part
+ ('notifications.test@test.mycompany.com', '"test" ', self.mail_server_notification),
+ # A mail server is configured for the entire domain name, so we can use the bounce
+ # email address because the mail server supports it
+ (self.default_bounce_address, '"Name" ', self.mail_server_domain),
+ ]
):
- with self.subTest(mail_from=mail_from):
- with self.mock_smtplib_connection():
- if provide_smtp:
- smtp_session = IrMailServer.connect(smtp_from=mail_from)
- message = self._build_email(mail_from=mail_from)
- IrMailServer.send_email(message, smtp_session=smtp_session)
- else:
- message = self._build_email(mail_from=mail_from)
- IrMailServer.send_email(message)
+ # test with and without providing an SMTP session, which should not impact test
+ for provide_smtp in [False, True]:
+ with self.subTest(mail_from=mail_from, provide_smtp=provide_smtp):
+ with self.mock_smtplib_connection():
+ if provide_smtp:
+ smtp_session = IrMailServer.connect(smtp_from=mail_from)
+ message = self._build_email(mail_from=mail_from)
+ IrMailServer.send_email(message, smtp_session=smtp_session)
+ else:
+ message = self._build_email(mail_from=mail_from)
+ IrMailServer.send_email(message)
- self.connect_mocked.assert_called_once()
- self.assertEqual(len(self.emails), 1)
- self.assertSMTPEmailsSent(
- smtp_from=expected_smtp_from,
- message_from=expected_msg_from,
- from_filter=expected_from_filter,
- )
+ self.connect_mocked.assert_called_once()
+ self.assertEqual(len(self.emails), 1)
+ self.assertSMTPEmailsSent(
+ smtp_from=expected_smtp_from,
+ message_from=expected_msg_from,
+ mail_server=expected_mail_server,
+ )
# remove the notification server
- # so will use the mail server
+ # so will use the mail server
# The mail server configured for the notifications email has been removed
# but we can still use the mail server configured for test.mycompany.com
# and so we will be able to use the bounce address
# because we use the mail server for "test.mycompany.com"
- self.server_notification.unlink()
+ self.mail_server_notification.unlink()
for provide_smtp in [False, True]:
with self.mock_smtplib_connection():
if provide_smtp:
@@ -334,13 +342,13 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
self.assertEqual(len(self.emails), 1)
self.assertSMTPEmailsSent(
smtp_from=self.default_bounce_address,
- message_from='"Name" ',
- from_filter='test.mycompany.com',
+ message_from='"Name" ',
+ mail_server=self.mail_server_domain,
)
@mute_logger('odoo.models.unlink')
def test_mail_server_send_email_IDNA(self):
- # Test that the mail from / recipient envelop are encoded using IDNA
+ """ Test that the mail from / recipient envelop are encoded using IDNA """
self.env['ir.config_parameter'].sudo().set_param('mail.catchall.domain', 'ééééééé.com')
with self.mock_smtplib_connection():
message = self._build_email(mail_from='test@ééééééé.com')
@@ -356,21 +364,30 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
@mute_logger('odoo.models.unlink')
def test_mail_server_send_email_default_from(self):
- # Test the case when the "mail.default.from" contains a full email address and not just the local part
- # the domain of this default email address can be different than the catchall domain
- self.env['ir.config_parameter'].sudo().set_param('mail.default.from', 'test@custom_domain.com')
- _custom_server = self.env['ir.mail_server'].create({
+ """ Test the case when the "mail.default.from" contains a full email address
+ and not just the local part the domain of this default email address can be
+ different than the catchall domain """
+ other_default_from = 'notifications.other.test@custom_domain.com'
+ self.env['ir.config_parameter'].sudo().set_param('mail.default.from', other_default_from)
+ custom_server = self.env['ir.mail_server'].create({
'from_filter': 'custom_domain.com',
'name': 'Custom Domain Server',
'smtp_host': 'smtp_host',
'smtp_encryption': 'none',
})
- for mail_from, (expected_smtp_from, expected_msg_from, expected_from_filter) in zip(
+ for mail_from, (expected_smtp_from, expected_msg_from, expected_mail_server) in zip(
[
- '"Name" ',
+ # custom server domain
+ '"Custom Server Name" ',
+ # unknown domain
+ '"Name" ',
+ # notification server domain
+ '"Name" '
], [
- ('test@custom_domain.com', '"Name" ', 'custom_domain.com'),
+ ('customer.test@custom_domain.com', '"Custom Server Name" ', custom_server),
+ (other_default_from, '"Name" ', custom_server),
+ (self.default_bounce_address, '"Name" ', self.mail_server_domain),
],
):
with self.subTest(mail_from=mail_from):
@@ -382,116 +399,85 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
smtp_from=expected_smtp_from,
smtp_to_list=['dest@xn--example--i1a.com'],
message_from=expected_msg_from,
- from_filter=expected_from_filter,
+ mail_server=expected_mail_server,
)
- @mute_logger('odoo.models.unlink')
- @patch.dict(config.options, {"from_filter": "test.mycompany.com", "smtp_server": "example.com"})
- def test_mail_server_config_bin(self):
- """Test the configuration provided in the odoo-bin arguments.
-
- This config is used when no mail server exists.
- """
- IrMailServer = self.env['ir.mail_server']
-
- # Remove all mail server so we will use the odoo-bin arguments
- self.env['ir.mail_server'].search([]).unlink()
- self.assertFalse(self.env['ir.mail_server'].search([]))
-
- for email_from, (expected_smtp_from, expected_msg_from) in zip(
- [
- # inside "from_filter" domain
- 'specific_user@test.mycompany.com',
- # outside "from_filter" domain
- 'test@unknown_domain.com',
- ], [
- (self.default_bounce_address, 'specific_user@test.mycompany.com'),
- # outside "from_filter" domain: we will use notifications emails in the
- # headers, and bounce address in the envelope because the "from_filter"
- # allows to use the entire domain
- (self.default_bounce_address, '"test" '),
- ]
- ):
- with self.subTest(email_from=email_from):
- with self.mock_smtplib_connection():
- message = self._build_email(mail_from=email_from)
- IrMailServer.send_email(message)
-
- self.connect_mocked.assert_called_once()
- self.assertSMTPEmailsSent(
- smtp_from=expected_smtp_from,
- message_from=expected_msg_from,
- from_filter='test.mycompany.com',
- )
-
- @mute_logger('odoo.models.unlink')
- @patch.dict(config.options, {'from_filter': 'test.mycompany.com', 'smtp_server': 'example.com'})
- def test_mail_server_config_bin_default_from_filter(self):
- """Test that the config parameter "mail.default.from_filter" overwrites
- the odoo-bin argument "--from-filter" """
- self.env['ir.config_parameter'].sudo().set_param('mail.default.from_filter', 'example.com')
-
- IrMailServer = self.env['ir.mail_server']
-
- # Remove all mail server so we will use the odoo-bin arguments
- IrMailServer.search([]).unlink()
- self.assertFalse(IrMailServer.search([]))
-
- # Use an email in the domain of the config parameter "mail.default.from_filter"
- with self.mock_smtplib_connection():
- message = self._build_email(mail_from='specific_user@example.com')
- IrMailServer.send_email(message)
-
- self.assertSMTPEmailsSent(
- smtp_from='specific_user@example.com',
- message_from='specific_user@example.com',
- from_filter='example.com',
- )
-
@mute_logger('odoo.models.unlink')
@patch.dict(config.options, {
"from_filter": "dummy@example.com, test.mycompany.com, dummy2@example.com",
"smtp_server": "example.com",
})
- def test_mail_server_config_bin_smtp_session(self):
- """Test the configuration provided in the odoo-bin arguments.
+ def test_mail_server_config_bin(self):
+ """ Test the configuration provided in the odoo-bin arguments. This config
+ is used when no mail server exists. Test with and without giving a
+ pre-configured SMTP session, should not impact results.
- This config is used when no mail server exists.
- Use a pre-configured SMTP session.
+ Also check "mail.default.from_filter" parameter usage that should overwrite
+ odoo-bin argument "--from-filter".
"""
IrMailServer = self.env['ir.mail_server']
# Remove all mail server so we will use the odoo-bin arguments
- self.env['ir.mail_server'].search([]).unlink()
- self.assertFalse(self.env['ir.mail_server'].search([]))
+ IrMailServer.search([]).unlink()
+ self.assertFalse(IrMailServer.search([]))
- for email_from, (expected_smtp_from, expected_msg_from) in zip(
+ for mail_from, (expected_smtp_from, expected_msg_from) in zip(
[
- # use an email in the domain of the "from_filter"
+ # inside "from_filter" domain
'specific_user@test.mycompany.com',
- # Use an email outside of the domain of the "from_filter"
+ '"Formatted Name" ',
+ '"Formatted Name" ',
+ '"Formatted Name" ',
+ # outside "from_filter" domain
'test@unknown_domain.com',
+ '"Formatted Name" ',
], [
+ # inside "from_filter" domain: no rewriting
(self.default_bounce_address, 'specific_user@test.mycompany.com'),
- # Use an email outside of the domain of the "from_filter"
- # So we will use the notifications email in the headers and the bounce address
- # in the envelop because the "from_filter" allows to use the entire domain
- (self.default_bounce_address, '"test" ')
+ (self.default_bounce_address, '"Formatted Name" '),
+ (self.default_bounce_address, '"Formatted Name" '),
+ (self.default_bounce_address, '"Formatted Name" '),
+ # outside "from_filter" domain: we will use notifications emails in the
+ # headers, and bounce address in the envelope because the "from_filter"
+ # allows to use the entire domain
+ (self.default_bounce_address, '"test" '),
+ (self.default_bounce_address, '"Formatted Name" '),
]
):
- with self.subTest(email_from=email_from):
- with self.mock_smtplib_connection():
- smtp_session = IrMailServer.connect(smtp_from=email_from)
- message = self._build_email(mail_from=email_from)
- IrMailServer.send_email(message, smtp_session=smtp_session)
+ for provide_smtp in [False, True]: # providing smtp session should ont impact test
+ with self.subTest(mail_from=mail_from, provide_smtp=provide_smtp):
+ with self.mock_smtplib_connection():
+ if provide_smtp:
+ smtp_session = IrMailServer.connect(smtp_from=mail_from)
+ message = self._build_email(mail_from=mail_from)
+ IrMailServer.send_email(message, smtp_session=smtp_session)
+ else:
+ message = self._build_email(mail_from=mail_from)
+ IrMailServer.send_email(message)
- self.connect_mocked.assert_called_once()
- self.assertSMTPEmailsSent(
- smtp_from=expected_smtp_from,
- message_from=expected_msg_from,
- from_filter='dummy@example.com, test.mycompany.com, dummy2@example.com',
- )
+ self.connect_mocked.assert_called_once()
+ self.assertEqual(len(self.emails), 1)
+ self.assertSMTPEmailsSent(
+ smtp_from=expected_smtp_from,
+ message_from=expected_msg_from,
+ from_filter="dummy@example.com, test.mycompany.com, dummy2@example.com",
+ )
+ # for from_filter in ICP, overwrite the one from odoo-bin
+ self.env['ir.config_parameter'].sudo().set_param('mail.default.from_filter', 'icp.example.com')
+
+ # Use an email in the domain of the config parameter "mail.default.from_filter"
+ with self.mock_smtplib_connection():
+ message = self._build_email(mail_from='specific_user@icp.example.com')
+ IrMailServer.send_email(message)
+
+ self.assertSMTPEmailsSent(
+ smtp_from='specific_user@icp.example.com',
+ message_from='specific_user@icp.example.com',
+ from_filter='icp.example.com',
+ )
+
+ @users('admin')
def test_mail_server_get_test_email_addresses(self):
"""Test the email used to test the mail server connection."""
test_server = self.env['ir.mail_server'].create({
@@ -509,12 +495,20 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
('notifications@example.com', 'dummy.com, full_email@example_2.com, dummy2.com'),
('notifications', 'dummy.com, full_email@example_2.com, dummy2.com'),
('notifications@example.com', 'example.com'),
+ # default relies on "odoo"
+ (False, 'example.com'),
+ # fallback on user email
+ ('example_2.com', False),
+ (False, False),
], [
'noreply@example_2.com',
'notifications@example_2.com',
'full_email@example_2.com',
'full_email@example_2.com',
'notifications@example.com',
+ 'odoo@example.com',
+ self.env.user.email,
+ self.env.user.email,
],
):
with self.subTest(default_from=default_from, from_filter=from_filter):
@@ -526,16 +520,16 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
@mute_logger('odoo.models.unlink')
@patch.dict(config.options, {'from_filter': 'fake.com', 'smtp_server': 'cli_example.com'})
def test_mail_server_config_cli(self):
- """Check the mail server when the "smtp_authentication" is "cli".
-
- Should take the configuration from the odoo-bin argument.
- The "from_filter" of the mail server should overwrite the one set
- in the CLI arguments.
+ """ Test the mail server configuration when the "smtp_authentication" is
+ "cli". It should take the configuration from the odoo-bin argument. The
+ "from_filter" of the mail server should overwrite the one set in the CLI
+ arguments.
"""
+ IrMailServer = self.env['ir.mail_server']
# should be ignored by the mail server
self.env['ir.config_parameter'].sudo().set_param('mail.default.from_filter', 'fake.com')
- self.env['ir.mail_server'].create([{
+ server_other = IrMailServer.create([{
'name': 'Server No From Filter',
'smtp_host': 'smtp_host',
'smtp_encryption': 'none',
@@ -543,27 +537,25 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
'from_filter': 'dummy@example.com, cli_example.com, dummy2@example.com',
}])
- IrMailServer = self.env['ir.mail_server']
+ for mail_from, (expected_smtp_from, expected_msg_from, expected_mail_server) in zip(
+ [
+ # check that the CLI server take the configuration in the odoo-bin argument
+ # except the from_filter which is taken on the mail server
+ 'test@cli_example.com',
+ # other mail servers still work
+ 'specific_user@test.mycompany.com',
+ ], [
+ ('test@cli_example.com', 'test@cli_example.com', server_other),
+ ('specific_user@test.mycompany.com', 'specific_user@test.mycompany.com', self.mail_server_user),
+ ],
+ ):
+ with self.subTest(mail_from=mail_from):
+ with self.mock_smtplib_connection():
+ message = self._build_email(mail_from=mail_from)
+ IrMailServer.send_email(message)
- # check that the CLI server take the configuration in the odoo-bin argument
- # except the from_filter which is taken on the mail server
- with self.mock_smtplib_connection():
- message = self._build_email(mail_from='test@cli_example.com')
- IrMailServer.send_email(message)
-
- self.assertSMTPEmailsSent(
- smtp_from='test@cli_example.com',
- message_from='test@cli_example.com',
- from_filter='dummy@example.com, cli_example.com, dummy2@example.com',
- )
-
- # other mail server still work
- with self.mock_smtplib_connection():
- message = self._build_email(mail_from='specific_user@test.mycompany.com')
- IrMailServer.send_email(message)
-
- self.assertSMTPEmailsSent(
- smtp_from='specific_user@test.mycompany.com',
- message_from='specific_user@test.mycompany.com',
- from_filter='specific_user@test.mycompany.com',
- )
+ self.assertSMTPEmailsSent(
+ smtp_from=expected_smtp_from,
+ message_from=expected_msg_from,
+ mail_server=expected_mail_server,
+ )