[MERGE] test_mail: reorganize tests, lessen data creation

PURPOSE

In order to rewrite some tests, remove low-level tests and add coverage
first step is to reorganize a bit test_mail content. Several tests cases
are spread among several files and finding back some feature coverage
tests is not easy.

SPECIFICATIONS

Quickly clean common class and lessen data creation by default. Offer some
tool methods, notably to create portal users used in some tests but not
all, or to create mail templates.

Reorganize tests, notably

  * put message_post related test in test_message_post;
  * have only composer related test in test_message_compose(r);
  * put alias tests in test_mail_gateway;
  * put moderation tests in test_channel, except those related to mail
    message model that should be in test_mail_message;
  * put mail.thread.cc specific behavior tests in mail_gateway for gateway
    related tests and test_discuss for recipient suggestion tests;
  * put composer with template tests in with composer tests, put post with
    template tests with post tests, put tracking multi company test with tracking
    tests;
  * rename mail-resend to message_management and multi_company to
    mail_redirect;

This merge does not change anything functionally. Tests are globally the same.
having better organized files will allow to rewrite and improve them.

LINKS

Task 1958697

closes odoo/odoo#36508

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Odoo's Mergebot
2019-09-06 16:31:57 +02:00
committed by GitHub
21 changed files with 820 additions and 744 deletions
+9 -11
View File
@@ -1,22 +1,20 @@
# -*- coding: utf-8 -*-
from . import test_invite
from . import test_ir_actions
from . import test_discuss
from . import test_mail_activity
from . import test_mail_followers
from . import test_mail_message
from . import test_mail_mail
from . import test_mail_race
from . import test_mail_resend
from . import test_mail_cc
from . import test_mail_channel
from . import test_mail_gateway
from . import test_mail_redirect
from . import test_mail_template
from . import test_message_compose
from . import test_message_composer
from . import test_message_management
from . import test_message_post
from . import test_message_track
from . import test_invite
from . import test_ir_actions
from . import test_update_notification
from . import test_discuss
from . import test_performance
from . import test_res_users
from . import test_odoobot
from . import test_multi_company
from . import test_performance
from . import test_update_notification
+48 -3
View File
@@ -27,15 +27,43 @@ class BaseFunctionalTest(common.SavepointCase):
def setUpClass(cls):
super(BaseFunctionalTest, cls).setUpClass()
cls.user_employee = mail_new_test_user(cls.env, login='ernest', groups='base.group_user', signature='--\nErnest', name='Ernest Employee')
cls.user_employee = mail_new_test_user(cls.env, login='employee', groups='base.group_user', signature='--\nErnest', name='Ernest Employee')
cls.partner_employee = cls.user_employee.partner_id
cls.user_admin = cls.env.ref('base.user_admin')
cls.partner_admin = cls.env.ref('base.partner_admin')
@classmethod
def _create_channel_listener(cls):
cls.channel_listen = cls.env['mail.channel'].with_context(cls._test_context).create({'name': 'Listener'})
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
@classmethod
def _create_portal_user(cls):
cls.user_portal = mail_new_test_user(cls.env, login='chell', groups='base.group_portal', name='Chell Gladys')
cls.partner_portal = cls.user_portal.partner_id
@classmethod
def _create_template(cls, model, template_values=None):
create_values = {
'name': 'TestTemplate',
'subject': 'About ${object.name}',
'body_html': '<p>Hello ${object.name}</p>',
'model_id': cls.env['ir.model']._get(model).id,
'user_signature': False,
}
if template_values:
create_values.update(template_values)
cls.email_template = cls.env['mail.template'].create(create_values)
return cls.email_template
@classmethod
def _init_mail_gateway(cls):
cls.alias_domain = 'test.com'
cls.alias_catchall = 'catchall.test'
cls.alias_bounce = 'bounce.test'
cls.env['ir.config_parameter'].set_param('mail.bounce.alias', cls.alias_bounce)
cls.env['ir.config_parameter'].set_param('mail.catchall.domain', cls.alias_domain)
cls.env['ir.config_parameter'].set_param('mail.catchall.alias', cls.alias_catchall)
@classmethod
def _reset_mail_context(cls, record):
@@ -62,6 +90,8 @@ class BaseFunctionalTest(common.SavepointCase):
init[partner] = {
'na_counter': len([n for n in init_notifs if n.res_partner_id == partner and not n.is_read]),
}
if hasattr(self, '_init_mock_build_email'):
self._init_mock_build_email()
yield
finally:
new_notifications = self.env['mail.notification'].sudo().search([
@@ -143,12 +173,27 @@ class BaseFunctionalTest(common.SavepointCase):
else:
raise AssertionError('Keys %s not found (expected: %s - returned: %s)' % (not_found_keys, repr(expected), repr(notif_messages)))
def assertTracking(self, message, data):
tracking_values = message.sudo().tracking_value_ids
for field_name, value_type, old_value, new_value in data:
tracking = tracking_values.filtered(lambda track: track.field == field_name)
self.assertEqual(len(tracking), 1)
if value_type in ('char', 'integer'):
self.assertEqual(tracking.old_value_char, old_value)
self.assertEqual(tracking.new_value_char, new_value)
elif value_type in ('many2one'):
self.assertEqual(tracking.old_value_integer, old_value and old_value.id or False)
self.assertEqual(tracking.new_value_integer, new_value and new_value.id or False)
self.assertEqual(tracking.old_value_char, old_value and old_value.display_name or '')
self.assertEqual(tracking.new_value_char, new_value and new_value.display_name or '')
else:
self.assertEqual(1, 0)
@contextmanager
def sudo(self, login):
old_uid = self.uid
try:
user = self.env['res.users'].sudo().search([('login', '=', login)])
self.test_record_old = self.test_record
# switch user
self.uid = user.id
self.env = self.env(user=self.uid)
+30 -59
View File
@@ -2,11 +2,16 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.test_mail.tests.common import BaseFunctionalTest, TestRecipients, MockEmails
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.tools import mute_logger
class TestChatterTweaks(BaseFunctionalTest, TestRecipients):
@classmethod
def setUpClass(cls):
super(TestChatterTweaks, cls).setUpClass()
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
def test_post_no_subscribe_author(self):
original = self.test_record.message_follower_ids
self.test_record.with_user(self.user_employee).with_context({'mail_create_nosubscribe': True}).message_post(
@@ -14,6 +19,7 @@ class TestChatterTweaks(BaseFunctionalTest, TestRecipients):
self.assertEqual(self.test_record.message_follower_ids.mapped('partner_id'), original.mapped('partner_id'))
self.assertEqual(self.test_record.message_follower_ids.mapped('channel_id'), original.mapped('channel_id'))
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_no_subscribe_recipients(self):
original = self.test_record.message_follower_ids
self.test_record.with_user(self.user_employee).with_context({'mail_create_nosubscribe': True}).message_post(
@@ -21,6 +27,7 @@ class TestChatterTweaks(BaseFunctionalTest, TestRecipients):
self.assertEqual(self.test_record.message_follower_ids.mapped('partner_id'), original.mapped('partner_id'))
self.assertEqual(self.test_record.message_follower_ids.mapped('channel_id'), original.mapped('channel_id'))
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_subscribe_recipients(self):
original = self.test_record.message_follower_ids
self.test_record.with_user(self.user_employee).with_context({'mail_create_nosubscribe': True, 'mail_post_autofollow': True}).message_post(
@@ -72,42 +79,23 @@ class TestChatterTweaks(BaseFunctionalTest, TestRecipients):
self.assertEqual(len(rec.sudo().mapped('message_ids.tracking_value_ids')), 0,
"Creation message without tracking values should have been posted")
def test_cache_invalidation(self):
""" Test that creating a mail-thread record does not invalidate the whole cache. """
# make a new record in cache
record = self.env['res.partner'].new({'name': 'Brave New Partner'})
self.assertTrue(record.name)
class TestNotifications(BaseFunctionalTest, MockEmails):
# creating a mail-thread record should not invalidate the whole cache
self.env['res.partner'].create({'name': 'Actual Partner'})
self.assertTrue(record.name)
def setUp(self):
self.partner_1 = self.env['res.partner'].with_context(BaseFunctionalTest._test_context).create({
'name': 'Valid Lelitre',
'email': 'valid.lelitre@agrolait.com'})
(self.user_employee | self.user_admin).write({'notification_type': 'inbox'})
super(TestNotifications, self).setUp()
class TestDiscuss(BaseFunctionalTest, TestRecipients, MockEmails):
def test_needaction(self):
with self.assertNotifications(partner_employee=(1, 'inbox', 'unread'), partner_admin=(0, '', '')):
self.test_record.message_post(
body='Test', message_type='comment', subtype='mail.mt_comment',
partner_ids=[self.user_employee.partner_id.id])
self.test_record.message_subscribe([self.partner_1.id])
with self.assertNotifications(partner_employee=(1, 'inbox', 'unread'), partner_admin=(0, '', ''), partner_1=(1, 'email', 'read')):
self.test_record.message_post(
body='Test', message_type='comment', subtype='mail.mt_comment',
partner_ids=[self.user_employee.partner_id.id])
def test_inactive_follower(self):
# In some case odoobot is follower of a record.
# Even if it shouldn't be the case, we want to be sure that odoobot is not notified
self.test_record._message_subscribe(self.user_employee.partner_id.ids)
with self.assertNotifications(partner_employee=(1, 'inbox', 'unread')):
self.test_record.message_post(
body='Test', message_type='comment', subtype='mail.mt_comment')
self.user_employee.active = False
# at this point, partner is still active and would receive an email notification
self.user_employee.partner_id._write({'active': False})
with self.assertNotifications(partner_employee=(0, '', '')):
self.test_record.message_post(
body='Test', message_type='comment', subtype='mail.mt_comment')
@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'})
def test_set_message_done_user(self):
with self.assertNotifications(partner_employee=(0, '', '')):
@@ -116,16 +104,6 @@ class TestNotifications(BaseFunctionalTest, MockEmails):
partner_ids=[self.user_employee.partner_id.id])
message.with_user(self.user_employee).set_message_done()
def test_set_message_done_portal(self):
user_portal = mail_new_test_user(self.env, login='chell', groups='base.group_portal', name='Chell Gladys', notification_type='inbox')
self.partner_portal = user_portal.partner_id
with self.assertNotifications(partner_employee=(1, 'inbox', 'unread'), partner_portal=(1, 'inbox', 'read')):
message = self.test_record.message_post(
body='Test', message_type='comment', subtype='mail.mt_comment',
partner_ids=[self.user_employee.partner_id.id, user_portal.partner_id.id])
message.with_user(user_portal).set_message_done()
def test_set_star(self):
msg = self.test_record.with_user(self.user_admin).message_post(body='My Body', subject='1')
msg_emp = self.env['mail.message'].with_user(self.user_employee).browse(msg.id)
@@ -143,19 +121,12 @@ class TestNotifications(BaseFunctionalTest, MockEmails):
self.assertFalse(msg.starred)
self.assertTrue(msg_emp.starred)
class TestChatterMisc(BaseFunctionalTest):
def test_alias_setup(self):
alias = self.env['mail.alias'].with_context(alias_model_name='mail.test').create({'alias_name': 'b4r+_#_R3wl$$'})
self.assertEqual(alias.alias_name, 'b4r+_-_r3wl-', 'Disallowed chars should be replaced by hyphens')
def test_cache_invalidation(self):
""" Test that creating a mail-thread record does not invalidate the whole cache. """
# make a new record in cache
record = self.env['res.partner'].new({'name': 'Brave New Partner'})
self.assertTrue(record.name)
# creating a mail-thread record should not invalidate the whole cache
self.env['res.partner'].create({'name': 'Actual Partner'})
self.assertTrue(record.name)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_cc_recipient_suggestion(self):
record = self.env['mail.test.cc'].create({'email_cc': 'cc1@example.com, cc2@example.com, cc3 <cc3@example.com>'})
suggestions = record._message_get_suggested_recipients()[record.id]
self.assertEqual(sorted(suggestions), [
(False, 'cc1@example.com', 'CC Email'),
(False, 'cc2@example.com', 'CC Email'),
(False, 'cc3 <cc3@example.com>', 'CC Email')
], 'cc should be in suggestions')
+5 -4
View File
@@ -9,21 +9,22 @@ class TestInvite(common.BaseFunctionalTest, common.MockEmails):
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_invite_email(self):
test_partner = self.env['res.partner'].with_context(common.BaseFunctionalTest._test_context).create({
test_record = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
test_partner = self.env['res.partner'].with_context(self._test_context).create({
'name': 'Valid Lelitre',
'email': 'valid.lelitre@agrolait.com'})
mail_invite = self.env['mail.wizard.invite'].with_context({
'default_res_model': 'mail.test.simple',
'default_res_id': self.test_record.id
'default_res_id': test_record.id
}).with_user(self.user_employee).create({
'partner_ids': [(4, test_partner.id), (4, self.user_admin.partner_id.id)],
'send_mail': True})
mail_invite.add_followers()
# check added followers and that emails were sent
self.assertEqual(self.test_record.message_partner_ids,
self.assertEqual(test_record.message_partner_ids,
test_partner | self.user_admin.partner_id)
self.assertEqual(self.test_record.message_follower_ids.mapped('channel_id'),
self.assertEqual(test_record.message_follower_ids.mapped('channel_id'),
self.env['mail.channel'])
self.assertEqual(len(self._mails), 2)
+4 -11
View File
@@ -2,27 +2,20 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.base.tests.test_ir_actions import TestServerActionsBase
from odoo.addons.test_mail.tests.common import BaseFunctionalTest
class TestServerActionsEmail(TestServerActionsBase):
class TestServerActionsEmail(BaseFunctionalTest, TestServerActionsBase):
def test_action_email(self):
email_template = self.env['mail.template'].create({
'name': 'TestTemplate',
'email_from': 'myself@example.com',
'email_to': 'brigitte@example.com',
'partner_to': '%s' % self.test_partner.id,
'model_id': self.res_partner_model.id,
'subject': 'About ${object.name}',
'body_html': '<p>Dear ${object.name}, your parent is ${object.parent_id and object.parent_id.name or "False"}</p>',
})
email_template = self._create_template('res.partner', {'partner_to': '%s' % self.test_partner.id})
self.action.write({'state': 'email', 'template_id': email_template.id})
self.action.with_context(self.context).run()
# check an email is waiting for sending
mail = self.env['mail.mail'].search([('subject', '=', 'About TestingPartner')])
self.assertEqual(len(mail), 1)
# check email content
self.assertEqual(mail.body, '<p>Dear TestingPartner, your parent is False</p>')
self.assertEqual(mail.body, '<p>Hello TestingPartner</p>')
def test_action_followers(self):
self.test_partner.message_unsubscribe(self.test_partner.message_partner_ids.ids)
+12 -17
View File
@@ -10,8 +10,8 @@ import pytz
from odoo import exceptions, tests
from odoo.addons.test_mail.tests.common import BaseFunctionalTest
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.addons.test_mail.models.test_mail_models import MailTestActivity
from odoo.tools import mute_logger
class TestActivityCommon(BaseFunctionalTest):
@@ -19,7 +19,7 @@ class TestActivityCommon(BaseFunctionalTest):
@classmethod
def setUpClass(cls):
super(TestActivityCommon, cls).setUpClass()
cls.test_record = cls.env['mail.test.activity'].with_context(BaseFunctionalTest._test_context).create({'name': 'Test'})
cls.test_record = cls.env['mail.test.activity'].with_context(cls._test_context).create({'name': 'Test'})
# reset ctx
cls.test_record = cls.test_record.with_context(
mail_create_nolog=False,
@@ -31,6 +31,7 @@ class TestActivityCommon(BaseFunctionalTest):
@tests.tagged('mail_activity')
class TestActivityRights(TestActivityCommon):
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_activity_security_user_access_other(self):
activity = self.test_record.with_user(self.user_employee).activity_schedule(
'test_mail.mail_act_test_todo',
@@ -38,12 +39,14 @@ class TestActivityRights(TestActivityCommon):
self.assertTrue(activity.can_write)
activity.write({'user_id': self.user_employee.id})
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_activity_security_user_access_own(self):
activity = self.test_record.with_user(self.user_employee).activity_schedule(
'test_mail.mail_act_test_todo')
self.assertTrue(activity.can_write)
activity.write({'user_id': self.user_admin.id})
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_activity_security_user_noaccess_automated(self):
def _employee_crash(*args, **kwargs):
""" If employee is test employee, consider he has no access on document """
@@ -90,7 +93,7 @@ class TestActivityRights(TestActivityCommon):
class TestActivityFlow(TestActivityCommon):
def test_activity_flow_employee(self):
with self.sudo('ernest'):
with self.sudo('employee'):
test_record = self.env['mail.test.activity'].browse(self.test_record.id)
self.assertEqual(test_record.activity_ids, self.env['mail.activity'])
@@ -118,19 +121,7 @@ class TestActivityFlow(TestActivityCommon):
self.assertEqual(test_record.activity_ids, self.env['mail.activity'])
self.assertEqual(test_record.message_ids[0].subtype_id, self.env.ref('mail.mt_activities'))
def test_activity_flow_portal(self):
portal_user = mail_new_test_user(self.env, login='chell', groups='base.group_portal', name='Chell Gladys')
with self.sudo('chell'):
test_record = self.env['mail.test.activity'].browse(self.test_record.id)
with self.assertRaises(exceptions.AccessError):
self.env['mail.activity'].create({
'summary': 'Test Activity',
'activity_type_id': self.env.ref('mail.mail_activity_data_email').id,
'res_model_id': self.env['ir.model']._get(test_record._name).id,
'res_id': test_record.id,
})
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_activity_notify_other_user(self):
self.user_admin.notification_type = 'email'
rec = self.test_record.with_user(self.user_employee)
@@ -151,6 +142,7 @@ class TestActivityFlow(TestActivityCommon):
self.assertEqual(activity.create_uid, self.user_employee)
self.assertEqual(activity.user_id, self.user_employee)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_activity_dont_notify_no_user_change(self):
self.user_employee.notification_type = 'email'
activity = self.test_record.activity_schedule('test_mail.mail_act_test_todo', user_id=self.user_employee.id)
@@ -162,9 +154,10 @@ class TestActivityFlow(TestActivityCommon):
@tests.tagged('mail_activity')
class TestActivityMixin(TestActivityCommon):
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_activity_mixin(self):
self.user_employee.tz = self.user_admin.tz
with self.sudo('ernest'):
with self.sudo('employee'):
self.test_record = self.env['mail.test.activity'].browse(self.test_record.id)
self.assertEqual(self.test_record.env.user, self.user_employee)
@@ -242,6 +235,7 @@ class TestActivityMixin(TestActivityCommon):
self.assertEqual(self.test_record.activity_ids, self.env['mail.activity'])
self.assertEqual(len(self.test_record.message_ids), 2)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_activity_mixin_archive(self):
rec = self.test_record.with_user(self.user_employee)
new_act = rec.activity_schedule(
@@ -255,6 +249,7 @@ class TestActivityMixin(TestActivityCommon):
self.assertEqual(rec.active, True)
self.assertEqual(rec.activity_ids, self.env['mail.activity'])
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_activity_mixin_reschedule_user(self):
rec = self.test_record.with_user(self.user_employee)
rec.activity_schedule(
-63
View File
@@ -1,63 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.test_mail.tests import common
from odoo.tests import tagged
from odoo.tools import mute_logger, email_split_and_format
from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE
@tagged('mail_thread_cc')
class TestMailCc(common.BaseFunctionalTest, common.MockEmails):
@classmethod
def setUpClass(cls):
super(TestMailCc, cls).setUpClass()
cls.email_from = 'Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>'
cls.alias = cls.env['mail.alias'].create({
'alias_name': 'cc_record',
'alias_user_id': False,
'alias_model_id': cls.env['ir.model']._get('mail.test.cc').id,
'alias_contact': 'everyone'})
cls.env['ir.config_parameter'].set_param('mail.bounce.alias', 'bounce.test')
cls.env['ir.config_parameter'].set_param('mail.catchall.domain', 'test.com')
cls.env['ir.config_parameter'].set_param('mail.catchall.alias', 'catchall.test')
@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',
cc='cc1@example.com, cc2@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
self.assertEqual(sorted(cc), ['cc1@example.com', 'cc2@example.com'])
@mute_logger('odoo.addons.mail.models.mail_thread')
def test_message_cc_update_with_old(self):
record = self.env['mail.test.cc'].create({'email_cc': 'cc1 <cc1@example.com>, cc2@example.com'})
self.alias.write({'alias_force_thread_id': record.id})
self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
cc='cc2 <cc2@example.com>, cc3@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
self.assertEqual(sorted(cc), ['cc1 <cc1@example.com>', 'cc2@example.com', 'cc3@example.com'], 'new cc should have been added on record (unique)')
@mute_logger('odoo.addons.mail.models.mail_thread')
def test_message_cc_update_no_old(self):
record = self.env['mail.test.cc'].create({})
self.alias.write({'alias_force_thread_id': record.id})
self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
cc='cc2 <cc2@example.com>, cc3@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
self.assertEqual(sorted(cc), ['cc2 <cc2@example.com>', 'cc3@example.com'], 'new cc should have been added on record (unique)')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_cc_recipient_suggestion(self):
record = self.env['mail.test.cc'].create({'email_cc': 'cc1@example.com, cc2@example.com, cc3 <cc3@example.com>'})
suggestions = record._message_get_suggested_recipients()[record.id]
self.assertEqual(sorted(suggestions), [
(False, 'cc1@example.com', 'CC Email'),
(False, 'cc2@example.com', 'CC Email'),
(False, 'cc3 <cc3@example.com>', 'CC Email')
], 'cc should be in suggestions')
+25 -4
View File
@@ -13,7 +13,7 @@ class TestChannelAccessRights(common.BaseFunctionalTest, common.MockEmails):
@classmethod
def setUpClass(cls):
super(TestChannelAccessRights, cls).setUpClass()
Channel = cls.env['mail.channel'].with_context(common.BaseFunctionalTest._test_context)
Channel = cls.env['mail.channel'].with_context(cls._test_context)
cls.user_public = mail_new_test_user(cls.env, login='bert', groups='base.group_public', name='Bert Tartignole')
cls.user_portal = mail_new_test_user(cls.env, login='chell', groups='base.group_portal', name='Chell Gladys')
@@ -113,13 +113,13 @@ class TestChannelFeatures(common.BaseFunctionalTest, common.MockEmails):
@classmethod
def setUpClass(cls):
super(TestChannelFeatures, cls).setUpClass()
cls.test_channel = cls.env['mail.channel'].with_context(common.BaseFunctionalTest._test_context).create({
cls.test_channel = cls.env['mail.channel'].with_context(cls._test_context).create({
'name': 'Test',
'description': 'Description',
'alias_name': 'test',
'public': 'public',
})
cls.test_partner = cls.env['res.partner'].with_context(common.BaseFunctionalTest._test_context).create({
cls.test_partner = cls.env['res.partner'].with_context(cls._test_context).create({
'name': 'Test Partner',
'email': 'test@example.com',
})
@@ -202,7 +202,7 @@ class TestChannelFeatures(common.BaseFunctionalTest, common.MockEmails):
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_channel_out_of_office(self):
self.user_employee.out_of_office_message = 'Out'
test_chat = self.env['mail.channel'].with_context(common.BaseFunctionalTest._test_context).create({
test_chat = self.env['mail.channel'].with_context(self._test_context).create({
'channel_partner_ids': [(4, self.user_employee.partner_id.id), (4, self.user_admin.partner_id.id)],
'public': 'private',
'channel_type': 'chat',
@@ -311,3 +311,24 @@ class TestChannelModeration(common.Moderation):
self.assertEqual(accepted_messages, msg_moderator | msg_email2 | msg_notif)
self.assertFalse(msg_admin.channel_ids)
self.assertEqual(msg_email2.channel_ids, self.channel_1)
def test_user_is_moderator(self):
self.assertTrue(self.user_employee.is_moderator)
self.assertFalse(self.user_admin.is_moderator)
self.assertTrue(self.user_employee_2.is_moderator)
def test_user_moderation_counter(self):
self._create_new_message(self.channel_1.id, status='pending_moderation', author=self.partner_admin)
self._create_new_message(self.channel_1.id, status='accepted', author=self.partner_admin)
self._create_new_message(self.channel_1.id, status='accepted', author=self.partner_employee)
self._create_new_message(self.channel_1.id, status='pending_moderation', author=self.partner_employee)
self._create_new_message(self.channel_1.id, status='accepted', author=self.partner_employee_2)
self.assertEqual(self.user_employee.moderation_counter, 2)
self.assertEqual(self.user_employee_2.moderation_counter, 0)
self.assertEqual(self.user_admin.moderation_counter, 0)
self.channel_1.write({'channel_partner_ids': [(4, self.partner_employee_2.id)], 'moderator_ids': [(4, self.user_employee_2.id)]})
self.assertEqual(self.user_employee.moderation_counter, 2)
self.assertEqual(self.user_employee_2.moderation_counter, 0)
self.assertEqual(self.user_admin.moderation_counter, 0)
+13 -13
View File
@@ -4,7 +4,6 @@
from psycopg2 import IntegrityError
from odoo.addons.test_mail.tests import common
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.tools.misc import mute_logger
@@ -13,6 +12,10 @@ class BaseFollowersTest(common.BaseFunctionalTest):
@classmethod
def setUpClass(cls):
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()
Subtype = cls.env['mail.message.subtype']
cls.mt_mg_def = Subtype.create({'name': 'mt_mg_def', 'default': True, 'res_model': 'mail.test.simple'})
cls.mt_cl_def = Subtype.create({'name': 'mt_cl_def', 'default': True, 'res_model': 'mail.test'})
@@ -54,15 +57,13 @@ class BaseFollowersTest(common.BaseFunctionalTest):
self.assertEqual(follower.subtype_ids, self.default_group_subtypes)
def test_followers_subtypes_default_internal(self):
user_portal = mail_new_test_user(self.env, login='chell', groups='base.group_portal', name='Chell Gladys')
test_record = self.test_record.with_user(self.user_employee)
test_record.message_subscribe(partner_ids=[user_portal.partner_id.id])
self.assertEqual(test_record.message_partner_ids, user_portal.partner_id)
test_record.message_subscribe(partner_ids=[self.partner_portal.id])
self.assertEqual(test_record.message_partner_ids, self.partner_portal)
follower = self.env['mail.followers'].search([
('res_model', '=', 'mail.test.simple'),
('res_id', '=', test_record.id),
('partner_id', '=', user_portal.partner_id.id)])
('partner_id', '=', self.partner_portal.id)])
self.assertEqual(follower.subtype_ids, self.default_group_subtypes_portal)
def test_followers_subtypes_specified(self):
@@ -120,8 +121,7 @@ class AdvancedFollowersTest(common.BaseFunctionalTest):
@classmethod
def setUpClass(cls):
super(AdvancedFollowersTest, cls).setUpClass()
cls.user_portal = mail_new_test_user(cls.env, login='chell', groups='base.group_portal', name='Chell Gladys')
cls._create_portal_user()
cls.test_track = cls.env['mail.test.track'].with_user(cls.user_employee).create({
'name': 'Test',
@@ -183,12 +183,12 @@ class AdvancedFollowersTest(common.BaseFunctionalTest):
* subscribing to a sub-record as creator applies default subtype values
* portal user should not have access to internal subtypes
"""
umbrella = self.env['mail.test'].with_context(common.BaseFunctionalTest._test_context).create({
umbrella = self.env['mail.test'].with_context(self._test_context).create({
'name': 'Project-Like',
})
umbrella.message_subscribe(partner_ids=[self.user_portal.partner_id.id])
self.assertEqual(umbrella.message_partner_ids, self.user_portal.partner_id)
umbrella.message_subscribe(partner_ids=[self.partner_portal.id])
self.assertEqual(umbrella.message_partner_ids, self.partner_portal)
sub1 = self.env['mail.test.track'].with_user(self.user_employee).create({
'name': 'Task-Like Test',
@@ -198,9 +198,9 @@ class AdvancedFollowersTest(common.BaseFunctionalTest):
all_defaults = self.env['mail.message.subtype'].search([('default', '=', True), '|', ('res_model', '=', 'mail.test.track'), ('res_model', '=', False)])
external_defaults = all_defaults.filtered(lambda subtype: not subtype.internal)
self.assertEqual(sub1.message_partner_ids, self.user_portal.partner_id | self.user_employee.partner_id)
self.assertEqual(sub1.message_partner_ids, self.partner_portal | self.user_employee.partner_id)
self.assertEqual(
sub1.message_follower_ids.filtered(lambda fol: fol.partner_id == self.user_portal.partner_id).subtype_ids,
sub1.message_follower_ids.filtered(lambda fol: fol.partner_id == self.partner_portal).subtype_ids,
external_defaults | self.sub_umb1)
self.assertEqual(
sub1.message_follower_ids.filtered(lambda fol: fol.partner_id == self.user_employee.partner_id).subtype_ids,
+53 -4
View File
@@ -15,7 +15,7 @@ from odoo.addons.test_mail.models.test_mail_models import MailTestGateway
from odoo.addons.test_mail.tests.common import BaseFunctionalTest, MockEmails
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.tests import tagged
from odoo.tools import mute_logger, pycompat
from odoo.tools import email_split_and_format, mute_logger, pycompat
@tagged('mail_gateway')
@@ -69,6 +69,14 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails):
self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(test_mail_data.MAIL_XHTML)))
@tagged('mail_gateway')
class TestMailAlias(BaseFunctionalTest):
def test_alias_setup(self):
alias = self.env['mail.alias'].with_context(alias_model_name='mail.test').create({'alias_name': 'b4r+_#_R3wl$$'})
self.assertEqual(alias.alias_name, 'b4r+_-_r3wl-', 'Disallowed chars should be replaced by hyphens')
@tagged('mail_gateway')
class TestMailgateway(BaseFunctionalTest, MockEmails):
@@ -105,9 +113,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
'message_id': '<123456-openerp-%s-mail.test.gateway@%s>' % (cls.test_record.id, socket.gethostname()),
})
cls.env['ir.config_parameter'].set_param('mail.bounce.alias', 'bounce.test')
cls.env['ir.config_parameter'].set_param('mail.catchall.domain', 'test.com')
cls.env['ir.config_parameter'].set_param('mail.catchall.alias', 'catchall.test')
cls._init_mail_gateway()
# --------------------------------------------------
# Base low-level tests
@@ -740,3 +746,46 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
self.assertEqual(len(record), 1)
self.assertEqual(record.name, 'Spammy')
self.assertEqual(record._name, 'mail.test.gateway')
class TestMailThreadCC(BaseFunctionalTest, MockEmails):
@classmethod
def setUpClass(cls):
super(TestMailThreadCC, cls).setUpClass()
cls.email_from = 'Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>'
cls.alias = cls.env['mail.alias'].create({
'alias_name': 'cc_record',
'alias_user_id': False,
'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',
cc='cc1@example.com, cc2@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
self.assertEqual(sorted(cc), ['cc1@example.com', 'cc2@example.com'])
@mute_logger('odoo.addons.mail.models.mail_thread')
def test_message_cc_update_with_old(self):
record = self.env['mail.test.cc'].create({'email_cc': 'cc1 <cc1@example.com>, cc2@example.com'})
self.alias.write({'alias_force_thread_id': record.id})
self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
cc='cc2 <cc2@example.com>, cc3@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
self.assertEqual(sorted(cc), ['cc1 <cc1@example.com>', 'cc2@example.com', 'cc3@example.com'], 'new cc should have been added on record (unique)')
@mute_logger('odoo.addons.mail.models.mail_thread')
def test_message_cc_update_no_old(self):
record = self.env['mail.test.cc'].create({})
self.alias.write({'alias_force_thread_id': record.id})
self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
cc='cc2 <cc2@example.com>, cc3@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
self.assertEqual(sorted(cc), ['cc2 <cc2@example.com>', 'cc3@example.com'], 'new cc should have been added on record (unique)')
+68 -15
View File
@@ -2,27 +2,16 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import itertools
import psycopg2
from odoo import api
from odoo.addons.base.models.ir_mail_server import MailDeliveryException
from odoo.addons.test_mail.tests import common as mail_common
from odoo.tests import common
from odoo.tools import mute_logger
from odoo.addons.base.models.ir_mail_server import MailDeliveryException
class TestMail(common.SavepointCase, mail_common.MockEmails):
@classmethod
def setUpClass(cls):
super(TestMail, cls).setUpClass()
cls.user_employee = cls.env['res.users'].with_context(mail_common.BaseFunctionalTest._test_context).create({
'name': 'Ernest Employee',
'login': 'ernest',
'email': 'e.e@example.com',
'signature': '--\nErnest',
'notification_type': 'email',
'groups_id': [(6, 0, [cls.env.ref('base.group_user').id])]
})
class TestMail(mail_common.BaseFunctionalTest, mail_common.MockEmails):
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_message_notify_from_mail_mail(self):
@@ -49,3 +38,67 @@ class TestMail(common.SavepointCase, mail_common.MockEmails):
})
self.assertRaises(MailDeliveryException, lambda: mail.send(raise_exception=True))
class TestMailRace(common.TransactionCase, mail_common.MockEmails):
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_bounce_during_send(self):
self.partner = self.env['res.partner'].create({
'name': 'Ernest Partner',
})
# we need to simulate a mail sent by the cron task, first create mail, message and notification by hand
mail = self.env['mail.mail'].create({
'body_html': '<p>Test</p>',
'notification': True,
'state': 'outgoing',
'recipient_ids': [(4, self.partner.id)]
})
message = self.env['mail.message'].create({
'subject': 'S',
'body': 'B',
'subtype_id': self.ref('mail.mt_comment'),
'notification_ids': [(0, 0, {
'res_partner_id': self.partner.id,
'mail_id': mail.id,
'notification_type': 'email',
'is_read': True,
'notification_status': 'ready',
})],
})
notif = self.env['mail.notification'].search([('res_partner_id', '=', self.partner.id)])
# we need to commit transaction or cr will keep the lock on notif
self.cr.commit()
# patch send_email in order to create a concurent update and check the notif is already locked by _send()
this = self # coding in javascript ruinned my life
bounce_deferred = []
@api.model
def send_email(self, message, *args, **kwargs):
with this.registry.cursor() as cr, mute_logger('odoo.sql_db'):
try:
# try ro aquire lock (no wait) on notification (should fail)
cr.execute("SELECT notification_status FROM mail_message_res_partner_needaction_rel WHERE id = %s FOR UPDATE NOWAIT", [notif.id])
except psycopg2.OperationalError:
# record already locked by send, all good
bounce_deferred.append(True)
else:
# this should trigger psycopg2.extensions.TransactionRollbackError in send().
# Only here to simulate the initial use case
# If the record is lock, this line would create a deadlock since we are in the same thread
# In practice, the update will wait the end of the send() transaction and set the notif as bounce, as expeced
cr.execute("UPDATE mail_message_res_partner_needaction_rel SET notification_status='bounce' WHERE id = %s", [notif.id])
return message['Message-Id']
self.env['ir.mail_server']._patch_method('send_email', send_email)
mail.send()
self.assertTrue(bounce_deferred, "The bounce should have been deferred")
self.assertEqual(notif.notification_status, 'sent')
# some cleaning since we commited the cr
notif.unlink()
message.unlink()
mail.unlink()
self.partner.unlink()
self.env.cr.commit()
+75 -52
View File
@@ -16,17 +16,16 @@ class TestMessageValues(common.BaseFunctionalTest, common.MockEmails):
def setUpClass(cls):
super(TestMessageValues, cls).setUpClass()
cls.alias_record = cls.env['mail.test'].with_context(common.BaseFunctionalTest._test_context).create({
cls._init_mail_gateway()
cls.alias_record = cls.env['mail.test'].with_context(cls._test_context).create({
'name': 'Pigs',
'alias_name': 'pigs',
'alias_contact': 'followers',
})
cls.Message = cls.env['mail.message'].with_user(cls.user_employee)
def test_mail_message_values_basic(self):
self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.domain')]).unlink()
@mute_logger('odoo.models.unlink')
def test_mail_message_values_no_document_values(self):
msg = self.Message.create({
'reply_to': 'test.reply@example.com',
'email_from': 'test.from@example.com',
@@ -35,39 +34,45 @@ class TestMessageValues(common.BaseFunctionalTest, common.MockEmails):
self.assertEqual(msg.reply_to, 'test.reply@example.com')
self.assertEqual(msg.email_from, 'test.from@example.com')
def test_mail_message_values_default(self):
self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.domain')]).unlink()
msg = self.Message.create({})
self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one')
self.assertEqual(msg.reply_to, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
@mute_logger('odoo.models.unlink')
def test_mail_message_values_alias(self):
alias_domain = 'example.com'
self.env['ir.config_parameter'].set_param('mail.catchall.domain', alias_domain)
self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.alias')]).unlink()
def test_mail_message_values_no_document(self):
msg = self.Message.create({})
self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one')
self.assertEqual(msg.reply_to, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
def test_mail_message_values_alias_catchall(self):
alias_domain = 'example.com'
alias_catchall = 'pokemon'
self.env['ir.config_parameter'].set_param('mail.catchall.domain', alias_domain)
self.env['ir.config_parameter'].set_param('mail.catchall.alias', alias_catchall)
msg = self.Message.create({})
self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one')
reply_to_name = self.env.company.name
reply_to_email = '%s@%s' % (alias_catchall, alias_domain)
reply_to_name = self.env.user.company_id.name
reply_to_email = '%s@%s' % (self.alias_catchall, self.alias_domain)
self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email)))
self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
def test_mail_message_values_document_no_alias(self):
# no alias domain -> author
self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.domain')]).unlink()
msg = self.Message.create({})
self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one')
self.assertEqual(msg.reply_to, formataddr((self.user_employee.name, self.user_employee.email)))
self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
# no alias catchall, no alias -> author
self.env['ir.config_parameter'].set_param('mail.catchall.domain', self.alias_domain)
self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.alias')]).unlink()
msg = self.Message.create({})
self.assertIn('-private', msg.message_id.split('@')[0], 'mail_message: message_id for a void message should be a "private" one')
self.assertEqual(msg.reply_to, formataddr((self.user_employee.name, self.user_employee.email)))
self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
@mute_logger('odoo.models.unlink')
def test_mail_message_values_document_alias(self):
msg = self.Message.create({
'model': 'mail.test',
'res_id': self.alias_record.id
})
self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id.split('@')[0])
reply_to_name = '%s %s' % (self.env.user.company_id.name, self.alias_record.name)
reply_to_email = '%s@%s' % (self.alias_record.alias_name, self.alias_domain)
self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email)))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
# no alias domain -> author
self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.domain')]).unlink()
msg = self.Message.create({
@@ -75,13 +80,11 @@ class TestMessageValues(common.BaseFunctionalTest, common.MockEmails):
'res_id': self.alias_record.id
})
self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id.split('@')[0])
self.assertEqual(msg.reply_to, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
self.assertEqual(msg.reply_to, formataddr((self.user_employee.name, self.user_employee.email)))
self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
@mute_logger('odoo.models.unlink')
def test_mail_message_values_document_alias(self):
alias_domain = 'example.com'
self.env['ir.config_parameter'].set_param('mail.catchall.domain', alias_domain)
# no catchall -> don't care, alias
self.env['ir.config_parameter'].set_param('mail.catchall.domain', self.alias_domain)
self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.alias')]).unlink()
msg = self.Message.create({
@@ -90,25 +93,45 @@ class TestMessageValues(common.BaseFunctionalTest, common.MockEmails):
})
self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id.split('@')[0])
reply_to_name = '%s %s' % (self.env.company.name, self.alias_record.name)
reply_to_email = '%s@%s' % (self.alias_record.alias_name, alias_domain)
reply_to_email = '%s@%s' % (self.alias_record.alias_name, self.alias_domain)
self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email)))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
def test_mail_message_values_document_alias_catchall(self):
alias_domain = 'example.com'
alias_catchall = 'pokemon'
self.env['ir.config_parameter'].set_param('mail.catchall.domain', alias_domain)
self.env['ir.config_parameter'].set_param('mail.catchall.alias', alias_catchall)
@mute_logger('odoo.models.unlink')
def test_mail_message_values_document_no_alias(self):
test_record = self.env['mail.test.simple'].create({'name': 'Test', 'email_from': 'ignasse@example.com'})
msg = self.Message.create({
'model': 'mail.test',
'res_id': self.alias_record.id
'model': 'mail.test.simple',
'res_id': test_record.id
})
self.assertIn('-openerp-%d-mail.test' % self.alias_record.id, msg.message_id.split('@')[0])
reply_to_name = '%s %s' % (self.env.company.name, self.alias_record.name)
reply_to_email = '%s@%s' % (self.alias_record.alias_name, alias_domain)
self.assertIn('-openerp-%d-mail.test.simple' % test_record.id, msg.message_id.split('@')[0])
reply_to_name = '%s %s' % (self.env.user.company_id.name, test_record.name)
reply_to_email = '%s@%s' % (self.alias_catchall, self.alias_domain)
self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email)))
self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
@mute_logger('odoo.models.unlink')
def test_mail_message_values_document_manual_alias(self):
test_record = self.env['mail.test.simple'].create({'name': 'Test', 'email_from': 'ignasse@example.com'})
alias = self.env['mail.alias'].create({
'alias_name': 'MegaLias',
'alias_user_id': False,
'alias_model_id': self.env['ir.model']._get('mail.test.simple').id,
'alias_parent_model_id': self.env['ir.model']._get('mail.test.simple').id,
'alias_parent_thread_id': test_record.id,
})
msg = self.Message.create({
'model': 'mail.test.simple',
'res_id': test_record.id
})
self.assertIn('-openerp-%d-mail.test.simple' % test_record.id, msg.message_id.split('@')[0])
reply_to_name = '%s %s' % (self.env.user.company_id.name, test_record.name)
reply_to_email = '%s@%s' % (alias.alias_name, self.alias_domain)
self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email)))
self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
def test_mail_message_values_no_auto_thread(self):
msg = self.Message.create({
@@ -139,7 +162,7 @@ class TestMessageAccess(common.BaseFunctionalTest, common.MockEmails):
cls.user_public = mail_new_test_user(cls.env, login='bert', groups='base.group_public', name='Bert Tartignole')
cls.user_portal = mail_new_test_user(cls.env, login='chell', groups='base.group_portal', name='Chell Gladys')
Channel = cls.env['mail.channel'].with_context(common.BaseFunctionalTest._test_context)
Channel = cls.env['mail.channel'].with_context(cls._test_context)
# Pigs: base group for tests
cls.group_pigs = Channel.create({
'name': 'Pigs',
-73
View File
@@ -1,73 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import psycopg2
from odoo import api
from odoo.addons.test_mail.tests import common as mail_common
from odoo.tests import common
from odoo.tools import mute_logger
class TestMailRace(common.TransactionCase, mail_common.MockEmails):
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_bounce_during_send(self):
self.partner = self.env['res.partner'].create({
'name': 'Ernest Partner',
})
# we need to simulate a mail sent by the cron task, first create mail, message and notification by hand
mail = self.env['mail.mail'].create({
'body_html': '<p>Test</p>',
'notification': True,
'state': 'outgoing',
'recipient_ids': [(4, self.partner.id)]
})
message = self.env['mail.message'].create({
'subject': 'S',
'body': 'B',
'subtype_id': self.ref('mail.mt_comment'),
'notification_ids': [(0, 0, {
'res_partner_id': self.partner.id,
'mail_id': mail.id,
'notification_type': 'email',
'is_read': True,
'notification_status': 'ready',
})],
})
notif = self.env['mail.notification'].search([('res_partner_id', '=', self.partner.id)])
# we need to commit transaction or cr will keep the lock on notif
self.cr.commit()
# patch send_email in order to create a concurent update and check the notif is already locked by _send()
this = self # coding in javascript ruinned my life
bounce_deferred = []
@api.model
def send_email(self, message, *args, **kwargs):
with this.registry.cursor() as cr, mute_logger('odoo.sql_db'):
try:
# try ro aquire lock (no wait) on notification (should fail)
cr.execute("SELECT notification_status FROM mail_message_res_partner_needaction_rel WHERE id = %s FOR UPDATE NOWAIT", [notif.id])
except psycopg2.OperationalError:
# record already locked by send, all good
bounce_deferred.append(True)
else:
# this should trigger psycopg2.extensions.TransactionRollbackError in send().
# Only here to simulate the initial use case
# If the record is lock, this line would create a deadlock since we are in the same thread
# In practice, the update will wait the end of the send() transaction and set the notif as bounce, as expeced
cr.execute("UPDATE mail_message_res_partner_needaction_rel SET notification_status='bounce' WHERE id = %s", [notif.id])
return message['Message-Id']
self.env['ir.mail_server']._patch_method('send_email', send_email)
mail.send()
self.assertTrue(bounce_deferred, "The bounce should have been deferred")
self.assertEqual(notif.notification_status, 'sent')
# some cleaning since we commited the cr
notif.unlink()
message.unlink()
mail.unlink()
self.partner.unlink()
self.env.cr.commit()
+22 -193
View File
@@ -2,191 +2,54 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
from datetime import datetime, timedelta
from unittest.mock import patch
from odoo.addons.test_mail.tests.common import BaseFunctionalTest, MockEmails, TestRecipients
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.tools import mute_logger, DEFAULT_SERVER_DATETIME_FORMAT
from odoo.tools import mute_logger
class TestMailTemplate(BaseFunctionalTest, MockEmails, TestRecipients):
def setUp(self):
super(TestMailTemplate, self).setUp()
@classmethod
def setUpClass(cls):
super(TestMailTemplate, cls).setUpClass()
cls.test_record = cls.env['mail.test.simple'].with_context(cls._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)],
cls.user_employee.write({
'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)],
})
self._attachments = [{
cls._attachments = [{
'name': 'first.txt',
'datas': base64.b64encode(b'My first attachment'),
'res_model': 'res.partner',
'res_id': self.user_admin.partner_id.id
'res_id': cls.user_admin.partner_id.id
}, {
'name': 'second.txt',
'datas': base64.b64encode(b'My second attachment'),
'res_model': 'res.partner',
'res_id': self.user_admin.partner_id.id
'res_id': cls.user_admin.partner_id.id
}]
self.email_1 = 'test1@example.com'
self.email_2 = 'test2@example.com'
self.email_3 = self.partner_1.email
self.email_template = self.env['mail.template'].create({
'model_id': self.env['ir.model']._get('mail.test.simple').id,
'name': 'Pigs Template',
'subject': '${object.name}',
'body_html': '${object.email_from}',
'user_signature': False,
'attachment_ids': [(0, 0, self._attachments[0]), (0, 0, self._attachments[1])],
'partner_to': '%s,%s' % (self.partner_2.id, self.user_admin.partner_id.id),
'email_to': '%s, %s' % (self.email_1, self.email_2),
'email_cc': '%s' % self.email_3})
cls.email_1 = 'test1@example.com'
cls.email_2 = 'test2@example.com'
cls.email_3 = cls.partner_1.email
cls._create_template('mail.test.simple', {
'attachment_ids': [(0, 0, cls._attachments[0]), (0, 0, cls._attachments[1])],
'partner_to': '%s,%s' % (cls.partner_2.id, cls.user_admin.partner_id.id),
'email_to': '%s, %s' % (cls.email_1, cls.email_2),
'email_cc': '%s' % cls.email_3,
})
# admin should receive emails
self.user_admin.write({'notification_type': 'email'})
cls.user_admin.write({'notification_type': 'email'})
# Force the attachments of the template to be in the natural order.
self.email_template.invalidate_cache(['attachment_ids'], ids=self.email_template.ids)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_w_template(self):
composer = self.env['mail.compose.message'].with_user(self.user_employee).with_context({
'default_composition_mode': 'comment',
'default_model': 'mail.test.simple',
'default_res_id': self.test_record.id,
'default_template_id': self.email_template.id,
}).create({'subject': 'Forget me subject', 'body': 'Dummy body'})
# perform onchange and send emails
values = composer.onchange_template_id(self.email_template.id, 'comment', self.test_record._name, self.test_record.id)['value']
composer.write(values)
composer.send_mail()
new_partners = self.env['res.partner'].search([('email', 'in', [self.email_1, self.email_2])])
self.assertEmails(
self.user_employee.partner_id,
[[self.partner_1], [self.partner_2], [new_partners[0]], [new_partners[1]], [self.partner_admin]],
subject=self.test_record.name,
body_content=self.test_record.email_from,
attachments=[('first.txt', b'My first attachment', 'text/plain'), ('second.txt', b'My second attachment', 'text/plain')])
def test_composer_template_onchange_attachments(self):
"""Tests that all attachments are added to the composer,
static attachments are not duplicated and while reports are re-generated,
and that intermediary attachments are dropped."""
composer = self.env['mail.compose.message'].with_context(default_attachment_ids=[]).create({})
report_template = self.env.ref('web.action_report_externalpreview')
template_1 = self.email_template.copy({
'report_template': report_template.id,
})
template_2 = self.email_template.copy({
'attachment_ids': False,
'report_template': report_template.id,
})
onchange_templates = [template_1, template_2, template_1, False]
attachments_onchange = [composer.attachment_ids]
# template_1 has two static attachments and one dynamically generated report,
# template_2 only has the report, so we should get 3, 1, 3 attachments
attachment_numbers = [0, 3, 1, 3, 0]
for template in onchange_templates:
onchange = composer.onchange_template_id(
template.id if template else False, 'comment', self.test_record._name, self.test_record.id
)
composer.update(onchange['value'])
attachments_onchange.append(composer.attachment_ids)
self.assertEqual(
[len(attachments) for attachments in attachments_onchange],
attachment_numbers,
)
self.assertTrue(
len(attachments_onchange[1] & attachments_onchange[3]) == 2,
"The two static attachments on the template should be common to the two onchanges"
)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_post_w_template(self):
self.test_record.with_user(self.user_employee).message_post_with_template(self.email_template.id, composition_mode='comment')
new_partners = self.env['res.partner'].search([('email', 'in', [self.email_1, self.email_2])])
self.assertEmails(
self.user_employee.partner_id,
[[self.partner_1], [self.partner_2], [new_partners[0]], [new_partners[1]], [self.partner_admin]],
subject=self.test_record.name,
body_content=self.test_record.email_from,
attachments=[('first.txt', b'My first attachment', 'text/plain'), ('second.txt', b'My second attachment', 'text/plain')])
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_w_template_mass_mailing(self):
test_record_2 = self.env['mail.test.simple'].with_context(BaseFunctionalTest._test_context).create({'name': 'Test2', 'email_from': 'laurie.poiret@example.com'})
composer = self.env['mail.compose.message'].with_user(self.user_employee).with_context({
'default_composition_mode': 'mass_mail',
# 'default_notify': True,
'default_notify': False,
'default_model': 'mail.test.simple',
'default_res_id': self.test_record.id,
'default_template_id': self.email_template.id,
'active_ids': [self.test_record.id, test_record_2.id]
}).create({})
values = composer.onchange_template_id(self.email_template.id, 'mass_mail', 'mail.test.simple', self.test_record.id)['value']
composer.write(values)
composer.send_mail()
new_partners = self.env['res.partner'].search([('email', 'in', [self.email_1, self.email_2])])
# hack to use assertEmails
self._mails_record1 = [dict(mail) for mail in self._mails if '%s-%s' % (self.test_record.id, self.test_record._name) in mail['message_id']]
self._mails_record2 = [dict(mail) for mail in self._mails if '%s-%s' % (test_record_2.id, test_record_2._name) in mail['message_id']]
self._mails = self._mails_record1
self.assertEmails(
self.user_employee.partner_id,
[[self.partner_1], [self.partner_2], [new_partners[0]], [new_partners[1]], [self.partner_admin]],
subject=self.test_record.name,
body_content=self.test_record.email_from,
attachments=[('first.txt', b'My first attachment', 'text/plain'), ('second.txt', b'My second attachment', 'text/plain')])
self._mails = self._mails_record2
self.assertEmails(
self.user_employee.partner_id,
[[self.partner_1], [self.partner_2], [new_partners[0]], [new_partners[1]], [self.partner_admin]],
subject=test_record_2.name,
body_content=test_record_2.email_from,
attachments=[('first.txt', b'My first attachment', 'text/plain'), ('second.txt', b'My second attachment', 'text/plain')])
message_1 = self.test_record.message_ids[0]
message_2 = test_record_2.message_ids[0]
# messages effectively posted
self.assertEqual(message_1.subject, self.test_record.name)
self.assertEqual(message_2.subject, test_record_2.name)
self.assertIn(self.test_record.email_from, message_1.body)
self.assertIn(test_record_2.email_from, message_2.body)
def test_composer_template_save(self):
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': 'mail.test.simple',
'default_res_id': self.test_record.id,
}).create({
'subject': 'Forget me subject',
'body': '<p>Dummy body</p>'
}).save_as_template()
# Test: email_template subject, body_html, model
last_template = self.env['mail.template'].search([('model', '=', 'mail.test.simple'), ('subject', '=', 'Forget me subject')], limit=1)
self.assertEqual(last_template.body_html, '<p>Dummy body</p>', 'email_template incorrect body_html')
cls.email_template.invalidate_cache(['attachment_ids'], ids=cls.email_template.ids)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_template_send_email(self):
mail_id = self.email_template.send_mail(self.test_record.id)
mail = self.env['mail.mail'].browse(mail_id)
self.assertEqual(mail.subject, self.test_record.name)
self.assertEqual(mail.subject, 'About %s' % self.test_record.name)
self.assertEqual(mail.email_to, self.email_template.email_to)
self.assertEqual(mail.email_cc, self.email_template.email_cc)
self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id)
@@ -241,37 +104,3 @@ class TestMailTemplate(BaseFunctionalTest, MockEmails, TestRecipients):
# self.env['mail.mail'].process_email_queue(ids=[mail_in_2_days.id])
# self.assertEqual(mail_in_2_days.state, 'outgoing')
# self.assertEqual(mail_now.exists() | mail_in_2_days.exists(), mail_in_2_days)
def test_create_partner_from_tracking_multicompany(self):
company1 = self.env['res.company'].create({'name': 'company1'})
self.env.user.write({'company_ids': [(4, company1.id, False)]})
self.assertNotEqual(self.env.company, company1)
email_new_partner = "diamonds@rust.com"
Partner = self.env['res.partner']
self.assertFalse(Partner.search([('email', '=', email_new_partner)]))
template = self.env['mail.template'].create({
'model_id': self.env['ir.model']._get('mail.test.track').id,
'name': 'AutoTemplate',
'subject': 'autoresponse',
'email_from': self.env.user.email_formatted,
'email_to': "${object.email_from}",
'body_html': "<div>A nice body</div>",
})
def patched_message_track_post_template(*args, **kwargs):
if args[0]._name == "mail.test.track":
args[0].message_post_with_template(template.id)
return True
with patch('odoo.addons.mail.models.mail_thread.MailThread._message_track_post_template', patched_message_track_post_template):
self.env['mail.test.track'].create({
'email_from': email_new_partner,
'company_id': company1.id,
'user_id': self.env.user.id, # trigger track template
})
new_partner = Partner.search([('email', '=', email_new_partner)])
self.assertTrue(new_partner)
self.assertEqual(new_partner.company_id, company1)
@@ -0,0 +1,321 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
from unittest.mock import patch
from odoo.addons.test_mail.tests.common import BaseFunctionalTest, MockEmails, TestRecipients
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.addons.test_mail.models.test_mail_models import MailTestSimple
from odoo.tools import mute_logger
class TestComposer(BaseFunctionalTest, MockEmails, TestRecipients):
@classmethod
def setUpClass(cls):
super(TestComposer, cls).setUpClass()
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
# configure mailing
cls.alias_domain = 'schlouby.fr'
cls.alias_catchall = 'test+catchall'
cls.env['ir.config_parameter'].set_param('mail.catchall.domain', cls.alias_domain)
cls.env['ir.config_parameter'].set_param('mail.catchall.alias', cls.alias_catchall)
# admin should not receive emails
cls.user_admin.write({'notification_type': 'email'})
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_comment(self):
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': self.test_record._name,
'default_res_id': self.test_record.id,
}).with_user(self.user_employee).create({
'body': '<p>Test Body</p>',
'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
})
composer.send_mail()
message = self.test_record.message_ids[0]
self.assertEqual(message.body, '<p>Test Body</p>')
self.assertEqual(message.author_id, self.user_employee.partner_id)
self.assertEqual(message.subject, 'Re: %s' % self.test_record.name)
self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
self.assertEqual(message.partner_ids, self.partner_1 | self.partner_2)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_comment_parent(self):
parent = self.test_record.message_post(body='Test')
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_parent_id': parent.id
}).with_user(self.user_employee).create({
'body': '<p>Mega</p>',
}).send_mail()
message = self.test_record.message_ids[0]
self.assertEqual(message.body, '<p>Mega</p>')
self.assertEqual(message.parent_id, parent)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_mass_mail(self):
test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2'})
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'mass_mail',
'default_model': self.test_record._name,
'default_res_id': False,
'active_ids': [self.test_record.id, test_record_2.id]
}).with_user(self.user_employee).create({
'subject': 'Testing ${object.name}',
'body': '<p>${object.name}</p>',
'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
})
composer.with_context({
'default_res_id': -1,
'active_ids': [self.test_record.id, test_record_2.id]
}).send_mail()
# check mail_mail
mails = self.env['mail.mail'].search([('subject', 'ilike', 'Testing')])
for mail in mails:
self.assertEqual(mail.recipient_ids, self.partner_1 | self.partner_2,
'compose wizard: mail_mail mass mailing: mail.mail in mass mail incorrect recipients')
# check message on test_record
message1 = self.test_record.message_ids[0]
self.assertEqual(message1.subject, 'Testing %s' % self.test_record.name)
self.assertEqual(message1.body, '<p>%s</p>' % self.test_record.name)
# check message on test_record_2
message1 = test_record_2.message_ids[0]
self.assertEqual(message1.subject, 'Testing %s' % test_record_2.name)
self.assertEqual(message1.body, '<p>%s</p>' % test_record_2.name)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_mass_mail_active_domain(self):
test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2'})
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'mass_mail',
'default_model': self.test_record._name,
'default_use_active_domain': True,
'active_ids': [self.test_record.id],
'active_domain': [('name', 'in', ['%s' % self.test_record.name, '%s' % test_record_2.name])],
}).with_user(self.user_employee).create({
'subject': 'From Composer Test',
'body': '${object.name}',
}).send_mail()
self.assertEqual(self.test_record.message_ids[0].subject, 'From Composer Test')
self.assertEqual(test_record_2.message_ids[0].subject, 'From Composer Test')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_message_compose_mass_mail_no_active_domain(self):
test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2'})
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'mass_mail',
'default_model': self.test_record._name,
'default_use_active_domain': False,
'active_ids': [self.test_record.id],
'active_domain': [('name', 'in', ['%s' % self.test_record.name, '%s' % test_record_2.name])],
}).with_user(self.user_employee).create({
'subject': 'From Composer Test',
'body': '${object.name}',
}).send_mail()
self.assertEqual(self.test_record.message_ids[0].subject, 'From Composer Test')
self.assertFalse(test_record_2.message_ids.ids)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_message_compose_portal_ok(self):
portal_user = mail_new_test_user(self.env, login='chell', groups='base.group_portal', name='Chell Gladys')
with patch.object(MailTestSimple, 'check_access_rights', return_value=True):
ComposerPortal = self.env['mail.compose.message'].with_user(portal_user)
ComposerPortal.with_context({
'default_composition_mode': 'comment',
'default_model': self.test_record._name,
'default_res_id': self.test_record.id,
}).create({
'subject': 'Subject',
'body': '<p>Body text</p>',
'partner_ids': []}).send_mail()
self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text</p>')
self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
ComposerPortal.with_context({
'default_composition_mode': 'comment',
'default_parent_id': self.test_record.message_ids.ids[0],
}).create({
'subject': 'Subject',
'body': '<p>Body text 2</p>'}).send_mail()
self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text 2</p>')
self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
class TestComposerWTpl(BaseFunctionalTest, MockEmails, TestRecipients):
@classmethod
def setUpClass(cls):
super(TestComposerWTpl, cls).setUpClass()
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
cls.user_employee.write({
'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)],
})
cls._attachments = [{
'name': 'first.txt',
'datas': base64.b64encode(b'My first attachment'),
'res_model': 'res.partner',
'res_id': cls.user_admin.partner_id.id
}, {
'name': 'second.txt',
'datas': base64.b64encode(b'My second attachment'),
'res_model': 'res.partner',
'res_id': cls.user_admin.partner_id.id
}]
cls.email_1 = 'test1@example.com'
cls.email_2 = 'test2@example.com'
cls.email_3 = cls.partner_1.email
cls._create_template('mail.test.simple', {
'attachment_ids': [(0, 0, cls._attachments[0]), (0, 0, cls._attachments[1])],
'partner_to': '%s,%s' % (cls.partner_2.id, cls.user_admin.partner_id.id),
'email_to': '%s, %s' % (cls.email_1, cls.email_2),
'email_cc': '%s' % cls.email_3,
})
# admin should receive emails
cls.user_admin.write({'notification_type': 'email'})
# Force the attachments of the template to be in the natural order.
cls.email_template.invalidate_cache(['attachment_ids'], ids=cls.email_template.ids)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_w_template(self):
composer = self.env['mail.compose.message'].with_user(self.user_employee).with_context({
'default_composition_mode': 'comment',
'default_model': 'mail.test.simple',
'default_res_id': self.test_record.id,
'default_template_id': self.email_template.id,
}).create({'subject': 'Forget me subject', 'body': 'Dummy body'})
# perform onchange and send emails
values = composer.onchange_template_id(self.email_template.id, 'comment', self.test_record._name, self.test_record.id)['value']
composer.write(values)
composer.send_mail()
new_partners = self.env['res.partner'].search([('email', 'in', [self.email_1, self.email_2])])
self.assertEmails(
self.user_employee.partner_id,
[[self.partner_1], [self.partner_2], [new_partners[0]], [new_partners[1]], [self.partner_admin]],
subject='About %s' % self.test_record.name,
body_content=self.test_record.name,
attachments=[('first.txt', b'My first attachment', 'text/plain'), ('second.txt', b'My second attachment', 'text/plain')])
def test_composer_template_onchange_attachments(self):
"""Tests that all attachments are added to the composer,
static attachments are not duplicated and while reports are re-generated,
and that intermediary attachments are dropped."""
composer = self.env['mail.compose.message'].with_context(default_attachment_ids=[]).create({})
report_template = self.env.ref('web.action_report_externalpreview')
template_1 = self.email_template.copy({
'report_template': report_template.id,
})
template_2 = self.email_template.copy({
'attachment_ids': False,
'report_template': report_template.id,
})
onchange_templates = [template_1, template_2, template_1, False]
attachments_onchange = [composer.attachment_ids]
# template_1 has two static attachments and one dynamically generated report,
# template_2 only has the report, so we should get 3, 1, 3 attachments
attachment_numbers = [0, 3, 1, 3, 0]
for template in onchange_templates:
onchange = composer.onchange_template_id(
template.id if template else False, 'comment', self.test_record._name, self.test_record.id
)
composer.update(onchange['value'])
attachments_onchange.append(composer.attachment_ids)
self.assertEqual(
[len(attachments) for attachments in attachments_onchange],
attachment_numbers,
)
self.assertTrue(
len(attachments_onchange[1] & attachments_onchange[3]) == 2,
"The two static attachments on the template should be common to the two onchanges"
)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_w_template_mass_mailing(self):
test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2', 'email_from': 'laurie.poiret@example.com'})
composer = self.env['mail.compose.message'].with_user(self.user_employee).with_context({
'default_composition_mode': 'mass_mail',
# 'default_notify': True,
'default_notify': False,
'default_model': 'mail.test.simple',
'default_res_id': self.test_record.id,
'default_template_id': self.email_template.id,
'active_ids': [self.test_record.id, test_record_2.id]
}).create({})
values = composer.onchange_template_id(self.email_template.id, 'mass_mail', 'mail.test.simple', self.test_record.id)['value']
composer.write(values)
composer.send_mail()
new_partners = self.env['res.partner'].search([('email', 'in', [self.email_1, self.email_2])])
# hack to use assertEmails
self._mails_record1 = [dict(mail) for mail in self._mails if '%s-%s' % (self.test_record.id, self.test_record._name) in mail['message_id']]
self._mails_record2 = [dict(mail) for mail in self._mails if '%s-%s' % (test_record_2.id, test_record_2._name) in mail['message_id']]
self._mails = self._mails_record1
self.assertEmails(
self.user_employee.partner_id,
[[self.partner_1], [self.partner_2], [new_partners[0]], [new_partners[1]], [self.partner_admin]],
subject='About %s' % self.test_record.name,
body_content=self.test_record.name,
attachments=[('first.txt', b'My first attachment', 'text/plain'), ('second.txt', b'My second attachment', 'text/plain')])
self._mails = self._mails_record2
self.assertEmails(
self.user_employee.partner_id,
[[self.partner_1], [self.partner_2], [new_partners[0]], [new_partners[1]], [self.partner_admin]],
subject='About %s' % test_record_2.name,
body_content=test_record_2.name,
attachments=[('first.txt', b'My first attachment', 'text/plain'), ('second.txt', b'My second attachment', 'text/plain')])
message_1 = self.test_record.message_ids[0]
message_2 = test_record_2.message_ids[0]
# messages effectively posted
self.assertEqual(message_1.subject, 'About %s' % self.test_record.name)
self.assertEqual(message_2.subject, 'About %s' % test_record_2.name)
self.assertIn(self.test_record.name, message_1.body)
self.assertIn(test_record_2.name, message_2.body)
def test_composer_template_save(self):
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': 'mail.test.simple',
'default_res_id': self.test_record.id,
}).create({
'subject': 'Forget me subject',
'body': '<p>Dummy body</p>'
}).save_as_template()
# Test: email_template subject, body_html, model
last_template = self.env['mail.template'].search([('model', '=', 'mail.test.simple'), ('subject', '=', 'Forget me subject')], limit=1)
self.assertEqual(last_template.body_html, '<p>Dummy body</p>', 'email_template incorrect body_html')
@@ -17,15 +17,17 @@ class TestMailResend(common.BaseFunctionalTest, common.MockEmails):
@classmethod
def setUpClass(cls):
super(TestMailResend, cls).setUpClass()
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
#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({
cls.partner1 = cls.env['res.partner'].with_context(cls._test_context).create({
'name': 'Partner 1',
'email': 'p1' # invalid email
})
cls.partner2 = cls.env['res.partner'].with_context(common.BaseFunctionalTest._test_context).create({
cls.partner2 = cls.env['res.partner'].with_context(cls._test_context).create({
'name': 'Partner 2',
'email': 'p2@example.com'
})
@@ -7,26 +7,61 @@ from email.utils import formataddr
from unittest.mock import patch
from odoo.addons.test_mail.tests.common import BaseFunctionalTest, MockEmails, TestRecipients
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE_PLAINTEXT
from odoo.addons.test_mail.models.test_mail_models import MailTestSimple
from odoo.exceptions import AccessError
from odoo.tools import mute_logger
class TestMessagePost(BaseFunctionalTest, MockEmails, TestRecipients):
class TestMessagePost(BaseFunctionalTest, TestRecipients, MockEmails):
def setUp(self):
super(TestMessagePost, self).setUp()
@classmethod
def setUpClass(cls):
super(TestMessagePost, cls).setUpClass()
cls._create_portal_user()
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
# configure mailing
self.alias_domain = 'schlouby.fr'
self.alias_catchall = 'test+catchall'
self.env['ir.config_parameter'].set_param('mail.catchall.domain', self.alias_domain)
self.env['ir.config_parameter'].set_param('mail.catchall.alias', self.alias_catchall)
cls.alias_domain = 'schlouby.fr'
cls.alias_catchall = 'test+catchall'
cls.env['ir.config_parameter'].set_param('mail.catchall.domain', cls.alias_domain)
cls.env['ir.config_parameter'].set_param('mail.catchall.alias', cls.alias_catchall)
# admin should not receive emails
self.user_admin.write({'notification_type': 'email'})
cls.user_admin.write({'notification_type': 'email'})
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_needaction(self):
(self.user_employee | self.user_admin).write({'notification_type': 'inbox'})
with self.assertNotifications(partner_employee=(1, 'inbox', 'unread'), partner_admin=(0, '', '')):
self.test_record.message_post(
body='Test', message_type='comment', subtype='mail.mt_comment',
partner_ids=[self.user_employee.partner_id.id])
self.test_record.message_subscribe([self.partner_1.id])
with self.assertNotifications(partner_employee=(1, 'inbox', 'unread'), partner_admin=(0, '', ''), partner_1=(1, 'email', 'read')):
self.test_record.message_post(
body='Test', message_type='comment', subtype='mail.mt_comment',
partner_ids=[self.user_employee.partner_id.id])
with self.assertNotifications(partner_admin=(0, '', ''), partner_1=(1, 'email', 'read'), partner_portal=(1, 'email', 'read')):
self.test_record.message_post(
body='Test', message_type='comment', subtype='mail.mt_comment',
partner_ids=[self.partner_portal.id])
def test_post_inactive_follower(self):
# In some case odoobot is follower of a record.
# Even if it shouldn't be the case, we want to be sure that odoobot is not notified
(self.user_employee | self.user_admin).write({'notification_type': 'inbox'})
self.test_record._message_subscribe(self.user_employee.partner_id.ids)
with self.assertNotifications(partner_employee=(1, 'inbox', 'unread')):
self.test_record.message_post(
body='Test', message_type='comment', subtype='mail.mt_comment')
self.user_employee.active = False
# at this point, partner is still active and would receive an email notification
self.user_employee.partner_id._write({'active': False})
with self.assertNotifications(partner_employee=(0, '', '')):
self.test_record.message_post(
body='Test', message_type='comment', subtype='mail.mt_comment')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_notifications(self):
@@ -153,22 +188,18 @@ class TestMessagePost(BaseFunctionalTest, MockEmails, TestRecipients):
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_portal_ok(self):
portal_user = mail_new_test_user(self.env, login='chell', groups='base.group_portal', name='Chell Gladys')
with patch.object(MailTestSimple, 'check_access_rights', return_value=True):
self.test_record.message_subscribe((self.partner_1 | self.user_employee.partner_id).ids)
new_msg = self.test_record.with_user(portal_user).message_post(
new_msg = self.test_record.with_user(self.user_portal).message_post(
body='<p>Test</p>', subject='Subject',
message_type='comment', subtype='mt_comment')
self.assertEqual(new_msg.sudo().notified_partner_ids, (self.partner_1 | self.user_employee.partner_id))
self.assertEmails(portal_user.partner_id, [[self.partner_1], [self.user_employee.partner_id]])
self.assertEmails(self.user_portal.partner_id, [[self.partner_1], [self.user_employee.partner_id]])
def test_post_portal_crash(self):
portal_user = mail_new_test_user(self.env, login='chell', groups='base.group_portal', name='Chell Gladys')
with self.assertRaises(AccessError):
self.test_record.with_user(portal_user).message_post(
self.test_record.with_user(self.user_portal).message_post(
body='<p>Test</p>', subject='Subject',
message_type='comment', subtype='mt_comment')
@@ -220,151 +251,43 @@ class TestMessagePost(BaseFunctionalTest, MockEmails, TestRecipients):
self.assertNotIn(new_notification, self.test_record.message_ids)
# todo xdo add test message_notify on thread with followers and stuff
class TestComposer(BaseFunctionalTest, MockEmails, TestRecipients):
def setUp(self):
super(TestComposer, self).setUp()
# configure mailing
self.alias_domain = 'schlouby.fr'
self.alias_catchall = 'test+catchall'
self.env['ir.config_parameter'].set_param('mail.catchall.domain', self.alias_domain)
self.env['ir.config_parameter'].set_param('mail.catchall.alias', self.alias_catchall)
# admin should not receive emails
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_post_w_template(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)],
})
_attachments = [{
'name': 'first.txt',
'datas': base64.b64encode(b'My first attachment'),
'res_model': 'res.partner',
'res_id': self.user_admin.partner_id.id
}, {
'name': 'second.txt',
'datas': base64.b64encode(b'My second attachment'),
'res_model': 'res.partner',
'res_id': self.user_admin.partner_id.id
}]
email_1 = 'test1@example.com'
email_2 = 'test2@example.com'
email_3 = self.partner_1.email
self._create_template('mail.test.simple', {
'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])],
'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,
})
# admin should receive emails
self.user_admin.write({'notification_type': 'email'})
# Force the attachments of the template to be in the natural order.
self.email_template.invalidate_cache(['attachment_ids'], ids=self.email_template.ids)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_comment(self):
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': self.test_record._name,
'default_res_id': self.test_record.id,
}).with_user(self.user_employee).create({
'body': '<p>Test Body</p>',
'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
})
composer.send_mail()
test_record.with_user(self.user_employee).message_post_with_template(self.email_template.id, composition_mode='comment')
message = self.test_record.message_ids[0]
self.assertEqual(message.body, '<p>Test Body</p>')
self.assertEqual(message.author_id, self.user_employee.partner_id)
self.assertEqual(message.subject, 'Re: %s' % self.test_record.name)
self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
self.assertEqual(message.partner_ids, self.partner_1 | self.partner_2)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_comment_parent(self):
parent = self.test_record.message_post(body='Test')
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_parent_id': parent.id
}).with_user(self.user_employee).create({
'body': '<p>Mega</p>',
}).send_mail()
message = self.test_record.message_ids[0]
self.assertEqual(message.body, '<p>Mega</p>')
self.assertEqual(message.parent_id, parent)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_mass_mail(self):
test_record_2 = self.env['mail.test.simple'].with_context(BaseFunctionalTest._test_context).create({'name': 'Test2'})
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'mass_mail',
'default_model': self.test_record._name,
'default_res_id': False,
'active_ids': [self.test_record.id, test_record_2.id]
}).with_user(self.user_employee).create({
'subject': 'Testing ${object.name}',
'body': '<p>${object.name}</p>',
'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
})
composer.with_context({
'default_res_id': -1,
'active_ids': [self.test_record.id, test_record_2.id]
}).send_mail()
# check mail_mail
mails = self.env['mail.mail'].search([('subject', 'ilike', 'Testing')])
for mail in mails:
self.assertEqual(mail.recipient_ids, self.partner_1 | self.partner_2,
'compose wizard: mail_mail mass mailing: mail.mail in mass mail incorrect recipients')
# check message on test_record
message1 = self.test_record.message_ids[0]
self.assertEqual(message1.subject, 'Testing %s' % self.test_record.name)
self.assertEqual(message1.body, '<p>%s</p>' % self.test_record.name)
# check message on test_record_2
message1 = test_record_2.message_ids[0]
self.assertEqual(message1.subject, 'Testing %s' % test_record_2.name)
self.assertEqual(message1.body, '<p>%s</p>' % test_record_2.name)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_mass_mail_active_domain(self):
test_record_2 = self.env['mail.test.simple'].with_context(BaseFunctionalTest._test_context).create({'name': 'Test2'})
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'mass_mail',
'default_model': self.test_record._name,
'default_use_active_domain': True,
'active_ids': [self.test_record.id],
'active_domain': [('name', 'in', ['%s' % self.test_record.name, '%s' % test_record_2.name])],
}).with_user(self.user_employee).create({
'subject': 'From Composer Test',
'body': '${object.name}',
}).send_mail()
self.assertEqual(self.test_record.message_ids[0].subject, 'From Composer Test')
self.assertEqual(test_record_2.message_ids[0].subject, 'From Composer Test')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_message_compose_mass_mail_no_active_domain(self):
test_record_2 = self.env['mail.test.simple'].with_context(BaseFunctionalTest._test_context).create({'name': 'Test2'})
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'mass_mail',
'default_model': self.test_record._name,
'default_use_active_domain': False,
'active_ids': [self.test_record.id],
'active_domain': [('name', 'in', ['%s' % self.test_record.name, '%s' % test_record_2.name])],
}).with_user(self.user_employee).create({
'subject': 'From Composer Test',
'body': '${object.name}',
}).send_mail()
self.assertEqual(self.test_record.message_ids[0].subject, 'From Composer Test')
self.assertFalse(test_record_2.message_ids.ids)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_message_compose_portal_ok(self):
portal_user = mail_new_test_user(self.env, login='chell', groups='base.group_portal', name='Chell Gladys')
with patch.object(MailTestSimple, 'check_access_rights', return_value=True):
ComposerPortal = self.env['mail.compose.message'].with_user(portal_user)
ComposerPortal.with_context({
'default_composition_mode': 'comment',
'default_model': self.test_record._name,
'default_res_id': self.test_record.id,
}).create({
'subject': 'Subject',
'body': '<p>Body text</p>',
'partner_ids': []}).send_mail()
self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text</p>')
self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
ComposerPortal.with_context({
'default_composition_mode': 'comment',
'default_parent_id': self.test_record.message_ids.ids[0],
}).create({
'subject': 'Subject',
'body': '<p>Body text 2</p>'}).send_mail()
self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text 2</p>')
self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
new_partners = self.env['res.partner'].search([('email', 'in', [email_1, email_2])])
self.assertEmails(
self.user_employee.partner_id,
[[self.partner_1], [self.partner_2], [new_partners[0]], [new_partners[1]], [self.partner_admin]],
subject='About %s' % test_record.name,
body_content=test_record.name,
attachments=[('first.txt', b'My first attachment', 'text/plain'), ('second.txt', b'My second attachment', 'text/plain')])
+37 -18
View File
@@ -1,32 +1,17 @@
# -*- coding: utf-8 -*-
from email.utils import formataddr
from unittest.mock import patch
from odoo.addons.test_mail.tests import common
class TestTracking(common.BaseFunctionalTest, common.MockEmails):
def assertTracking(self, message, data):
tracking_values = message.sudo().tracking_value_ids
for field_name, value_type, old_value, new_value in data:
tracking = tracking_values.filtered(lambda track: track.field == field_name)
self.assertEqual(len(tracking), 1)
if value_type in ('char', 'integer'):
self.assertEqual(tracking.old_value_char, old_value)
self.assertEqual(tracking.new_value_char, new_value)
elif value_type in ('many2one'):
self.assertEqual(tracking.old_value_integer, old_value and old_value.id or False)
self.assertEqual(tracking.new_value_integer, new_value and new_value.id or False)
self.assertEqual(tracking.old_value_char, old_value and old_value.display_name or '')
self.assertEqual(tracking.new_value_char, new_value and new_value.display_name or '')
else:
self.assertEqual(1, 0)
def setUp(self):
super(TestTracking, self).setUp()
record = self.env['mail.test.full'].with_user(self.user_employee).with_context(common.BaseFunctionalTest._test_context).create({
record = self.env['mail.test.full'].with_user(self.user_employee).with_context(self._test_context).create({
'name': 'Test',
})
self.record = record.with_context(mail_notrack=False)
@@ -120,7 +105,7 @@ class TestTracking(common.BaseFunctionalTest, common.MockEmails):
def test_message_track_template_at_create(self):
""" Create a record with tracking template on create, template should be sent."""
Model = self.env['mail.test.full'].with_user(self.user_employee).with_context(common.BaseFunctionalTest._test_context)
Model = self.env['mail.test.full'].with_user(self.user_employee).with_context(self._test_context)
Model = Model.with_context(mail_notrack=False)
record = Model.create({
'name': 'Test',
@@ -185,3 +170,37 @@ class TestTracking(common.BaseFunctionalTest, common.MockEmails):
('name', '=', 'email_from')])
ir_model_field.with_context(_force_unlink=True).unlink()
self.assertEqual(len(record_sudo.message_ids.tracking_value_ids), 0)
def test_create_partner_from_tracking_multicompany(self):
company1 = self.env['res.company'].create({'name': 'company1'})
self.env.user.write({'company_ids': [(4, company1.id, False)]})
self.assertNotEqual(self.env.company, company1)
email_new_partner = "diamonds@rust.com"
Partner = self.env['res.partner']
self.assertFalse(Partner.search([('email', '=', email_new_partner)]))
template = self.env['mail.template'].create({
'model_id': self.env['ir.model']._get('mail.test.track').id,
'name': 'AutoTemplate',
'subject': 'autoresponse',
'email_from': self.env.user.email_formatted,
'email_to': "${object.email_from}",
'body_html': "<div>A nice body</div>",
})
def patched_message_track_post_template(*args, **kwargs):
if args[0]._name == "mail.test.track":
args[0].message_post_with_template(template.id)
return True
with patch('odoo.addons.mail.models.mail_thread.MailThread._message_track_post_template', patched_message_track_post_template):
self.env['mail.test.track'].create({
'email_from': email_new_partner,
'company_id': company1.id,
'user_id': self.env.user.id, # trigger track template
})
new_partner = Partner.search([('email', '=', email_new_partner)])
self.assertTrue(new_partner)
self.assertEqual(new_partner.company_id, company1)
+9 -6
View File
@@ -11,18 +11,21 @@ from odoo.tests import tagged
@tagged("odoobot")
class TestOdoobot(BaseFunctionalTest, MockEmails, TestRecipients):
def setUp(self):
super(TestOdoobot, self).setUp()
self.odoobot = self.env.ref("base.partner_root")
self.message_post_default_kwargs = {
@classmethod
def setUpClass(cls):
super(TestOdoobot, cls).setUpClass()
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
cls.odoobot = cls.env.ref("base.partner_root")
cls.message_post_default_kwargs = {
'body': '',
'attachment_ids': [],
'message_type': 'comment',
'partner_ids': [],
'subtype': 'mail.mt_comment'
}
self.odoobot_ping_body = '<a href="http://odoo.com/web#model=res.partner&amp;id=%s" class="o_mail_redirect" data-oe-id="%s" data-oe-model="res.partner" target="_blank">@OdooBot</a>' % (self.odoobot.id, self.odoobot.id)
self.test_record_employe = self.test_record.with_user(self.user_employee)
cls.odoobot_ping_body = '<a href="http://odoo.com/web#model=res.partner&amp;id=%s" class="o_mail_redirect" data-oe-id="%s" data-oe-model="res.partner" target="_blank">@OdooBot</a>' % (cls.odoobot.id, cls.odoobot.id)
cls.test_record_employe = cls.test_record.with_user(cls.user_employee)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_fetch_listener(self):
-34
View File
@@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.test_mail.tests import common
from odoo.tests import tagged
@tagged('moderation')
class TestMessageModeration(common.Moderation):
@classmethod
def setUpClass(cls):
super(TestMessageModeration, cls).setUpClass()
def test_is_moderator(self):
self.assertTrue(self.user_employee.is_moderator)
self.assertFalse(self.user_admin.is_moderator)
self.assertTrue(self.user_employee_2.is_moderator)
def test_moderation_counter(self):
self._create_new_message(self.channel_1.id, status='pending_moderation', author=self.partner_admin)
self._create_new_message(self.channel_1.id, status='accepted', author=self.partner_admin)
self._create_new_message(self.channel_1.id, status='accepted', author=self.partner_employee)
self._create_new_message(self.channel_1.id, status='pending_moderation', author=self.partner_employee)
self._create_new_message(self.channel_1.id, status='accepted', author=self.partner_employee_2)
self.assertEqual(self.user_employee.moderation_counter, 2)
self.assertEqual(self.user_employee_2.moderation_counter, 0)
self.assertEqual(self.user_admin.moderation_counter, 0)
self.channel_1.write({'channel_partner_ids': [(4, self.partner_employee_2.id)], 'moderator_ids': [(4, self.user_employee_2.id)]})
self.assertEqual(self.user_employee.moderation_counter, 2)
self.assertEqual(self.user_employee_2.moderation_counter, 0)
self.assertEqual(self.user_admin.moderation_counter, 0)