diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py
index 004b2a959ae..26fbd22c4d3 100644
--- a/addons/mail/tests/common.py
+++ b/addons/mail/tests/common.py
@@ -501,12 +501,22 @@ class MockEmail(common.BaseCase, MockSmtplibCase):
expected['email_to'] = email_to_list
# fetch mail
- sent_mail = next(
- (mail for mail in self._mails
- if set(mail['email_to']) == set(expected['email_to']) and mail['email_from'] == expected['email_from']
- ), False)
- 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))
+ sent_mails = [
+ mail for mail in self._mails
+ if set(mail['email_to']) == set(expected['email_to']) and mail['email_from'] == expected['email_from']
+ ]
+ if len(sent_mails) > 1 and values.get('subject'):
+ # try to better filter
+ sent_mail = next((mail for mail in sent_mails if mail['subject'] == values['subject']), False)
+ else:
+ sent_mail = sent_mails[0] if sent_mails else False
+ debug_info = ''
+ if not sent_mail:
+ debug_info = '-'.join('From: %s-To: %s' % (mail['email_from'], mail['email_to']) for mail in self._mails)
+ self.assertTrue(
+ bool(sent_mail),
+ 'Expected mail from %s to %s not found in %s' % (expected['email_from'], expected['email_to'], debug_info)
+ )
# assert values
for val in direct_check:
diff --git a/addons/test_mail/data/mail_template_data.xml b/addons/test_mail/data/mail_template_data.xml
index 45452973061..944a1b7a95f 100644
--- a/addons/test_mail/data/mail_template_data.xml
+++ b/addons/test_mail/data/mail_template_data.xml
@@ -34,7 +34,7 @@
- Hello Hello
Test _message_log_batch
') + for test_record in test_records + ), + ) + for test_record, new_note in zip(test_records, new_notes): + self.assertMessageFields( + new_note, + {'author_id': self.partner_employee, + 'body': 'Test _message_log_batch
', + 'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)), + 'is_internal': True, + 'message_type': 'notification', + 'model': test_record._name, + 'notified_partner_ids': self.env['res.partner'], + 'reply_to': formataddr((self.company_admin.name, '%s@%s' % (self.alias_catchall, self.alias_domain))), + 'res_id': test_record.id, + 'subtype_id': self.env.ref('mail.mt_note'), + } + ) + + @users('employee') + def test_message_log_with_view(self): + test_records = self.test_records.with_env(self.env) + test_records.message_subscribe(self.partner_employee_2.ids) + + new_notes = test_records._message_log_with_view( + 'test_mail.mail_template_simple_test', + values={'partner': self.user_employee.partner_id} + ) + for test_record, new_note in zip(test_records, new_notes): + self.assertMessageFields( + new_note, + {'author_id': self.partner_employee, + 'body': f'Hello {self.user_employee.name}, this comes from {test_record.name}.
', + 'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)), + 'is_internal': True, + 'message_type': 'notification', + 'model': test_record._name, + 'notified_partner_ids': self.env['res.partner'], + 'reply_to': formataddr((self.company_admin.name, '%s@%s' % (self.alias_catchall, self.alias_domain))), + 'res_id': test_record.id, + 'subtype_id': self.env.ref('mail.mt_note'), + } + ) + + +@tagged('mail_post') +class TestMessagePost(TestMessagePostCommon, CronMixinCase): + + def test_initial_values(self): + self.assertFalse(self.test_record.message_ids) + self.assertFalse(self.test_record.message_follower_ids) + self.assertFalse(self.test_record.message_partner_ids) + @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') @users('employee') def test_message_post(self): @@ -508,6 +577,37 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase): partner_ids=self.partner_portal.ids, ) + @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') + @users('employee') + def test_message_post_defaults(self): + """ Test default values when posting a classic message. """ + test_record = self.env['mail.test.simple'].create([{'name': 'Defaults'}]) + creation_msg = test_record.message_ids + self.assertEqual(len(creation_msg), 1) + + with self.mock_mail_app(): + new_message = test_record.message_post( + body='Body', + partner_ids=[self.partner_employee_2.id], + ) + + self.assertMessageFields( + new_message, + {'author_id': self.partner_employee, + 'body': 'Body
', + 'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)), + 'is_internal': False, + 'message_type': 'notification', + 'model': test_record._name, + 'notified_partner_ids': self.partner_employee_2, + 'parent_id': creation_msg, + 'record_name': test_record.name, + 'reply_to': formataddr((f'{self.company_admin.name} {test_record.name}', f'{self.alias_catchall}@{self.alias_domain}')), + 'res_id': test_record.id, + 'subtype_id': self.env.ref('mail.mt_note'), + } + ) + @users('employee') def test_message_post_inactive_follower(self): """ Test posting with inactive followers does not notify them (e.g. odoobot) """ @@ -854,100 +954,202 @@ class TestMessagePostHelpers(TestMessagePostCommon): 'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)], }) + cls.user_employee.write({ + 'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)], + }) + + cls.test_records, cls.test_partners = cls._create_records_for_batch( + 'mail.test.ticket', + 10, + ) + @users('employee') - def test_message_log_with_view(self): - test_record = self.env['mail.test.simple'].browse(self.test_record.ids) - new_note = test_record._message_log_with_view( - 'test_mail.mail_template_simple_test', - values={'partner': self.user_employee.partner_id} - ) - self.assertMessageFields( - new_note, - {'author_id': self.partner_employee, - 'body': 'Hello %s,
' % self.user_employee.name, - 'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)), - 'is_internal': True, - 'message_type': 'notification', - 'model': test_record._name, - 'notified_partner_ids': self.env['res.partner'], - 'reply_to': formataddr((self.company_admin.name, '%s@%s' % (self.alias_catchall, self.alias_domain))), - 'res_id': test_record.id, - 'subtype_id': self.env.ref('mail.mt_note'), - } - ) - @mute_logger('odoo.addons.mail.models.mail_mail') - def test_message_post_w_template(self): - test_record = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'}) - + def test_message_mail_with_template(self): + """ Test sending mass mail on documents based on a template """ _attachments = self._generate_attachments_data(count=2, res_id=0, res_model='mail.template') email_1 = 'test1@example.com' email_2 = 'test2@example.com' - email_3 = self.partner_1.email - template = self._create_template('mail.test.simple', { + template = self._create_template('mail.test.ticket', { 'attachment_ids': [(0, 0, attach_vals) for attach_vals in _attachments], - 'partner_to': '%s,%s' % (self.partner_2.id, self.user_admin.partner_id.id), - 'email_to': '%s, %s' % (email_1, email_2), - 'email_cc': '%s' % email_3, + 'auto_delete': True, + # After the HTML sanitizer, it will become "Body for:
Hello {self.user_employee.partner_id.name}, this comes from {test_record.name}.
', + 'subject': 'About mass mailing', + }, + fields_values={ + 'auto_delete': False, + 'is_internal': False, + 'is_notification': True, # not auto_delete_message -> keep underlying mail.message + 'message_type': 'email', + 'model': test_record._name, + 'notified_partner_ids': self.env['res.partner'], + 'recipient_ids': test_record.customer_id, + 'to_delete': False, + 'subtype_id': self.env['mail.message.subtype'], + 'reply_to': formataddr((f'{self.company_admin.name} {test_record.name}', f'{self.alias_catchall}@{self.alias_domain}')), + 'res_id': test_record.id, + } + ) + + @users('employee') + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_message_post_with_template(self): + """ Test posting on a document based on a template content """ + _attachments = self._generate_attachments_data(count=2, res_id=0, res_model='mail.template') + email_1 = 'test1@example.com' + email_2 = 'test2@example.com' + template = self._create_template('mail.test.ticket', { + 'attachment_ids': [(0, 0, attach_vals) for attach_vals in _attachments], + 'auto_delete': True, + 'email_cc': self.partner_1.email, + 'email_to': f'{email_1}, {email_2}', + 'partner_to': '{{ object.customer_id.id }},%s' % self.partner_2.id, + }) + template.attachment_ids.write({'res_id': template.id}) # Force the attachments of the template to be in the natural order. self.email_template.invalidate_recordset(['attachment_ids']) + test_record = self.test_records.with_env(self.env)[0] + test_record.message_subscribe(test_record.customer_id.ids) with self.mock_mail_gateway(): - test_record.with_user(self.user_employee).message_post_with_template(self.email_template.id, composition_mode='comment') + _new_mail, new_message = test_record.with_user(self.user_employee).message_post_with_template( + self.email_template.id, + composition_mode='comment' + ) + # created partners from inline email addresses new_partners = self.env['res.partner'].search([('email', 'in', [email_1, email_2])]) - for r in [self.partner_1, self.partner_2, new_partners[0], new_partners[1], self.partner_admin]: - self.assertSentEmail( - self.user_employee.partner_id, - [r], - subject='About %s' % test_record.name, - body_content=test_record.name, - attachments=[ - ('AttFileName_00.txt', b'AttContent_00', 'text/plain'), - ('AttFileName_01.txt', b'AttContent_01', 'text/plain'), - ] - ) + self.assertEqual(len(new_partners), 2, + 'Post with template: should have created partners based on template emails') - @mute_logger('odoo.addons.mail.models.mail_mail') - def test_message_post_w_template_mass_mode(self): - test_record = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'}) - self.user_employee.write({ - 'groups_id': [(4, self.env.ref('base.group_partner_manager').id)], - }) + # check notifications have been sent + self.assertMailNotifications(new_message, [{ + 'content': f'Hello {test_record.name}
', + 'message_type': 'notification', + 'notif': [ + {'partner': self.partner_1, 'type': 'email'}, + {'partner': self.partner_2, 'type': 'email'}, + {'partner': new_partners[0], 'type': 'email'}, + {'partner': new_partners[1], 'type': 'email'}, + {'partner': test_record.customer_id, 'type': 'email'}, + ], + 'subtype': 'mail.mt_comment', + }]) + self.assertMessageFields( + new_message, + {'author_id': self.partner_employee, + 'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)), + 'is_internal': False, + 'model': test_record._name, + 'reply_to': formataddr((f'{self.company_admin.name} {test_record.name}', f'{self.alias_catchall}@{self.alias_domain}')), + 'res_id': test_record.id, + } + ) - _attachments = self._generate_attachments_data(count=2, res_id=0, res_model='mail.template') - template = self._create_template('mail.test.simple', { - 'attachment_ids': [(0, 0, attach_vals) for attach_vals in _attachments], - # After the HTML sanitizer, it will become "Body for:
Hello {self.user_employee.partner_id.name}, this comes from {test_record.name}.
', + 'message_type': 'notification', + 'notif': [ + {'partner': test_record.customer_id, 'type': 'email'}, + ], + 'subtype': 'mail.mt_comment', + }]) + self.assertMessageFields( + new_message, + {'author_id': self.partner_employee, + 'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)), + 'is_internal': False, + 'message_type': 'notification', + 'model': test_record._name, + 'reply_to': formataddr((f'{self.company_admin.name} {test_record.name}', f'{self.alias_catchall}@{self.alias_domain}')), + 'res_id': test_record.id, + } ) diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py index a4f74b4ba6f..2d3d8ef92bc 100644 --- a/addons/test_mail/tests/test_performance.py +++ b/addons/test_mail/tests/test_performance.py @@ -19,6 +19,13 @@ class BaseMailPerformance(MailCommon, TransactionCaseWithUserDemo): # creating partners is required notably witn template usage cls.user_employee.write({'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)]}) + cls.user_test = cls.env['res.users'].with_context(cls._test_context).create({ + 'name': 'Paulette Testouille', + 'login': 'paul', + 'email': 'user.test.paulette@example.com', + 'notification_type': 'inbox', + 'groups_id': [(6, 0, [cls.env.ref('base.group_user').id])], + }) cls.customer = cls.env['res.partner'].with_context(cls._test_context).create({ 'country_id': cls.env.ref('base.be').id, @@ -37,6 +44,57 @@ class BaseMailPerformance(MailCommon, TransactionCaseWithUserDemo): self.patch(self.env.registry, 'ready', True) self.flush_tracking() + def _create_test_records(self): + test_record_full = self.env['mail.test.ticket'].with_context(self._test_context).create({ + 'name': 'TestRecord', + 'customer_id': self.customer.id, + 'user_id': self.user_test.id, + 'email_from': 'nopartner.test@example.com', + }) + test_template_full = self.env['mail.template'].create({ + 'name': 'TestTemplate', + 'model_id': self.env['ir.model']._get('mail.test.ticket').id, + 'subject': 'About {{ object.name }}', + 'body_html': 'Hello
Hello
Hello
Test _message_log
', message_type='comment') + @users('__system__', 'employee') + @warmup + def test_message_log_batch(self): + records = self.env['mail.test.simple'].create([ + {'name': f'Test_{idx}'} + for idx in range(10) + ]) + + with self.assertQueryCount(__system__=1, employee=1): + records._message_log_batch( + bodies=dict( + (record.id, 'Test _message_log
') + for record in records + ), + message_type='comment') + + @users('__system__', 'employee') + @warmup + def test_message_log_with_view(self): + records = self.env['mail.test.simple'].create([ + {'name': f'Test_{idx}'} + for idx in range(10) + ]) + + with self.assertQueryCount(__system__=11, employee=11): + records._message_log_with_view( + 'test_mail.mail_template_simple_test', + values={'partner': self.customer.with_env(self.env)} + ) + @users('__system__', 'employee') @warmup def test_message_log_with_post(self): @@ -575,7 +642,7 @@ class TestMailAPIPerformance(BaseMailPerformance): def test_message_post_one_email_notification(self): record = self.env['mail.test.simple'].create({'name': 'Test'}) - with self.assertQueryCount(__system__=24, employee=28): + with self.assertQueryCount(__system__=24, employee=27): record.message_post( body='Test Post Performances with an email ping
', partner_ids=self.customer.ids, @@ -793,6 +860,29 @@ class TestMailComplexPerformance(BaseMailPerformance): self.assertEqual(record.message_ids[0].body, 'Adding stuff on %s
' % record.name) self.assertEqual(record.message_ids[0].notified_partner_ids, self.partners | self.user_portal.partner_id | self.customer) + @mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') + @users('__system__', 'employee') + @warmup + def test_complex_message_post_view(self): + _partners, test_records, test_template = self._create_test_records_for_batch() + + with self.assertQueryCount(__system__=3, employee=3): + composer = self.env['mail.compose.message'].with_context({ + 'active_ids': test_records.ids, + 'default_composition_mode': 'mass_mail', + 'default_model': test_records._name, + 'default_template_id': test_template.id, + }).create({}) + composer._onchange_template_id_wrapper() + + with self.assertQueryCount(__system__=121, employee=141): + messages_as_sudo = test_records.message_post_with_view( + 'test_mail.mail_template_simple_test', + values={'partner': self.user_test.partner_id}, + ) + + self.assertEqual(len(messages_as_sudo), 10) + @mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') @users('__system__', 'employee') @warmup