Files
odoo_source/addons/test_mail/tests/test_mail_resend.py
T
Thibault Delavallée 488a5d5af5 [IMP] mail: prepare new SMS model by updating mail.notification
This commit prepares SMS refactoring by updating some mail models. Purpose
is to be able to store SMS notification information inside existing mail
flow and models.

Main changes

  * mail.notification: define a notification_type field to store the medium
    used to notify people. In mail two ways exist: inbox and email. It will
    ease introduction of SMS notification mechanism. This field replaces the
    is_email boolean field;
  * mail.notification: make res_partner_id field not required. This means
    notifications could be linked to something else than a partner. An SMS
    for example. For Inbox and email notifications partner is still required
    and a constraint is added accordingly;
  * mail.thread: let notification methods handle the creation and update of
    their notification instead of creating them in _notify_thread and tweaking
    them in sub notification methods;
  * mail.thread: clearly move inbox-style notification in its own method like
    notify by email;

Some lighter code changes

  * propagate message_type to notification recipient computation. It will
    allow for example to be more precise when computing a notification type
    depending on the message_type. For example, send a notification by SMS
    when the message_type is SMS;
  * ease inheritance of ``_notify_thread`` by returning computed recipients
    data. It will allow to work on it without having to re-compute it;
  * propagate kwargs from message_post and message_notify to notify methods.
    This allow to avoid depending on context and set explicit parameters.
    Drawback is that message_post and notify must separate kwargs used to
    create a message and those that are propagated to notification methods;
  * update various notification check to ensure they work on email
    notifications, notably the resend and cancel wizards;

One side effect of this commit is that notifications are created by the
relevant notification method. Previously all notifications were created
by writing on needaction_partner_ids fields then updated according to the
notification process. Notably there could be too much notification created
when sending emails due to _notify_customize_recipients not being correctly
synchronized with notification. This issue is now solved as only really
sent emails create notifications.

Migration tips

  * notification_type: notification.is_email and 'email' else 'inbox';
  * remove is_email;

Related to task 1922163
Linked to PR #33510
2019-07-12 14:54:11 +00:00

131 lines
7.4 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import itertools
from unittest.mock import patch
from odoo import api
from odoo.addons.base.models.ir_mail_server import IrMailServer
from odoo.addons.test_mail.tests import common
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.tests import tagged
from odoo.tools import mute_logger
@tagged('resend_test')
class TestMailResend(common.BaseFunctionalTest, common.MockEmails):
@classmethod
def setUpClass(cls):
super(TestMailResend, cls).setUpClass()
#Two users
cls.user1 = mail_new_test_user(cls.env, login='e1', groups='base.group_public', name='Employee 1', email='e1') # invalid email
cls.user2 = mail_new_test_user(cls.env, login='e2', groups='base.group_portal', name='Employee 2', email='e2@example.com')
#Two partner
cls.partner1 = cls.env['res.partner'].with_context(common.BaseFunctionalTest._test_context).create({
'name': 'Partner 1',
'email': 'p1' # invalid email
})
cls.partner2 = cls.env['res.partner'].with_context(common.BaseFunctionalTest._test_context).create({
'name': 'Partner 2',
'email': 'p2@example.com'
})
@api.model
def send_email(self, message, *args, **kwargs):
assert '@' in message['To'], self.NO_VALID_RECIPIENT
return message['Message-Id']
cls.bus_update_failure = []
def sendone(self, channel, message):
if 'type' in message and message['type'] == 'mail_failure':
cls.bus_update_failure.append((channel, message))
cls.env['ir.mail_server']._patch_method('send_email', send_email)
cls.env['bus.bus']._patch_method('sendone', sendone)
cls.partners = cls.env['res.partner'].concat(cls.user1.partner_id, cls.user2.partner_id, cls.partner1, cls.partner2)
cls.invalid_email_partners = cls.env['res.partner'].concat(cls.user1.partner_id, cls.partner1)
def setUp(self):
super(TestMailResend, self).setUp()
TestMailResend.bus_update_failure = []
def assertNotifStates(self, states, message):
notif = self.env['mail.notification'].search([('mail_message_id', '=', message.id)], order="res_partner_id asc")
self.assertEquals(tuple(notif.mapped('notification_status')), states)
return notif
def assertBusMessage(self, partners):
partner_ids = [elem[0][2] for elem in self.bus_update_failure]
self.assertEquals(partner_ids, [partner.id for partner in partners])
self.bus_update_failure.clear()
@classmethod
def tearDownClass(cls):
# Remove mocks
cls.env['ir.mail_server']._revert_method('send_email')
cls.env['bus.bus']._revert_method('sendone')
super(TestMailResend, cls).tearDownClass()
def assertEmails(self, *args, **kwargs):
res = super(TestMailResend, self).assertEmails(*args, **kwargs)
self._mails.clear()
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_resend_workflow(self):
cls = TestMailResend
#missconfigured server
@api.model
def connect_failure(**kwargs):
raise Exception
with patch.object(IrMailServer, 'connect', side_effect=connect_failure):
message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
self.assertBusMessage([self.partner_admin])
self.assertEmails(self.partner_admin, [])
self.assertNotifStates(('exception', 'exception', 'exception', 'exception'), message)
wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({})
self.assertEqual(wizard.notification_ids.mapped('res_partner_id'), self.partners, "wizard should manage notifications for each failed partner")
wizard.resend_mail_action()
self.assertBusMessage([self.partner_admin] * 3) # three more failure sent on bus, one for each mail in failure and one for resend
self.assertEmails(self.partner_admin, self.partners)
self.assertNotifStates(('exception', 'sent', 'exception', 'sent'), message)
self.user1.write({"email": 'u1@example.com'})
self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({}).resend_mail_action()
self.assertBusMessage([self.partner_admin] * 2) # two more failure update sent on bus, one for failed mail and one for resend
self.assertEmails(self.partner_admin, self.invalid_email_partners)
self.assertNotifStates(('sent', 'sent', 'exception', 'sent'), message)
self.partner1.write({"email": 'p1@example.com'})
self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({}).resend_mail_action()
self.assertBusMessage([self.partner_admin]) # A success update should be sent on bus once the email has no more failure
self.assertEmails(self.partner_admin, self.partner1)
self.assertNotifStates(('sent', 'sent', 'sent', 'sent'), message)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_send_no_failure(self):
self.user1.write({"email": 'u1@example.com'})
self.partner1.write({"email": 'p1@example.com'})
message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
self.assertBusMessage([]) # one update for cancell
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_remove_mail_become_canceled(self):
message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
self.assertEmails(self.partner_admin, self.partners)
self.assertBusMessage([self.partner_admin] * 2) # two failure sent on bus, one for each mail
wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({})
partners = wizard.partner_ids.mapped("partner_id")
self.assertEqual(self.invalid_email_partners, partners)
wizard.partner_ids.filtered(lambda p: p.partner_id == self.partner1).write({"resend": False})
wizard.resend_mail_action()
self.assertEmails(self.partner_admin, self.user1)
self.assertBusMessage([self.partner_admin] * 2) # two more failure sent on bus, one for failure, one for resend
self.assertNotifStates(('exception', 'sent', 'canceled', 'sent'), message)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_cancel_all(self):
message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
self.assertNotifStates(('exception', 'sent', 'exception', 'sent'), message)
self.assertBusMessage([self.partner_admin] * 2)
wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({})
wizard.cancel_mail_action()
self.assertNotifStates(('canceled', 'sent', 'canceled', 'sent'), message)
self.assertBusMessage([self.partner_admin]) # one update for cancell