From a45afdd830f4181b692b2b7c1de5696ae9d1deec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 9 Dec 2022 14:46:52 +0000 Subject: [PATCH] [IMP] mail: add tests for private partners in composer Add tests showing current behavior of partner_ids fields on composer when dealing with private partners. Some use cases are added as unit tests * default_partner_ids: coming notably from suggested recipients; * template usage adding a partner (e.g. partner_to); * when not having the rights to see private partners, access error should be raised; * mail gateway when a private partner sends an email; X-original-commit: 2153eec790b730761fe50d6bea5cd4bacedce97a Part-of: odoo/odoo#109522 Co-authored-by: Robin Engels --- addons/mail/tests/test_mail_composer.py | 198 +++++++++++++++++++- addons/test_mail/tests/test_mail_gateway.py | 60 ++++-- 2 files changed, 237 insertions(+), 21 deletions(-) diff --git a/addons/mail/tests/test_mail_composer.py b/addons/mail/tests/test_mail_composer.py index 9b573efb5be..846c98f8f84 100644 --- a/addons/mail/tests/test_mail_composer.py +++ b/addons/mail/tests/test_mail_composer.py @@ -2,7 +2,9 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.addons.mail.tests.common import MailCommon -from odoo.tests import tagged, users +from odoo.exceptions import AccessError +from odoo.tests import Form, tagged, users +from odoo.tools import mute_logger @tagged('mail_composer') @@ -30,14 +32,199 @@ class TestMailComposer(MailCommon): """ cls.mail_template = cls.env['mail.template'].create({ - 'name': 'Test template with mso conditionals', - 'subject': 'MSO FTW', + 'auto_delete': True, 'body_html': cls.body_html, 'lang': '{{ object.lang }}', - 'auto_delete': True, - 'model_id': cls.env.ref('base.model_res_partner').id, + 'model_id': cls.env['ir.model']._get_id('res.partner'), + 'subject': 'MSO FTW', + 'name': 'Test template with mso conditionals', }) + +@tagged('mail_composer') +class TestMailComposerForm(TestMailComposer): + """ Test mail composer form view usage. """ + + @classmethod + def setUpClass(cls): + super(TestMailComposerForm, cls).setUpClass() + + cls.user_employee.write({'groups_id': [ + (4, cls.env.ref('base.group_private_addresses').id), + (4, cls.env.ref('base.group_partner_manager').id), + ]}) + cls.partner_private, cls.partner_private_2, cls.partner_classic = cls.env['res.partner'].create([ + { + 'email': 'private.customer@text.example.com', + 'phone': '0032455112233', + 'name': 'Private Customer', + 'type': 'private', + }, + { + 'email': 'private.customer.2@test.example.com', + 'phone': '0032455445566', + 'name': 'Private Customer 2', + 'type': 'private', + }, + { + 'email': 'not.private@test.example.com', + 'phone': '0032455778899', + 'name': 'Classic Customer', + 'type': 'contact', + } + ]) + + @mute_logger('odoo.addons.mail.models.mail_mail') + @users('employee') + def test_composer_default_recipients(self): + """ Test usage of a private partner in composer, as default value """ + partner_classic = self.partner_classic.with_env(self.env) + test_record = self.test_record.with_env(self.env) + + form = Form(self.env['mail.compose.message'].with_context({ + 'default_partner_ids': partner_classic.ids, + 'default_model': test_record._name, + 'default_res_id': test_record.id, + })) + form.body = '

Hello

' + self.assertEqual( + form.partner_ids._get_ids(), partner_classic.ids, + 'Default populates the field' + ) + saved_form = form.save() + self.assertEqual( + saved_form.partner_ids, partner_classic, + 'Default value is kept at save' + ) + + with self.mock_mail_gateway(): + saved_form._action_send_mail() + + message = self.test_record.message_ids[0] + self.assertEqual(message.body, '

Hello

') + self.assertEqual(message.partner_ids, partner_classic) + self.assertEqual(message.subject, f'{test_record.name}') + + @mute_logger('odoo.addons.mail.models.mail_mail') + @users('employee') + def test_composer_default_recipients_private(self): + """ Test usage of a private partner in composer, as default value """ + partner_private = self.partner_private.with_env(self.env) + partner_classic = self.partner_classic.with_env(self.env) + test_record = self.test_record.with_env(self.env) + + form = Form(self.env['mail.compose.message'].with_context({ + 'default_partner_ids': (partner_private + partner_classic).ids, + 'default_model': test_record._name, + 'default_res_id': test_record.id, + })) + form.body = '

Hello

' + self.assertEqual( + sorted(form.partner_ids._get_ids()), + sorted((partner_private + partner_classic).ids), + 'Default populates the field' + ) + saved_form = form.save() + self.assertEqual( + # saved_form.partner_ids, partner_private + partner_classic, + saved_form.partner_ids, partner_classic, + 'Default value is kept at save (FIXME: loosing private partner)' + ) + + with self.mock_mail_gateway(): + saved_form._action_send_mail() + + message = self.test_record.message_ids[0] + self.assertEqual(message.body, '

Hello

') + # self.assertEqual(message.partner_ids, partner_private + partner_classic) + self.assertEqual( + message.partner_ids, partner_classic, + 'FIXME: loosing private partner' + ) + self.assertEqual(message.subject, f'{test_record.name}') + + @mute_logger('odoo.addons.base.models.ir_rule', 'odoo.addons.mail.models.mail_mail') + @users('employee') + def test_composer_default_recipients_private_norights(self): + """ Test usage of a private partner in composer when not having the + rights to see them, as default value """ + self.user_employee.write({'groups_id': [ + (3, self.env.ref('base.group_private_addresses').id), + ]}) + with self.assertRaises(AccessError): + _name = self.partner_private.with_env(self.env).name + + partner_classic = self.partner_classic.with_env(self.env) + test_record = self.test_record.with_env(self.env) + + with self.assertRaises(AccessError): + _form = Form(self.env['mail.compose.message'].with_context({ + 'default_partner_ids': (self.partner_private + partner_classic).ids, + 'default_model': test_record._name, + 'default_res_id': test_record.id, + })) + + @mute_logger('odoo.addons.mail.models.mail_mail') + @users('employee') + def test_composer_template_recipients_private(self): + """ Test usage of a private partner in composer, comint from template + value """ + email_to_new = 'new.customer@test.example.com' + self.mail_template.write({ + 'email_to': f'{self.partner_private_2.email_formatted}, {email_to_new}', + 'partner_to': f'{self.partner_private.id},{self.partner_classic.id}', + }) + template = self.mail_template.with_env(self.env) + partner_private = self.partner_private.with_env(self.env) + partner_private_2 = self.partner_private_2.with_env(self.env) + partner_classic = self.partner_classic.with_env(self.env) + test_record = self.test_record.with_env(self.env) + + form = Form(self.env['mail.compose.message'].with_context({ + 'default_model': test_record._name, + 'default_res_id': test_record.id, + 'default_template_id': template.id, + })) + + # transformation from email_to into partner_ids: find or create + existing_partner = self.env['res.partner'].search( + [('email_normalized', '=', self.partner_private_2.email_normalized)] + ) + self.assertEqual(existing_partner, partner_private_2, 'Should find existing private contact') + new_partner = self.env['res.partner'].search( + [('email_normalized', '=', email_to_new)] + ) + self.assertEqual(new_partner.type, 'contact', 'Should create a new contact') + + self.assertEqual( + sorted(form.partner_ids._get_ids()), + sorted((partner_private + partner_classic + partner_private_2 + new_partner).ids), + 'Template populates the field with both email_to and partner_to' + ) + saved_form = form.save() + self.assertEqual( + # saved_form.partner_ids, partner_private + partner_classic + partner_private_2 + new_partner, + saved_form.partner_ids, partner_classic + new_partner, + 'Template value is kept at save (FIXME: loosing private partner)' + ) + + with self.mock_mail_gateway(): + saved_form._action_send_mail() + + message = self.test_record.message_ids[0] + self.assertIn('

Hello sir!

', message.body) + # self.assertEqual(message.partner_ids, partner_private + partner_classic + partner_private_2 + new_partner) + self.assertEqual( + message.partner_ids, partner_classic + new_partner, + 'FIXME: loosing private partner' + ) + self.assertEqual(message.subject, 'MSO FTW') + + +@tagged('mail_composer') +class TestMailComposerRendering(TestMailComposer): + """ Test rendering and support of various html tweaks in composer """ + @users('employee') def test_mail_mass_mode_template_with_mso(self): mail_compose_message = self.env['mail.compose.message'].create({ @@ -55,6 +242,7 @@ class TestMailComposer(MailCommon): 'We must preserve (mso) comments in email html' ) + @mute_logger('odoo.addons.mail.models.mail_mail') @users('employee') def test_mail_mass_mode_compose_with_mso(self): composer = self.env['mail.compose.message'].with_context({ diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py index 1a79c8cdeb6..9bec4123ac4 100644 --- a/addons/test_mail/tests/test_mail_gateway.py +++ b/addons/test_mail/tests/test_mail_gateway.py @@ -118,6 +118,7 @@ class TestEmailParsing(TestMailCommon): class TestMailAlias(TestMailCommon): @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', @@ -309,7 +310,24 @@ class TestMailgateway(TestMailCommon): @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_process_followers(self): - """ Incoming email: recognized author not archived and not odoobot: added as follower """ + """ Incoming email: recognized author not archived and not odoobot: + added as follower. Also test corner cases: archived, private. """ + partner_archived, partner_private = self.env['res.partner'].create([ + { + 'active': False, + 'email': 'archived.customer@text.example.com', + 'phone': '0032455112233', + 'name': 'Archived Customer', + 'type': 'contact', + }, + { + 'email': 'private.customer@text.example.com', + 'phone': '0032455112233', + 'name': 'Private Customer', + 'type': 'private', + }, + ]) + with self.mock_mail_gateway(): record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com') @@ -335,20 +353,17 @@ class TestMailgateway(TestMailCommon): 'message_process: unrecognized email -> no follower') # archived partner -> no follower - self.partner_1.active = False - self.partner_1.flush_recordset() with self.mock_mail_gateway(): record3 = self.format_and_process( - MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com', - subject='Yet Another Email') + MAIL_TEMPLATE, partner_archived.email_formatted, 'groups@test.com', + subject='Archived Partner') self.assertEqual(record3.message_ids[0].author_id, self.env['res.partner']) - self.assertEqual(record3.message_ids[0].email_from, self.partner_1.email_formatted) + self.assertEqual(record3.message_ids[0].email_from, partner_archived.email_formatted) self.assertEqual(record3.message_follower_ids.partner_id, self.env['res.partner'], - 'message_process: unrecognized email -> no follower') + 'message_process: archived partner -> no follower') self.assertEqual(record3.message_partner_ids, self.env['res.partner'], - 'message_process: unrecognized email -> no follower') - + 'message_process: archived partner -> no follower') # partner_root -> never again odoobot = self.env.ref('base.partner_root') @@ -362,9 +377,22 @@ class TestMailgateway(TestMailCommon): self.assertEqual(record4.message_ids[0].author_id, odoobot) self.assertEqual(record4.message_ids[0].email_from, odoobot.email_formatted) self.assertEqual(record4.message_follower_ids.partner_id, self.env['res.partner'], - 'message_process: unrecognized email -> no follower') + 'message_process: odoobot -> no follower') self.assertEqual(record4.message_partner_ids, self.env['res.partner'], - 'message_process: unrecognized email -> no follower') + 'message_process: odoobot -> no follower') + + # private partner + with self.mock_mail_gateway(): + record5 = self.format_and_process( + MAIL_TEMPLATE, partner_private.email_formatted, 'groups@test.com', + subject='Private Partner') + + self.assertEqual(record5.message_ids[0].author_id, partner_private) + self.assertEqual(record5.message_ids[0].email_from, partner_private.email_formatted) + self.assertEqual(record5.message_follower_ids.partner_id, partner_private, + 'message_process: private partner is recognized') + self.assertEqual(record5.message_partner_ids, partner_private, + 'message_process: private partner is recognized') # -------------------------------------------------- # Author recognition @@ -399,7 +427,7 @@ class TestMailgateway(TestMailCommon): 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') + @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink') 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}) @@ -720,7 +748,7 @@ class TestMailgateway(TestMailCommon): self.assertEqual(record.message_ids[0].create_uid, self.user_employee) self.assertEqual(record.message_ids[0].author_id, self.user_employee.partner_id) - @mute_logger('odoo.addons.mail.models.mail_thread') + @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, @@ -1017,7 +1045,7 @@ class TestMailgateway(TestMailCommon): self.assertEqual(self.partner_1.message_bounce, 0) self.assertEqual(self.test_record.message_bounce, 1) - @mute_logger('odoo.addons.mail.models.mail_thread') + @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink') def test_message_process_bounce_records_channel(self): """ Test blacklist allow to multi-bounce and auto update of mail.channel """ other_record = self.env['mail.test.gateway'].create({ @@ -1090,7 +1118,7 @@ class TestMailgateway(TestMailCommon): self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) self.assertEqual(self.fake_email.child_ids, self.test_record.message_ids[0]) - @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') + @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail', 'odoo.tests') def test_message_process_references_multi_parent(self): """ Incoming email with multiple references """ reply1 = self._create_gateway_message( @@ -1149,7 +1177,7 @@ class TestMailgateway(TestMailCommon): self.assertEqual(new_msg.parent_id, self.fake_email, 'Mail: flattening attach to original message') self.assertEqual(new_msg.subtype_id, self.env.ref('mail.mt_comment'), 'Mail: parent should be a comment (before flattening)') - @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') + @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail', 'odoo.tests') def test_message_process_references_multi_parent_notflat(self): """ Incoming email with multiple references with ``_mail_flat_thread`` being False (mail.group/mail.channel behavior like). """