From d1361d9e3aaef6a3481d396db4edf5b3b4a283df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 13 Jan 2021 15:02:56 +0000 Subject: [PATCH] [IMP] (test_)mail: move and rewrite message tests related to channel or discuss Purpose of this commit is to move moderation tests directly into mail channel test file. That way it is easier to maintain or update them as they are linked to that specific model and not global to mail.thread. Also containing * remove unnecessary tests or merge tests belonging to the same category; * make tests more readable; Followup of odoo/odoo@e9571a4 Followup of odoo/odoo@1fadb3ec5d8d9c87888d6c814a6c9e65ae0a9f4a Task ID-2421795 closes odoo/odoo#64501 Signed-off-by: Thibault Delavallee (tde) --- addons/mail/tests/test_mail_channel.py | 95 ++++++++++ addons/test_mail/tests/test_mail_message.py | 177 ------------------ .../tests/test_mail_thread_internals.py | 53 +++++- 3 files changed, 147 insertions(+), 178 deletions(-) diff --git a/addons/mail/tests/test_mail_channel.py b/addons/mail/tests/test_mail_channel.py index b358a15d808..a26b8da1d7c 100644 --- a/addons/mail/tests/test_mail_channel.py +++ b/addons/mail/tests/test_mail_channel.py @@ -387,6 +387,101 @@ class TestChannelModeration(MailCommon): self.assertEqual(channel.channel_last_seen_partner_ids.partner_id, self.partner_employee | self.partner_root) self.assertEqual(channel.moderator_ids, self.user_employee) + @mute_logger('odoo.models.unlink') + def test_message_moderate_ok(self): + msg_c1_admin1 = self._add_messages(self.channel, 'Body11', author=self.partner_admin, moderation_status='pending_moderation') + msg_c1_admin2 = self._add_messages(self.channel, 'Body12', author=self.partner_admin, moderation_status='pending_moderation') + msg_c1_emplo2 = self._add_messages(self.channel, 'Body21', author=self.partner_employee_2, moderation_status='pending_moderation') + + self._reset_bus() + self.assertFalse(msg_c1_admin1.channel_ids | msg_c1_admin2.channel_ids | msg_c1_emplo2.channel_ids) + + # accept + msg_c1_admin1.with_user(self.user_employee)._moderate('accept') + self.assertEqual(msg_c1_admin1.channel_ids, self.channel) + self.assertEqual(msg_c1_admin1.moderation_status, 'accepted') + self.assertEqual(msg_c1_admin2.moderation_status, 'pending_moderation') + self.assertBusNotifications([(self.cr.dbname, 'mail.channel', self.channel.id)]) + + # allow + self._reset_bus() + (msg_c1_admin1 | msg_c1_emplo2).with_user(self.user_employee)._moderate('allow') + self.assertEqual(msg_c1_admin1.channel_ids, self.channel) + self.assertEqual(msg_c1_admin2.channel_ids, self.channel) + self.assertEqual(msg_c1_emplo2.channel_ids, self.channel) + self.assertEqual(msg_c1_admin1.moderation_status, 'accepted') + self.assertEqual(msg_c1_admin2.moderation_status, 'accepted') + self.assertEqual(msg_c1_emplo2.moderation_status, 'accepted') + self.assertBusNotifications([ + (self.cr.dbname, 'mail.channel', self.channel.id), + (self.cr.dbname, 'mail.channel', self.channel.id)]) + + @mute_logger('odoo.models.unlink') + def test_message_moderate_reject(self): + msg_c1_admin1 = self._add_messages(self.channel, '

Body11

', author=self.partner_admin, moderation_status='pending_moderation') + msg_c1_admin2 = self._add_messages(self.channel, '

Body12

', author=self.partner_admin, moderation_status='pending_moderation') + msg_c1_emplo2 = self._add_messages(self.channel, '

Body21

', author=self.partner_employee_2, moderation_status='pending_moderation') + msg_c1_portal = self._add_messages(self.channel, '

Body12

', author=self.partner_portal, moderation_status='pending_moderation') + id2, id4 = msg_c1_admin2.id, msg_c1_portal.id # save ids because unlink will discard them + + self.assertFalse(msg_c1_admin1.channel_ids | msg_c1_admin2.channel_ids | msg_c1_emplo2.channel_ids | msg_c1_portal.channel_ids) + + # test reject: should also send a rejection email + with self.mock_mail_gateway(): + (msg_c1_admin1 | msg_c1_emplo2).with_user(self.user_employee)._moderate('reject', title='RejectTitle', comment='RejectComment') + self.assertEqual(len(self._new_mails), 2) + + self.assertMailMailWEmails([self.partner_admin.email_formatted, self.partner_employee_2.email_formatted], 'outgoing', 'RejectComment', + fields_values={'subject': 'RejectTitle', 'author_id': self.partner_employee}) + self.assertTrue(all('RejectComment' in body for body in self._new_mails.mapped('body_html'))) + self.assertTrue(all('Body' in body for body in self._new_mails.mapped('body_html'))) + + # test discard: silently remove + self._reset_bus() + (msg_c1_admin2 + msg_c1_portal).with_user(self.user_employee)._moderate_discard() + + # check all generated bus notifications + self.assertBusNotifications( + [(self.cr.dbname, 'res.partner', self.partner_admin.id), + (self.cr.dbname, 'res.partner', self.partner_employee.id), + (self.cr.dbname, 'res.partner', self.partner_portal.id)], + [{'type': 'deletion', 'message_ids': [id2]}, # author of 1 message + {'type': 'deletion', 'message_ids': [id2, id4]}, # moderator + {'type': 'deletion', 'message_ids': [id4]}] # author of 1 message + ) + + @mute_logger('odoo.models.unlink') + def test_message_notify_moderators(self): + # create pending messages in another channel to have two notification to push + msg_c1_admin1 = self._add_messages(self.channel, '

Body11

', author=self.partner_admin, moderation_status='pending_moderation') + msg_c1_admin2 = self._add_messages(self.channel, '

Body12

', author=self.partner_admin, moderation_status='pending_moderation') + msg_c1_portal = self._add_messages(self.channel, '

Body21

', author=self.partner_employee_2, moderation_status='pending_moderation') + + channel_2 = self.env['mail.channel'].create({ + 'name': 'Moderation_1', + 'email_send': True, + 'moderation': True, + 'channel_partner_ids': [(4, self.partner_admin.id)], + 'moderator_ids': [(4, self.user_admin.id)], + }) + msg_c2_portal = self._add_messages(channel_2, 'Body31', author=self.partner_employee_2, moderation_status='pending_moderation') + + # one notification for each moderator: employee (channel1), admin (channel2) + with self.assertPostNotifications([{ + 'content': 'Hello %s' % self.partner_employee.name, + 'message_type': 'user_notification', 'subtype': 'mail.mt_note', + 'notif': [{ + 'partner': self.partner_employee, + 'type': 'inbox'}] + }, { + 'content': 'Hello %s' % self.partner_admin.name, + 'message_type': 'user_notification', 'subtype': 'mail.mt_note', + 'notif': [{ + 'partner': self.partner_admin, + 'type': 'inbox'}] + }]): + self.env['mail.message']._notify_moderators() + @users('employee') def test_moderation_fields(self): channel = self.env['mail.channel'].browse(self.channel.ids) diff --git a/addons/test_mail/tests/test_mail_message.py b/addons/test_mail/tests/test_mail_message.py index 256921dd244..b24c552f855 100644 --- a/addons/test_mail/tests/test_mail_message.py +++ b/addons/test_mail/tests/test_mail_message.py @@ -378,180 +378,3 @@ class TestMessageAccess(TestMailCommon): self.message.write({'model': 'mail.channel', 'res_id': self.group_public.id, 'moderation_status': 'pending_moderation'}) with self.assertRaises(AccessError): self.message.with_user(self.user_employee).write({'moderation_status': 'accepted'}) - - @mute_logger('openerp.addons.mail.models.mail_mail') - def test_mark_all_as_read(self): - self.user_employee.notification_type = 'inbox' - emp_partner = self.user_employee.partner_id.with_user(self.user_employee) - - group_private = self.env['mail.channel'].with_context({ - 'mail_create_nolog': True, - 'mail_create_nosubscribe': True, - 'mail_channel_noautofollow': True, - }).create({ - 'name': 'Private', - 'description': 'Private James R.', - 'public': 'private', - 'alias_name': 'private', - 'alias_contact': 'followers'} - ).with_context({'mail_create_nosubscribe': False}) - - # mark all as read clear needactions - msg1 = group_private.message_post(body='Test', message_type='comment', subtype_xmlid='mail.mt_comment', partner_ids=[emp_partner.id]) - self._reset_bus() - emp_partner.env['mail.message'].mark_all_as_read(domain=[]) - self.assertBusNotifications([(self.cr.dbname, 'res.partner', emp_partner.id)], [{ 'type': 'mark_as_read', 'message_ids': [msg1.id] }]) - na_count = emp_partner.get_needaction_count() - self.assertEqual(na_count, 0, "mark all as read should conclude all needactions") - - # mark all as read also clear inaccessible needactions - msg2 = group_private.message_post(body='Zest', message_type='comment', subtype_xmlid='mail.mt_comment', partner_ids=[emp_partner.id]) - needaction_accessible = len(emp_partner.env['mail.message'].search([['needaction', '=', True]])) - self.assertEqual(needaction_accessible, 1, "a new message to a partner is readable to that partner") - - msg2.sudo().partner_ids = self.env['res.partner'] - emp_partner.env['mail.message'].search([['needaction', '=', True]]) - needaction_length = len(emp_partner.env['mail.message'].search([['needaction', '=', True]])) - self.assertEqual(needaction_length, 1, "message should still be readable when notified") - - na_count = emp_partner.get_needaction_count() - self.assertEqual(na_count, 1, "message not accessible is currently still counted") - - self._reset_bus() - emp_partner.env['mail.message'].mark_all_as_read(domain=[]) - self.assertBusNotifications([(self.cr.dbname, 'res.partner', emp_partner.id)], [{ 'type': 'mark_as_read', 'message_ids': [msg2.id] }]) - na_count = emp_partner.get_needaction_count() - self.assertEqual(na_count, 0, "mark all read should conclude all needactions even inacessible ones") - - @mute_logger('openerp.addons.mail.models.mail_mail') - def test_mark_all_as_read_share(self): - self.user_portal.notification_type = 'inbox' - portal_partner = self.user_portal.partner_id.with_user(self.user_portal) - - # mark all as read clear needactions - self.group_pigs.message_post(body='Test', message_type='comment', subtype_xmlid='mail.mt_comment', partner_ids=[portal_partner.id]) - portal_partner.env['mail.message'].mark_all_as_read(domain=[]) - na_count = portal_partner.get_needaction_count() - self.assertEqual(na_count, 0, "mark all as read should conclude all needactions") - - # mark all as read also clear inaccessible needactions - new_msg = self.group_pigs.message_post(body='Zest', message_type='comment', subtype_xmlid='mail.mt_comment', partner_ids=[portal_partner.id]) - needaction_accessible = len(portal_partner.env['mail.message'].search([['needaction', '=', True]])) - self.assertEqual(needaction_accessible, 1, "a new message to a partner is readable to that partner") - - new_msg.sudo().partner_ids = self.env['res.partner'] - needaction_length = len(portal_partner.env['mail.message'].search([['needaction', '=', True]])) - self.assertEqual(needaction_length, 1, "message should still be readable when notified") - - na_count = portal_partner.get_needaction_count() - self.assertEqual(na_count, 1, "message not accessible is currently still counted") - - portal_partner.env['mail.message'].mark_all_as_read(domain=[]) - na_count = portal_partner.get_needaction_count() - self.assertEqual(na_count, 0, "mark all read should conclude all needactions even inacessible ones") - - -@tagged('moderation') -class TestMessageModeration(TestMailCommon): - - @classmethod - def setUpClass(cls): - super(TestMessageModeration, cls).setUpClass() - - cls.channel_1 = cls.env['mail.channel'].create({ - 'name': 'Moderation_1', - 'email_send': True, - 'moderation': True - }) - cls.user_employee.write({'moderation_channel_ids': [(6, 0, [cls.channel_1.id])]}) - cls.user_portal = cls._create_portal_user() - - # A pending moderation message needs to have field channel_ids empty. Moderators - # need to be able to notify a pending moderation message (in a channel they moderate). - cls.msg_c1_admin1 = cls._add_messages(cls.channel_1, 'Body11', author=cls.partner_admin, moderation_status='pending_moderation') - cls.msg_c1_admin2 = cls._add_messages(cls.channel_1, 'Body12', author=cls.partner_admin, moderation_status='pending_moderation') - cls.msg_c1_portal = cls._add_messages(cls.channel_1, 'Body21', author=cls.partner_portal, moderation_status='pending_moderation') - - @mute_logger('odoo.models.unlink') - def test_moderate_accept(self): - self._reset_bus() - self.assertFalse(self.msg_c1_admin1.channel_ids | self.msg_c1_admin2.channel_ids | self.msg_c1_portal.channel_ids) - - self.msg_c1_admin1.with_user(self.user_employee)._moderate('accept') - self.assertEqual(self.msg_c1_admin1.channel_ids, self.channel_1) - self.assertEqual(self.msg_c1_admin1.moderation_status, 'accepted') - self.assertEqual(self.msg_c1_admin2.moderation_status, 'pending_moderation') - self.assertBusNotifications([(self.cr.dbname, 'mail.channel', self.channel_1.id)]) - - @mute_logger('odoo.models.unlink') - def test_moderate_allow(self): - self._reset_bus() - - self.msg_c1_admin1.with_user(self.user_employee)._moderate('allow') - self.assertEqual(self.msg_c1_admin1.channel_ids, self.channel_1) - self.assertEqual(self.msg_c1_admin2.channel_ids, self.channel_1) - self.assertEqual(self.msg_c1_admin1.moderation_status, 'accepted') - self.assertEqual(self.msg_c1_admin2.moderation_status, 'accepted') - self.assertBusNotifications([ - (self.cr.dbname, 'mail.channel', self.channel_1.id), - (self.cr.dbname, 'mail.channel', self.channel_1.id)]) - - @mute_logger('odoo.models.unlink') - def test_moderate_reject(self): - with self.mock_mail_gateway(): - (self.msg_c1_admin1 | self.msg_c1_portal).with_user(self.user_employee)._moderate_send_reject_email('Title', 'Message to author') - self.assertEqual(len(self._new_mails), 2) - for mail in self._new_mails: - self.assertEqual(mail.author_id, self.partner_employee) - self.assertEqual(mail.subject, 'Title') - self.assertEqual(mail.state, 'outgoing') - self.assertEqual( - set(self._new_mails.mapped('email_to')), - set([self.msg_c1_admin1.email_from, self.msg_c1_portal.email_from]) - ) - self.assertEqual( - set(self._new_mails.mapped('body_html')), - set(['
Message to author
\n%s\n' % self.msg_c1_admin1.body, '
Message to author
\n%s\n' % self.msg_c1_portal.body]) - ) # TDE note: \n are added by append content to html, because why not - - @mute_logger('odoo.models.unlink') - def test_moderate_discard(self): - self._reset_bus() - id1, id2, id3 = self.msg_c1_admin1.id, self.msg_c1_admin2.id, self.msg_c1_portal.id # save ids because unlink will discard them - (self.msg_c1_admin1 | self.msg_c1_admin2 | self.msg_c1_portal).with_user(self.user_employee)._moderate_discard() - - self.assertBusNotifications( - [(self.cr.dbname, 'res.partner', self.partner_admin.id), - (self.cr.dbname, 'res.partner', self.partner_employee.id), - (self.cr.dbname, 'res.partner', self.partner_portal.id)], - [{'type': 'deletion', 'message_ids': [id1, id2]}, # author of 2 messages - {'type': 'deletion', 'message_ids': [id1, id2, id3]}, # moderator - {'type': 'deletion', 'message_ids': [id3]}] # author of 1 message - ) - - @mute_logger('odoo.models.unlink') - def test_notify_moderators(self): - # create pending messages in another channel to have two notification to push - channel_2 = self.env['mail.channel'].create({ - 'name': 'Moderation_1', - 'email_send': True, - 'moderation': True - }) - self.user_admin.write({'moderation_channel_ids': [(6, 0, [channel_2.id])]}) - self.msg_c2_portal = self._add_messages(channel_2, 'Body31', author=self.partner_portal, moderation_status='pending_moderation') - - # one notification for each moderator: employee (channel1), admin (channel2) - with self.assertPostNotifications([{ - 'content': 'Hello %s' % self.partner_employee.name, - 'message_type': 'user_notification', 'subtype': 'mail.mt_note', - 'notif': [{ - 'partner': self.partner_employee, - 'type': 'inbox'}] - }, { - 'content': 'Hello %s' % self.partner_admin.name, - 'message_type': 'user_notification', 'subtype': 'mail.mt_note', - 'notif': [{ - 'partner': self.partner_admin, - 'type': 'inbox'}] - }]): - self.env['mail.message']._notify_moderators() diff --git a/addons/test_mail/tests/test_mail_thread_internals.py b/addons/test_mail/tests/test_mail_thread_internals.py index afdf252210c..307049cdaff 100644 --- a/addons/test_mail/tests/test_mail_thread_internals.py +++ b/addons/test_mail/tests/test_mail_thread_internals.py @@ -1,8 +1,12 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from unittest.mock import patch +from unittest.mock import DEFAULT from werkzeug.urls import url_parse, url_decode +from odoo import exceptions +from odoo.addons.test_mail.models.test_mail_models import MailTestSimple from odoo.addons.test_mail.tests.common import TestMailCommon, TestRecipients from odoo.tests.common import tagged, HttpCase from odoo.tools import mute_logger @@ -107,7 +111,54 @@ class TestDiscuss(TestMailCommon, TestRecipients): @classmethod def setUpClass(cls): super(TestDiscuss, cls).setUpClass() - cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'}) + cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({ + 'name': 'Test', + 'email_from': 'ignasse@example.com' + }) + + @mute_logger('openerp.addons.mail.models.mail_mail') + def test_mark_all_as_read(self): + def _employee_crash(*args, **kwargs): + """ If employee is test employee, consider he has no access on document """ + recordset = args[0] + if recordset.env.uid == self.user_employee.id and not recordset.env.su: + if kwargs.get('raise_exception', True): + raise exceptions.AccessError('Hop hop hop Ernest, please step back.') + return False + return DEFAULT + + with patch.object(MailTestSimple, 'check_access_rights', autospec=True, side_effect=_employee_crash): + with self.assertRaises(exceptions.AccessError): + self.env['mail.test.simple'].with_user(self.user_employee).browse(self.test_record.ids).read(['name']) + + employee_partner = self.env['res.partner'].with_user(self.user_employee).browse(self.partner_employee.ids) + + # mark all as read clear needactions + msg1 = self.test_record.message_post(body='Test', message_type='comment', subtype_xmlid='mail.mt_comment', partner_ids=[employee_partner.id]) + self._reset_bus() + employee_partner.env['mail.message'].mark_all_as_read(domain=[]) + self.assertBusNotifications([(self.cr.dbname, 'res.partner', employee_partner.id)], [{ 'type': 'mark_as_read', 'message_ids': [msg1.id] }]) + na_count = employee_partner.get_needaction_count() + self.assertEqual(na_count, 0, "mark all as read should conclude all needactions") + + # mark all as read also clear inaccessible needactions + msg2 = self.test_record.message_post(body='Zest', message_type='comment', subtype_xmlid='mail.mt_comment', partner_ids=[employee_partner.id]) + needaction_accessible = len(employee_partner.env['mail.message'].search([['needaction', '=', True]])) + self.assertEqual(needaction_accessible, 1, "a new message to a partner is readable to that partner") + + msg2.sudo().partner_ids = self.env['res.partner'] + employee_partner.env['mail.message'].search([['needaction', '=', True]]) + needaction_length = len(employee_partner.env['mail.message'].search([['needaction', '=', True]])) + self.assertEqual(needaction_length, 1, "message should still be readable when notified") + + na_count = employee_partner.get_needaction_count() + self.assertEqual(na_count, 1, "message not accessible is currently still counted") + + self._reset_bus() + employee_partner.env['mail.message'].mark_all_as_read(domain=[]) + self.assertBusNotifications([(self.cr.dbname, 'res.partner', employee_partner.id)], [{ 'type': 'mark_as_read', 'message_ids': [msg2.id] }]) + na_count = employee_partner.get_needaction_count() + self.assertEqual(na_count, 0, "mark all read should conclude all needactions even inacessible ones") def test_set_message_done_user(self): with self.assertSinglePostNotifications([{'partner': self.partner_employee, 'type': 'inbox'}], message_info={'content': 'Test'}):