From 700a84edc91024305632edfa73b1fa3a63acfe80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 31 Jul 2023 07:43:54 +0000 Subject: [PATCH 1/4] [FIX] google_calendar: avoid browse(False) instead of void records If no partner is found for a given email after calling '_mail_search_on_user' and '_mail_find_partner_from_emails' the returned result is a void recordset for the email. When rebrowsing partners afterwards, browsing p.id for each partner leads to potentially browsing False, which gives a different recordset from a void recordset. This further causes a crash in '_write_from_google' as he considers [False] as not being void. This commit fixes that by correctly filtering out void partners. This is globally a followup of odoo/odoo#99330. Task-3453577 (TestMail: Update Alias/Gateway tests for MC) Prepares Task-36879 (Mail: Support MultiCompany Aliases) Part-of: odoo/odoo#130768 --- addons/google_calendar/models/google_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/google_calendar/models/google_sync.py b/addons/google_calendar/models/google_sync.py index 49f5e0edeb4..c7721e291fd 100644 --- a/addons/google_calendar/models/google_sync.py +++ b/addons/google_calendar/models/google_sync.py @@ -310,7 +310,7 @@ class GoogleSync(models.AbstractModel): email not in [partner.email_normalized for partner in partners]] if remaining: partners += self.env['mail.thread']._mail_find_partner_from_emails(remaining, records=self, force_create=True) - unsorted_partners = self.env['res.partner'].browse([p.id for p in partners]) + unsorted_partners = self.env['res.partner'].browse([p.id for p in partners if p.id]) # partners needs to be sorted according to the emails order provided by google k = {value: idx for idx, value in enumerate(emails)} result = unsorted_partners.sorted(key=lambda p: k.get(p.email_normalized, -1)) From 71c58a7e936f5cc3e350b4ab5cfc239d942e00fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 31 Jul 2023 09:23:29 +0000 Subject: [PATCH 2/4] [MOV] test_mail: move alias tests in their own file Move alias-specific tests in their own file. This model deserves its own test file containing unit tests for both alias model and alias mixin. This helps improving and debugging the model, instead of having them mixed with gateway-specific tests. Activate multi-company in setup, preparing multi-company support for aliases and alias domains. Task-3453577 (TestMail: Update Alias/Gateway tests for MC) Prepares Task-36879 (Mail: Support MultiCompany Aliases) Part-of: odoo/odoo#130768 --- addons/test_mail/tests/__init__.py | 1 + addons/test_mail/tests/test_mail_alias.py | 154 ++++++++++++++++++++ addons/test_mail/tests/test_mail_gateway.py | 135 ----------------- 3 files changed, 155 insertions(+), 135 deletions(-) create mode 100644 addons/test_mail/tests/test_mail_alias.py diff --git a/addons/test_mail/tests/__init__.py b/addons/test_mail/tests/__init__.py index 80cd71ffd88..29b8297d59d 100644 --- a/addons/test_mail/tests/__init__.py +++ b/addons/test_mail/tests/__init__.py @@ -3,6 +3,7 @@ from . import test_invite from . import test_ir_actions from . import test_mail_activity +from . import test_mail_alias from . import test_mail_composer from . import test_mail_composer_mixin from . import test_mail_followers diff --git a/addons/test_mail/tests/test_mail_alias.py b/addons/test_mail/tests/test_mail_alias.py new file mode 100644 index 00000000000..46c90d94b2d --- /dev/null +++ b/addons/test_mail/tests/test_mail_alias.py @@ -0,0 +1,154 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import exceptions +from odoo.addons.mail.tests.common import MailCommon +from odoo.tests import tagged +from odoo.tests.common import users +from odoo.tools import mute_logger + + +class TestMailAliasCommon(MailCommon): + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls._activate_multi_company() + + +@tagged('mail_gateway', 'mail_alias', 'multi_company') +class TestMailAlias(TestMailAliasCommon): + """ Test alias model features, constraints and behavior. """ + + def test_alias_domain_allowed_validation(self): + """ Check the validation of `mail.catchall.domain.allowed` system parameter""" + for value in [',', ',,', ', ,']: + with self.assertRaises(exceptions.ValidationError, + msg="The value '%s' should not be allowed" % value): + self.env['ir.config_parameter'].set_param('mail.catchall.domain.allowed', value) + + for value, expected in [ + ('', False), + ('hello.com', 'hello.com'), + ('hello.com,,', 'hello.com'), + ('hello.com,bonjour.com', 'hello.com,bonjour.com'), + ('hello.COM, BONJOUR.com', 'hello.com,bonjour.com'), + ]: + self.env['ir.config_parameter'].set_param('mail.catchall.domain.allowed', value) + self.assertEqual(self.env['ir.config_parameter'].get_param('mail.catchall.domain.allowed'), expected) + + def test_alias_name_unique(self): + alias_model_id = self.env['ir.model']._get('mail.test.gateway').id + catchall_alias = self.env['ir.config_parameter'].sudo().get_param('mail.catchall.alias') + bounce_alias = self.env['ir.config_parameter'].sudo().get_param('mail.bounce.alias') + + # test you cannot create aliases matching bounce / catchall + with self.assertRaises(exceptions.UserError), self.cr.savepoint(): + self.env['mail.alias'].create({'alias_model_id': alias_model_id, 'alias_name': catchall_alias}) + with self.assertRaises(exceptions.UserError), self.cr.savepoint(): + self.env['mail.alias'].create({'alias_model_id': alias_model_id, 'alias_name': bounce_alias}) + + new_mail_alias = self.env['mail.alias'].create({ + 'alias_model_id': alias_model_id, + 'alias_name': 'unused.test.alias' + }) + + # test that re-using catchall and bounce alias raises UserError + with self.assertRaises(exceptions.UserError), self.cr.savepoint(): + new_mail_alias.write({ + 'alias_name': catchall_alias + }) + with self.assertRaises(exceptions.UserError), self.cr.savepoint(): + new_mail_alias.write({ + 'alias_name': bounce_alias + }) + + new_mail_alias.write({'alias_name': 'another.unused.test.alias'}) + + # test that duplicating an alias should have blank name + copy_new_mail_alias = new_mail_alias.copy() + self.assertFalse(copy_new_mail_alias.alias_name) + + # cannot set catchall / bounce to used alias + with self.assertRaises(exceptions.UserError), self.cr.savepoint(): + self.env['ir.config_parameter'].sudo().set_param('mail.catchall.alias', new_mail_alias.alias_name) + with self.assertRaises(exceptions.UserError), self.cr.savepoint(): + self.env['ir.config_parameter'].sudo().set_param('mail.bounce.alias', new_mail_alias.alias_name) + + def test_alias_name_sanitize(self): + alias = self.env['mail.alias'].create({ + 'alias_model_id': self.env['ir.model']._get('mail.test.container').id, + 'alias_name': 'bidule...inc.', + }) + self.assertEqual(alias.alias_name, 'bidule.inc', 'Emails cannot start or end with a dot, there cannot be a sequence of dots.') + + def test_alias_setup(self): + alias = self.env['mail.alias'].create({ + 'alias_model_id': self.env['ir.model']._get('mail.test.container').id, + 'alias_name': 'b4r+_#_R3wl$$', + }) + self.assertEqual(alias.alias_name, 'b4r+_-_r3wl-', 'Disallowed chars should be replaced by hyphens') + + with self.assertRaises(exceptions.ValidationError): + alias.write({'alias_defaults': "{'custom_field': brokendict"}) + + +@tagged('mail_gateway', 'mail_alias', 'multi_company') +class TestMailAliasMixin(TestMailAliasCommon): + """ Test alias mixin implementation, synchornization of alias records + based on owner records. """ + + @users('employee') + @mute_logger('odoo.addons.base.models.ir_model') + def test_alias_creation(self): + record = self.env['mail.test.container'].create({ + 'name': 'Test Record', + 'alias_name': 'alias.test', + 'alias_contact': 'followers', + }) + self.assertEqual(record.alias_id.alias_model_id, self.env['ir.model']._get('mail.test.container')) + self.assertEqual(record.alias_id.alias_force_thread_id, record.id) + self.assertEqual(record.alias_id.alias_parent_model_id, self.env['ir.model']._get('mail.test.container')) + self.assertEqual(record.alias_id.alias_parent_thread_id, record.id) + self.assertEqual(record.alias_id.alias_name, 'alias.test') + self.assertEqual(record.alias_id.alias_contact, 'followers') + + record.write({ + 'alias_name': 'better.alias.test', + 'alias_defaults': "{'default_name': 'defaults'}" + }) + self.assertEqual(record.alias_id.alias_name, 'better.alias.test') + self.assertEqual(record.alias_id.alias_defaults, "{'default_name': 'defaults'}") + + with self.assertRaises(exceptions.AccessError): + record.write({ + 'alias_force_thread_id': 0, + }) + + with self.assertRaises(exceptions.AccessError): + record.write({ + 'alias_model_id': self.env['ir.model']._get('mail.test.gateway').id, + }) + + with self.assertRaises(exceptions.ValidationError): + record.write({'alias_defaults': "{'custom_field': brokendict"}) + + @users('employee') + def test_alias_mixin_copy_content(self): + self.assertFalse(self.env.user.has_group('base.group_system'), 'Test user should not have Administrator access') + + record = self.env['mail.test.container'].create({ + 'name': 'Test Record', + 'alias_name': 'test.record', + 'alias_contact': 'followers', + 'alias_bounced_content': False, + }) + self.assertFalse(record.alias_bounced_content) + record_copy = record.copy() + self.assertFalse(record_copy.alias_bounced_content) + + new_content = '

Bounced Content

' + record_copy.write({'alias_bounced_content': new_content}) + self.assertEqual(record_copy.alias_bounced_content, new_content) + record_copy2 = record_copy.copy() + self.assertEqual(record_copy2.alias_bounced_content, new_content) diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py index 40857f8ef19..049e0d003a2 100644 --- a/addons/test_mail/tests/test_mail_gateway.py +++ b/addons/test_mail/tests/test_mail_gateway.py @@ -16,7 +16,6 @@ from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE from odoo.addons.test_mail.models.test_mail_models import MailTestGateway from odoo.sql_db import Cursor from odoo.tests import tagged -from odoo.tests.common import users from odoo.tools import email_split_and_format, formataddr, mute_logger @@ -139,140 +138,6 @@ class TestEmailParsing(MailCommon): # Test that the parsing of XHTML mails does not fail self.env['mail.thread'].message_parse(self.from_string(test_mail_data.MAIL_XHTML)) -@tagged('mail_gateway') -class TestMailAlias(MailCommon): - - @users('employee') - @mute_logger('odoo.addons.base.models.ir_model') - def test_alias_creation(self): - record = self.env['mail.test.container'].create({ - 'name': 'Test Record', - 'alias_name': 'alias.test', - 'alias_contact': 'followers', - }) - self.assertEqual(record.alias_id.alias_model_id, self.env['ir.model']._get('mail.test.container')) - self.assertEqual(record.alias_id.alias_force_thread_id, record.id) - self.assertEqual(record.alias_id.alias_parent_model_id, self.env['ir.model']._get('mail.test.container')) - self.assertEqual(record.alias_id.alias_parent_thread_id, record.id) - self.assertEqual(record.alias_id.alias_name, 'alias.test') - self.assertEqual(record.alias_id.alias_contact, 'followers') - - record.write({ - 'alias_name': 'better.alias.test', - 'alias_defaults': "{'default_name': 'defaults'}" - }) - self.assertEqual(record.alias_id.alias_name, 'better.alias.test') - self.assertEqual(record.alias_id.alias_defaults, "{'default_name': 'defaults'}") - - with self.assertRaises(exceptions.AccessError): - record.write({ - 'alias_force_thread_id': 0, - }) - - with self.assertRaises(exceptions.AccessError): - record.write({ - 'alias_model_id': self.env['ir.model']._get('mail.test.gateway').id, - }) - - with self.assertRaises(exceptions.ValidationError): - record.write({'alias_defaults': "{'custom_field': brokendict"}) - - def test_alias_domain_allowed_validation(self): - """ Check the validation of `mail.catchall.domain.allowed` system parameter""" - for value in [',', ',,', ', ,']: - with self.assertRaises(exceptions.ValidationError, - msg="The value '%s' should not be allowed" % value): - self.env['ir.config_parameter'].set_param('mail.catchall.domain.allowed', value) - - for value, expected in [ - ('', False), - ('hello.com', 'hello.com'), - ('hello.com,,', 'hello.com'), - ('hello.com,bonjour.com', 'hello.com,bonjour.com'), - ('hello.COM, BONJOUR.com', 'hello.com,bonjour.com'), - ]: - self.env['ir.config_parameter'].set_param('mail.catchall.domain.allowed', value) - self.assertEqual(self.env['ir.config_parameter'].get_param('mail.catchall.domain.allowed'), expected) - - def test_alias_sanitize(self): - alias = self.env['mail.alias'].create({ - 'alias_model_id': self.env['ir.model']._get('mail.test.container').id, - 'alias_name': 'bidule...inc.', - }) - self.assertEqual(alias.alias_name, 'bidule.inc', 'Emails cannot start or end with a dot, there cannot be a sequence of dots.') - - def test_alias_setup(self): - alias = self.env['mail.alias'].create({ - 'alias_model_id': self.env['ir.model']._get('mail.test.container').id, - 'alias_name': 'b4r+_#_R3wl$$', - }) - self.assertEqual(alias.alias_name, 'b4r+_-_r3wl-', 'Disallowed chars should be replaced by hyphens') - - with self.assertRaises(exceptions.ValidationError): - alias.write({'alias_defaults': "{'custom_field': brokendict"}) - - def test_alias_name_unique(self): - alias_model_id = self.env['ir.model']._get('mail.test.gateway').id - catchall_alias = self.env['ir.config_parameter'].sudo().get_param('mail.catchall.alias') - bounce_alias = self.env['ir.config_parameter'].sudo().get_param('mail.bounce.alias') - - # test you cannot create aliases matching bounce / catchall - with self.assertRaises(exceptions.UserError), self.cr.savepoint(): - self.env['mail.alias'].create({'alias_model_id': alias_model_id, 'alias_name': catchall_alias}) - with self.assertRaises(exceptions.UserError), self.cr.savepoint(): - self.env['mail.alias'].create({'alias_model_id': alias_model_id, 'alias_name': bounce_alias}) - - new_mail_alias = self.env['mail.alias'].create({ - 'alias_model_id': alias_model_id, - 'alias_name': 'unused.test.alias' - }) - - # test that re-using catchall and bounce alias raises UserError - with self.assertRaises(exceptions.UserError), self.cr.savepoint(): - new_mail_alias.write({ - 'alias_name': catchall_alias - }) - with self.assertRaises(exceptions.UserError), self.cr.savepoint(): - new_mail_alias.write({ - 'alias_name': bounce_alias - }) - - new_mail_alias.write({'alias_name': 'another.unused.test.alias'}) - - # test that duplicating an alias should have blank name - copy_new_mail_alias = new_mail_alias.copy() - self.assertFalse(copy_new_mail_alias.alias_name) - - # cannot set catchall / bounce to used alias - with self.assertRaises(exceptions.UserError), self.cr.savepoint(): - self.env['ir.config_parameter'].sudo().set_param('mail.catchall.alias', new_mail_alias.alias_name) - with self.assertRaises(exceptions.UserError), self.cr.savepoint(): - self.env['ir.config_parameter'].sudo().set_param('mail.bounce.alias', new_mail_alias.alias_name) - - -@tagged('mail_gateway') -class TestMailAliasMixin(MailCommon): - - @users('employee') - def test_alias_mixin_copy_content(self): - self.assertFalse(self.env.user.has_group('base.group_system'), 'Test user should not have Administrator access') - - record = self.env['mail.test.container'].create({ - 'name': 'Test Record', - 'alias_name': 'test.record', - 'alias_contact': 'followers', - 'alias_bounced_content': False, - }) - self.assertFalse(record.alias_bounced_content) - record_copy = record.copy() - self.assertFalse(record_copy.alias_bounced_content) - - new_content = '

Bounced Content

' - record_copy.write({'alias_bounced_content': new_content}) - self.assertEqual(record_copy.alias_bounced_content, new_content) - record_copy2 = record_copy.copy() - self.assertEqual(record_copy2.alias_bounced_content, new_content) - @tagged('mail_gateway') class TestMailgateway(MailCommon): From b5949d1672966bb76aab5463b6291a956d844965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 27 Oct 2021 06:30:42 +0000 Subject: [PATCH 3/4] [REF] test_mail, various: prepare gateway / alias tests Rename alias domain and aliases used a test data. This allows to make them easier to read, follow, grep and understand. Activate multi-company on alias and gateway tests, ensuring it currently has few impact on tests. Task-3453577 (TestMail: Update Alias/Gateway tests for MC) Prepares Task-36879 (Mail: Support MultiCompany Aliases) Part-of: odoo/odoo#130768 --- .../test_account_incoming_supplier_invoice.py | 2 +- .../crm/tests/test_crm_lead_notification.py | 5 +- .../tests/test_sync_google2odoo.py | 8 +- addons/mail/tests/common.py | 25 +- addons/test_mail/data/test_mail_data.py | 2 +- addons/test_mail/tests/test_mail_alias.py | 1 - addons/test_mail/tests/test_mail_composer.py | 6 +- addons/test_mail/tests/test_mail_followers.py | 2 +- addons/test_mail/tests/test_mail_gateway.py | 229 ++++++++++-------- addons/test_mail/tests/test_mail_mail.py | 11 +- .../test_mail/tests/test_mail_multicompany.py | 24 +- addons/test_mail/tests/test_message_track.py | 2 +- addons/test_mail/tests/test_performance.py | 11 +- .../tests/test_mailing_server.py | 22 +- odoo/addons/base/tests/common.py | 8 +- odoo/addons/base/tests/test_ir_mail_server.py | 162 ++++++------- odoo/addons/base/tests/test_mail.py | 4 +- 17 files changed, 277 insertions(+), 247 deletions(-) diff --git a/addons/account/tests/test_account_incoming_supplier_invoice.py b/addons/account/tests/test_account_incoming_supplier_invoice.py index 373fcbab346..47a8efa31da 100644 --- a/addons/account/tests/test_account_incoming_supplier_invoice.py +++ b/addons/account/tests/test_account_incoming_supplier_invoice.py @@ -12,7 +12,7 @@ class TestAccountIncomingSupplierInvoice(AccountTestInvoicingCommon): def setUpClass(cls, chart_template_ref=None): super().setUpClass(chart_template_ref=chart_template_ref) - cls.env['ir.config_parameter'].sudo().set_param('mail.catchall.domain', 'test-company.odoo.com') + cls.env['ir.config_parameter'].sudo().set_param('mail.catchall.domain', 'test.mycompany.com') cls.internal_user = cls.env['res.users'].create({ 'name': 'Internal User', diff --git a/addons/crm/tests/test_crm_lead_notification.py b/addons/crm/tests/test_crm_lead_notification.py index 7cc3143d7b6..7cecc31d9a6 100644 --- a/addons/crm/tests/test_crm_lead_notification.py +++ b/addons/crm/tests/test_crm_lead_notification.py @@ -49,7 +49,6 @@ class NewLeadNotification(TestCrmCommon): crm_team_model = self.env['ir.model'].search([('model', '=', 'crm.team')]) crm_lead_model = self.env['ir.model'].search([('model', '=', 'crm.lead')]) - self.env["ir.config_parameter"].sudo().set_param("mail.catchall.domain", 'aqualung.com') crm_team0 = self.env['crm.team'].create({ 'name': 'crm team 0', @@ -83,7 +82,7 @@ Date: Thu, 27 Dec 2018 16:27:45 +0100 Message-ID: Subject: sale team 0 in company 0 From: A client -To: sale_team_0@aqualung.com +To: sale_team_0@test.mycompany.com Content-Type: multipart/alternative; boundary="000000000000a47519057e029630" --000000000000a47519057e029630 @@ -104,7 +103,7 @@ Date: Thu, 27 Dec 2018 16:27:45 +0100 Message-ID: Subject: sale team 1 in company 1 From: B client -To: sale_team_1@aqualung.com +To: sale_team_1@test.mycompany.com Content-Type: multipart/alternative; boundary="000000000000a47519057e029630" --000000000000a47519057e029630 diff --git a/addons/google_calendar/tests/test_sync_google2odoo.py b/addons/google_calendar/tests/test_sync_google2odoo.py index e6732f2a8f6..4c32f605e3d 100644 --- a/addons/google_calendar/tests/test_sync_google2odoo.py +++ b/addons/google_calendar/tests/test_sync_google2odoo.py @@ -1303,10 +1303,9 @@ class TestSyncGoogle2Odoo(TestSyncGoogle): @patch_api def test_alias_email_sync_recurrence(self): - catchall_domain = self.env['ir.config_parameter'].sudo().get_param("mail.catchall.domain") + self.env['ir.config_parameter'].sudo().set_param('mail.catchall.domain', 'test.mycompany.com') alias_model = self.env['ir.model'].search([('model', '=', 'calendar.event')]) - self.env['mail.alias'].create({'alias_name': 'sale', 'alias_model_id': alias_model.id}) - alias_email = 'sale@%s' % catchall_domain if catchall_domain else 'sale@' + mail_alias = self.env['mail.alias'].create({'alias_name': 'sale', 'alias_model_id': alias_model.id}) google_id = 'oj44nep1ldf8a3ll02uip0c9aa' base_event = self.env['calendar.event'].create({ @@ -1333,7 +1332,8 @@ class TestSyncGoogle2Odoo(TestSyncGoogle): 'reminders': {'useDefault': True}, "attendees": [ { - "email": alias_email, "state": "accepted", + "email": mail_alias.display_name, + "state": "accepted", }, ], 'updated': self.now, diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py index 2757ef48099..055eb69de80 100644 --- a/addons/mail/tests/common.py +++ b/addons/mail/tests/common.py @@ -84,7 +84,7 @@ class MockEmail(common.BaseCase, MockSmtplibCase): @classmethod def _init_mail_gateway(cls): - cls.alias_domain = 'test.com' + cls.alias_domain = 'test.mycompany.com' cls.alias_catchall = 'catchall.test' cls.alias_bounce = 'bounce.test' cls.default_from = 'notifications' @@ -92,13 +92,15 @@ class MockEmail(common.BaseCase, MockSmtplibCase): 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) + + # 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': 'test.com', + {'from_filter': cls.alias_domain, 'name': 'Domain Based Server', 'smtp_encryption': 'none', 'smtp_host': 'smtp_host', @@ -1087,25 +1089,26 @@ class MailCommon(common.TransactionCase, MailCase): @classmethod def setUpClass(cls): super(MailCommon, cls).setUpClass() - # give default values for all email aliases and domain - cls._init_mail_gateway() - cls._init_outgoing_gateway() # ensure admin configuration cls.user_admin = cls.env.ref('base.user_admin') - + cls.partner_admin = cls.env.ref('base.partner_admin') + cls.company_admin = cls.user_admin.company_id + cls.company_admin.write({'email': 'company@example.com'}) with patch.object(Users, '_notify_security_setting_update', side_effect=lambda *args, **kwargs: None): cls.user_admin.write({ 'country_id': cls.env.ref('base.be').id, 'email': 'test.admin@test.example.com', 'notification_type': 'inbox', }) - cls.partner_admin = cls.env.ref('base.partner_admin') - cls.company_admin = cls.user_admin.company_id - cls.company_admin.write({'email': 'company@example.com'}) # have root available at hand, just in case cls.user_root = cls.env.ref('base.user_root') cls.partner_root = cls.user_root.partner_id + # give default values for all email aliases and domain + cls._init_mail_gateway() + cls._init_outgoing_gateway() + + # by default avoid rendering restriction complexity cls.env['ir.config_parameter'].set_param('mail.restrict.template.rendering', False) # test standard employee @@ -1164,6 +1167,7 @@ class MailCommon(common.TransactionCase, MailCase): """ Create another company, add it to admin and create an user that belongs to that new company. It allows to test flows with users from different companies. """ + # new company cls.company_2 = cls.env['res.company'].create({ 'currency_id': cls.env.ref('base.CAD').id, 'email': 'company_2@test.example.com', @@ -1171,6 +1175,7 @@ class MailCommon(common.TransactionCase, MailCase): }) cls.user_admin.write({'company_ids': [(4, cls.company_2.id)]}) + # employee specific to second company cls.user_employee_c2 = mail_new_test_user( cls.env, login='employee_c2', groups='base.group_user', @@ -1189,7 +1194,7 @@ class MailCommon(common.TransactionCase, MailCase): company_id=cls.company_2.id, company_ids=[(6, 0, (cls.company_admin + cls.company_2).ids)], email='etchenne@example.com', - groups='base.group_user,base.group_erp_manager,mail.group_mail_template_editor', + groups='base.group_user,base.group_erp_manager,mail.group_mail_template_editor,base.group_partner_manager', login='erp_manager', name='Etchenne Tchagada', notification_type='inbox', diff --git a/addons/test_mail/data/test_mail_data.py b/addons/test_mail/data/test_mail_data.py index 5597df0c611..c823fa60888 100644 --- a/addons/test_mail/data/test_mail_data.py +++ b/addons/test_mail/data/test_mail_data.py @@ -219,7 +219,7 @@ Date: Sun, 26 Mar 2023 05:23:22 +0200 Message-ID: {msg_id} Subject: {subject} From: "Sylvie Lelitre" -To: groups@test.com +To: groups@test.mycompany.com Content-Type: multipart/mixed; boundary="000000000000b951de05f7c47a9e" --000000000000b951de05f7c47a9e diff --git a/addons/test_mail/tests/test_mail_alias.py b/addons/test_mail/tests/test_mail_alias.py index 46c90d94b2d..c853615f410 100644 --- a/addons/test_mail/tests/test_mail_alias.py +++ b/addons/test_mail/tests/test_mail_alias.py @@ -82,7 +82,6 @@ class TestMailAlias(TestMailAliasCommon): }) self.assertEqual(alias.alias_name, 'bidule.inc', 'Emails cannot start or end with a dot, there cannot be a sequence of dots.') - def test_alias_setup(self): alias = self.env['mail.alias'].create({ 'alias_model_id': self.env['ir.model']._get('mail.test.container').id, 'alias_name': 'b4r+_#_R3wl$$', diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py index b74993a726b..e2a3917cc94 100644 --- a/addons/test_mail/tests/test_mail_composer.py +++ b/addons/test_mail/tests/test_mail_composer.py @@ -2278,7 +2278,7 @@ class TestComposerResultsMass(TestMailComposer): default_template_id=self.template.id) )) composer_form.reply_to_mode = 'new' - composer_form.reply_to = "{{ '\"' + object.name + '\" <%s>' % 'dynamic.reply.to@test.com' }}" + composer_form.reply_to = "{{ '\"' + object.name + '\" <%s>' % 'dynamic.reply.to@test.mycompany.com' }}" composer = composer_form.save() self.assertTrue(composer.reply_to_force_new) with self.mock_mail_gateway(mail_unlink_sent=False): @@ -2294,7 +2294,7 @@ class TestComposerResultsMass(TestMailComposer): 'email_from': self.partner_employee_2.email_formatted, 'reply_to': formataddr(( f'{record.name}', - 'dynamic.reply.to@test.com' + 'dynamic.reply.to@test.mycompany.com' )), 'subject': 'TemplateSubject %s' % record.name, }, @@ -2302,7 +2302,7 @@ class TestComposerResultsMass(TestMailComposer): 'email_from': self.partner_employee_2.email_formatted, 'reply_to': formataddr(( f'{record.name}', - 'dynamic.reply.to@test.com' + 'dynamic.reply.to@test.mycompany.com' )), }, ) diff --git a/addons/test_mail/tests/test_mail_followers.py b/addons/test_mail/tests/test_mail_followers.py index 0f6f1c1b6be..d3fff6e69cb 100644 --- a/addons/test_mail/tests/test_mail_followers.py +++ b/addons/test_mail/tests/test_mail_followers.py @@ -464,7 +464,7 @@ class AdvancedResponsibleNotifiedTest(MailCommon): def test_auto_subscribe_notify_email(self): """ Responsible is notified when assigned """ - partner = self.env['res.partner'].create({"name": "demo1", "email": "demo1@test.com"}) + partner = self.env['res.partner'].create({"name": "demo1", "email": "demo1@test.mycompany.com"}) notified_user = self.env['res.users'].create({ 'login': 'demo1', 'partner_id': partner.id, diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py index 049e0d003a2..75f144db9ef 100644 --- a/addons/test_mail/tests/test_mail_gateway.py +++ b/addons/test_mail/tests/test_mail_gateway.py @@ -86,7 +86,7 @@ class TestEmailParsing(MailCommon): def test_message_parse_eml(self): # Test that the parsing of mail with embedded emails as eml(msg) which generates empty attachments, can be processed. - mail = self.format(test_mail_data.MAIL_EML_ATTACHMENT, email_from='"Sylvie Lelitre" ', to='generic@test.com') + mail = self.format(test_mail_data.MAIL_EML_ATTACHMENT, email_from='"Sylvie Lelitre" ', to=f'generic@{self.alias_domain}') self.env['mail.thread'].message_parse(self.from_string(mail)) def test_message_parse_eml_bounce_headers(self): @@ -130,7 +130,7 @@ class TestEmailParsing(MailCommon): def test_message_parse_plaintext(self): """ Incoming email in plaintext should be stored as html """ - mail = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='"Sylvie Lelitre" ', to='generic@test.com') + mail = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='"Sylvie Lelitre" ', to=f'generic@{self.alias_domain}') res = self.env['mail.thread'].message_parse(self.from_string(mail)) self.assertIn('
\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n
', res['body']) @@ -145,7 +145,9 @@ class TestMailgateway(MailCommon): @classmethod def setUpClass(cls): super(TestMailgateway, cls).setUpClass() - cls.test_model = cls.env['ir.model']._get('mail.test.gateway') + cls._activate_multi_company() + + cls.mail_test_gateway_model = cls.env['ir.model']._get('mail.test.gateway') cls.email_from = '"Sylvie Lelitre" ' cls.test_record = cls.env['mail.test.gateway'].with_context(cls._test_context).create({ @@ -159,10 +161,11 @@ class TestMailgateway(MailCommon): }) # groups@.. will cause the creation of new mail.test.gateway cls.alias = cls.env['mail.alias'].create({ + 'alias_contact': 'everyone', + 'alias_model_id': cls.mail_test_gateway_model.id, 'alias_name': 'groups', 'alias_user_id': False, - 'alias_model_id': cls.test_model.id, - 'alias_contact': 'everyone'}) + }) # Set a first message on public group to test update and hierarchy cls.fake_email = cls._create_gateway_message(cls.test_record, '123456') @@ -190,7 +193,7 @@ class TestMailgateway(MailCommon): @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_alias_basic(self): """ Test details of created message going through mailgateway """ - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Specific') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Specific') # Test: one group created by mailgateway administrator as user_id is not set self.assertEqual(len(record), 1, 'message_process: a new mail.test should have been created') @@ -207,7 +210,7 @@ class TestMailgateway(MailCommon): @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_process_cid(self): - record = self.format_and_process(test_mail_data.MAIL_MULTIPART_IMAGE, self.email_from, 'groups@test.com') + record = self.format_and_process(test_mail_data.MAIL_MULTIPART_IMAGE, self.email_from, f'groups@{self.alias_domain}') message = record.message_ids[0] for attachment in message.attachment_ids: self.assertIn('/web/image/%s' % attachment.id, message.body) @@ -228,7 +231,7 @@ class TestMailgateway(MailCommon): }) with self.mock_mail_gateway(): - record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com') + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, f'groups@{self.alias_domain}') self.assertEqual(record.message_ids[0].author_id, self.partner_1, 'message_process: recognized email -> author_id') @@ -241,7 +244,7 @@ class TestMailgateway(MailCommon): # just an email -> no follower with self.mock_mail_gateway(): record2 = self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'groups@test.com', + MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Another Email') self.assertEqual(record2.message_ids[0].author_id, self.env['res.partner']) @@ -254,7 +257,7 @@ class TestMailgateway(MailCommon): # archived partner -> no follower with self.mock_mail_gateway(): record3 = self.format_and_process( - MAIL_TEMPLATE, partner_archived.email_formatted, 'groups@test.com', + MAIL_TEMPLATE, partner_archived.email_formatted, f'groups@{self.alias_domain}', subject='Archived Partner') self.assertEqual(record3.message_ids[0].author_id, self.env['res.partner']) @@ -270,7 +273,7 @@ class TestMailgateway(MailCommon): odoobot.email = 'odoobot@example.com' with self.mock_mail_gateway(): record4 = self.format_and_process( - MAIL_TEMPLATE, odoobot.email_formatted, 'groups@test.com', + MAIL_TEMPLATE, odoobot.email_formatted, f'groups@{self.alias_domain}', subject='Odoobot Automatic Answer') self.assertEqual(record4.message_ids[0].author_id, odoobot) @@ -287,7 +290,7 @@ class TestMailgateway(MailCommon): @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_email_email_from(self): """ Incoming email: not recognized author: email_from, no author_id, no followers """ - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}') self.assertFalse(record.message_ids[0].author_id, 'message_process: unrecognized email -> no author_id') self.assertEqual(record.message_ids[0].email_from, self.email_from) self.assertEqual(len(record.message_partner_ids), 0, @@ -297,7 +300,7 @@ class TestMailgateway(MailCommon): def test_message_process_email_author(self): """ Incoming email: recognized author: email_from, author_id, added as follower """ with self.mock_mail_gateway(): - record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com', subject='Test1') + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, f'groups@{self.alias_domain}', subject='Test1') self.assertEqual(record.message_ids[0].author_id, self.partner_1, 'message_process: recognized email -> author_id') @@ -306,45 +309,48 @@ class TestMailgateway(MailCommon): # Email recognized if partner has a formatted email self.partner_1.write({'email': '"Valid Lelitre" <%s>' % self.partner_1.email}) - record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email, 'groups@test.com', subject='Test2') + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email, f'groups@{self.alias_domain}', subject='Test2') self.assertEqual(record.message_ids[0].author_id, self.partner_1, 'message_process: recognized email -> author_id') self.assertEqual(record.message_ids[0].email_from, self.partner_1.email) self.assertNotSentEmail() # No notification / bounce should be sent - @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink') + @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.tests') def test_message_process_email_partner_find(self): """ Finding the partner based on email, based on partner / user / follower """ self.alias.write({'alias_force_thread_id': self.test_record.id}) from_1 = self.env['res.partner'].create({'name': 'Brice Denisse', 'email': 'from.test@example.com'}) - self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, 'groups@test.com') + self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, f'groups@{self.alias_domain}') self.assertEqual(self.test_record.message_ids[0].author_id, from_1) self.test_record.message_unsubscribe([from_1.id]) from_2 = mail_new_test_user(self.env, login='B', groups='base.group_user', name='User Denisse', email='from.test@example.com') - self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, 'groups@test.com') + self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, f'groups@{self.alias_domain}') self.assertEqual(self.test_record.message_ids[0].author_id, from_2.partner_id) self.test_record.message_unsubscribe([from_2.partner_id.id]) from_3 = self.env['res.partner'].create({'name': 'FOllower Denisse', 'email': 'from.test@example.com'}) self.test_record.message_subscribe([from_3.id]) - self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, 'groups@test.com') + self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, f'groups@{self.alias_domain}') self.assertEqual(self.test_record.message_ids[0].author_id, from_3) @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_email_author_exclude_alias(self): """ Do not set alias as author to avoid including aliases in discussions """ - from_1 = self.env['res.partner'].create({'name': 'Brice Denisse', 'email': 'from.test@test.com'}) + from_1 = self.env['res.partner'].create({ + 'name': 'Brice Denisse', + 'email': f'from.test@{self.alias_domain}', + }) self.env['mail.alias'].create({ 'alias_name': 'from.test', 'alias_model_id': self.env['ir.model']._get('mail.test.gateway').id }) - record = self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, 'groups@test.com') + record = self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, f'groups@{self.alias_domain}') self.assertFalse(record.message_ids[0].author_id) self.assertEqual(record.message_ids[0].email_from, from_1.email_formatted) @@ -362,9 +368,9 @@ class TestMailgateway(MailCommon): # Test: custom bounced content with self.mock_mail_gateway(): - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Should Bounce') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Should Bounce') self.assertFalse(record, 'message_process: should have bounced') - self.assertSentEmail('"MAILER-DAEMON" ', ['whatever-2a840@postmaster.twitter.com'], body_content='

What Is Dead May Never Die

') + self.assertSentEmail(f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', ['whatever-2a840@postmaster.twitter.com'], body_content='

What Is Dead May Never Die

') for empty_content in [ '


', '


', '


', @@ -380,11 +386,12 @@ class TestMailgateway(MailCommon): # Test: with "empty" bounced content (simulate view, putting always '


' in html field) with self.mock_mail_gateway(): - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Should Bounce') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Should Bounce') self.assertFalse(record, 'message_process: should have bounced') # Check if default (hardcoded) value is in the mail content self.assertSentEmail( - '"MAILER-DAEMON" ', ['whatever-2a840@postmaster.twitter.com'], + f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', + ['whatever-2a840@postmaster.twitter.com'], body_content='

Dear Sender,

The message below could not be accepted by the address %s' % self.alias.display_name.lower() ) @@ -432,7 +439,7 @@ class TestMailgateway(MailCommon): self.assertEqual(alias_valid.alias_status, 'not_tested') with self.mock_mail_gateway(), patch('odoo.addons.mail.models.mail_alias.Alias._set_alias_invalid', autospec=True) as _set_alias_invalid_mock: - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'valid@test.com', subject='Valid', + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'valid@{self.alias_domain}', subject='Valid', target_model=test_model_track.model) _set_alias_invalid_mock.assert_not_called() self.assertNotSentEmail() @@ -443,7 +450,7 @@ class TestMailgateway(MailCommon): container_custom.unlink() with self.assertRaises(Exception), patch('odoo.addons.mail.models.mail_alias.Alias._set_alias_invalid', autospec=True) as _set_alias_invalid_mock: - self.format_and_process(MAIL_TEMPLATE, self.email_from, 'valid@test.com', subject='Invalid', + self.format_and_process(MAIL_TEMPLATE, self.email_from, f'valid@{self.alias_domain}', subject='Invalid', target_model=test_model_track.model) # method executed in another transaction, so we cannot test its result directly but just below @@ -456,11 +463,11 @@ class TestMailgateway(MailCommon): alias._set_alias_invalid(message, message_dict) self.assertEqual(alias_valid.alias_status, 'invalid') - self.assertSentEmail('"MAILER-DAEMON" ', + self.assertSentEmail(f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', [alias_valid.alias_user_id.partner_id.email_formatted], subject='Re: Invalid') # Not sent to self.email_from because a return path is present in MAIL_TEMPLATE - self.assertSentEmail('"MAILER-DAEMON" ', + self.assertSentEmail(f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', ['whatever-2a840@postmaster.twitter.com'], subject='Re: Invalid', body=alias_valid._get_alias_invalid_body(message_dict)) @@ -476,7 +483,7 @@ class TestMailgateway(MailCommon): self.assertEqual(self.alias.alias_status, 'not_tested') - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Specific') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Specific') _set_alias_invalid.assert_not_called() self.assertEqual(self.alias.alias_status, 'valid') @@ -488,7 +495,7 @@ class TestMailgateway(MailCommon): self.alias.write({'alias_defaults': '""'}) self.assertEqual(self.alias.alias_status, 'not_tested', 'Updating alias_defaults must reset status') - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Specific2') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Specific2') _set_alias_invalid.assert_not_called() self.assertEqual(len(record), 1) @@ -504,7 +511,7 @@ class TestMailgateway(MailCommon): """ Test alias ownership """ self.alias.write({'alias_user_id': self.user_employee.id}) - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}') self.assertEqual(len(record), 1) res = record.get_metadata()[0].get('create_uid') or [None] self.assertEqual(res[0], self.user_employee.id) @@ -514,7 +521,7 @@ class TestMailgateway(MailCommon): """ Incoming email: everyone: new record + message_new """ self.alias.write({'alias_contact': 'everyone'}) - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Specific') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Specific') self.assertEqual(len(record), 1) self.assertEqual(len(record.message_ids), 1) @@ -525,9 +532,9 @@ class TestMailgateway(MailCommon): # Test: no group created, email bounced with self.mock_mail_gateway(): - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Should Bounce') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Should Bounce') self.assertFalse(record) - self.assertSentEmail('"MAILER-DAEMON" ', ['whatever-2a840@postmaster.twitter.com'], subject='Re: Should Bounce') + self.assertSentEmail(f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', ['whatever-2a840@postmaster.twitter.com'], subject='Re: Should Bounce') @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') def test_message_process_alias_followers_bounce(self): @@ -540,22 +547,30 @@ class TestMailgateway(MailCommon): # Test: unknown on followers alias -> bounce with self.mock_mail_gateway(): - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Should Bounce') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Should Bounce') self.assertFalse(record, 'message_process: should have bounced') - self.assertSentEmail('"MAILER-DAEMON" ', ['whatever-2a840@postmaster.twitter.com'], subject='Re: Should Bounce') + self.assertSentEmail( + f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', + ['whatever-2a840@postmaster.twitter.com'], + subject='Re: Should Bounce' + ) # Test: partner on followers alias -> bounce self._init_mail_mock() with self.mock_mail_gateway(): - record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com', subject='Should Bounce') + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, f'groups@{self.alias_domain}', subject='Should Bounce') self.assertFalse(record, 'message_process: should have bounced') - self.assertSentEmail('"MAILER-DAEMON" ', ['whatever-2a840@postmaster.twitter.com'], subject='Re: Should Bounce') + self.assertSentEmail( + f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', + ['whatever-2a840@postmaster.twitter.com'], + subject='Re: Should Bounce' + ) @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_alias_partner(self): """ Incoming email from a known partner on a Partners alias -> ok (+ test on alias.user_id) """ self.alias.write({'alias_contact': 'partners'}) - record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com') + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, f'groups@{self.alias_domain}') # Test: one group created by alias user self.assertEqual(len(record), 1) @@ -570,7 +585,7 @@ class TestMailgateway(MailCommon): 'alias_parent_thread_id': self.test_record.id, }) self.test_record.message_subscribe(partner_ids=[self.partner_1.id]) - record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com') + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, f'groups@{self.alias_domain}') # Test: one group created by Raoul (or Sylvie maybe, if we implement it) self.assertEqual(len(record), 1) @@ -583,7 +598,7 @@ class TestMailgateway(MailCommon): self.test_record.message_subscribe(partner_ids=[self.partner_1.id]) with self.mock_mail_gateway(): record = self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'groups@test.com>', + MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', msg_id='<1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>', subject='Re: cats') # Test: no new group + new message @@ -608,7 +623,7 @@ class TestMailgateway(MailCommon): return DEFAULT with patch.object(MailTestGateway, 'check_access_rights', autospec=True, side_effect=_employee_crash): - record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, 'groups@test.com', subject='NoEmployeeAllowed') + record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, f'groups@{self.alias_domain}', subject='NoEmployeeAllowed') self.assertEqual(record.create_uid, self.user_employee) self.assertEqual(record.message_ids[0].subject, 'NoEmployeeAllowed') self.assertEqual(record.message_ids[0].create_uid, self.user_employee) @@ -616,19 +631,19 @@ class TestMailgateway(MailCommon): @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_process_create_uid_email(self): - record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, 'groups@test.com', subject='Email Found') + record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, f'groups@{self.alias_domain}', subject='Email Found') self.assertEqual(record.create_uid, self.user_employee) self.assertEqual(record.message_ids[0].subject, 'Email Found') self.assertEqual(record.message_ids[0].create_uid, self.user_employee) self.assertEqual(record.message_ids[0].author_id, self.user_employee.partner_id) - record = self.format_and_process(MAIL_TEMPLATE, 'Another name <%s>' % self.user_employee.email, 'groups@test.com', subject='Email OtherName') + record = self.format_and_process(MAIL_TEMPLATE, 'Another name <%s>' % self.user_employee.email, f'groups@{self.alias_domain}', subject='Email OtherName') self.assertEqual(record.create_uid, self.user_employee) self.assertEqual(record.message_ids[0].subject, 'Email OtherName') self.assertEqual(record.message_ids[0].create_uid, self.user_employee) self.assertEqual(record.message_ids[0].author_id, self.user_employee.partner_id) - record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_normalized, 'groups@test.com', subject='Email SimpleEmail') + record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_normalized, f'groups@{self.alias_domain}', subject='Email SimpleEmail') self.assertEqual(record.create_uid, self.user_employee) self.assertEqual(record.message_ids[0].subject, 'Email SimpleEmail') self.assertEqual(record.message_ids[0].create_uid, self.user_employee) @@ -637,13 +652,13 @@ class TestMailgateway(MailCommon): @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink') def test_message_process_create_uid_email_follower(self): self.alias.write({ - 'alias_parent_model_id': self.test_model.id, + 'alias_parent_model_id': self.mail_test_gateway_model.id, 'alias_parent_thread_id': self.test_record.id, }) follower_user = mail_new_test_user(self.env, login='better', groups='base.group_user', name='Ernest Follower', email=self.user_employee.email) self.test_record.message_subscribe(follower_user.partner_id.ids) - record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, 'groups@test.com', subject='FollowerWinner') + record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, f'groups@{self.alias_domain}', subject='FollowerWinner') self.assertEqual(record.create_uid, follower_user) self.assertEqual(record.message_ids[0].subject, 'FollowerWinner') self.assertEqual(record.message_ids[0].create_uid, follower_user) @@ -652,7 +667,7 @@ class TestMailgateway(MailCommon): # name order win self.test_record.message_unsubscribe(follower_user.partner_id.ids) self.test_record.flush_recordset() - record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, 'groups@test.com', subject='FirstFoundWinner') + record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, f'groups@{self.alias_domain}', subject='FirstFoundWinner') self.assertEqual(record.create_uid, self.user_employee) self.assertEqual(record.message_ids[0].subject, 'FirstFoundWinner') self.assertEqual(record.message_ids[0].create_uid, self.user_employee) @@ -879,7 +894,11 @@ class TestMailgateway(MailCommon): '"My Super Catchall" <%s@%s>' % (self.alias_catchall, self.alias_domain), subject='Should Bounce') self.assertFalse(record) - self.assertSentEmail('"MAILER-DAEMON" ', ['whatever-2a840@postmaster.twitter.com'], subject='Re: Should Bounce') + self.assertSentEmail( + f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', + ['whatever-2a840@postmaster.twitter.com'], + subject='Re: Should Bounce' + ) @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_route_write_to_catchall_other_recipients_first(self): @@ -917,8 +936,7 @@ class TestMailgateway(MailCommon): self.assertEqual(self.partner_1.message_bounce, 0) self.assertEqual(self.test_record.message_bounce, 0) - bounced_mail_id = 4442 - bounce_email_to = '%s@%s' % ('bounce.test', 'test.com') + bounce_email_to = f'{self.alias_bounce}@{self.alias_domain}' record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, bounce_email_to, subject='Undelivered Mail Returned to Sender') self.assertFalse(record) # No information found in bounce email -> not possible to do anything except avoiding email @@ -931,7 +949,7 @@ class TestMailgateway(MailCommon): self.assertEqual(self.partner_1.message_bounce, 0) self.assertEqual(self.test_record.message_bounce, 0) - record = self.format_and_process(MAIL_TEMPLATE, 'MAILER-DAEMON@example.com', 'groups@test.com', subject='Undelivered Mail Returned to Sender') + record = self.format_and_process(MAIL_TEMPLATE, 'MAILER-DAEMON@example.com', f'groups@{self.alias_domain}', subject='Undelivered Mail Returned to Sender') self.assertFalse(record) # No information found in bounce email -> not possible to do anything except avoiding email self.assertEqual(self.partner_1.message_bounce, 0) @@ -948,7 +966,7 @@ class TestMailgateway(MailCommon): # no notification to find, won't be able to find the correct recipient extra = self.fake_email.message_id - record = self.format_and_process(email, self.partner_1.email_formatted, 'bounce.test@%test.com', subject='Undelivered Mail Returned to Sender', extra=extra) + record = self.format_and_process(email, self.partner_1.email_formatted, f'{self.alias_bounce}@{self.alias_domain}', subject='Undelivered Mail Returned to Sender', extra=extra) self.assertFalse(record) self.assertEqual(self.partner_1.message_bounce, 0) self.assertEqual(self.test_record.message_bounce, 0) @@ -959,7 +977,7 @@ class TestMailgateway(MailCommon): "res_partner_id": self.partner_1.id, "mail_message_id": self.fake_email.id, }) - record = self.format_and_process(email, self.partner_1.email_formatted, 'bounce.test@%test.com', subject='Undelivered Mail Returned to Sender', extra=extra) + record = self.format_and_process(email, self.partner_1.email_formatted, f'{self.alias_bounce}@{self.alias_domain}', subject='Undelivered Mail Returned to Sender', extra=extra) self.assertFalse(record) self.assertEqual(self.partner_1.message_bounce, 1) self.assertEqual(self.test_record.message_bounce, 1) @@ -970,8 +988,7 @@ class TestMailgateway(MailCommon): self.assertEqual(self.partner_1.message_bounce, 0) self.assertEqual(self.test_record.message_bounce, 0) - bounced_mail_id = 4442 - bounce_email_to = '%s@%s' % ('bounce.test', 'test.com') + bounce_email_to = f'{self.alias_bounce}@{self.alias_domain}' record = self.format_and_process(test_mail_data.MAIL_BOUNCE, self.partner_1.email_formatted, bounce_email_to, subject='Undelivered Mail Returned to Sender') self.assertFalse(record) # Missing in reply to message_id -> cannot find original record @@ -989,8 +1006,7 @@ class TestMailgateway(MailCommon): 'res_partner_id': self.partner_1.id, }) - bounced_mail_id = 4442 - bounce_email_to = '%s@%s' % ('bounce.test', 'test.com') + bounce_email_to = f'{self.alias_bounce}@{self.alias_domain}' extra = self.fake_email.message_id record = self.format_and_process(test_mail_data.MAIL_BOUNCE, self.partner_1.email_formatted, bounce_email_to, subject='Undelivered Mail Returned to Sender', extra=extra) self.assertFalse(record) @@ -1009,8 +1025,7 @@ class TestMailgateway(MailCommon): self.assertEqual(self.partner_1.message_bounce, 0) self.assertEqual(self.test_record.message_bounce, 0) - bounced_mail_id = 4442 - bounce_email_to = '%s@%s' % ('bounce.test', 'test.com') + bounce_email_to = f'{self.alias_bounce}@{self.alias_domain}' extra = self.fake_email.message_id record = self.format_and_process(test_mail_data.MAIL_BOUNCE, 'Whatever ', bounce_email_to, subject='Undelivered Mail Returned to Sender', extra=extra) self.assertFalse(record) @@ -1024,7 +1039,7 @@ class TestMailgateway(MailCommon): self.assertEqual(self.test_record.message_bounce, 0) extra = self.fake_email.message_id - record = self.format_and_process(test_mail_data.MAIL_BOUNCE, 'Whatever ', 'groups@test.com', subject='Undelivered Mail Returned to Sender', extra=extra) + record = self.format_and_process(test_mail_data.MAIL_BOUNCE, 'Whatever ', f'groups@{self.alias_domain}', subject='Undelivered Mail Returned to Sender', extra=extra) self.assertFalse(record) self.assertEqual(self.partner_1.message_bounce, 0) self.assertEqual(self.test_record.message_bounce, 1) @@ -1035,7 +1050,7 @@ class TestMailgateway(MailCommon): email = email.replace('MAILER-DAEMON@mail2.test.ironsky', 'email@mail2.test.ironsky') self.assertIn('report-type=delivery-status', email) extra = self.fake_email.message_id - record = self.format_and_process(email, 'Whatever ', 'groups@test.com', subject='Undelivered Mail Returned to Sender', extra=extra) + record = self.format_and_process(email, 'Whatever ', f'groups@{self.alias_domain}', subject='Undelivered Mail Returned to Sender', extra=extra) self.assertFalse(record) self.assertEqual(self.partner_1.message_bounce, 0) self.assertEqual(self.test_record.message_bounce, 2) @@ -1064,7 +1079,7 @@ class TestMailgateway(MailCommon): extra = self.fake_email.message_id for i in range(10): - record = self.format_and_process(test_mail_data.MAIL_BOUNCE, 'A third name <%s>' % self.partner_1.email, 'groups@test.com', subject='Undelivered Mail Returned to Sender', extra=extra) + record = self.format_and_process(test_mail_data.MAIL_BOUNCE, 'A third name <%s>' % self.partner_1.email, f'groups@{self.alias_domain}', subject='Undelivered Mail Returned to Sender', extra=extra) self.assertFalse(record) self.assertEqual(self.partner_1.message_bounce, 10) self.assertEqual(self.test_record.message_bounce, 0) @@ -1082,7 +1097,7 @@ class TestMailgateway(MailCommon): }) extra = self.fake_email.message_id - record = self.format_and_process(test_mail_data.MAIL_BOUNCE, self.partner_1.email_formatted, 'groups@test.com', subject='Undelivered Mail Returned to Sender', extra=extra) + record = self.format_and_process(test_mail_data.MAIL_BOUNCE, self.partner_1.email_formatted, f'groups@{self.alias_domain}', subject='Undelivered Mail Returned to Sender', extra=extra) self.assertFalse(record) self.assertEqual(self.partner_1.message_bounce, 1) self.assertEqual(self.test_record.message_bounce, 0) @@ -1138,7 +1153,7 @@ class TestMailgateway(MailCommon): """ Incoming email using in-rely-to should go into the right destination even with a wrong destination """ init_msg_count = len(self.test_record.message_ids) self.format_and_process( - MAIL_TEMPLATE, 'valid.other@gmail.com', 'erroneous@test.com>', + MAIL_TEMPLATE, 'valid.other@gmail.com', f'erroneous@{self.alias_domain}', subject='Re: news', extra='In-Reply-To:\r\n\t%s\n' % self.fake_email.message_id) self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) @@ -1149,7 +1164,7 @@ class TestMailgateway(MailCommon): """ Incoming email using references should go into the right destination even with a wrong destination """ init_msg_count = len(self.test_record.message_ids) self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'erroneous@test.com', + MAIL_TEMPLATE, self.email_from, f'erroneous@{self.alias_domain}', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id) self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) @@ -1175,7 +1190,7 @@ class TestMailgateway(MailCommon): # reply to reply1 using multiple references self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'groups@test.com', + MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Reply to reply1', extra=f'References: {reply1.message_id} {self.fake_email.message_id}' ) @@ -1185,7 +1200,7 @@ class TestMailgateway(MailCommon): # ordering should not impact self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'groups@test.com', + MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Reply to reply1 (order issue)', extra=f'References: {self.fake_email.message_id} {reply1.message_id}' ) @@ -1195,7 +1210,7 @@ class TestMailgateway(MailCommon): # history with last one being a note self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'groups@test.com', + MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Reply to reply1_1', extra=f'References: {reply1_1.message_id} {self.fake_email.message_id}' ) @@ -1206,7 +1221,7 @@ class TestMailgateway(MailCommon): # messed up history (two child branches): gateway initial parent is newest one # (then may change with flattening when posting on record) self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'groups@test.com', + MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Reply to reply2_1 (with noise)', extra=f'References: {reply1_1.message_id} {reply2_1.message_id}' ) @@ -1242,7 +1257,7 @@ class TestMailgateway(MailCommon): ) self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'test.gateway@test.com', + MAIL_TEMPLATE, self.email_from, f'test.gateway@{self.alias_domain}', subject='Reply to reply1', extra=f'References: {reply1.message_id}' ) @@ -1251,7 +1266,7 @@ class TestMailgateway(MailCommon): self.assertEqual(new_msg.subtype_id, self.env.ref('mail.mt_comment'), 'Mail: parent should be a comment') self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'test.gateway@test.com', + MAIL_TEMPLATE, self.email_from, f'test.gateway@{self.alias_domain}', subject='Reply to reply1_1 (with noise)', extra=f'References: {reply1_1.message_id} {reply1.message_id} {reply1.message_id}' ) @@ -1260,7 +1275,7 @@ class TestMailgateway(MailCommon): self.assertEqual(new_msg.subtype_id, self.env.ref('mail.mt_note'), 'Mail: reply to a note should be a note') self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'test.gateway@test.com', + MAIL_TEMPLATE, self.email_from, f'test.gateway@{self.alias_domain}', subject='Reply to reply2_1 (with noise)', extra=f'References: {reply2_1.message_id} {reply1_1.message_id}' ) @@ -1270,7 +1285,7 @@ class TestMailgateway(MailCommon): # no references: new discussion thread started self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'test.gateway@test.com', + MAIL_TEMPLATE, self.email_from, f'test.gateway@{self.alias_domain}', subject='New thread', extra='References:' ) @@ -1281,7 +1296,7 @@ class TestMailgateway(MailCommon): # mixed up references: newer message wins self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'test.gateway@test.com', + MAIL_TEMPLATE, self.email_from, f'test.gateway@{self.alias_domain}', subject='New thread', extra=f'References: {new_thread.message_id} {reply1_1.message_id}' ) @@ -1298,7 +1313,7 @@ class TestMailgateway(MailCommon): }) init_msg_count = len(self.test_record.message_ids) self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'erroneous@test.com', + MAIL_TEMPLATE, self.email_from, f'erroneous@{self.alias_domain}', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id) self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) @@ -1319,7 +1334,7 @@ class TestMailgateway(MailCommon): }) init_msg_count = len(self.test_record.message_ids) self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'erroneous@test.com', + MAIL_TEMPLATE, self.email_from, f'erroneous@{self.alias_domain}', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % buggy_message_id) self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) @@ -1336,7 +1351,7 @@ class TestMailgateway(MailCommon): }) init_msg_count = len(self.test_record.message_ids) res_test = self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'test.alias@test.com', + MAIL_TEMPLATE, self.email_from, f'test.alias@{self.alias_domain}', subject='My Dear Forward', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, target_model='mail.test.container') @@ -1356,7 +1371,7 @@ class TestMailgateway(MailCommon): }) init_msg_count = len(self.test_record.message_ids) res_test = self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'test.alias@test.com', + MAIL_TEMPLATE, self.email_from, f'test.alias@{self.alias_domain}', subject='My Dear Forward', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, target_model='mail.test.container') @@ -1375,8 +1390,11 @@ class TestMailgateway(MailCommon): }) init_msg_count = len(self.test_record.message_ids) res_test = self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'catchall.test@test.com', cc='test.alias@test.com', - subject='My Dear Forward', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, + MAIL_TEMPLATE, self.email_from, + f'{self.alias_catchall}@{self.alias_domain}', + cc=f'test.alias@{self.alias_domain}', + subject='My Dear Forward', + extra=f'References: <2233@a.com>\r\n\t<3edss_dsa@b.com> {self.fake_email.message_id}', target_model='mail.test.container') self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) @@ -1392,14 +1410,18 @@ class TestMailgateway(MailCommon): reply_to_force_new=False, subtype_xmlid='mail.mt_comment', ) - self.assertEqual(record_msg.reply_to, formataddr(('%s %s' % (self.user_employee.company_id.name, first_record.name), '%s@%s' % ('catchall.test', 'test.com')))) + self.assertEqual( + record_msg.reply_to, + formataddr((f'{self.user_employee.company_id.name} {first_record.name}', + f'{self.alias_catchall}@{self.alias_domain}')) + ) mail_msg = first_record.message_post( subject='Replies to Record', - reply_to='groups@test.com', + reply_to=f'groups@{self.alias_domain}', reply_to_force_new=True, subtype_xmlid='mail.mt_comment', ) - self.assertEqual(mail_msg.reply_to, 'groups@test.com') + self.assertEqual(mail_msg.reply_to, f'groups@{self.alias_domain}') # reply to mail but should be considered as a new mail for alias msgID = '' @@ -1484,7 +1506,7 @@ class TestMailgateway(MailCommon): """ Incoming email with ref holding model / res_id but that does not match any message in the thread: must raise since OpenERP saas-3 """ self.assertRaises(ValueError, self.format_and_process, MAIL_TEMPLATE, - self.partner_1.email_formatted, 'noone@test.com', subject='spam', + self.partner_1.email_formatted, f'noone@{self.alias_domain}', subject='spam', extra='In-Reply-To: <12321321-openerp-%d-mail.test.gateway@%s>' % (self.test_record.id, socket.gethostname())) # when 6.1 messages are present, compat mode is available @@ -1494,7 +1516,7 @@ class TestMailgateway(MailCommon): self.assertRaises( ValueError, self.format_and_process, MAIL_TEMPLATE, - self.partner_1.email_formatted, 'noone@test.com>', subject='spam', + self.partner_1.email_formatted, f'noone@{self.alias_domain}>', subject='spam', extra='In-Reply-To: <12321321-openerp-%d-mail.test.gateway@%s>' % (self.test_record.id, socket.gethostname())) # Test created messages @@ -1507,19 +1529,19 @@ class TestMailgateway(MailCommon): self.alias.write({'alias_force_thread_id': self.test_record.id,}) # Post a base message - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Re: super cats', msg_id='<123?456.diff1@agrolait.com>') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Re: super cats', msg_id='<123.456.diff1@agrolait.com>') self.assertFalse(record) self.assertEqual(len(self.test_record.message_ids), 2) # Do: due to some issue, same email goes back into the mailgateway record = self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Re: news', - msg_id='<123?456.diff1@agrolait.com>', extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>\n') + MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', subject='Re: news', + msg_id='<123.456.diff1@agrolait.com>', extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>\n') self.assertFalse(record) self.assertEqual(len(self.test_record.message_ids), 2) # Test: message_id is still unique - no_of_msg = self.env['mail.message'].search_count([('message_id', 'ilike', '<123?456.diff1@agrolait.com>')]) + no_of_msg = self.env['mail.message'].search_count([('message_id', 'ilike', '<123.456.diff1@agrolait.com>')]) self.assertEqual(no_of_msg, 1, 'message_process: message with already existing message_id should not have been duplicated') @@ -1528,7 +1550,7 @@ class TestMailgateway(MailCommon): """ Incoming email with model that does not accepts incoming emails must raise """ self.assertRaises(ValueError, self.format_and_process, - MAIL_TEMPLATE, self.email_from, 'noone@test.com', + MAIL_TEMPLATE, self.email_from, f'noone@{self.alias_domain}', subject='spam', extra='', model='res.country') @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') @@ -1536,14 +1558,14 @@ class TestMailgateway(MailCommon): """ Incoming email without model and without alias must raise """ self.assertRaises(ValueError, self.format_and_process, - MAIL_TEMPLATE, self.email_from, 'noone@test.com', + MAIL_TEMPLATE, self.email_from, f'noone@{self.alias_domain}', subject='spam', extra='') @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_fallback(self): """ Incoming email with model that accepting incoming emails as fallback """ record = self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'noone@test.com', + MAIL_TEMPLATE, self.email_from, f'noone@{self.alias_domain}', subject='Spammy', extra='', model='mail.test.gateway') self.assertEqual(len(record), 1) self.assertEqual(record.name, 'Spammy') @@ -1556,7 +1578,7 @@ class TestMailgateway(MailCommon): for encoding in ['', 'UTF-8', 'UTF-16LE', 'UTF-32BE']: file_content_b64 = base64.b64encode(file_content.encode(encoding or 'utf-8')).decode() record = self.format_and_process(test_mail_data.MAIL_FILE_ENCODING, - self.email_from, 'groups@test.com', + self.email_from, f'groups@{self.alias_domain}', subject='Test Charset %s' % encoding or 'Unset', charset='; charset="%s"' % encoding if encoding else '', content=file_content_b64 @@ -1632,7 +1654,7 @@ class TestMailgateway(MailCommon): new_record, msg='The loop should have been detected and the record should not have been created') - self.assertSentEmail('"MAILER-DAEMON" ', [email_from]) + self.assertSentEmail(f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', [email_from]) self.assertIn('-loop-detection-bounce-email@', self._mails[0]['references'], msg='The "bounce email" tag must be in the reference') @@ -1671,7 +1693,7 @@ class TestMailgateway(MailCommon): """ Incoming email containing an xml attachment with unknown characters (�) but an ASCII charset should not raise an Exception. UTF-8 is used as a safe fallback. """ - record = self.format_and_process(test_mail_data.MAIL_MULTIPART_INVALID_ENCODING, self.email_from, 'groups@test.com') + record = self.format_and_process(test_mail_data.MAIL_MULTIPART_INVALID_ENCODING, self.email_from, f'groups@{self.alias_domain}') self.assertEqual(record.message_ids.attachment_ids.name, 'bis3_with_error_encoding_address.xml') # NB: the xml received by email contains b"Chauss\xef\xbf\xbd\xef\xbf\xbde" with "\xef\xbf\xbd" being the @@ -1688,7 +1710,7 @@ class TestMailgateway(MailCommon): """ For incoming email containing an xml attachment with omitted charset and containing an UTF8 payload we should parse the attachment using UTF-8. """ - record = self.format_and_process(test_mail_data.MAIL_MULTIPART_OMITTED_CHARSET, self.email_from, 'groups@test.com') + record = self.format_and_process(test_mail_data.MAIL_MULTIPART_OMITTED_CHARSET, self.email_from, f'groups@{self.alias_domain}') self.assertEqual(record.message_ids.attachment_ids.name, 'bis3.xml') self.assertEqual("Chaussée de Bruxelles", record.message_ids.attachment_ids.raw.decode()) @@ -1711,9 +1733,12 @@ class TestMailgateway(MailCommon): 'res_id': None, }) - self.env['mail.alias'].create({'alias_name': 'test', 'alias_model_id': self.env['ir.model']._get('mail.test.gateway').id}) + self.env['mail.alias'].create({ + 'alias_name': 'test', + 'alias_model_id': self.env['ir.model']._get('mail.test.gateway').id, + }) record = self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'test@test.com', + MAIL_TEMPLATE, self.email_from, f'test@{self.alias_domain}', subject=message.message_id, extra=f'In-Reply-To:\r\n\t{message.message_id}\n', model=None) @@ -1739,7 +1764,7 @@ class TestMailThreadCC(MailCommon): @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_cc_new(self): - record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com', + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, f'cc_record@{self.alias_domain}', cc='cc1@example.com, cc2@example.com', target_model='mail.test.cc') cc = email_split_and_format(record.email_cc) self.assertEqual(sorted(cc), ['cc1@example.com', 'cc2@example.com']) @@ -1749,7 +1774,7 @@ class TestMailThreadCC(MailCommon): record = self.env['mail.test.cc'].create({'email_cc': 'cc1 , cc2@example.com'}) self.alias.write({'alias_force_thread_id': record.id}) - self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com', + self.format_and_process(MAIL_TEMPLATE, self.email_from, f'cc_record@{self.alias_domain}', cc='cc2 , cc3@example.com', target_model='mail.test.cc') cc = email_split_and_format(record.email_cc) self.assertEqual(sorted(cc), ['"cc1" ', 'cc2@example.com', 'cc3@example.com'], 'new cc should have been added on record (unique)') @@ -1759,7 +1784,7 @@ class TestMailThreadCC(MailCommon): record = self.env['mail.test.cc'].create({}) self.alias.write({'alias_force_thread_id': record.id}) - self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com', + self.format_and_process(MAIL_TEMPLATE, self.email_from, f'cc_record@{self.alias_domain}', cc='cc2 , cc3@example.com', target_model='mail.test.cc') cc = email_split_and_format(record.email_cc) self.assertEqual(sorted(cc), ['"cc2" ', 'cc3@example.com'], 'new cc should have been added on record (unique)') diff --git a/addons/test_mail/tests/test_mail_mail.py b/addons/test_mail/tests/test_mail_mail.py index 77d365e0341..0c7cad69a8c 100644 --- a/addons/test_mail/tests/test_mail_mail.py +++ b/addons/test_mail/tests/test_mail_mail.py @@ -666,7 +666,10 @@ class TestMailMail(MailCommon): Batch are defined by the mail server and the email from field. """ - self.assertEqual(self.env['ir.mail_server']._get_default_from_address(), 'notifications@test.com') + self.assertEqual( + self.env['ir.mail_server']._get_default_from_address(), + f'notifications@{self.alias_domain}' + ) mail_values = { 'body_html': '

Test

', @@ -711,7 +714,7 @@ class TestMailMail(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='notifications@test.com', mail_server_id=self.server_notification.id), + 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), @@ -719,9 +722,9 @@ class TestMailMail(MailCommon): any_order=True, ) - self.assert_email_sent_smtp(message_from='"test" ', + self.assert_email_sent_smtp(message_from=f'"test" ', emails_count=5, from_filter=self.server_notification.from_filter) - self.assert_email_sent_smtp(message_from='"test_2" ', + self.assert_email_sent_smtp(message_from=f'"test_2" ', emails_count=5, from_filter=self.server_notification.from_filter) self.assert_email_sent_smtp(message_from='user_1@test_2.com', emails_count=5, from_filter=self.server_domain_2.from_filter) self.assert_email_sent_smtp(message_from='user_2@test_2.com', emails_count=5, from_filter=self.server_domain_2.from_filter) diff --git a/addons/test_mail/tests/test_mail_multicompany.py b/addons/test_mail/tests/test_mail_multicompany.py index 0a924acf763..d22563743fe 100644 --- a/addons/test_mail/tests/test_mail_multicompany.py +++ b/addons/test_mail/tests/test_mail_multicompany.py @@ -12,17 +12,15 @@ from odoo.addons.mail.models.mail_message import Message from odoo.addons.mail.tests.common import MailCommon from odoo.addons.test_mail.tests.common import TestRecipients from odoo.exceptions import AccessError -from odoo.tests import tagged -from odoo.tests.common import users, HttpCase +from odoo.tests import tagged, users, HttpCase from odoo.tools import formataddr, mute_logger -@tagged('multi_company') -class TestMultiCompanySetup(MailCommon, TestRecipients): +class TestMailMCCommon(MailCommon, TestRecipients): @classmethod def setUpClass(cls): - super(TestMultiCompanySetup, cls).setUpClass() + super().setUpClass() cls._activate_multi_company() cls.test_model = cls.env['ir.model']._get('mail.test.gateway') @@ -45,11 +43,12 @@ class TestMultiCompanySetup(MailCommon, TestRecipients): 'email': 'valid.lelitre@agrolait.com', }) # groups@.. will cause the creation of new mail.test.gateway - cls.alias = cls.env['mail.alias'].create({ + cls.mail_alias = cls.env['mail.alias'].create({ + 'alias_contact': 'everyone', + 'alias_model_id': cls.test_model.id, 'alias_name': 'groups', 'alias_user_id': False, - 'alias_model_id': cls.test_model.id, - 'alias_contact': 'everyone'}) + }) # Set a first message on public group to test update and hierarchy cls.fake_email = cls.env['mail.message'].create({ @@ -63,20 +62,21 @@ class TestMultiCompanySetup(MailCommon, TestRecipients): }) def setUp(self): - super(TestMultiCompanySetup, self).setUp() + super().setUp() # patch registry to simulate a ready environment self.patch(self.env.registry, 'ready', True) self.flush_tracking() + +@tagged('multi_company') +class TestMultiCompanySetup(TestMailMCCommon): + @users('erp_manager') @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') def test_mail_composer_wtpl_populate_new_recipient(self): """ Test auto-populate of auto created partner with related record values when sending a mail with a template. """ - self.user_erp_manager.write({ - 'groups_id': [(4, self.env.ref('base.group_partner_manager').id)], - }) companies = self.company_admin + self.company_2 test_records = self.env['mail.test.ticket.mc'].create([ { diff --git a/addons/test_mail/tests/test_message_track.py b/addons/test_mail/tests/test_message_track.py index ee72f8b628d..152fd91a52c 100644 --- a/addons/test_mail/tests/test_message_track.py +++ b/addons/test_mail/tests/test_message_track.py @@ -208,7 +208,7 @@ class TestTracking(MailCommon): 'alias_contact': 'everyone', 'alias_defaults': custom_values}) record = self.format_and_process(MAIL_TEMPLATE, '"Sylvie Lelitre" ', - 'groups@test.com', target_field='customer_id', subject=custom_values['customer_id'], + 'groups@test.mycompany.com', target_field='customer_id', subject=custom_values['customer_id'], target_model='mail.test.ticket') with self.mock_mail_gateway(mail_unlink_sent=False): diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py index 35872c7c587..818703e5ae3 100644 --- a/addons/test_mail/tests/test_performance.py +++ b/addons/test_mail/tests/test_performance.py @@ -247,7 +247,7 @@ class TestBaseMailPerformance(BaseMailPerformance): with self.assertQueryCount(admin=1, employee=1): rec.write({ 'name': 'Test2', - 'email_from': 'test@test.com', + 'email_from': 'test@test.mycompany.com', }) @@ -738,11 +738,10 @@ class TestMailAPIPerformance(BaseMailPerformance): for record in test_records: self.assertEqual( reply_to[record.id], - formataddr( - ("%s %s" % (self.env.user.company_id.name, record.name), - "%s@%s" % (record.alias_name, self.alias_domain) - ) - ) + formataddr(( + record.name, + f"{record.alias_name}@{self.alias_domain}" + )) ) diff --git a/addons/test_mass_mailing/tests/test_mailing_server.py b/addons/test_mass_mailing/tests/test_mailing_server.py index e03c95303c1..0a768f3e69c 100644 --- a/addons/test_mass_mailing/tests/test_mailing_server.py +++ b/addons/test_mass_mailing/tests/test_mailing_server.py @@ -8,7 +8,7 @@ from odoo.tests.common import users from odoo.tools import mute_logger -@tagged('mass_mailing') +@tagged('mass_mailing', 'mail_server') class TestMassMailingServer(TestMassMailCommon): @classmethod @@ -82,12 +82,12 @@ class TestMassMailingServer(TestMassMailCommon): mailings = self.env['mailing.mailing'].create([{ 'subject': 'Mailing', 'body_html': 'Body for ', - 'email_from': 'specific_user@test.com', + 'email_from': 'specific_user@test.mycompany.com', 'mailing_model_id': self.env['ir.model']._get('mailing.test.optout').id, }, { 'subject': 'Mailing', 'body_html': 'Body for ', - 'email_from': 'unknown_name@test.com', + 'email_from': 'unknown_name@test.mycompany.com', 'mailing_model_id': self.env['ir.model']._get('mailing.test.optout').id, }]) with self.mock_smtplib_connection(): @@ -95,17 +95,17 @@ class TestMassMailingServer(TestMassMailCommon): self.assertEqual(self.find_mail_server_mocked.call_count, 2, 'Must be called only once per mail from') self.assert_email_sent_smtp( - smtp_from='specific_user@test.com', - message_from='specific_user@test.com', + smtp_from='specific_user@test.mycompany.com', + message_from='specific_user@test.mycompany.com', from_filter=self.server_user.from_filter, emails_count=8, ) self.assert_email_sent_smtp( # Must use the bounce address here because the mail server - # is configured for the entire domain "test.com" + # is configured for the entire domain "test.mycompany.com" smtp_from=lambda x: 'bounce' in x, - message_from='unknown_name@test.com', + message_from='unknown_name@test.mycompany.com', from_filter=self.server_domain.from_filter, emails_count=8, ) @@ -127,8 +127,8 @@ class TestMassMailingServer(TestMassMailCommon): self.assertEqual(self.find_mail_server_mocked.call_count, 1) self.assert_email_sent_smtp( - smtp_from='notifications@test.com', - message_from='"Testing" ', + smtp_from='notifications@test.mycompany.com', + message_from='"Testing" ', from_filter=self.server_notification.from_filter, emails_count=8, ) @@ -156,8 +156,8 @@ class TestMassMailingServer(TestMassMailCommon): self.assertEqual(self.find_mail_server_mocked.call_count, 1, 'Must not be called when mail server is forced') self.assert_email_sent_smtp( - smtp_from='specific_user@test.com', - message_from='specific_user@test.com', + smtp_from='specific_user@test.mycompany.com', + message_from='specific_user@test.mycompany.com', from_filter=self.server_user.from_filter, emails_count=8, ) diff --git a/odoo/addons/base/tests/common.py b/odoo/addons/base/tests/common.py index c2c7b6ea409..e5b76f781fa 100644 --- a/odoo/addons/base/tests/common.py +++ b/odoo/addons/base/tests/common.py @@ -373,7 +373,7 @@ class MockSmtplibCase: @classmethod def _init_mail_config(cls): cls.alias_bounce = 'bounce.test' - cls.alias_domain = 'test.com' + cls.alias_domain = 'test.mycompany.com' cls.default_from = 'notifications' 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) @@ -395,15 +395,15 @@ class MockSmtplibCase: ) = cls.env['ir.mail_server'].create([ { 'name': 'Domain based server', - 'from_filter': 'test.com', + 'from_filter': 'test.mycompany.com', ** ir_mail_server_values, }, { 'name': 'User specific server', - 'from_filter': 'specific_user@test.com', + 'from_filter': 'specific_user@test.mycompany.com', ** ir_mail_server_values, }, { 'name': 'Server Notifications', - 'from_filter': 'notifications@test.com', + 'from_filter': 'notifications@test.mycompany.com', ** ir_mail_server_values, }, { 'name': 'Server No From Filter', diff --git a/odoo/addons/base/tests/test_ir_mail_server.py b/odoo/addons/base/tests/test_ir_mail_server.py index 47cdcc6cf2d..333bdd813af 100644 --- a/odoo/addons/base/tests/test_ir_mail_server.py +++ b/odoo/addons/base/tests/test_ir_mail_server.py @@ -39,9 +39,9 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): ('admin@mail.example.com', 'mail.EXAMPLE.com'), ('admin@mail.example.com', 'admin@mail.example.com'), ('admin@mail.example.com', False), - ('"fake@test.com" ', 'mail.example.com'), - ('"fake@test.com" ', 'mail.example.com'), - ('"fake@test.com" ', 'test.com, mail.example.com, test2.com'), + ('"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(match_from_filter(email, from_filter)) @@ -49,12 +49,12 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): # Should not match tests = [ ('admin@mail.example.com', 'test@mail.example.com'), - ('admin@mail.example.com', 'test.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.com" ', 'test.com, wrong.mail.example.com, test3.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(match_from_filter(email, from_filter)) @@ -114,37 +114,37 @@ 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') - mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='specific_user@test.com') + mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='specific_user@test.mycompany.com') self.assertEqual(mail_server, self.server_user) - self.assertEqual(mail_from, 'specific_user@test.com') + self.assertEqual(mail_from, 'specific_user@test.mycompany.com') - mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='"Name name@strange.name" ') + mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='"Name name@strange.name" ') self.assertEqual(mail_server, self.server_user, 'Must extract email from full name') - self.assertEqual(mail_from, '"Name name@strange.name" ', 'Must keep the given mail from') + self.assertEqual(mail_from, '"Name name@strange.name" ', 'Must keep the given mail from') # Should not be case sensitive - mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='specific_user@test.com') + mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='specific_user@test.mycompany.com') self.assertEqual(mail_server, self.server_user, 'Mail from is case insensitive') - self.assertEqual(mail_from, 'specific_user@test.com', 'Should not change the mail from') + self.assertEqual(mail_from, 'specific_user@test.mycompany.com', 'Should not change the mail from') - mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='unknown_email@test.com') + mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='unknown_email@test.mycompany.com') self.assertEqual(mail_server, self.server_domain) - self.assertEqual(mail_from, 'unknown_email@test.com') + self.assertEqual(mail_from, 'unknown_email@test.mycompany.com') # Cover a different condition that the "email case insensitive" test - mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='unknown_email@TEST.COM') + mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='unknown_email@TEST.MYCOMPANY.COM') self.assertEqual(mail_server, self.server_domain, 'Domain is case insensitive') - self.assertEqual(mail_from, 'unknown_email@TEST.COM', 'Domain is case insensitive') + self.assertEqual(mail_from, 'unknown_email@TEST.MYCOMPANY.COM', 'Domain is case insensitive') mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='"Test" ') self.assertEqual(mail_server, self.server_notification, 'Should take the notification email') - self.assertEqual(mail_from, 'notifications@test.com') + self.assertEqual(mail_from, 'notifications@test.mycompany.com') # 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.com, domain2.com" - mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='"Test" ') + self.server_user.from_filter = "domain1.com, specific_user@test.mycompany.com, domain2.com" + mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='"Test" ') self.assertEqual(mail_server, self.server_user, "The entire email matches, the server should be used") - self.assertEqual(mail_from, '"Test" ') + self.assertEqual(mail_from, '"Test" ') mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='"Example" ') self.assertEqual(mail_server, self.server_user, "The domain matches, the server should be used") self.assertEqual(mail_from, '"Example" ') @@ -153,9 +153,9 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): self.assertEqual(mail_from, '"Example" ') # check that we take the domain server and not the "user specific" server - mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='"Test" ') + mail_server, mail_from = self.env['ir.mail_server']._find_mail_server(email_from='"Test" ') self.assertEqual(mail_server, self.server_domain) - self.assertEqual(mail_from, '"Test" ') + self.assertEqual(mail_from, '"Test" ') # remove the notifications email to simulate a mis-configured Odoo database # so we do not have the choice, we have to spoof the FROM @@ -173,15 +173,15 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): # A mail server is configured for the email with self.mock_smtplib_connection(): - message = self._build_email(mail_from='specific_user@test.com') + message = self._build_email(mail_from='specific_user@test.mycompany.com') IrMailServer.send_email(message) self.assertEqual(len(self.emails), 1) self.assert_email_sent_smtp( - smtp_from='specific_user@test.com', - message_from='specific_user@test.com', - from_filter='specific_user@test.com', + smtp_from='specific_user@test.mycompany.com', + message_from='specific_user@test.mycompany.com', + from_filter='specific_user@test.mycompany.com', ) # No mail server are configured for the email address, @@ -193,9 +193,9 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): self.assertEqual(len(self.emails), 1) self.assert_email_sent_smtp( - smtp_from='notifications@test.com', - message_from='"Name" ', - from_filter='notifications@test.com', + smtp_from='notifications@test.mycompany.com', + message_from='"Name" ', + from_filter='notifications@test.mycompany.com', ) # Same situation, but the original email has no name part @@ -206,33 +206,33 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): self.assertEqual(len(self.emails), 1) self.assert_email_sent_smtp( - smtp_from='notifications@test.com', - message_from='"test" ', - from_filter='notifications@test.com', + smtp_from='notifications@test.mycompany.com', + message_from='"test" ', + from_filter='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 with self.mock_smtplib_connection(): - message = self._build_email(mail_from='unknown_name@test.com') + message = self._build_email(mail_from='unknown_name@test.mycompany.com') IrMailServer.send_email(message) self.assertEqual(len(self.emails), 1) self.assert_email_sent_smtp( smtp_from=default_bounce_adress, - message_from='unknown_name@test.com', - from_filter='test.com', + message_from='unknown_name@test.mycompany.com', + from_filter='test.mycompany.com', ) # remove the notification server - # so will use the mail server + # so will use the mail server self.server_notification.unlink() # The mail server configured for the notifications email has been removed - # but we can still use the mail server configured for test.com + # 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.com" + # because we use the mail server for "test.mycompany.com" with self.mock_smtplib_connection(): message = self._build_email(mail_from='"Name" ') IrMailServer.send_email(message) @@ -241,8 +241,8 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): self.assert_email_sent_smtp( smtp_from=default_bounce_adress, - message_from='"Name" ', - from_filter='test.com', + message_from='"Name" ', + from_filter='test.mycompany.com', ) # Test that the mail from / recipient envelop are encoded using IDNA @@ -279,14 +279,14 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): # Test when forcing the mail server and when smtp_encryption is "starttls" self.server_domain.smtp_encryption = "starttls" with self.mock_smtplib_connection(): - message = self._build_email(mail_from='specific_user@test.com') + message = self._build_email(mail_from='specific_user@test.mycompany.com') IrMailServer.send_email(message, mail_server_id=self.server_domain.id) self.connect_mocked.assert_called_once() self.assert_email_sent_smtp( - smtp_from='specific_user@test.com', - message_from='specific_user@test.com', - from_filter='test.com', + smtp_from='specific_user@test.mycompany.com', + message_from='specific_user@test.mycompany.com', + from_filter='test.mycompany.com', ) @mute_logger('odoo.models.unlink') @@ -300,15 +300,15 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): # A mail server is configured for the email with self.mock_smtplib_connection(): - smtp_session = IrMailServer.connect(smtp_from='specific_user@test.com') - message = self._build_email(mail_from='specific_user@test.com') + smtp_session = IrMailServer.connect(smtp_from='specific_user@test.mycompany.com') + message = self._build_email(mail_from='specific_user@test.mycompany.com') IrMailServer.send_email(message, smtp_session=smtp_session) self.connect_mocked.assert_called_once() self.assert_email_sent_smtp( - smtp_from='specific_user@test.com', - message_from='specific_user@test.com', - from_filter='specific_user@test.com', + smtp_from='specific_user@test.mycompany.com', + message_from='specific_user@test.mycompany.com', + from_filter='specific_user@test.mycompany.com', ) # No mail server are configured for the email address, @@ -320,31 +320,31 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): self.connect_mocked.assert_called_once() self.assert_email_sent_smtp( - smtp_from='notifications@test.com', - message_from='"Name" ', - from_filter='notifications@test.com', + smtp_from='notifications@test.mycompany.com', + message_from='"Name" ', + from_filter='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 with self.mock_smtplib_connection(): - smtp_session = IrMailServer.connect(smtp_from='unknown_name@test.com') - message = self._build_email(mail_from='unknown_name@test.com') + smtp_session = IrMailServer.connect(smtp_from='unknown_name@test.mycompany.com') + message = self._build_email(mail_from='unknown_name@test.mycompany.com') IrMailServer.send_email(message, smtp_session=smtp_session) self.connect_mocked.assert_called_once() self.assert_email_sent_smtp( smtp_from=default_bounce_adress, - message_from='unknown_name@test.com', - from_filter='test.com', + message_from='unknown_name@test.mycompany.com', + from_filter='test.mycompany.com', ) # remove the notification server - # so will use the mail server + # so will use the mail server self.server_notification.unlink() # The mail server configured for the notifications email has been removed - # but we can still use the mail server configured for test.com + # but we can still use the mail server configured for test.mycompany.com with self.mock_smtplib_connection(): smtp_session = IrMailServer.connect(smtp_from='"Name" ') message = self._build_email(mail_from='"Name" ') @@ -353,12 +353,12 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): self.connect_mocked.assert_called_once() self.assert_email_sent_smtp( smtp_from=default_bounce_adress, - message_from='"Name" ', - from_filter='test.com', + message_from='"Name" ', + from_filter='test.mycompany.com', ) @mute_logger('odoo.models.unlink') - @patch.dict(config.options, {"from_filter": "test.com", "smtp_server": "example.com"}) + @patch.dict(config.options, {"from_filter": "test.mycompany.com", "smtp_server": "example.com"}) def test_mail_server_binary_arguments_domain(self): """Test the configuration provided in the odoo-bin arguments. @@ -373,26 +373,26 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): # Use an email in the domain of the "from_filter" with self.mock_smtplib_connection(): - message = self._build_email(mail_from='specific_user@test.com') + message = self._build_email(mail_from='specific_user@test.mycompany.com') IrMailServer.send_email(message) self.connect_mocked.assert_called_once() self.assert_email_sent_smtp( smtp_from=default_bounce_adress, - message_from='specific_user@test.com', - from_filter='test.com', + message_from='specific_user@test.mycompany.com', + from_filter='test.mycompany.com', ) # Test if the domain name is normalized before comparison with self.mock_smtplib_connection(): - message = self._build_email(mail_from='specific_user@test.com') + message = self._build_email(mail_from='specific_user@test.mycompany.com') IrMailServer.send_email(message) self.connect_mocked.assert_called_once() self.assert_email_sent_smtp( smtp_from=default_bounce_adress, - message_from='specific_user@test.com', - from_filter='test.com', + message_from='specific_user@test.mycompany.com', + from_filter='test.mycompany.com', ) # Use an email outside of the domain of the "from_filter" @@ -405,13 +405,13 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): self.connect_mocked.assert_called_once() self.assert_email_sent_smtp( smtp_from=default_bounce_adress, - message_from='"test" ', - from_filter='test.com', + message_from='"test" ', + from_filter='test.mycompany.com', ) @mute_logger('odoo.models.unlink') @patch.dict(config.options, { - "from_filter": "dummy@example.com, test.com, dummy2@example.com", + "from_filter": "dummy@example.com, test.mycompany.com, dummy2@example.com", "smtp_server": "example.com", }) def test_mail_server_binary_arguments_domain_smtp_session(self): @@ -429,15 +429,15 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): # Use an email in the domain of the "from_filter" with self.mock_smtplib_connection(): - smtp_session = IrMailServer.connect(smtp_from='specific_user@test.com') - message = self._build_email(mail_from='specific_user@test.com') + smtp_session = IrMailServer.connect(smtp_from='specific_user@test.mycompany.com') + message = self._build_email(mail_from='specific_user@test.mycompany.com') IrMailServer.send_email(message, smtp_session=smtp_session) self.connect_mocked.assert_called_once() self.assert_email_sent_smtp( smtp_from=default_bounce_adress, - message_from='specific_user@test.com', - from_filter='dummy@example.com, test.com, dummy2@example.com', + message_from='specific_user@test.mycompany.com', + from_filter='dummy@example.com, test.mycompany.com, dummy2@example.com', ) # Use an email outside of the domain of the "from_filter" @@ -451,8 +451,8 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): self.connect_mocked.assert_called_once() self.assert_email_sent_smtp( smtp_from=default_bounce_adress, - message_from='"test" ', - from_filter='dummy@example.com, test.com, dummy2@example.com', + message_from='"test" ', + from_filter='dummy@example.com, test.mycompany.com, dummy2@example.com', ) def test_mail_server_get_email_addresses(self): @@ -518,17 +518,17 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): # other mail server still work with self.mock_smtplib_connection(): - message = self._build_email(mail_from='specific_user@test.com') + message = self._build_email(mail_from='specific_user@test.mycompany.com') IrMailServer.send_email(message) self.assert_email_sent_smtp( - smtp_from='specific_user@test.com', - message_from='specific_user@test.com', - from_filter='specific_user@test.com', + smtp_from='specific_user@test.mycompany.com', + message_from='specific_user@test.mycompany.com', + from_filter='specific_user@test.mycompany.com', ) @mute_logger('odoo.models.unlink') - @patch.dict(config.options, {'from_filter': 'test.com', 'smtp_server': 'example.com'}) + @patch.dict(config.options, {'from_filter': 'test.mycompany.com', 'smtp_server': 'example.com'}) def test_mail_server_mail_default_from_filter(self): """Test that the config parameter "mail.default.from_filter" overwrite the odoo-bin argument "--from-filter" diff --git a/odoo/addons/base/tests/test_mail.py b/odoo/addons/base/tests/test_mail.py index eb8371caf34..48e12c9d4bb 100644 --- a/odoo/addons/base/tests/test_mail.py +++ b/odoo/addons/base/tests/test_mail.py @@ -500,13 +500,13 @@ class EmailConfigCase(TransactionCase): """Email from setting is respected.""" # ICP setting is more important ICP = self.env["ir.config_parameter"].sudo() - ICP.set_param("mail.catchall.domain", "example.org") + ICP.set_param("mail.catchall.domain", "test.mycompany.com") ICP.set_param("mail.default.from", "icp") message = self.env["ir.mail_server"].build_email( False, "recipient@example.com", "Subject", "The body of an email", ) - self.assertEqual(message["From"], "icp@example.org") + self.assertEqual(message["From"], "icp@test.mycompany.com") # Without ICP, the config file/CLI setting is used ICP.set_param("mail.default.from", False) message = self.env["ir.mail_server"].build_email( From aa69554a618597d31138292903969cfd4c6aa86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 31 Jul 2023 08:58:57 +0000 Subject: [PATCH 4/4] [REF] base, : move and cleanup ir mail server tests Have all ir.mail.server tests as well as their configuration-related tests moved into 'test_ir_mail_server' file. That way all tests are contained in the same file, easing their update. Task-3453577 (TestMail: Update Alias/Gateway tests for MC) Prepares Task-36879 (Mail: Support MultiCompany Aliases) Part-of: odoo/odoo#130768 --- odoo/addons/base/tests/common.py | 17 ++ odoo/addons/base/tests/test_ir_mail_server.py | 91 +++++++++- odoo/addons/base/tests/test_mail.py | 170 ++++-------------- 3 files changed, 136 insertions(+), 142 deletions(-) diff --git a/odoo/addons/base/tests/common.py b/odoo/addons/base/tests/common.py index e5b76f781fa..86f35ae2f35 100644 --- a/odoo/addons/base/tests/common.py +++ b/odoo/addons/base/tests/common.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import threading + from contextlib import contextmanager from unittest.mock import patch @@ -337,6 +339,21 @@ class MockSmtplibCase: self.find_mail_server_mocked = find_mail_server_mocked yield + 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', + headers={'Return-Path': return_path} if return_path else None, + subject='subject', + **kwargs, + ) + + def _send_email(self, msg, smtp_session): + with patch.object(threading.current_thread(), 'testing', False): + self.env['ir.mail_server'].send_email(msg, smtp_session=smtp_session) + return smtp_session.messages.pop() + def assert_email_sent_smtp(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. diff --git a/odoo/addons/base/tests/test_ir_mail_server.py b/odoo/addons/base/tests/test_ir_mail_server.py index 333bdd813af..89a4415b4f3 100644 --- a/odoo/addons/base/tests/test_ir_mail_server.py +++ b/odoo/addons/base/tests/test_ir_mail_server.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import email.message +import email.policy + from unittest.mock import patch from odoo import tools @@ -12,6 +15,47 @@ from odoo.tools import mute_logger from odoo.tools import config +class _FakeSMTP: + """SMTP stub""" + def __init__(self): + self.messages = [] + self.from_filter = 'example.com' + + # Python 3 before 3.7.4 + def sendmail(self, smtp_from, smtp_to_list, message_str, + mail_options=(), rcpt_options=()): + self.messages.append(message_str) + + # Python 3.7.4+ + def send_message(self, message, smtp_from, smtp_to_list, + mail_options=(), rcpt_options=()): + self.messages.append(message.as_string()) + + +@tagged('mail_server') +class EmailConfigCase(TransactionCase): + + @patch.dict(config.options, {"email_from": "settings@example.com"}) + def test_default_email_from(self, *args): + """Email from setting is respected.""" + # ICP setting is more important + ICP = self.env["ir.config_parameter"].sudo() + ICP.set_param("mail.catchall.domain", "test.mycompany.com") + ICP.set_param("mail.default.from", "icp") + message = self.env["ir.mail_server"].build_email( + False, "recipient@example.com", "Subject", + "The body of an email", + ) + self.assertEqual(message["From"], "icp@test.mycompany.com") + # Without ICP, the config file/CLI setting is used + ICP.set_param("mail.default.from", False) + message = self.env["ir.mail_server"].build_email( + False, "recipient@example.com", "Subject", + "The body of an email", + ) + self.assertEqual(message["From"], "settings@example.com") + + @tagged('mail_server') class TestIrMailServer(TransactionCase, MockSmtplibCase): @@ -21,14 +65,49 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): cls._init_mail_config() cls._init_mail_servers() - def _build_email(self, mail_from, return_path=None): - return self.env['ir.mail_server'].build_email( - email_from=mail_from, - email_to='dest@example-é.com', - subject='subject', body='body', - headers={'Return-Path': return_path} if return_path else None + def test_bpo_34424_35805(self): + """Ensure all email sent are bpo-34424 and bpo-35805 free""" + fake_smtp = _FakeSMTP() + msg = email.message.EmailMessage(policy=email.policy.SMTP) + msg['From'] = '"Joé Doe" ' + msg['To'] = '"Joé Doe" ' + + # Message-Id & References fields longer than 77 chars (bpo-35805) + msg['Message-Id'] = '<929227342217024.1596730490.324691772460938-example-30661-some.reference@test-123.example.com>' + msg['References'] = '<345227342212345.1596730777.324691772483620-example-30453-other.reference@test-123.example.com>' + + msg_on_the_wire = self._send_email(msg, fake_smtp) + self.assertEqual(msg_on_the_wire, + 'From: =?utf-8?q?Jo=C3=A9?= Doe \r\n' + 'To: =?utf-8?q?Jo=C3=A9?= Doe \r\n' + 'Message-Id: <929227342217024.1596730490.324691772460938-example-30661-some.reference@test-123.example.com>\r\n' + 'References: <345227342212345.1596730777.324691772483620-example-30453-other.reference@test-123.example.com>\r\n' + '\r\n' ) + def test_content_alternative_correct_order(self): + """ + RFC-1521 7.2.3. The Multipart/alternative subtype + > the alternatives appear in an order of increasing faithfulness + > to the original content. In general, the best choice is the + > LAST part of a type supported by the recipient system's local + > environment. + + Also, the MIME-Version header should be present in BOTH the + enveloppe AND the parts + """ + fake_smtp = _FakeSMTP() + msg = self._build_email("test@example.com", body='

Hello world

', subtype='html') + msg_on_the_wire = self._send_email(msg, fake_smtp) + + self.assertGreater(msg_on_the_wire.index('text/html'), msg_on_the_wire.index('text/plain'), + "The html part should be preferred (=appear after) to the text part") + self.assertEqual(msg_on_the_wire.count('==============='), 2 + 2, # +2 for the header and the footer + "There should be 2 parts: one text and one html") + self.assertEqual(msg_on_the_wire.count('MIME-Version: 1.0'), 3, + "There should be 3 headers MIME-Version: one on the enveloppe, " + "one on the html part, one on the text part") + def test_match_from_filter(self): """Test the from_filter field on the "ir.mail_server".""" match_from_filter = self.env['ir.mail_server']._match_from_filter diff --git a/odoo/addons/base/tests/test_mail.py b/odoo/addons/base/tests/test_mail.py index 48e12c9d4bb..c10542d4de4 100644 --- a/odoo/addons/base/tests/test_mail.py +++ b/odoo/addons/base/tests/test_mail.py @@ -2,23 +2,18 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from markupsafe import Markup -from unittest.mock import patch -import email.policy -import email.message import re -import threading from odoo.addons.base.models.ir_mail_server import extract_rfc2822_addresses from odoo.addons.base.models.ir_qweb_fields import nl2br_enclose from odoo.tests import tagged -from odoo.tests.common import BaseCase, TransactionCase +from odoo.tests.common import BaseCase from odoo.tools import ( is_html_empty, html_to_inner_content, html_sanitize, append_content_to_html, plaintext2html, email_split, email_domain_normalize, misc, formataddr, prepend_html_content, - config, ) from . import test_mail_examples @@ -28,6 +23,24 @@ from . import test_mail_examples class TestSanitizer(BaseCase): """ Test the html sanitizer that filters html to remove unwanted attributes """ + def test_abrupt_close(self): + payload = """ -->""" + html_result = html_sanitize(payload) + self.assertNotIn('alert(1)', html_result) + + payload = """ -->""" + html_result = html_sanitize(payload) + self.assertNotIn('alert(1)', html_result) + + def test_abrut_malformed(self): + payload = """""" + html_result = html_sanitize(payload) + self.assertNotIn('alert(1)', html_result) + + payload = """""" + html_result = html_sanitize(payload) + self.assertNotIn('alert(1)', html_result) + def test_basic_sanitizer(self): cases = [ ("yop", "

yop

"), # simple @@ -39,6 +52,20 @@ class TestSanitizer(BaseCase): html = html_sanitize(content) self.assertEqual(html, expected, 'html_sanitize is broken') + def test_comment_malformed(self): + html = ''' --> comment --> out of context balise --!>''' + html_result = html_sanitize(html) + self.assertNotIn('alert(1)', html_result) + + def test_comment_multiline(self): + payload = """ +
--> + """ + html_result = html_sanitize(payload) + self.assertNotIn('alert(1)', html_result) + def test_evil_malicious_code(self): # taken from https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Tests cases = [ @@ -438,6 +465,7 @@ class TestHtmlTools(BaseCase): self.assertEqual(result, "
test
test
") +@tagged('mail_tools') class TestEmailTools(BaseCase): """ Test some of our generic utility functions for emails """ @@ -492,133 +520,3 @@ class TestEmailTools(BaseCase): self.assertEqual(email_domain_normalize("Test.Com"), "test.com", "Should have normalized the domain") self.assertEqual(email_domain_normalize("email@test.com"), False, "The domain is not valid, should return False") self.assertEqual(email_domain_normalize(False), False, "The domain is not valid, should return False") - - -class EmailConfigCase(TransactionCase): - @patch.dict(config.options, {"email_from": "settings@example.com"}) - def test_default_email_from(self, *args): - """Email from setting is respected.""" - # ICP setting is more important - ICP = self.env["ir.config_parameter"].sudo() - ICP.set_param("mail.catchall.domain", "test.mycompany.com") - ICP.set_param("mail.default.from", "icp") - message = self.env["ir.mail_server"].build_email( - False, "recipient@example.com", "Subject", - "The body of an email", - ) - self.assertEqual(message["From"], "icp@test.mycompany.com") - # Without ICP, the config file/CLI setting is used - ICP.set_param("mail.default.from", False) - message = self.env["ir.mail_server"].build_email( - False, "recipient@example.com", "Subject", - "The body of an email", - ) - self.assertEqual(message["From"], "settings@example.com") - - -class _FakeSMTP: - """SMTP stub""" - def __init__(self): - self.messages = [] - self.from_filter = 'example.com' - - # Python 3 before 3.7.4 - def sendmail(self, smtp_from, smtp_to_list, message_str, - mail_options=(), rcpt_options=()): - self.messages.append(message_str) - - # Python 3.7.4+ - def send_message(self, message, smtp_from, smtp_to_list, - mail_options=(), rcpt_options=()): - self.messages.append(message.as_string()) - - -class TestEmailMessage(TransactionCase): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls._fake_smtp = _FakeSMTP() - - def build_email(self, **kwargs): - kwargs.setdefault('email_from', 'from@example.com') - kwargs.setdefault('email_to', 'to@example.com') - kwargs.setdefault('subject', 'subject') - return self.env['ir.mail_server'].build_email(**kwargs) - - def send_email(self, msg): - with patch.object(threading.current_thread(), 'testing', False): - self.env['ir.mail_server'].send_email(msg, smtp_session=self._fake_smtp) - return self._fake_smtp.messages.pop() - - def test_bpo_34424_35805(self): - """Ensure all email sent are bpo-34424 and bpo-35805 free""" - msg = email.message.EmailMessage(policy=email.policy.SMTP) - msg['From'] = '"Joé Doe" ' - msg['To'] = '"Joé Doe" ' - - # Message-Id & References fields longer than 77 chars (bpo-35805) - msg['Message-Id'] = '<929227342217024.1596730490.324691772460938-example-30661-some.reference@test-123.example.com>' - msg['References'] = '<345227342212345.1596730777.324691772483620-example-30453-other.reference@test-123.example.com>' - - msg_on_the_wire = self.send_email(msg) - self.assertEqual(msg_on_the_wire, - 'From: =?utf-8?q?Jo=C3=A9?= Doe \r\n' - 'To: =?utf-8?q?Jo=C3=A9?= Doe \r\n' - 'Message-Id: <929227342217024.1596730490.324691772460938-example-30661-some.reference@test-123.example.com>\r\n' - 'References: <345227342212345.1596730777.324691772483620-example-30453-other.reference@test-123.example.com>\r\n' - '\r\n' - ) - - def test_alternative_correct_order(self): - """ - RFC-1521 7.2.3. The Multipart/alternative subtype - > the alternatives appear in an order of increasing faithfulness - > to the original content. In general, the best choice is the - > LAST part of a type supported by the recipient system's local - > environment. - - Also, the MIME-Version header should be present in BOTH the - enveloppe AND the parts - """ - msg = self.build_email(body='

Hello world

', subtype='html') - msg_on_the_wire = self.send_email(msg) - - self.assertGreater(msg_on_the_wire.index('text/html'), msg_on_the_wire.index('text/plain'), - "The html part should be preferred (=appear after) to the text part") - self.assertEqual(msg_on_the_wire.count('==============='), 2 + 2, # +2 for the header and the footer - "There should be 2 parts: one text and one html") - self.assertEqual(msg_on_the_wire.count('MIME-Version: 1.0'), 3, - "There should be 3 headers MIME-Version: one on the enveloppe, " - "one on the html part, one on the text part") - - def test_comment_malformed(self): - html = ''' --> comment --> out of context balise --!>''' - html_result = html_sanitize(html) - self.assertNotIn('alert(1)', html_result) - - def test_multiline(self): - payload = """ -
--> - """ - html_result = html_sanitize(payload) - self.assertNotIn('alert(1)', html_result) - - def test_abrupt_close(self): - payload = """ -->""" - html_result = html_sanitize(payload) - self.assertNotIn('alert(1)', html_result) - - payload = """ -->""" - html_result = html_sanitize(payload) - self.assertNotIn('alert(1)', html_result) - - def test_abrut_malformed(self): - payload = """""" - html_result = html_sanitize(payload) - self.assertNotIn('alert(1)', html_result) - - payload = """""" - html_result = html_sanitize(payload) - self.assertNotIn('alert(1)', html_result)