From 6d166d60880837c27560e559b6ef2cac41d16aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 2 Nov 2021 12:39:40 +0000 Subject: [PATCH] [IMP] base, mail_*: use MockSmtplibCase and improve tests tooling mail_* = mail, test_mail, test_mail_full, test_mass_mailing PURPOSE Improve tooling of mail tests. Use SMTP mockup class available in base tests in mail tests to have a better and simplified mock tool when dealing with mail tests. Remove old fashioned code. SPECIFICATIONS Include ``MockSmtplibCase`` directly into ``MockEmail`` so that we re-use existing components for mocking outgoing emails. Also make parameters for gateway coherent between those two classes (use same test values). Remove 'sim_error' weird parameter of Mail ``mock_mail_gateway`` and use standard mock 'side_effect' instead in some specific tests, notably on SMTP.connect() mock. Add some assert / tools method and improve existing tools, used for creating or improving test data. This will be used in future commits when adding new tests. Add outgoing mail servers data in mail tests, allowing to make tests closer to real life use cases. Remove unnecessary calls to ``_init_mail_gateway`` as it is automatically done in ``MailCommon.setUpClass()``. Try to consolidate starting test data, notably with countries, phone numbers or email in mind to be sure test mail are deterministic. Task-2673913 (TestMail: Cleanup and improve test coverage) Part-of: odoo/odoo#86393 --- addons/mail/tests/common.py | 165 +++++++++++------- .../mail/tests/test_mail_channel_partner.py | 14 +- addons/test_mail/tests/common.py | 34 ++-- addons/test_mail/tests/test_mail_composer.py | 11 +- .../tests/test_mail_composer_mixin.py | 1 - addons/test_mail/tests/test_mail_followers.py | 1 - addons/test_mail/tests/test_mail_gateway.py | 4 - addons/test_mail/tests/test_mail_mail.py | 5 +- addons/test_mail/tests/test_mail_message.py | 1 - .../test_mail/tests/test_mail_multicompany.py | 2 - .../tests/test_mail_thread_internals.py | 8 +- .../tests/test_message_management.py | 6 +- addons/test_mail/tests/test_performance.py | 13 +- .../tests/test_sms_performance.py | 2 - .../tests/test_mailing_server.py | 5 +- odoo/addons/base/tests/common.py | 26 +-- odoo/addons/base/tests/test_ir_mail_server.py | 9 +- 17 files changed, 174 insertions(+), 133 deletions(-) diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py index 2fe5f1bcb1b..4277c53892b 100644 --- a/addons/mail/tests/common.py +++ b/addons/mail/tests/common.py @@ -11,10 +11,9 @@ from contextlib import contextmanager from functools import partial from lxml import html from unittest.mock import patch -from smtplib import SMTPServerDisconnected -from odoo import exceptions -from odoo.addons.base.models.ir_mail_server import IrMailServer, MailDeliveryException +from odoo.addons.base.models.ir_mail_server import IrMailServer +from odoo.addons.base.tests.common import MockSmtplibCase from odoo.addons.bus.models.bus import ImBus, json_dump from odoo.addons.mail.models.mail_mail import MailMail from odoo.addons.mail.models.mail_message import Message @@ -22,10 +21,14 @@ from odoo.addons.mail.models.mail_notification import MailNotification from odoo.tests import common, new_test_user from odoo.tools import formataddr, pycompat -mail_new_test_user = partial(new_test_user, context={'mail_create_nolog': True, 'mail_create_nosubscribe': True, 'mail_notrack': True, 'no_reset_password': True}) +mail_new_test_user = partial(new_test_user, context={'mail_create_nolog': True, + 'mail_create_nosubscribe': True, + 'mail_notrack': True, + 'no_reset_password': True, + 'mail_channel_nosubscribe': True}) -class MockEmail(common.BaseCase): +class MockEmail(common.BaseCase, MockSmtplibCase): """ Tools, helpers and asserts for mailgateway-related tests Useful reminders @@ -39,37 +42,19 @@ class MockEmail(common.BaseCase): # ------------------------------------------------------------ @contextmanager - def mock_mail_gateway(self, mail_unlink_sent=False, sim_error=None): + def mock_mail_gateway(self, mail_unlink_sent=False): build_email_origin = IrMailServer.build_email + send_email_origin = IrMailServer.send_email mail_create_origin = MailMail.create mail_unlink_origin = MailMail.unlink self.mail_unlink_sent = mail_unlink_sent self._init_mail_mock() - def _ir_mail_server_connect(model, *args, **kwargs): - if sim_error and sim_error == 'connect_smtp_notfound': - raise exceptions.UserError( - "Missing SMTP Server\nPlease define at least one SMTP server, or provide the SMTP parameters explicitly.") - if sim_error and sim_error == 'connect_failure': - raise Exception("Some exception") - return None - def _ir_mail_server_build_email(model, *args, **kwargs): self._mails.append(kwargs) self._mails_args.append(args) return build_email_origin(model, *args, **kwargs) - def _ir_mail_server_send_email(model, message, *args, **kwargs): - if '@' not in message['To']: - raise AssertionError(model.NO_VALID_RECIPIENT) - if sim_error and sim_error == 'send_assert': - raise AssertionError('AssertionError') - elif sim_error and sim_error == 'send_disconnect': - raise SMTPServerDisconnected('SMTPServerDisconnected') - elif sim_error and sim_error == 'send_delivery': - raise MailDeliveryException('MailDeliveryException') - return message['Message-Id'] - def _mail_mail_create(model, *args, **kwargs): res = mail_create_origin(model, *args, **kwargs) self._new_mails += res.sudo() @@ -80,11 +65,13 @@ class MockEmail(common.BaseCase): return mail_unlink_origin(model, *args, **kwargs) return True - with patch.object(IrMailServer, 'connect', autospec=True, wraps=IrMailServer, side_effect=_ir_mail_server_connect) as ir_mail_server_connect_mock, \ - patch.object(IrMailServer, 'build_email', autospec=True, wraps=IrMailServer, side_effect=_ir_mail_server_build_email) as ir_mail_server_build_email_mock, \ - patch.object(IrMailServer, 'send_email', autospec=True, wraps=IrMailServer, side_effect=_ir_mail_server_send_email) as ir_mail_server_send_email_mock, \ - patch.object(MailMail, 'create', autospec=True, wraps=MailMail, side_effect=_mail_mail_create) as _mail_mail_create_mock, \ - patch.object(MailMail, 'unlink', autospec=True, wraps=MailMail, side_effect=_mail_mail_unlink) as mail_mail_unlink_mock: + with self.mock_smtplib_connection(), \ + patch.object(IrMailServer, 'build_email', autospec=True, wraps=IrMailServer, side_effect=_ir_mail_server_build_email) as build_email_mocked, \ + patch.object(IrMailServer, 'send_email', autospec=True, wraps=IrMailServer, side_effect=send_email_origin) as send_email_mocked, \ + patch.object(MailMail, 'create', autospec=True, wraps=MailMail, side_effect=_mail_mail_create), \ + patch.object(MailMail, 'unlink', autospec=True, wraps=MailMail, side_effect=_mail_mail_unlink): + self.build_email_mocked = build_email_mocked + self.send_email_mocked = send_email_mocked yield def _init_mail_mock(self): @@ -92,10 +79,6 @@ class MockEmail(common.BaseCase): self._mails_args = [] self._new_mails = self.env['mail.mail'].sudo() - # ------------------------------------------------------------ - # GATEWAY TOOLS - # ------------------------------------------------------------ - @classmethod def _init_mail_gateway(cls): cls.alias_domain = 'test.com' @@ -108,6 +91,26 @@ class MockEmail(common.BaseCase): cls.env['ir.config_parameter'].set_param('mail.default.from', cls.default_from) 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', + 'name': 'Domain Based Server', + 'smtp_encryption': 'none', + 'smtp_host': 'smtp_host', + }, + {'from_filter': False, + 'name': 'No FromFilter Server', + 'smtp_encryption': 'none', + 'smtp_host': 'smtp_host', + } + ]) + + # ------------------------------------------------------------ + # GATEWAY TOOLS + # ------------------------------------------------------------ + def format(self, template, to='groups@example.com, other@gmail.com', subject='Frogs', email_from='Sylvie Lelitre ', return_path='', cc='', extra='', msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>', @@ -316,7 +319,7 @@ class MockEmail(common.BaseCase): # ------------------------------------------------------------ def assertMailMail(self, recipients, status, - check_mail_mail=True, mail_message=None, author=None, + mail_message=None, author=None, content=None, fields_values=None, email_values=None): """ Assert mail.mail records are created and maybe sent as emails. Allow asserting their content. Records to check are the one generated when @@ -335,8 +338,6 @@ class MockEmail(common.BaseCase): :param email_values: if given, should be a dictionary of keys / values allowing to check sent email additional values (if any). See ``assertSentEmail``; - - :param check_mail_mail: deprecated, use ``assertSentEmail`` if False """ found_mail = self._find_mail_mail_wpartners(recipients, status, mail_message=mail_message, author=author) self.assertTrue(bool(found_mail)) @@ -370,8 +371,6 @@ class MockEmail(common.BaseCase): :param email_values: if given, should be a dictionary of keys / values allowing to check sent email additional values (if any). See ``assertSentEmail``; - - :param check_mail_mail: deprecated, use ``assertSentEmail`` if False """ for email_to in emails: found_mail = self._find_mail_mail_wemail(email_to, status, mail_message=mail_message, author=author) @@ -410,6 +409,16 @@ class MockEmail(common.BaseCase): found_mail[fname], fvalue, 'Mail: expected %s for %s, got %s' % (fvalue, fname, found_mail[fname])) + def assertMessageFields(self, message, fields_values): + """ Just a quick helper to check a mail.message content by giving directly + a dict for fields. Allows to hide a lot of assertEqual under a simple + call with a dictionary of expected values. """ + for fname, fvalue in fields_values.items(): + self.assertEqual( + message[fname], fvalue, + 'Message: expected %s for %s, got %s' % (fvalue, fname, message[fname]) + ) + def assertNoMail(self, recipients, mail_message=None, author=None): """ Check no mail.mail and email was generated during gateway mock. """ try: @@ -435,8 +444,10 @@ class MockEmail(common.BaseCase): :param values: dictionary of additional values to check email content; """ base_expected = {} - for fname in ['reply_to', 'subject', 'attachments', 'body', 'references', - 'body_content', 'body_alternative_content', 'references_content']: + for fname in ['author_id', 'reply_to', + 'subject', 'body', 'body_content', 'body_alternative_content', + 'references', 'references_content', + 'attachments']: if fname in values: base_expected[fname] = values[fname] @@ -461,9 +472,14 @@ class MockEmail(common.BaseCase): debug_info = '-'.join('From: %s-To: %s' % (mail['email_from'], mail['email_to']) for mail in self._mails) if not bool(sent_mail) else '' self.assertTrue(bool(sent_mail), 'Expected mail from %s to %s not found in %s' % (expected['email_from'], expected['email_to'], debug_info)) - for val in ['reply_to', 'subject', 'references', 'attachments']: + for val in ['reply_to', 'subject', 'references']: if val in expected: self.assertEqual(expected[val], sent_mail[val], 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val])) + if 'attachments' in expected: + self.assertEqual( + sorted(expected['attachments']), sorted(sent_mail['attachments']), + 'Value for %s: expected %s, received %s' % ('attachments', expected['attachments'], sent_mail['attachments']) + ) if 'attachments_info' in values: attachments = sent_mail['attachments'] for attachment_info in values['attachments_info']: @@ -473,12 +489,18 @@ class MockEmail(common.BaseCase): if attachment_info.get('type'): self.assertEqual(attachment[2], attachment_info['type']) self.assertEqual(len(values['attachments_info']), len(attachments)) - for val in ['body']: + for val in ['body', 'body_alternative']: if val in expected: - self.assertHtmlEqual(expected[val], sent_mail[val], 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val])) - for val in ['body_content', 'body_alternative', 'references_content']: + self.assertHtmlEqual( + expected[val], sent_mail[val], + 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val]) + ) + for val in ['body_content', 'body_alternative_content', 'references_content']: if val in expected: - self.assertIn(expected[val], sent_mail[val[:-8]], 'Value for %s: %s does not contain %s' % (val, sent_mail[val[:-8]], expected[val])) + self.assertIn( + expected[val], sent_mail[val[:-8]], + 'Value for %s: %s does not contain %s' % (val, sent_mail[val[:-8]], expected[val]) + ) class MailCase(MockEmail): @@ -496,10 +518,11 @@ class MailCase(MockEmail): ("UNKNOWN", "Unknown error") """ _test_context = { + 'mail_channel_nosubscribe': True, 'mail_create_nolog': True, 'mail_create_nosubscribe': True, 'mail_notrack': True, - 'no_reset_password': True + 'no_reset_password': True, } @classmethod @@ -507,7 +530,7 @@ class MailCase(MockEmail): return record.with_context( mail_create_nolog=False, mail_create_nosubscribe=False, - mail_notrack=False + mail_notrack=False, ) def flush_tracking(self): @@ -627,19 +650,19 @@ class MailCase(MockEmail): # ------------------------------------------------------------ @contextmanager - def assertSinglePostNotifications(self, recipients_info, message_info=None, mail_unlink_sent=False, sim_error=None): + def assertSinglePostNotifications(self, recipients_info, message_info=None, mail_unlink_sent=False): """ Shortcut to assertMsgNotifications when having a single message to check. """ r_info = dict(message_info if message_info else {}) r_info.setdefault('content', '') r_info['notif'] = recipients_info - with self.assertPostNotifications([r_info], mail_unlink_sent=mail_unlink_sent, sim_error=sim_error): + with self.assertPostNotifications([r_info], mail_unlink_sent=mail_unlink_sent): yield @contextmanager - def assertPostNotifications(self, recipients_info, mail_unlink_sent=False, sim_error=None): + def assertPostNotifications(self, recipients_info, mail_unlink_sent=False): """ Check content of notifications. """ try: - with self.mock_mail_gateway(mail_unlink_sent=mail_unlink_sent, sim_error=sim_error), self.mock_bus(), self.mock_mail_app(): + with self.mock_mail_gateway(mail_unlink_sent=mail_unlink_sent), self.mock_bus(), self.mock_mail_app(): yield finally: done_msgs, done_notifs = self.assertMailNotifications(self._new_msgs, recipients_info) @@ -659,7 +682,7 @@ class MailCase(MockEmail): @contextmanager def assertNoNotifications(self): try: - with self.mock_mail_gateway(mail_unlink_sent=False, sim_error=None), self.mock_bus(), self.mock_mail_app(): + with self.mock_mail_gateway(mail_unlink_sent=False), self.mock_bus(), self.mock_mail_app(): yield finally: self.assertFalse(bool(self._new_msgs)) @@ -891,9 +914,14 @@ class MailCommon(common.TransactionCase, MailCase): 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.user_admin.write({'notification_type': 'inbox'}) + 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'}) @@ -905,9 +933,11 @@ class MailCommon(common.TransactionCase, MailCase): # test standard employee cls.user_employee = mail_new_test_user( - cls.env, login='employee', - groups='base.group_user,mail.group_mail_template_editor', + cls.env, company_id=cls.company_admin.id, + country_id=cls.env.ref('base.be').id, + groups='base.group_user,mail.group_mail_template_editor', + login='employee', name='Ernest Employee', notification_type='inbox', signature='--\nErnest' @@ -939,12 +969,26 @@ class MailCommon(common.TransactionCase, MailCase): groups='base.group_user', company_id=cls.company_2.id, company_ids=[(4, cls.company_2.id)], + email='enguerrand@example.com', name='Enguerrand Employee C2', notification_type='inbox', signature='--\nEnguerrand' ) cls.partner_employee_c2 = cls.user_employee_c2.partner_id + # test erp manager employee + cls.user_erp_manager = mail_new_test_user( + cls.env, + 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', + login='erp_manager', + name='Etchenne Tchagada', + notification_type='inbox', + signature='--\nEtchenne', + ) + @classmethod def _activate_multi_lang(cls, lang_code='es_ES', layout_arch_db=None, test_record=False, test_template=False): """ Summary of es_ES matching done here (a bit hardcoded to ease tests) @@ -1097,15 +1141,18 @@ class MailCommon(common.TransactionCase, MailCase): }) cls.env['ir.translation'].create(translations_tocreate) - def _generate_attachments_data(self, count, res_model=None, res_id=None): + def _generate_attachments_data(self, count, res_model=None, res_id=None, attach_values=None): # attachment visibility depends on what they are attached to + attach_values = attach_values or {} if res_model is None: res_model = self.template._name if res_id is None: res_id = self.template.id return [{ - 'name': '%02d.txt' % x, - 'datas': base64.b64encode(b'Att%02d' % x), + 'datas': base64.b64encode(b'AttContent_%02d' % x), + 'name': 'AttFileName_%02d.txt' % x, + 'mimetype': 'text/plain', 'res_model': res_model, 'res_id': res_id, + **attach_values, } for x in range(count)] diff --git a/addons/mail/tests/test_mail_channel_partner.py b/addons/mail/tests/test_mail_channel_partner.py index 8f6e8a66424..f7ba426648f 100644 --- a/addons/mail/tests/test_mail_channel_partner.py +++ b/addons/mail/tests/test_mail_channel_partner.py @@ -1,10 +1,14 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from functools import partial + from odoo.addons.mail.tests.common import mail_new_test_user from odoo.addons.mail.tests.common import MailCommon from odoo.exceptions import AccessError, UserError +mail_channel_new_test_user = partial(mail_new_test_user, context={'mail_channel_nosubscribe': False}) + class TestMailChannelMembers(MailCommon): @@ -22,23 +26,23 @@ class TestMailChannelMembers(MailCommon): 'res_id': cls.secret_group.id, }) - cls.user_1 = mail_new_test_user( + cls.user_1 = mail_channel_new_test_user( cls.env, login='user_1', name='User 1', groups='base.group_user,mail.secret_group') - cls.user_2 = mail_new_test_user( + cls.user_2 = mail_channel_new_test_user( cls.env, login='user_2', name='User 2', groups='base.group_user,mail.secret_group') - cls.user_3 = mail_new_test_user( + cls.user_3 = mail_channel_new_test_user( cls.env, login='user_3', name='User 3', groups='base.group_user,mail.secret_group') - cls.user_portal = mail_new_test_user( + cls.user_portal = mail_channel_new_test_user( cls.env, login='user_portal', name='User Portal', groups='base.group_portal') - cls.user_public = mail_new_test_user( + cls.user_public = mail_channel_new_test_user( cls.env, login='user_ublic', name='User Public', groups='base.group_public') diff --git a/addons/test_mail/tests/common.py b/addons/test_mail/tests/common.py index ae43c3ae437..88759108d48 100644 --- a/addons/test_mail/tests/common.py +++ b/addons/test_mail/tests/common.py @@ -9,27 +9,29 @@ class TestMailCommon(MailCommon): """ Main entry point for functional tests. """ @classmethod - def _create_channel_listener(cls): - cls.channel_listen = cls.env['mail.channel'].with_context(cls._test_context).create({'name': 'Listener'}) - - @classmethod - def _create_records_for_batch(cls, model, count): - # TDE note: to be cleaned in master + def _create_records_for_batch(cls, model, count, additional_values=None): + additional_values = additional_values or {} records = cls.env[model] partners = cls.env['res.partner'] country_id = cls.env.ref('base.be').id - partners = cls.env['res.partner'].with_context(**cls._test_context).create([{ - 'name': 'Partner_%s' % (x), - 'email': '_test_partner_%s@example.com' % (x), - 'country_id': country_id, - 'mobile': '047500%02d%02d' % (x, x) - } for x in range(count)]) + base_values = [ + {'name': 'Test_%s' % idx, + **additional_values, + } for idx in range(count) + ] - records = cls.env[model].with_context(**cls._test_context).create([{ - 'name': 'Test_%s' % (x), - 'customer_id': partners[x].id, - } for x in range(count)]) + if 'customer_id' in cls.env[model]: + partners = cls.env['res.partner'].with_context(**cls._test_context).create([{ + 'name': 'Partner_%s' % idx, + 'email': '_test_partner_%s@example.com' % idx, + 'country_id': country_id, + 'mobile': '047500%02d%02d' % (idx, idx) + } for idx in range(count)]) + for values, partner in zip(base_values, partners): + values['customer_id'] = partner.id + + records = cls.env[model].with_context(**cls._test_context).create(base_values) cls.records = cls._reset_mail_context(records) cls.partners = partners diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py index 01c6587ae17..57f4adfbadd 100644 --- a/addons/test_mail/tests/test_mail_composer.py +++ b/addons/test_mail/tests/test_mail_composer.py @@ -18,7 +18,6 @@ class TestMailComposer(TestMailCommon, TestRecipients): @classmethod def setUpClass(cls): super(TestMailComposer, cls).setUpClass() - cls._init_mail_gateway() # ensure employee can create partners, necessary for templates cls.user_employee.write({ @@ -609,8 +608,8 @@ class TestComposerResultsComment(TestMailComposer): 'email_from': self.test_record.user_id.email_formatted, # set by template 'subject': 'TemplateSubject %s' % self.test_record.name, 'attachments_info': [ - {'name': '00.txt', 'raw': b'Att00', 'type': 'text/plain'}, - {'name': '01.txt', 'raw': b'Att01', 'type': 'text/plain'}, + {'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'}, + {'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'}, {'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'}, ] }, @@ -624,8 +623,8 @@ class TestComposerResultsComment(TestMailComposer): 'email_from': self.test_record.user_id.email_formatted, # set by template 'subject': 'TemplateSubject %s' % self.test_record.name, 'attachments_info': [ - {'name': '00.txt', 'raw': b'Att00', 'type': 'text/plain'}, - {'name': '01.txt', 'raw': b'Att01', 'type': 'text/plain'}, + {'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'}, + {'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'}, {'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'}, ] }, @@ -638,7 +637,7 @@ class TestComposerResultsComment(TestMailComposer): # attachments are copied on message and linked to document self.assertEqual( set(message.attachment_ids.mapped('name')), - set(['00.txt', '01.txt', 'TestReport for %s.html' % self.test_record.name]) + set(['AttFileName_00.txt', 'AttFileName_01.txt', 'TestReport for %s.html' % self.test_record.name]) ) self.assertEqual(set(message.attachment_ids.mapped('res_model')), set([self.test_record._name])) self.assertEqual(set(message.attachment_ids.mapped('res_id')), set(self.test_record.ids)) diff --git a/addons/test_mail/tests/test_mail_composer_mixin.py b/addons/test_mail/tests/test_mail_composer_mixin.py index d13473f7801..5b85c34ed5c 100644 --- a/addons/test_mail/tests/test_mail_composer_mixin.py +++ b/addons/test_mail/tests/test_mail_composer_mixin.py @@ -12,7 +12,6 @@ class TestMailComposerMixin(TestMailCommon, TestRecipients): @classmethod def setUpClass(cls): super(TestMailComposerMixin, cls).setUpClass() - cls._init_mail_gateway() cls.mail_template = cls.env['mail.template'].create({ 'subject': 'Subject for {{ object.name }}', diff --git a/addons/test_mail/tests/test_mail_followers.py b/addons/test_mail/tests/test_mail_followers.py index 8e6a54c0501..4a1e0d94258 100644 --- a/addons/test_mail/tests/test_mail_followers.py +++ b/addons/test_mail/tests/test_mail_followers.py @@ -14,7 +14,6 @@ class BaseFollowersTest(TestMailCommon): super(BaseFollowersTest, cls).setUpClass() cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'}) cls._create_portal_user() - cls._create_channel_listener() # allow employee to update partners cls.user_employee.write({'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)]}) diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py index aff9f63bf71..d5e1e669325 100644 --- a/addons/test_mail/tests/test_mail_gateway.py +++ b/addons/test_mail/tests/test_mail_gateway.py @@ -247,8 +247,6 @@ class TestMailgateway(TestMailCommon): 'message_id': '<123456-openerp-%s-mail.test.gateway@%s>' % (cls.test_record.id, socket.gethostname()), }) - cls._init_mail_gateway() - # -------------------------------------------------- # Base low-level tests # -------------------------------------------------- @@ -1277,8 +1275,6 @@ class TestMailThreadCC(TestMailCommon): 'alias_model_id': cls.env['ir.model']._get('mail.test.cc').id, 'alias_contact': 'everyone'}) - cls._init_mail_gateway() - @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', diff --git a/addons/test_mail/tests/test_mail_mail.py b/addons/test_mail/tests/test_mail_mail.py index 04f48e1a992..0f587e54c02 100644 --- a/addons/test_mail/tests/test_mail_mail.py +++ b/addons/test_mail/tests/test_mail_mail.py @@ -5,19 +5,17 @@ import psycopg2 from unittest.mock import call from odoo import api -from odoo.addons.base.tests.common import MockSmtplibCase from odoo.addons.test_mail.tests.common import TestMailCommon from odoo.tests import common, tagged from odoo.tools import mute_logger @tagged('mail_mail') -class TestMailMail(TestMailCommon, MockSmtplibCase): +class TestMailMail(TestMailCommon): @classmethod def setUpClass(cls): super(TestMailMail, cls).setUpClass() - cls._init_mail_gateway() cls._init_mail_servers() cls.server_domain_2 = cls.env['ir.mail_server'].create({ @@ -133,6 +131,7 @@ class TestMailMail(TestMailCommon, MockSmtplibCase): self.assert_email_sent_smtp(message_from='user_1@test_2.com', emails_count=5, from_filter=self.server_domain.from_filter) +@tagged('mail_mail') class TestMailMailRace(common.TransactionCase): @mute_logger('odoo.addons.mail.models.mail_mail') diff --git a/addons/test_mail/tests/test_mail_message.py b/addons/test_mail/tests/test_mail_message.py index f4bfe292a68..0ebbc5e9f05 100644 --- a/addons/test_mail/tests/test_mail_message.py +++ b/addons/test_mail/tests/test_mail_message.py @@ -18,7 +18,6 @@ class TestMessageValues(TestMailCommon): def setUpClass(cls): super(TestMessageValues, cls).setUpClass() - cls._init_mail_gateway() cls.alias_record = cls.env['mail.test.container'].with_context(cls._test_context).create({ 'name': 'Pigs', 'alias_name': 'pigs', diff --git a/addons/test_mail/tests/test_mail_multicompany.py b/addons/test_mail/tests/test_mail_multicompany.py index 18082651e69..58309a7482f 100644 --- a/addons/test_mail/tests/test_mail_multicompany.py +++ b/addons/test_mail/tests/test_mail_multicompany.py @@ -47,8 +47,6 @@ class TestMultiCompanySetup(TestMailCommon, TestRecipients): 'message_id': '<123456-openerp-%s-mail.test.gateway@%s>' % (cls.test_record.id, socket.gethostname()), }) - cls._init_mail_gateway() - @users('employee') def test_notify_reply_to_computation(self): test_record = self.env['mail.test.gateway'].browse(self.test_record.ids) diff --git a/addons/test_mail/tests/test_mail_thread_internals.py b/addons/test_mail/tests/test_mail_thread_internals.py index 58c7a0b2538..56b9258070a 100644 --- a/addons/test_mail/tests/test_mail_thread_internals.py +++ b/addons/test_mail/tests/test_mail_thread_internals.py @@ -103,6 +103,7 @@ class TestChatterTweaks(TestMailCommon, TestRecipients): self.assertTrue(record.name) +@tagged('mail_thread') class TestDiscuss(TestMailCommon, TestRecipients): @classmethod @@ -239,7 +240,12 @@ class TestDiscuss(TestMailCommon, TestRecipients): partner_ids=[self.user_employee.partner_id.id] ) self.assertFalse(message.has_error) - with self.mock_mail_gateway(sim_error='connect_smtp_notfound'): + + with self.mock_mail_gateway(): + def _connect(*args, **kwargs): + raise Exception("Some exception") + self.connect_mocked.side_effect = _connect + self.user_admin.notification_type = 'email' message2 = self.test_record.with_user(self.user_employee).message_post( body='Test', message_type='comment', subtype_xmlid='mail.mt_comment', diff --git a/addons/test_mail/tests/test_message_management.py b/addons/test_mail/tests/test_message_management.py index 57d24e47f92..e1e5ab203e9 100644 --- a/addons/test_mail/tests/test_message_management.py +++ b/addons/test_mail/tests/test_message_management.py @@ -34,8 +34,10 @@ class TestMailResend(TestMailCommon): def test_mail_resend_workflow(self): with self.assertSinglePostNotifications( [{'partner': partner, 'type': 'email', 'status': 'exception'} for partner in self.partners], - message_info={'message_type': 'notification'}, - sim_error='connect_failure'): + message_info={'message_type': 'notification'}): + def _connect(*args, **kwargs): + raise Exception("Some exception") + self.connect_mocked.side_effect = _connect message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype_xmlid='mail.mt_comment', message_type='notification') wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({}) diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py index 29a014a18a2..a6076f63606 100644 --- a/addons/test_mail/tests/test_performance.py +++ b/addons/test_mail/tests/test_performance.py @@ -48,6 +48,7 @@ class BaseMailPerformance(TransactionCaseWithUserDemo): # patch registry to simulate a ready environment self.patch(self.env.registry, 'ready', True) self._flush_tracking() + self._init_mail_gateway() def _init_mail_gateway(self): # setup mail gateway @@ -113,8 +114,6 @@ class TestBaseMailPerformance(BaseMailPerformance): } ]) - self._init_mail_gateway() - @users('__system__', 'demo') @warmup def test_read_mail(self): @@ -223,8 +222,6 @@ class TestMailAPIPerformance(BaseMailPerformance): 'groups_id': [(6, 0, [self.env.ref('base.group_user').id])], }) - self._init_mail_gateway() - # automatically follow activities, for backward compatibility concerning query count self.env.ref('mail.mt_activities').write({'default': True}) @@ -531,10 +528,8 @@ class TestMailComplexPerformance(BaseMailPerformance): self.partners |= Partners.create({'name': 'Test %s' % x, 'email': 'test%s@example.com' % x}) self.container.message_subscribe(self.partners.ids, subtype_ids=[ self.env.ref('mail.mt_comment').id, - self.env.ref('test_mail.st_mail_test_container_child_full').id] - ) - - self._init_mail_gateway() + self.env.ref('test_mail.st_mail_test_container_child_full').id + ]) # `test_complex_mail_mail_send` self.container.flush() @@ -991,8 +986,6 @@ class TestMailHeavyPerformancePost(BaseMailPerformance): 'res_id': 0, } for i in range(3)] - self._init_mail_gateway() - @mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') @users('employee') @warmup diff --git a/addons/test_mail_full/tests/test_sms_performance.py b/addons/test_mail_full/tests/test_sms_performance.py index 24108442039..1e63a832f0c 100644 --- a/addons/test_mail_full/tests/test_sms_performance.py +++ b/addons/test_mail_full/tests/test_sms_performance.py @@ -29,8 +29,6 @@ class TestSMSPerformance(BaseMailPerformance, sms_common.SMSCase): } for x in range(0, 10) ]) - self._init_mail_gateway() - @mute_logger('odoo.addons.sms.models.sms_sms') @users('employee') @warmup diff --git a/addons/test_mass_mailing/tests/test_mailing_server.py b/addons/test_mass_mailing/tests/test_mailing_server.py index 0bc1d35c222..53986f491a5 100644 --- a/addons/test_mass_mailing/tests/test_mailing_server.py +++ b/addons/test_mass_mailing/tests/test_mailing_server.py @@ -2,20 +2,17 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.addons.test_mass_mailing.tests.common import TestMassMailCommon -from odoo.addons.base.tests.common import MockSmtplibCase from odoo.tests import tagged from odoo.tests.common import users from odoo.tools import mute_logger @tagged('mass_mailing') -class TestMassMailingServer(TestMassMailCommon, MockSmtplibCase): +class TestMassMailingServer(TestMassMailCommon): @classmethod def setUpClass(cls): super(TestMassMailingServer, cls).setUpClass() - cls._init_mail_gateway() - cls.env['ir.mail_server'].search([]).unlink() cls._init_mail_servers() cls.recipients = cls._create_mailing_test_records(model='mailing.test.optout', count=8) diff --git a/odoo/addons/base/tests/common.py b/odoo/addons/base/tests/common.py index 0fd845209db..28ddff19b06 100644 --- a/odoo/addons/base/tests/common.py +++ b/odoo/addons/base/tests/common.py @@ -252,14 +252,14 @@ class MockSmtplibCase: self.testing_smtp_session = TestingSMTPSession() IrMailServer = self.env['ir.mail_server'] - connect = IrMailServer.connect - find_mail_server = IrMailServer._find_mail_server + connect_origin = IrMailServer.connect + find_mail_server_origin = IrMailServer._find_mail_server - with patch.object(type(IrMailServer), '_is_test_mode', lambda self: False), \ - patch('smtplib.SMTP_SSL', side_effect=lambda *args, **kwargs: self.testing_smtp_session), \ + with patch('smtplib.SMTP_SSL', side_effect=lambda *args, **kwargs: self.testing_smtp_session), \ patch('smtplib.SMTP', side_effect=lambda *args, **kwargs: self.testing_smtp_session), \ - patch.object(type(IrMailServer), 'connect', side_effect=connect) as connect_mocked, \ - patch.object(type(IrMailServer), '_find_mail_server', side_effect=find_mail_server) as find_mail_server_mocked: + patch.object(type(IrMailServer), '_is_test_mode', lambda self: False), \ + patch.object(type(IrMailServer), 'connect', wraps=IrMailServer, side_effect=connect_origin) as connect_mocked, \ + patch.object(type(IrMailServer), '_find_mail_server', side_effect=find_mail_server_origin) as find_mail_server_mocked: self.connect_mocked = connect_mocked self.find_mail_server_mocked = find_mail_server_mocked yield @@ -298,14 +298,16 @@ class MockSmtplibCase: ) @classmethod - def _init_mail_servers(cls): - cls.env['ir.config_parameter'].sudo().set_param('mail.catchall.domain', 'test.com') - cls.env['ir.config_parameter'].sudo().set_param('mail.default.from', 'notifications') - cls.env['ir.config_parameter'].sudo().set_param('mail.bounce.alias', 'bounce') - - cls.alias_bounce = 'bounce' + def _init_mail_config(cls): + cls.alias_bounce = 'bounce.test' cls.alias_domain = 'test.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) + cls.env['ir.config_parameter'].sudo().set_param('mail.bounce.alias', cls.alias_bounce) + @classmethod + def _init_mail_servers(cls): cls.env['ir.mail_server'].search([]).unlink() ir_mail_server_values = { diff --git a/odoo/addons/base/tests/test_ir_mail_server.py b/odoo/addons/base/tests/test_ir_mail_server.py index 448f04c2d06..c2a330067f9 100644 --- a/odoo/addons/base/tests/test_ir_mail_server.py +++ b/odoo/addons/base/tests/test_ir_mail_server.py @@ -15,6 +15,7 @@ from odoo.tools import mute_logger class TestIrMailServer(TransactionCase, MockSmtplibCase): def setUp(self): + self._init_mail_config() self._init_mail_servers() def _build_email(self, mail_from, return_path=None): @@ -231,7 +232,7 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): self.assertEqual(len(self.emails), 1) self.assert_email_sent_smtp( - smtp_from='bounce@xn--9caaaaaaa.com', + smtp_from='bounce.test@xn--9caaaaaaa.com', smtp_to_list=['dest@xn--example--i1a.com'], message_from='test@=?utf-8?b?w6nDqcOpw6nDqcOpw6k=?=.com', from_filter=False, @@ -322,7 +323,7 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): ) @mute_logger('odoo.models.unlink') - @patch.dict("odoo.tools.config.options", {"from_filter": "test.com"}) + @patch.dict("odoo.tools.config.options", {"from_filter": "test.com", "smtp_server": "example.com"}) def test_mail_server_binary_arguments_domain(self): """Test the configuration provided in the odoo-bin arguments. @@ -374,7 +375,7 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): ) @mute_logger('odoo.models.unlink') - @patch.dict("odoo.tools.config.options", {"from_filter": "test.com"}) + @patch.dict("odoo.tools.config.options", {"from_filter": "test.com", "smtp_server": "example.com"}) def test_mail_server_binary_arguments_domain_smtp_session(self): """Test the configuration provided in the odoo-bin arguments. @@ -417,7 +418,7 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase): ) @mute_logger('odoo.models.unlink') - @patch.dict('odoo.tools.config.options', {'from_filter': 'test.com'}) + @patch.dict('odoo.tools.config.options', {'from_filter': 'test.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"