[MERGE] (test_)mail(_full): cleanup composer and post tests, add portal / rating

PURPOSE

Cleanup and improve test coverage in test_mail(_full), notably about composer,
message_post API (and its sub-methods) and attachments management.

SPECIFICATIONS

Improve tooling of mail tests. Use SMTP mockup class available in base tests
in mail tests to have a better and simplified mock tool when dealing with
mail tests. Remove old fashioned code.

Add tests related to mail failure management and its effect on mail.mail
and related mail.notification records.

Cleanup and improve composer-related tests. As composer will soon be improved
better ensure we don't break flows.

Cleanup and improve post-related tests. As composer and posting methods will
soon be improved better ensure we don't break flows.

Add some first tests about rating and rating mixin:
  * rating preparation: token, information from the record to rate;
  * rating apply: posting message, author;

See sub commits for more details.

LINKS

Task-2673913 (TestMail: Cleanup and improve test coverage)
Task-2704390 (TestMailFull: add first rating tests)

Prepares Task-2088884 (Mail: Composer Onchange to Editable Computed Stored)
Prepares Task-2710804 (Mail: Clean MailThread API)
Prepares Task-2684479 (Mail: Better send errors management)
Prepares Task-36879 (Mail: Support MultiCompany Aliases)

closes odoo/odoo#86393

Related: odoo/enterprise#25267
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Odoo's Mergebot
2022-03-24 13:57:37 +01:00
committed by GitHub
35 changed files with 2099 additions and 704 deletions
+7 -7
View File
@@ -472,20 +472,20 @@ class MailActivity(models.Model):
def action_done(self):
""" Wrapper without feedback because web button add context as
parameter, therefore setting context to feedback """
messages, next_activities = self._action_done()
return messages.ids and messages.ids[0] or False
return self.action_feedback()
def action_feedback(self, feedback=False, attachment_ids=None):
self = self.with_context(clean_context(self.env.context))
messages, next_activities = self._action_done(feedback=feedback, attachment_ids=attachment_ids)
return messages.ids and messages.ids[0] or False
messages, _next_activities = self.with_context(
clean_context(self.env.context)
)._action_done(feedback=feedback, attachment_ids=attachment_ids)
return messages[0].id if messages else False
def action_done_schedule_next(self):
""" Wrapper without feedback because web button add context as
parameter, therefore setting context to feedback """
return self.action_feedback_schedule_next()
def action_feedback_schedule_next(self, feedback=False):
def action_feedback_schedule_next(self, feedback=False, attachment_ids=None):
ctx = dict(
clean_context(self.env.context),
default_previous_activity_type_id=self.activity_type_id.id,
@@ -493,7 +493,7 @@ class MailActivity(models.Model):
default_res_id=self.res_id,
default_res_model=self.res_model,
)
messages, next_activities = self._action_done(feedback=feedback) # will unlink activity, dont access self after that
_messages, next_activities = self._action_done(feedback=feedback, attachment_ids=attachment_ids) # will unlink activity, dont access self after that
if next_activities:
return False
return {
+2 -2
View File
@@ -475,7 +475,7 @@ class MailActivityMixin(models.AbstractModel):
activities.write(write_vals)
return activities
def activity_feedback(self, act_type_xmlids, user_id=None, feedback=None):
def activity_feedback(self, act_type_xmlids, user_id=None, feedback=None, attachment_ids=None):
""" Set activities as done, limiting to some activity types and
optionally to a given user. """
if self.env.context.get('mail_activity_automation_skip'):
@@ -488,7 +488,7 @@ class MailActivityMixin(models.AbstractModel):
return False
activities = self.activity_search(act_type_xmlids, user_id=user_id)
if activities:
activities.action_feedback(feedback=feedback)
activities.action_feedback(feedback=feedback, attachment_ids=attachment_ids)
return True
def activity_unlink(self, act_type_xmlids, user_id=None):
+106 -59
View File
@@ -11,10 +11,9 @@ from contextlib import contextmanager
from functools import partial
from lxml import html
from unittest.mock import patch
from smtplib import SMTPServerDisconnected
from odoo import exceptions
from odoo.addons.base.models.ir_mail_server import IrMailServer, MailDeliveryException
from odoo.addons.base.models.ir_mail_server import IrMailServer
from odoo.addons.base.tests.common import MockSmtplibCase
from odoo.addons.bus.models.bus import ImBus, json_dump
from odoo.addons.mail.models.mail_mail import MailMail
from odoo.addons.mail.models.mail_message import Message
@@ -22,10 +21,14 @@ from odoo.addons.mail.models.mail_notification import MailNotification
from odoo.tests import common, new_test_user
from odoo.tools import formataddr, pycompat
mail_new_test_user = partial(new_test_user, context={'mail_create_nolog': True, 'mail_create_nosubscribe': True, 'mail_notrack': True, 'no_reset_password': True})
mail_new_test_user = partial(new_test_user, context={'mail_create_nolog': True,
'mail_create_nosubscribe': True,
'mail_notrack': True,
'no_reset_password': True,
'mail_channel_nosubscribe': True})
class MockEmail(common.BaseCase):
class MockEmail(common.BaseCase, MockSmtplibCase):
""" Tools, helpers and asserts for mailgateway-related tests
Useful reminders
@@ -39,37 +42,19 @@ class MockEmail(common.BaseCase):
# ------------------------------------------------------------
@contextmanager
def mock_mail_gateway(self, mail_unlink_sent=False, sim_error=None):
def mock_mail_gateway(self, mail_unlink_sent=False):
build_email_origin = IrMailServer.build_email
send_email_origin = IrMailServer.send_email
mail_create_origin = MailMail.create
mail_unlink_origin = MailMail.unlink
self.mail_unlink_sent = mail_unlink_sent
self._init_mail_mock()
def _ir_mail_server_connect(model, *args, **kwargs):
if sim_error and sim_error == 'connect_smtp_notfound':
raise exceptions.UserError(
"Missing SMTP Server\nPlease define at least one SMTP server, or provide the SMTP parameters explicitly.")
if sim_error and sim_error == 'connect_failure':
raise Exception("Some exception")
return None
def _ir_mail_server_build_email(model, *args, **kwargs):
self._mails.append(kwargs)
self._mails_args.append(args)
return build_email_origin(model, *args, **kwargs)
def _ir_mail_server_send_email(model, message, *args, **kwargs):
if '@' not in message['To']:
raise AssertionError(model.NO_VALID_RECIPIENT)
if sim_error and sim_error == 'send_assert':
raise AssertionError('AssertionError')
elif sim_error and sim_error == 'send_disconnect':
raise SMTPServerDisconnected('SMTPServerDisconnected')
elif sim_error and sim_error == 'send_delivery':
raise MailDeliveryException('MailDeliveryException')
return message['Message-Id']
def _mail_mail_create(model, *args, **kwargs):
res = mail_create_origin(model, *args, **kwargs)
self._new_mails += res.sudo()
@@ -80,11 +65,13 @@ class MockEmail(common.BaseCase):
return mail_unlink_origin(model, *args, **kwargs)
return True
with patch.object(IrMailServer, 'connect', autospec=True, wraps=IrMailServer, side_effect=_ir_mail_server_connect) as ir_mail_server_connect_mock, \
patch.object(IrMailServer, 'build_email', autospec=True, wraps=IrMailServer, side_effect=_ir_mail_server_build_email) as ir_mail_server_build_email_mock, \
patch.object(IrMailServer, 'send_email', autospec=True, wraps=IrMailServer, side_effect=_ir_mail_server_send_email) as ir_mail_server_send_email_mock, \
patch.object(MailMail, 'create', autospec=True, wraps=MailMail, side_effect=_mail_mail_create) as _mail_mail_create_mock, \
patch.object(MailMail, 'unlink', autospec=True, wraps=MailMail, side_effect=_mail_mail_unlink) as mail_mail_unlink_mock:
with self.mock_smtplib_connection(), \
patch.object(IrMailServer, 'build_email', autospec=True, wraps=IrMailServer, side_effect=_ir_mail_server_build_email) as build_email_mocked, \
patch.object(IrMailServer, 'send_email', autospec=True, wraps=IrMailServer, side_effect=send_email_origin) as send_email_mocked, \
patch.object(MailMail, 'create', autospec=True, wraps=MailMail, side_effect=_mail_mail_create), \
patch.object(MailMail, 'unlink', autospec=True, wraps=MailMail, side_effect=_mail_mail_unlink):
self.build_email_mocked = build_email_mocked
self.send_email_mocked = send_email_mocked
yield
def _init_mail_mock(self):
@@ -92,10 +79,6 @@ class MockEmail(common.BaseCase):
self._mails_args = []
self._new_mails = self.env['mail.mail'].sudo()
# ------------------------------------------------------------
# GATEWAY TOOLS
# ------------------------------------------------------------
@classmethod
def _init_mail_gateway(cls):
cls.alias_domain = 'test.com'
@@ -108,6 +91,26 @@ class MockEmail(common.BaseCase):
cls.env['ir.config_parameter'].set_param('mail.default.from', cls.default_from)
cls.mailer_daemon_email = formataddr(('MAILER-DAEMON', '%s@%s' % (cls.alias_bounce, cls.alias_domain)))
@classmethod
def _init_outgoing_gateway(cls):
cls.env['ir.mail_server'].search([]).unlink()
cls.mail_server_domain, cls.mail_server_global = cls.env['ir.mail_server'].create([
{'from_filter': 'test.com',
'name': 'Domain Based Server',
'smtp_encryption': 'none',
'smtp_host': 'smtp_host',
},
{'from_filter': False,
'name': 'No FromFilter Server',
'smtp_encryption': 'none',
'smtp_host': 'smtp_host',
}
])
# ------------------------------------------------------------
# GATEWAY TOOLS
# ------------------------------------------------------------
def format(self, template, to='groups@example.com, other@gmail.com', subject='Frogs',
email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', return_path='', cc='',
extra='', msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>',
@@ -316,7 +319,7 @@ class MockEmail(common.BaseCase):
# ------------------------------------------------------------
def assertMailMail(self, recipients, status,
check_mail_mail=True, mail_message=None, author=None,
mail_message=None, author=None,
content=None, fields_values=None, email_values=None):
""" Assert mail.mail records are created and maybe sent as emails. Allow
asserting their content. Records to check are the one generated when
@@ -335,8 +338,6 @@ class MockEmail(common.BaseCase):
:param email_values: if given, should be a dictionary of keys / values
allowing to check sent email additional values (if any).
See ``assertSentEmail``;
:param check_mail_mail: deprecated, use ``assertSentEmail`` if False
"""
found_mail = self._find_mail_mail_wpartners(recipients, status, mail_message=mail_message, author=author)
self.assertTrue(bool(found_mail))
@@ -370,8 +371,6 @@ class MockEmail(common.BaseCase):
:param email_values: if given, should be a dictionary of keys / values
allowing to check sent email additional values (if any).
See ``assertSentEmail``;
:param check_mail_mail: deprecated, use ``assertSentEmail`` if False
"""
for email_to in emails:
found_mail = self._find_mail_mail_wemail(email_to, status, mail_message=mail_message, author=author)
@@ -410,6 +409,16 @@ class MockEmail(common.BaseCase):
found_mail[fname], fvalue,
'Mail: expected %s for %s, got %s' % (fvalue, fname, found_mail[fname]))
def assertMessageFields(self, message, fields_values):
""" Just a quick helper to check a mail.message content by giving directly
a dict for fields. Allows to hide a lot of assertEqual under a simple
call with a dictionary of expected values. """
for fname, fvalue in fields_values.items():
self.assertEqual(
message[fname], fvalue,
'Message: expected %s for %s, got %s' % (fvalue, fname, message[fname])
)
def assertNoMail(self, recipients, mail_message=None, author=None):
""" Check no mail.mail and email was generated during gateway mock. """
try:
@@ -435,8 +444,10 @@ class MockEmail(common.BaseCase):
:param values: dictionary of additional values to check email content;
"""
base_expected = {}
for fname in ['reply_to', 'subject', 'attachments', 'body', 'references',
'body_content', 'body_alternative_content', 'references_content']:
for fname in ['author_id', 'reply_to',
'subject', 'body', 'body_content', 'body_alternative_content',
'references', 'references_content',
'attachments']:
if fname in values:
base_expected[fname] = values[fname]
@@ -461,9 +472,14 @@ class MockEmail(common.BaseCase):
debug_info = '-'.join('From: %s-To: %s' % (mail['email_from'], mail['email_to']) for mail in self._mails) if not bool(sent_mail) else ''
self.assertTrue(bool(sent_mail), 'Expected mail from %s to %s not found in %s' % (expected['email_from'], expected['email_to'], debug_info))
for val in ['reply_to', 'subject', 'references', 'attachments']:
for val in ['reply_to', 'subject', 'references']:
if val in expected:
self.assertEqual(expected[val], sent_mail[val], 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val]))
if 'attachments' in expected:
self.assertEqual(
sorted(expected['attachments']), sorted(sent_mail['attachments']),
'Value for %s: expected %s, received %s' % ('attachments', expected['attachments'], sent_mail['attachments'])
)
if 'attachments_info' in values:
attachments = sent_mail['attachments']
for attachment_info in values['attachments_info']:
@@ -473,12 +489,18 @@ class MockEmail(common.BaseCase):
if attachment_info.get('type'):
self.assertEqual(attachment[2], attachment_info['type'])
self.assertEqual(len(values['attachments_info']), len(attachments))
for val in ['body']:
for val in ['body', 'body_alternative']:
if val in expected:
self.assertHtmlEqual(expected[val], sent_mail[val], 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val]))
for val in ['body_content', 'body_alternative', 'references_content']:
self.assertHtmlEqual(
expected[val], sent_mail[val],
'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val])
)
for val in ['body_content', 'body_alternative_content', 'references_content']:
if val in expected:
self.assertIn(expected[val], sent_mail[val[:-8]], 'Value for %s: %s does not contain %s' % (val, sent_mail[val[:-8]], expected[val]))
self.assertIn(
expected[val], sent_mail[val[:-8]],
'Value for %s: %s does not contain %s' % (val, sent_mail[val[:-8]], expected[val])
)
class MailCase(MockEmail):
@@ -496,10 +518,11 @@ class MailCase(MockEmail):
("UNKNOWN", "Unknown error")
"""
_test_context = {
'mail_channel_nosubscribe': True,
'mail_create_nolog': True,
'mail_create_nosubscribe': True,
'mail_notrack': True,
'no_reset_password': True
'no_reset_password': True,
}
@classmethod
@@ -507,7 +530,7 @@ class MailCase(MockEmail):
return record.with_context(
mail_create_nolog=False,
mail_create_nosubscribe=False,
mail_notrack=False
mail_notrack=False,
)
def flush_tracking(self):
@@ -627,19 +650,19 @@ class MailCase(MockEmail):
# ------------------------------------------------------------
@contextmanager
def assertSinglePostNotifications(self, recipients_info, message_info=None, mail_unlink_sent=False, sim_error=None):
def assertSinglePostNotifications(self, recipients_info, message_info=None, mail_unlink_sent=False):
""" Shortcut to assertMsgNotifications when having a single message to check. """
r_info = dict(message_info if message_info else {})
r_info.setdefault('content', '')
r_info['notif'] = recipients_info
with self.assertPostNotifications([r_info], mail_unlink_sent=mail_unlink_sent, sim_error=sim_error):
with self.assertPostNotifications([r_info], mail_unlink_sent=mail_unlink_sent):
yield
@contextmanager
def assertPostNotifications(self, recipients_info, mail_unlink_sent=False, sim_error=None):
def assertPostNotifications(self, recipients_info, mail_unlink_sent=False):
""" Check content of notifications. """
try:
with self.mock_mail_gateway(mail_unlink_sent=mail_unlink_sent, sim_error=sim_error), self.mock_bus(), self.mock_mail_app():
with self.mock_mail_gateway(mail_unlink_sent=mail_unlink_sent), self.mock_bus(), self.mock_mail_app():
yield
finally:
done_msgs, done_notifs = self.assertMailNotifications(self._new_msgs, recipients_info)
@@ -659,7 +682,7 @@ class MailCase(MockEmail):
@contextmanager
def assertNoNotifications(self):
try:
with self.mock_mail_gateway(mail_unlink_sent=False, sim_error=None), self.mock_bus(), self.mock_mail_app():
with self.mock_mail_gateway(mail_unlink_sent=False), self.mock_bus(), self.mock_mail_app():
yield
finally:
self.assertFalse(bool(self._new_msgs))
@@ -891,9 +914,14 @@ class MailCommon(common.TransactionCase, MailCase):
super(MailCommon, cls).setUpClass()
# give default values for all email aliases and domain
cls._init_mail_gateway()
cls._init_outgoing_gateway()
# ensure admin configuration
cls.user_admin = cls.env.ref('base.user_admin')
cls.user_admin.write({'notification_type': 'inbox'})
cls.user_admin.write({
'country_id': cls.env.ref('base.be').id,
'email': 'test.admin@test.example.com',
'notification_type': 'inbox',
})
cls.partner_admin = cls.env.ref('base.partner_admin')
cls.company_admin = cls.user_admin.company_id
cls.company_admin.write({'email': 'company@example.com'})
@@ -905,9 +933,11 @@ class MailCommon(common.TransactionCase, MailCase):
# test standard employee
cls.user_employee = mail_new_test_user(
cls.env, login='employee',
groups='base.group_user,mail.group_mail_template_editor',
cls.env,
company_id=cls.company_admin.id,
country_id=cls.env.ref('base.be').id,
groups='base.group_user,mail.group_mail_template_editor',
login='employee',
name='Ernest Employee',
notification_type='inbox',
signature='--\nErnest'
@@ -939,12 +969,26 @@ class MailCommon(common.TransactionCase, MailCase):
groups='base.group_user',
company_id=cls.company_2.id,
company_ids=[(4, cls.company_2.id)],
email='enguerrand@example.com',
name='Enguerrand Employee C2',
notification_type='inbox',
signature='--\nEnguerrand'
)
cls.partner_employee_c2 = cls.user_employee_c2.partner_id
# test erp manager employee
cls.user_erp_manager = mail_new_test_user(
cls.env,
company_id=cls.company_2.id,
company_ids=[(6, 0, (cls.company_admin + cls.company_2).ids)],
email='etchenne@example.com',
groups='base.group_user,base.group_erp_manager,mail.group_mail_template_editor',
login='erp_manager',
name='Etchenne Tchagada',
notification_type='inbox',
signature='--\nEtchenne',
)
@classmethod
def _activate_multi_lang(cls, lang_code='es_ES', layout_arch_db=None, test_record=False, test_template=False):
""" Summary of es_ES matching done here (a bit hardcoded to ease tests)
@@ -1097,15 +1141,18 @@ class MailCommon(common.TransactionCase, MailCase):
})
cls.env['ir.translation'].create(translations_tocreate)
def _generate_attachments_data(self, count, res_model=None, res_id=None):
def _generate_attachments_data(self, count, res_model=None, res_id=None, attach_values=None):
# attachment visibility depends on what they are attached to
attach_values = attach_values or {}
if res_model is None:
res_model = self.template._name
if res_id is None:
res_id = self.template.id
return [{
'name': '%02d.txt' % x,
'datas': base64.b64encode(b'Att%02d' % x),
'datas': base64.b64encode(b'AttContent_%02d' % x),
'name': 'AttFileName_%02d.txt' % x,
'mimetype': 'text/plain',
'res_model': res_model,
'res_id': res_id,
**attach_values,
} for x in range(count)]
@@ -1,10 +1,14 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from functools import partial
from odoo.addons.mail.tests.common import mail_new_test_user
from odoo.addons.mail.tests.common import MailCommon
from odoo.exceptions import AccessError, UserError
mail_channel_new_test_user = partial(mail_new_test_user, context={'mail_channel_nosubscribe': False})
class TestMailChannelMembers(MailCommon):
@@ -22,23 +26,23 @@ class TestMailChannelMembers(MailCommon):
'res_id': cls.secret_group.id,
})
cls.user_1 = mail_new_test_user(
cls.user_1 = mail_channel_new_test_user(
cls.env, login='user_1',
name='User 1',
groups='base.group_user,mail.secret_group')
cls.user_2 = mail_new_test_user(
cls.user_2 = mail_channel_new_test_user(
cls.env, login='user_2',
name='User 2',
groups='base.group_user,mail.secret_group')
cls.user_3 = mail_new_test_user(
cls.user_3 = mail_channel_new_test_user(
cls.env, login='user_3',
name='User 3',
groups='base.group_user,mail.secret_group')
cls.user_portal = mail_new_test_user(
cls.user_portal = mail_channel_new_test_user(
cls.env, login='user_portal',
name='User Portal',
groups='base.group_portal')
cls.user_public = mail_new_test_user(
cls.user_public = mail_channel_new_test_user(
cls.env, login='user_ublic',
name='User Public',
groups='base.group_public')
+11 -5
View File
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests import users
from odoo.addons.mail.tests.common import MailCommon
from odoo.tests import tagged, users
@tagged('mail_composer')
class TestMailComposer(MailCommon):
@classmethod
def setUpClass(cls):
super(TestMailComposer, cls).setUpClass()
@@ -47,9 +49,11 @@ class TestMailComposer(MailCommon):
values = mail_compose_message.get_mail_values(self.partner_employee.ids)
self.assertIn(self.body_html,
self.assertIn(
self.body_html,
values[self.partner_employee.id]['body_html'],
'We must preserve (mso) comments in email html')
'We must preserve (mso) comments in email html'
)
@users('employee')
def test_mail_mass_mode_compose_with_mso(self):
@@ -69,6 +73,8 @@ class TestMailComposer(MailCommon):
values = composer.get_mail_values(self.partner_employee.ids)
self.assertIn(self.body_html,
self.assertIn(
self.body_html,
values[self.partner_employee.id]['body_html'],
'We must preserve (mso) comments in email html')
'We must preserve (mso) comments in email html'
)
+1 -1
View File
@@ -545,7 +545,7 @@ class MailComposer(models.TransientModel):
elif template_id:
values = self.generate_email_for_composer(
template_id, [res_id],
['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachment_ids', 'mail_server_id']
['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachment_ids', 'mail_server_id']
)[res_id]
# transform attachments into attachment_ids; not attached to the document because this will
# be done further in the posting process, allowing to clean database if email not send
+28 -2
View File
@@ -60,8 +60,10 @@ class MailTestActivity(models.Model):
summary=action_summary
)
def action_close(self, action_feedback):
self.activity_feedback(['test_mail.mail_act_test_todo'], feedback=action_feedback)
def action_close(self, action_feedback, attachment_ids=None):
self.activity_feedback(['test_mail.mail_act_test_todo'],
feedback=action_feedback,
attachment_ids=attachment_ids)
class MailTestTicket(models.Model):
@@ -111,6 +113,18 @@ class MailTestTicket(models.Model):
return super(MailTestTicket, self)._track_subtype(init_values)
class MailTestTicketMC(models.Model):
""" Just mail.test.ticket, but multi company. Kept as different model to
avoid messing with existing tests, notably performance, and ease backward
comparison. """
_description = 'Ticket-like model'
_name = 'mail.test.ticket.mc'
_inherit = ['mail.test.ticket']
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company)
container_id = fields.Many2one('mail.test.container.mc', tracking=True)
class MailTestContainer(models.Model):
""" This model can be used in tests when container records like projects
or teams are required. """
@@ -144,6 +158,18 @@ class MailTestContainer(models.Model):
values['alias_parent_thread_id'] = self.id
return values
class MailTestContainerMC(models.Model):
""" Just mail.test.container, but multi company. Kept as different model to
avoid messing with existing tests, notably performance, and ease backward
comparison. """
_description = 'Project-like model with alias (MC)'
_name = 'mail.test.container.mc'
_mail_post_access = 'read'
_inherit = ['mail.test.container']
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company)
class MailTestComposerMixin(models.Model):
""" A simple invite-like wizard using the composer mixin, rendering on
itself. """
@@ -11,12 +11,16 @@ access_mail_test_activity_portal,mail.test.activity.portal,model_mail_test_activ
access_mail_test_activity_user,mail.test.activity.user,model_mail_test_activity,base.group_user,1,1,1,1
access_mail_test_field_type_portal,mail.test.field.type.portal,model_mail_test_field_type,base.group_portal,0,0,0,0
access_mail_test_field_type_user,mail.test.field.type.user,model_mail_test_field_type,base.group_user,1,1,1,1
access_mail_test_ticket_portal,mail.test.ticket.portal,model_mail_test_ticket,base.group_portal,0,0,0,0
access_mail_test_ticket_portal,mail.test.ticket.portal,model_mail_test_ticket,base.group_portal,1,0,0,0
access_mail_test_ticket_user,mail.test.ticket.user,model_mail_test_ticket,base.group_user,1,1,1,1
access_mail_test_ticket_mc_portal,mail.test.ticket.mc.portal,model_mail_test_ticket_mc,base.group_portal,1,0,0,0
access_mail_test_ticket_mc_user,mail.test.ticket.mc.user,model_mail_test_ticket_mc,base.group_user,1,1,1,1
access_mail_test_composer_mixin_all,mail.test.composer.mixin.all,model_mail_test_composer_mixin,,0,0,0,0
access_mail_test_composer_mixin_user,mail.test.composer.mixin.user,model_mail_test_composer_mixin,base.group_user,1,1,1,1
access_mail_test_container_portal,mail.test.container_portal,model_mail_test_container,base.group_portal,1,1,0,0
access_mail_test_container_portal,mail.test.container_portal,model_mail_test_container,base.group_portal,1,0,0,0
access_mail_test_container_user,mail.test.container.user,model_mail_test_container,base.group_user,1,1,1,1
access_mail_test_container_mc_portal,mail.test.container.mc.portal,model_mail_test_container_mc,base.group_portal,1,0,0,0
access_mail_test_container_mc_user,mail.test.container.mc.user,model_mail_test_container_mc,base.group_user,1,1,1,1
access_mail_test_cc_portal,mail.test.cc.portal,model_mail_test_cc,base.group_portal,1,0,0,0
access_mail_test_cc_user,mail.test.cc.user,model_mail_test_cc,base.group_user,1,1,1,1
access_mail_test_lang_portal,mail.test.lang.portal,model_mail_test_lang,base.group_portal,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
11 access_mail_test_activity_user mail.test.activity.user model_mail_test_activity base.group_user 1 1 1 1
12 access_mail_test_field_type_portal mail.test.field.type.portal model_mail_test_field_type base.group_portal 0 0 0 0
13 access_mail_test_field_type_user mail.test.field.type.user model_mail_test_field_type base.group_user 1 1 1 1
14 access_mail_test_ticket_portal mail.test.ticket.portal model_mail_test_ticket base.group_portal 0 1 0 0 0
15 access_mail_test_ticket_user mail.test.ticket.user model_mail_test_ticket base.group_user 1 1 1 1
16 access_mail_test_ticket_mc_portal mail.test.ticket.mc.portal model_mail_test_ticket_mc base.group_portal 1 0 0 0
17 access_mail_test_ticket_mc_user mail.test.ticket.mc.user model_mail_test_ticket_mc base.group_user 1 1 1 1
18 access_mail_test_composer_mixin_all mail.test.composer.mixin.all model_mail_test_composer_mixin 0 0 0 0
19 access_mail_test_composer_mixin_user mail.test.composer.mixin.user model_mail_test_composer_mixin base.group_user 1 1 1 1
20 access_mail_test_container_portal mail.test.container_portal model_mail_test_container base.group_portal 1 1 0 0 0
21 access_mail_test_container_user mail.test.container.user model_mail_test_container base.group_user 1 1 1 1
22 access_mail_test_container_mc_portal mail.test.container.mc.portal model_mail_test_container_mc base.group_portal 1 0 0 0
23 access_mail_test_container_mc_user mail.test.container.mc.user model_mail_test_container_mc base.group_user 1 1 1 1
24 access_mail_test_cc_portal mail.test.cc.portal model_mail_test_cc base.group_portal 1 0 0 0
25 access_mail_test_cc_user mail.test.cc.user model_mail_test_cc base.group_user 1 1 1 1
26 access_mail_test_lang_portal mail.test.lang.portal model_mail_test_lang base.group_portal 1 0 0 0
@@ -1,11 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="mail_test_multi_company_rule" model="ir.rule">
<field name="name">Mail Test Multi Company</field>
<field name="model_id" ref="test_mail.model_mail_test_multi_company"/>
<field eval="True" name="global"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
<record id="mail_test_multi_company_rule" model="ir.rule">
<field name="name">Mail Test Multi Company</field>
<field name="model_id" ref="test_mail.model_mail_test_multi_company"/>
<field eval="True" name="global"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
<!-- TICKET-LIKE -->
<record id="mail_test_ticket_rule_portal" model="ir.rule">
<field name="name">Portal Mail Test Ticket</field>
<field name="model_id" ref="test_mail.model_mail_test_ticket"/>
<field name="domain_force">[('message_partner_ids', 'in', [user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<!-- MULTI COMPANY TICKET LIKE -->
<record id="mail_test_ticket_mc_rule" model="ir.rule">
<field name="name">Mail Test Ticket Multi Company</field>
<field name="model_id" ref="test_mail.model_mail_test_ticket_mc"/>
<field name="global" eval="True"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
<record id="mail_test_ticket_mc_rule_portal" model="ir.rule">
<field name="name">Portal Mail Test Ticket Multi Company</field>
<field name="model_id" ref="test_mail.model_mail_test_ticket_mc"/>
<field name="domain_force">[('message_partner_ids', 'in', [user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<!-- PROJECT-LIKE -->
<record id="mail_test_container_rule_portal" model="ir.rule">
<field name="name">Portal Mail Test Container</field>
<field name="model_id" ref="test_mail.model_mail_test_container"/>
<field name="domain_force">[('message_partner_ids', 'in', [user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
<!-- MULTI COMPANY PROJECT LIKE -->
<record id="mail_test_container_mc_rule" model="ir.rule">
<field name="name">Mail Test Container Multi Company</field>
<field name="model_id" ref="test_mail.model_mail_test_container_mc"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
<field name="global" eval="True"/>
</record>
<record id="mail_test_container_mc_rule_portal" model="ir.rule">
<field name="name">Portal Mail Test Container Multi Company</field>
<field name="model_id" ref="test_mail.model_mail_test_container_mc"/>
<field name="domain_force">[('message_partner_ids', 'in', [user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
</odoo>
+18 -16
View File
@@ -9,27 +9,29 @@ class TestMailCommon(MailCommon):
""" Main entry point for functional tests. """
@classmethod
def _create_channel_listener(cls):
cls.channel_listen = cls.env['mail.channel'].with_context(cls._test_context).create({'name': 'Listener'})
@classmethod
def _create_records_for_batch(cls, model, count):
# TDE note: to be cleaned in master
def _create_records_for_batch(cls, model, count, additional_values=None):
additional_values = additional_values or {}
records = cls.env[model]
partners = cls.env['res.partner']
country_id = cls.env.ref('base.be').id
partners = cls.env['res.partner'].with_context(**cls._test_context).create([{
'name': 'Partner_%s' % (x),
'email': '_test_partner_%s@example.com' % (x),
'country_id': country_id,
'mobile': '047500%02d%02d' % (x, x)
} for x in range(count)])
base_values = [
{'name': 'Test_%s' % idx,
**additional_values,
} for idx in range(count)
]
records = cls.env[model].with_context(**cls._test_context).create([{
'name': 'Test_%s' % (x),
'customer_id': partners[x].id,
} for x in range(count)])
if 'customer_id' in cls.env[model]:
partners = cls.env['res.partner'].with_context(**cls._test_context).create([{
'name': 'Partner_%s' % idx,
'email': '_test_partner_%s@example.com' % idx,
'country_id': country_id,
'mobile': '047500%02d%02d' % (idx, idx)
} for idx in range(count)])
for values, partner in zip(base_values, partners):
values['customer_id'] = partner.id
records = cls.env[model].with_context(**cls._test_context).create(base_values)
cls.records = cls._reset_mail_context(records)
cls.partners = partners
+361 -112
View File
@@ -11,6 +11,7 @@ from odoo.tests import tagged
from odoo.tests.common import users, Form
from odoo.tools import mute_logger
@tagged('mail_composer')
class TestMailComposer(TestMailCommon, TestRecipients):
""" Test Composer internals """
@@ -18,7 +19,6 @@ class TestMailComposer(TestMailCommon, TestRecipients):
@classmethod
def setUpClass(cls):
super(TestMailComposer, cls).setUpClass()
cls._init_mail_gateway()
# ensure employee can create partners, necessary for templates
cls.user_employee.write({
@@ -47,7 +47,10 @@ class TestMailComposer(TestMailCommon, TestRecipients):
'customer_id': cls.partner_1.id,
'user_id': cls.user_employee_2.id,
})
cls.test_records, cls.test_partners = cls._create_records_for_batch('mail.test.ticket', 2)
cls.test_records, cls.test_partners = cls._create_records_for_batch(
'mail.test.ticket', 2,
additional_values={'user_id': cls.user_employee_2.id}
)
cls.test_report = cls.env['ir.actions.report'].create({
'name': 'Test Report on mail test ticket',
@@ -59,24 +62,17 @@ class TestMailComposer(TestMailCommon, TestRecipients):
cls.test_from = '"John Doe" <john@example.com>'
cls.mail_server = cls.env['ir.mail_server'].create({
'name': 'Dummy Test Server',
'smtp_host': 'smtp.pizza.moc',
'smtp_port': 17,
'smtp_encryption': 'ssl',
'sequence': 666,
})
cls.template = cls.env['mail.template'].create({
'auto_delete': True,
'name': 'TestTemplate',
'subject': 'TemplateSubject {{ object.name }}',
'body_html': '<p>TemplateBody <t t-esc="object.name"></t></p>',
'partner_to': '{{ object.customer_id.id if object.customer_id else "" }}',
'email_to': '{{ (object.email_from if not object.customer_id else "") }}',
'email_from': '{{ (object.user_id.email_formatted or user.email_formatted) }}',
'mail_server_id': cls.mail_server_domain.id,
'model_id': cls.env['ir.model']._get('mail.test.ticket').id,
'mail_server_id': cls.mail_server.id,
'auto_delete': True,
'reply_to': '{{ ctx.get("custom_reply_to") or "info@test.example.com" }}',
})
def _get_web_context(self, records, add_web=True, **values):
@@ -108,15 +104,28 @@ class TestComposerForm(TestMailComposer):
@users('employee')
def test_mail_composer_comment(self):
composer_form = Form(self.env['mail.compose.message'].with_context(self._get_web_context(self.test_record, add_web=True)))
composer_form = Form(self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_record, add_web=True)
))
# auto_delete not displayed currently in view
# self.assertFalse(composer_form.auto_delete)
# self.assertFalse(composer_form.auto_delete_message)
# author_id not displayed currently in view
# self.assertEqual(composer_form.author_id, self.env.user.partner_id)
self.assertFalse(composer_form.body)
self.assertEqual(composer_form.composition_mode, 'comment')
self.assertEqual(composer_form.email_from, self.env.user.email_formatted)
self.assertFalse(composer_form.mail_server_id)
self.assertEqual(composer_form.model, self.test_record._name)
self.assertFalse(composer_form.partner_ids)
# record name not displayed currently in view
# self.assertEqual(composer_form.record_name, self.test_record.name, 'MailComposer: comment mode should compute record name')
self.assertFalse(composer_form.reply_to)
self.assertFalse(composer_form.reply_to_force_new)
self.assertEqual(composer_form.res_id, self.test_record.id)
self.assertEqual(
composer_form.subject, 'Re: %s' % self.test_record.name,
'MailComposer: comment mode should have default subject Re: record_name')
# record name not displayed currently in view
# self.assertEqual(composer_form.record_name, self.test_record.name, 'MailComposer: comment mode should compute record name')
self.assertFalse(composer_form.reply_to_force_new)
self.assertEqual(composer_form.composition_mode, 'comment')
self.assertEqual(composer_form.model, self.test_record._name)
@users('employee')
def test_mail_composer_comment_attachments(self):
@@ -156,7 +165,7 @@ class TestComposerForm(TestMailComposer):
report_attachments = [att for att in composer_form.attachment_ids if att not in template_1_attachments]
self.assertEqual(len(report_attachments), 1)
tpl_attachments = composer_form.attachment_ids[:] - report_attachments[0]
self.assertEqual(tpl_attachments, self.env['ir.attachment'])
self.assertFalse(tpl_attachments)
# change back to template 1
composer_form.template_id = template_1
@@ -170,29 +179,77 @@ class TestComposerForm(TestMailComposer):
composer_form.template_id = self.env['mail.template']
self.assertEqual(len(composer_form.attachment_ids), 0)
@users('employee')
def test_mail_composer_comment_wtpl(self):
composer_form = Form(self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_record, add_web=True, default_template_id=self.template.id)
))
# auto_delete not displayed currently in view
# self.assertTrue(composer_form.auto_delete)
# self.assertFalse(composer_form.auto_delete_message)
# author_id not displayed currently in view
# self.assertEqual(composer_form.author_id, self.env.user.partner_id)
self.assertEqual(composer_form.body, '<p>TemplateBody %s</p>' % self.test_record.name)
self.assertEqual(composer_form.composition_mode, 'comment')
self.assertEqual(composer_form.email_from, self.user_employee_2.email_formatted)
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
self.assertEqual(composer_form.model, self.test_record._name)
self.assertEqual(composer_form.partner_ids[:], self.partner_1)
# record name not displayed currently in view
# self.assertEqual(composer_form.record_name, self.test_record.name, 'MailComposer: comment mode should compute record name')
self.assertEqual(composer_form.reply_to, 'info@test.example.com')
self.assertFalse(composer_form.reply_to_force_new)
self.assertEqual(composer_form.res_id, self.test_record.id)
self.assertEqual(composer_form.subject, 'TemplateSubject %s' % self.test_record.name)
@users('employee')
def test_mail_composer_mass(self):
composer_form = Form(self.env['mail.compose.message'].with_context(self._get_web_context(self.test_records, add_web=True)))
self.assertFalse(composer_form.subject, 'MailComposer: mass mode should have void default subject if no template')
composer_form = Form(self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_records, add_web=True)
))
# auto_delete not displayed currently in view
# self.assertFalse(composer_form.auto_delete)
# self.assertFalse(composer_form.auto_delete_message)
# author_id not displayed currently in view
# self.assertEqual(composer_form.author_id, self.env.user.partner_id)
self.assertFalse(composer_form.body)
self.assertEqual(composer_form.composition_mode, 'mass_mail')
self.assertEqual(composer_form.email_from, self.env.user.email_formatted)
self.assertFalse(composer_form.mail_server_id)
self.assertEqual(composer_form.model, self.test_records._name)
# record name not displayed currently in view
# self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
self.assertFalse(composer_form.reply_to)
self.assertFalse(composer_form.reply_to_force_new)
self.assertEqual(composer_form.composition_mode, 'mass_mail')
self.assertEqual(composer_form.model, self.test_records._name)
self.assertEqual(composer_form.res_id, self.test_records[0].id,
'MailComposer: even in mass mode web active_id presence may add a res_id')
self.assertFalse(composer_form.subject, 'MailComposer: mass mode should have void default subject if no template')
@users('employee')
def test_mail_composer_mass_wtpl(self):
ctx = self._get_web_context(self.test_records, add_web=True, default_template_id=self.template.id)
composer_form = Form(self.env['mail.compose.message'].with_context(ctx))
self.assertEqual(composer_form.subject, self.template.subject,
'MailComposer: mass mode should have template raw subject if template')
composer_form = Form(self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_records, add_web=True, default_template_id=self.template.id)
))
# auto_delete not displayed currently in view
# self.assertTrue(composer_form.auto_delete)
# self.assertFalse(composer_form.auto_delete_message)
# author_id not displayed currently in view
# self.assertEqual(composer_form.author_id, self.env.user.partner_id)
self.assertEqual(composer_form.body, self.template.body_html,
'MailComposer: mass mode should have template raw body if template')
self.assertEqual(composer_form.composition_mode, 'mass_mail')
self.assertEqual(composer_form.email_from, self.template.email_from,
'MailComposer: mass mode should have template raw email_from if template')
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
self.assertEqual(composer_form.model, self.test_records._name)
# record name not displayed currently in view
# self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
self.assertEqual(composer_form.reply_to, self.template.reply_to)
self.assertFalse(composer_form.reply_to_force_new)
self.assertEqual(composer_form.composition_mode, 'mass_mail')
self.assertEqual(composer_form.model, self.test_records._name)
self.assertEqual(composer_form.res_id, self.test_records[0].id,
'MailComposer: even in mass mode web active_id presence may add a res_id')
self.assertEqual(composer_form.subject, self.template.subject,
'MailComposer: mass mode should have template raw subject if template')
@tagged('mail_composer')
@@ -294,6 +351,10 @@ class TestComposerInternals(TestMailComposer):
self.assertEqual(composer.subject, 'My amazing subject')
self.assertEqual(composer.body, '<p>Test Body</p>')
self.assertEqual(composer.mail_server_id.id, False)
if composition_mode == 'comment':
self.assertEqual(composer.record_name, self.test_record.name)
else:
self.assertFalse(composer.record_name)
# changing template should update its content
composer.write({'template_id': self.template.id})
@@ -305,10 +366,12 @@ class TestComposerInternals(TestMailComposer):
self.assertEqual(composer.subject, 'TemplateSubject %s' % self.test_record.name)
self.assertEqual(composer.body, '<p>TemplateBody %s</p>' % self.test_record.name)
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
self.assertEqual(composer.record_name, self.test_record.name)
else:
self.assertEqual(composer.subject, self.template.subject)
self.assertEqual(composer.body, self.template.body_html)
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
self.assertFalse(composer.record_name)
# manual values is kept over template
composer.write({'subject': 'Back to my amazing subject'})
@@ -322,17 +385,19 @@ class TestComposerInternals(TestMailComposer):
# values are reset
if composition_mode == 'comment':
self.assertEqual(composer.subject, 'Re: %s' % self.test_record.name)
self.assertEqual(composer.body, '')
self.assertFalse(composer.body)
# TDE FIXME: server id is kept, not sure why
# self.assertFalse(composer.mail_server_id.id)
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
self.assertEqual(composer.record_name, self.test_record.name)
else:
# values are reset TDE FIXME: strange for subject
self.assertEqual(composer.subject, 'Back to my amazing subject')
self.assertEqual(composer.body, '')
self.assertFalse(composer.body)
# TDE FIXME: server id is kept, not sure why
# self.assertFalse(composer.mail_server_id.id)
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
self.assertFalse(composer.record_name)
# 2. check with default
ctx['default_template_id'] = self.template.id
@@ -347,10 +412,12 @@ class TestComposerInternals(TestMailComposer):
self.assertEqual(composer.subject, 'TemplateSubject %s' % self.test_record.name)
self.assertEqual(composer.body, '<p>TemplateBody %s</p>' % self.test_record.name)
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
self.assertEqual(composer.record_name, self.test_record.name)
else:
self.assertEqual(composer.subject, self.template.subject)
self.assertEqual(composer.body, self.template.body_html)
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
self.assertFalse(composer.record_name)
# 3. check at create
ctx.pop('default_template_id')
@@ -365,10 +432,12 @@ class TestComposerInternals(TestMailComposer):
self.assertEqual(composer.subject, 'TemplateSubject %s' % self.test_record.name)
self.assertEqual(composer.body, '<p>TemplateBody %s</p>' % self.test_record.name)
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
self.assertEqual(composer.record_name, self.test_record.name)
else:
self.assertEqual(composer.subject, self.template.subject)
self.assertEqual(composer.body, self.template.body_html)
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
self.assertFalse(composer.record_name)
# 4. template + user input
ctx['default_template_id'] = self.template.id
@@ -376,15 +445,155 @@ class TestComposerInternals(TestMailComposer):
'subject': 'My amazing subject',
'body': '<p>Test Body</p>',
'mail_server_id': False,
'record_name': 'CustomName',
})
# creation values are taken
self.assertEqual(composer.subject, 'My amazing subject')
self.assertEqual(composer.body, '<p>Test Body</p>')
self.assertEqual(composer.mail_server_id.id, False)
self.assertEqual(composer.record_name, 'CustomName')
@users('employee')
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail')
@mute_logger('odoo.models.unlink')
def test_mail_composer_recipients(self):
""" Test content management (partner_ids, reply_to) in both comment and
mass mailing mode. Template update is also tested. Add some tests for
partner creation based on unknown emails as this is part of the process. """
base_recipients = self.partner_1 + self.partner_2
self.template.write({
'email_cc': 'test.cc.1@test.example.com, test.cc.2@test.example.com'
})
for composition_mode in ['comment', 'mass_mail']:
self.assertFalse(
self.env['res.partner'].search([
('email_normalized', 'in', ['test.cc.1@test.example.com',
'test.cc.2@test.example.com'])
])
)
if composition_mode == 'comment':
ctx = self._get_web_context(self.test_record, add_web=False)
else:
ctx = self._get_web_context(self.test_records, add_web=False)
# 1. check without template + template update
composer = self.env['mail.compose.message'].with_context(ctx).create({
'body': '<p>Test Body</p>',
'partner_ids': base_recipients.ids,
'reply_to': False,
'subject': 'My amazing subject',
})
# creation values are taken
self.assertEqual(composer.partner_ids, base_recipients)
self.assertFalse(composer.reply_to)
self.assertFalse(composer.reply_to_force_new)
self.assertEqual(composer.reply_to_mode, 'update')
# changing template should update its content
composer.write({'template_id': self.template.id})
# currently onchange necessary
composer._onchange_template_id_wrapper()
new_partners = self.env['res.partner'].search(
[('email_normalized', 'in', ['test.cc.1@test.example.com',
'test.cc.2@test.example.com'])
]
)
# values come from template
if composition_mode == 'comment':
self.assertEqual(len(new_partners), 2)
self.assertEqual(composer.partner_ids, self.partner_1 + new_partners, 'Template took customer_id as set on record')
self.assertEqual(composer.reply_to, 'info@test.example.com', 'Template was rendered')
self.assertFalse(composer.reply_to_force_new) # should not change in comment mode
else:
self.assertEqual(len(new_partners), 0)
self.assertEqual(composer.partner_ids, base_recipients, 'Mass mode: kept original values')
self.assertEqual(composer.reply_to, self.template.reply_to, 'Mass mode: raw template value')
self.assertFalse(composer.reply_to_force_new) # should probably become True, not supported currently
# manual values is kept over template
composer.write({'partner_ids': [(5, 0), (4, self.partner_admin.id)]})
self.assertEqual(composer.partner_ids, self.partner_admin)
# reset template should reset values
composer.write({'template_id': False})
# currently onchange necessary
composer._onchange_template_id_wrapper()
# values are kepts, not sure why
if composition_mode == 'comment':
self.assertEqual(composer.partner_ids, self.partner_admin, 'Values are kept, not sure why')
self.assertEqual(composer.reply_to, 'info@test.example.com', 'Values are kept')
self.assertFalse(composer.reply_to_force_new)
else:
self.assertEqual(composer.partner_ids, self.partner_admin, 'Mass mode: kept current value')
self.assertEqual(composer.reply_to, '{{ ctx.get("custom_reply_to") or "info@test.example.com" }}', 'Mass mode: kept current value')
self.assertFalse(composer.reply_to_force_new)
# 2. check with default
ctx['default_template_id'] = self.template.id
composer = self.env['mail.compose.message'].with_context(ctx).create({
'template_id': self.template.id,
})
# currently onchange necessary
composer._onchange_template_id_wrapper()
# values come from template
if composition_mode == 'comment':
self.assertEqual(composer.partner_ids, self.partner_1 + new_partners)
else:
self.assertFalse(composer.partner_ids)
if composition_mode == 'comment':
self.assertEqual(composer.reply_to, "info@test.example.com")
else:
self.assertEqual(composer.reply_to, self.template.reply_to)
self.assertFalse(composer.reply_to_force_new) # note: this should be updated with reply-to
self.assertEqual(composer.reply_to_mode, 'update') # note: this should be updated with reply-to
# 3. check at create
ctx.pop('default_template_id')
composer = self.env['mail.compose.message'].with_context(ctx).create({
'template_id': self.template.id,
})
# currently onchange necessary
composer._onchange_template_id_wrapper()
# values come from template
if composition_mode == 'comment':
self.assertEqual(composer.partner_ids, self.partner_1 + new_partners)
else:
self.assertFalse(composer.partner_ids)
if composition_mode == 'comment':
self.assertEqual(composer.reply_to, "info@test.example.com")
else:
self.assertEqual(composer.reply_to, self.template.reply_to)
self.assertFalse(composer.reply_to_force_new)
self.assertEqual(composer.reply_to_mode, 'update')
# 4. template + user input
ctx['default_template_id'] = self.template.id
composer = self.env['mail.compose.message'].with_context(ctx).create({
'body': '<p>Test Body</p>',
'partner_ids': base_recipients.ids,
'subject': 'My amazing subject',
'reply_to': False,
})
# creation values are taken
self.assertEqual(composer.partner_ids, base_recipients)
self.assertFalse(composer.reply_to)
self.assertFalse(composer.reply_to_force_new)
self.assertEqual(composer.reply_to_mode, 'update')
self.env['res.partner'].search([
('email_normalized', 'in', ['test.cc.1@test.example.com',
'test.cc.2@test.example.com'])
]).unlink()
@users('employee')
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
def test_mail_composer_parent(self):
""" Test specific management in comment mode when having parent_id set:
record_name, subject, parent's partners. """
@@ -397,11 +606,13 @@ class TestComposerInternals(TestMailComposer):
})
# creation values taken from parent
self.assertEqual(composer.subject, 'Re: %s' % self.test_record.name)
self.assertEqual(composer.body, '<p>Test Body</p>')
self.assertEqual(composer.partner_ids, self.partner_1 + self.partner_2)
self.assertEqual(composer.record_name, self.test_record.name)
self.assertEqual(composer.subject, 'Re: %s' % self.test_record.name)
@users('user_rendering_restricted')
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
def test_mail_composer_rights_attachments(self):
""" Ensure a user without write access to a template can send an email"""
template_1 = self.template.copy({
@@ -437,9 +648,13 @@ class TestComposerInternals(TestMailComposer):
sorted(self.test_record.message_ids[0].attachment_ids.mapped('name')),
sorted(template_1_attachment_name))
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
def test_mail_composer_rights_portal(self):
portal_user = self._create_portal_user()
# give read access to the record to portal (for check access rule)
self.test_record.message_subscribe(partner_ids=portal_user.partner_id.ids)
# patch check access rights for write access, required to post a message by default
with patch.object(MailTestTicket, 'check_access_rights', return_value=True):
self.env['mail.compose.message'].with_user(portal_user).with_context(
self._get_web_context(self.test_record)
@@ -487,7 +702,7 @@ class TestComposerResultsComment(TestMailComposer):
notification and emails generated during this process. """
@users('employee')
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail')
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
def test_mail_composer_notifications_delete(self):
""" Notifications are correctly deleted once sent """
composer = self.env['mail.compose.message'].with_context(
@@ -504,9 +719,29 @@ class TestComposerResultsComment(TestMailComposer):
self.assertEqual(message.notified_partner_ids, self.partner_employee_2 + self.partner_1 + self.partner_2)
# global outgoing
self.assertEqual(len(self._new_mails), 2, 'Should have created 2 mail.mail (1 for users, 1 for customers)')
self.assertEqual(len(self._mails), 3, 'Should have sent an email each recipient')
self.assertEqual(self._new_mails.exists(), self.env['mail.mail'], 'Should have deleted mail.mail records')
self.assertEqual(len(self._new_mails), 2, 'Should have created 2 mail.mail (1 for users, 1 for customers)')
self.assertFalse(self._new_mails.exists(), 'Should have deleted mail.mail records')
# Check ``auto_delete`` field usage (note: currently not correctly managed)
composer = self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_record),
).create({
'auto_delete': False,
'body': '<p>Test Body</p>',
'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
})
with self.mock_mail_gateway(mail_unlink_sent=True):
composer._action_send_mail()
# notifications
message = self.test_record.message_ids[0]
self.assertEqual(message.notified_partner_ids, self.partner_employee_2 + self.partner_1 + self.partner_2)
# global outgoing
self.assertEqual(len(self._mails), 3, 'Should have sent an email each recipient')
self.assertEqual(len(self._new_mails), 2, 'Should have created 2 mail.mail (1 for users, 1 for customers)')
self.assertEqual(len(self._new_mails.exists()), 0, 'To fix: does not respect auto_delete')
# ensure ``mail_auto_delete`` context key allow to override this behavior
composer = self.env['mail.compose.message'].with_context(
@@ -524,12 +759,12 @@ class TestComposerResultsComment(TestMailComposer):
self.assertEqual(message.notified_partner_ids, self.partner_employee_2 + self.partner_1 + self.partner_2)
# global outgoing
self.assertEqual(len(self._new_mails), 2, 'Should have created 2 mail.mail (1 for users, 1 for customers)')
self.assertEqual(len(self._mails), 3, 'Should have sent an email each recipient')
self.assertEqual(len(self._new_mails), 2, 'Should have created 2 mail.mail (1 for users, 1 for customers)')
self.assertEqual(len(self._new_mails.exists()), 2, 'Should not have deleted mail.mail records')
@users('employee')
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail')
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
def test_mail_composer_recipients(self):
""" Test partner_ids given to composer are given to the final message. """
composer = self.env['mail.compose.message'].with_context(
@@ -541,8 +776,8 @@ class TestComposerResultsComment(TestMailComposer):
composer._action_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.body, '<p>Test Body</p>')
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)
@@ -607,10 +842,11 @@ class TestComposerResultsComment(TestMailComposer):
email_values={
'body_content': 'TemplateBody %s' % self.test_record.name,
'email_from': self.test_record.user_id.email_formatted, # set by template
'mail_server_id': self.mail_server_domain,
'subject': 'TemplateSubject %s' % self.test_record.name,
'attachments_info': [
{'name': '00.txt', 'raw': b'Att00', 'type': 'text/plain'},
{'name': '01.txt', 'raw': b'Att01', 'type': 'text/plain'},
{'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'},
{'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'},
{'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'},
]
},
@@ -622,10 +858,11 @@ class TestComposerResultsComment(TestMailComposer):
email_values={
'body_content': 'TemplateBody %s' % self.test_record.name,
'email_from': self.test_record.user_id.email_formatted, # set by template
'mail_server_id': self.mail_server_domain,
'subject': 'TemplateSubject %s' % self.test_record.name,
'attachments_info': [
{'name': '00.txt', 'raw': b'Att00', 'type': 'text/plain'},
{'name': '01.txt', 'raw': b'Att01', 'type': 'text/plain'},
{'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'},
{'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'},
{'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'},
]
},
@@ -638,7 +875,7 @@ class TestComposerResultsComment(TestMailComposer):
# attachments are copied on message and linked to document
self.assertEqual(
set(message.attachment_ids.mapped('name')),
set(['00.txt', '01.txt', 'TestReport for %s.html' % self.test_record.name])
set(['AttFileName_00.txt', 'AttFileName_01.txt', 'TestReport for %s.html' % self.test_record.name])
)
self.assertEqual(set(message.attachment_ids.mapped('res_model')), set([self.test_record._name]))
self.assertEqual(set(message.attachment_ids.mapped('res_id')), set(self.test_record.ids))
@@ -656,10 +893,73 @@ class TestComposerResultsMass(TestMailComposer):
'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)],
})
@users('employee')
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
def test_mail_composer_delete(self):
""" Check mail / msg delete support """
# ensure initial data
self.assertTrue(self.template.auto_delete)
self.assertEqual(self.test_records.user_id, self.user_employee_2)
self.assertEqual(self.test_records.message_partner_ids, self.partner_employee_2)
composer_form = Form(self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_records, add_web=True,
default_template_id=self.template.id)
))
composer = composer_form.save()
with self.mock_mail_gateway(mail_unlink_sent=True), self.mock_mail_app():
composer._action_send_mail()
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
self.assertFalse(self._new_mails.exists(), 'Should have deleted mail.mail records')
self.assertEqual(len(self._new_msgs), 2, 'Should have created 1 mail.mail per record')
self.assertEqual(self._new_msgs.exists(), self._new_msgs, 'Should not have deleted mail.message records')
# force composer auto_delete field
composer_form = Form(self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_records, add_web=True,
default_template_id=self.template.id)
))
composer = composer_form.save()
composer.auto_delete = False
with self.mock_mail_gateway(mail_unlink_sent=True), self.mock_mail_app():
composer._action_send_mail()
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
# self.assertEqual(self._new_mails.exists(), self._new_mails, 'Should not have deleted mail.mail records')
self.assertFalse(self._new_mails.exists(), 'Template is forced over composer value, which is not correct')
self.assertEqual(len(self._new_msgs), 2, 'Should have created 1 mail.mail per record')
self.assertEqual(self._new_msgs.exists(), self._new_msgs, 'Should not have deleted mail.message records')
# check composer auto_delete_message
composer_form = Form(self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_records, add_web=True,
default_template_id=self.template.id)
))
composer = composer_form.save()
composer.auto_delete_message = True
with self.mock_mail_gateway(mail_unlink_sent=True), self.mock_mail_app():
composer._action_send_mail()
# global outgoing
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
self.assertFalse(self._new_mails.exists(), 'Should have deleted mail.mail records')
self.assertEqual(len(self._new_msgs), 2, 'Should have created 1 mail.mail per record')
self.assertFalse(self._new_msgs.exists(), 'Should have deleted mail.message records')
@users('employee')
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
def test_mail_composer_wtpl(self):
self.template.auto_delete = False # keep sent emails to check content
# ensure initial data
self.assertEqual(self.test_records.user_id, self.user_employee_2)
self.assertEqual(self.test_records.message_partner_ids, self.partner_employee_2)
composer_form = Form(self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_records, add_web=True,
default_template_id=self.template.id)
@@ -679,15 +979,18 @@ class TestComposerResultsMass(TestMailComposer):
# template is sent directly using customer field, meaning we have recipients
self.assertMailMail(record.customer_id, 'sent',
mail_message=message,
author=self.partner_employee)
author=self.partner_employee,
email_values={
'email_from': self.partner_employee_2.email_formatted,
})
# message content
self.assertEqual(message.subject, 'TemplateSubject %s' % record.name)
self.assertEqual(message.body, '<p>TemplateBody %s</p>' % record.name)
self.assertEqual(message.author_id, self.user_employee.partner_id)
# post-related fields are void
self.assertEqual(message.subtype_id, self.env['mail.message.subtype'])
self.assertEqual(message.partner_ids, self.env['res.partner'])
self.assertFalse(message.subtype_id)
self.assertFalse(message.partner_ids)
@users('employee')
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
@@ -712,8 +1015,8 @@ class TestComposerResultsMass(TestMailComposer):
self.assertEqual(len(attachs), 2)
# ensure initial data
self.assertEqual(self.test_records.user_id, self.env['res.users'])
self.assertEqual(self.test_records.message_partner_ids, self.env['res.partner'])
self.assertEqual(self.test_records.user_id, self.user_employee_2)
self.assertEqual(self.test_records.message_partner_ids, self.partner_employee_2)
# launch composer in mass mode
composer_form = Form(self.env['mail.compose.message'].with_context(
@@ -752,72 +1055,17 @@ class TestComposerResultsMass(TestMailComposer):
author=self.partner_employee,
email_values={
'body_content': 'TemplateBody %s' % record.name,
'email_from': self.partner_employee_2.email_formatted,
'mail_server_id': self.mail_server_global.id,
'subject': 'TemplateSubject %s' % record.name,
# 'attachments': [
# ('00.txt', b'Att00', 'text/plain'),
# ('01.txt', b'Att01', 'text/plain'),
# ('report.test_mail.mail_test_ticket_test_template.html', b'My second attachment', 'text/plain')
# ]
'attachments_info': [
{'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'},
{'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'},
{'name': 'TestReport for %s.html' % record.name, 'type': 'text/plain'},
]
}
)
@users('employee')
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
def test_mail_composer_wtpl_delete(self):
self.template.auto_delete = True
composer_form = Form(self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_records, add_web=True,
default_template_id=self.template.id)
))
composer = composer_form.save()
with self.mock_mail_gateway(mail_unlink_sent=True):
composer._action_send_mail()
# global outgoing
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
self.assertEqual(self._new_mails.exists(), self.env['mail.mail'], 'Should have deleted mail.mail records')
for record in self.test_records:
# message copy is kept
message = record.message_ids[0]
# template is sent directly using customer field
self.assertSentEmail(self.partner_employee, record.customer_id)
# message content
self.assertEqual(message.subject, 'TemplateSubject %s' % record.name)
self.assertEqual(message.body, '<p>TemplateBody %s</p>' % record.name)
self.assertEqual(message.author_id, self.user_employee.partner_id)
# post-related fields are void
self.assertEqual(message.subtype_id, self.env['mail.message.subtype'])
self.assertEqual(message.partner_ids, self.env['res.partner'])
@users('employee')
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
def test_mail_composer_wtpl_delete_notif(self):
self.template.auto_delete = True
composer_form = Form(self.env['mail.compose.message'].with_context(
self._get_web_context(self.test_records, add_web=True,
default_template_id=self.template.id,
default_auto_delete_message=True)
))
composer = composer_form.save()
with self.mock_mail_gateway(mail_unlink_sent=True):
composer._action_send_mail()
# global outgoing
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
self.assertEqual(self._new_mails.exists(), self.env['mail.mail'], 'Should have deleted mail.mail records')
for record in self.test_records:
# message copy is unlinked
self.assertEqual(record.message_ids, self.env['mail.message'], 'Should have deleted mail.message records')
# template is sent directly using customer field
self.assertSentEmail(self.partner_employee, record.customer_id)
@users('employee')
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
def test_mail_composer_wtpl_recipients(self):
@@ -841,7 +1089,8 @@ class TestComposerResultsMass(TestMailComposer):
for record in self.test_records:
# template is sent directly using customer field
self.assertSentEmail(self.partner_employee, record.customer_id)
self.assertSentEmail(self.partner_employee_2.email_formatted, record.customer_id,
author_id=self.partner_employee)
# 2: active_domain not taken into account if use_active_domain is False
composer_form = Form(self.env['mail.compose.message'].with_context(
@@ -12,7 +12,6 @@ class TestMailComposerMixin(TestMailCommon, TestRecipients):
@classmethod
def setUpClass(cls):
super(TestMailComposerMixin, cls).setUpClass()
cls._init_mail_gateway()
cls.mail_template = cls.env['mail.template'].create({
'subject': 'Subject for {{ object.name }}',
@@ -14,7 +14,6 @@ class BaseFollowersTest(TestMailCommon):
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()
# allow employee to update partners
cls.user_employee.write({'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)]})
@@ -247,8 +247,6 @@ class TestMailgateway(TestMailCommon):
'message_id': '<123456-openerp-%s-mail.test.gateway@%s>' % (cls.test_record.id, socket.gethostname()),
})
cls._init_mail_gateway()
# --------------------------------------------------
# Base low-level tests
# --------------------------------------------------
@@ -1277,8 +1275,6 @@ class TestMailThreadCC(TestMailCommon):
'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',
+396 -5
View File
@@ -2,22 +2,25 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import psycopg2
import smtplib
from OpenSSL.SSL import Error as SSLError
from socket import gaierror, timeout
from unittest.mock import call
from odoo import api
from odoo.addons.base.tests.common import MockSmtplibCase
from odoo.addons.base.models.ir_mail_server import MailDeliveryException
from odoo.addons.test_mail.tests.common import TestMailCommon
from odoo.tests import common, tagged
from odoo.tools import mute_logger
@tagged('mail_mail')
class TestMailMail(TestMailCommon, MockSmtplibCase):
class TestMailMail(TestMailCommon):
@classmethod
def setUpClass(cls):
super(TestMailMail, cls).setUpClass()
cls._init_mail_gateway()
cls._init_mail_servers()
cls.server_domain_2 = cls.env['ir.mail_server'].create({
@@ -31,9 +34,36 @@ class TestMailMail(TestMailCommon, MockSmtplibCase):
'email_from': 'ignasse@example.com',
}).with_context({})
cls.test_message = cls.test_record.message_post(body='<p>Message</p>', subject='Subject')
cls.test_mail = cls.env['mail.mail'].create([{
'body': '<p>Body</p>',
'email_from': False,
'email_to': 'test@example.com',
'is_notification': True,
'subject': 'Subject',
}])
cls.test_notification = cls.env['mail.notification'].create({
'is_read': False,
'mail_mail_id': cls.test_mail.id,
'mail_message_id': cls.test_message.id,
'notification_type': 'email',
'res_partner_id': cls.partner_employee.id, # not really used for matching except multi-recipients
})
cls.emails_falsy = [False, '', ' ']
cls.emails_invalid = ['buggy', 'buggy, wrong']
cls.emails_invalid_ascii = ['raoul@example¢¡.com']
cls.emails_valid = ['raoul¢¡@example.com', 'raoul@example.com']
def _reset_data(self):
self._init_mail_mock()
self.test_mail.write({'failure_reason': False, 'failure_type': False, 'state': 'outgoing'})
self.test_notification.write({'failure_type': False, 'notification_status': 'ready'})
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_message_notify_from_mail_mail(self):
# Due ot post-commit hooks, store send emails in every step
def test_mail_mail_recipients(self):
""" Partner_ids is a field used from mail_message, but not from mail_mail. """
mail = self.env['mail.mail'].sudo().create({
'body_html': '<p>Test</p>',
'email_to': 'test@example.com',
@@ -44,6 +74,18 @@ class TestMailMail(TestMailCommon, MockSmtplibCase):
self.assertSentEmail(mail.env.user.partner_id, ['test@example.com'])
self.assertEqual(len(self._mails), 1)
mail = self.env['mail.mail'].sudo().create({
'body_html': '<p>Test</p>',
'email_to': 'test@example.com',
'recipient_ids': [(4, self.user_employee.partner_id.id)],
})
with self.mock_mail_gateway():
mail.send()
self.assertSentEmail(mail.env.user.partner_id, ['test@example.com'])
self.assertSentEmail(mail.env.user.partner_id, [self.user_employee.email_formatted])
self.assertEqual(len(self._mails), 2)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_mail_return_path(self):
# mail without thread-enabled record
base_values = {
@@ -65,6 +107,354 @@ class TestMailMail(TestMailCommon, MockSmtplibCase):
mail.send()
self.assertEqual(self._mails[0]['headers']['Return-Path'], '%s@%s' % (self.alias_bounce, self.alias_domain))
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_mail_send_exceptions_origin(self):
""" Test various use case with exceptions and errors and see how they are
managed and stored at mail and notification level. """
mail, notification = self.test_mail, self.test_notification
# MailServer.build_email(): invalid from
self.env['ir.config_parameter'].set_param('mail.default.from', '')
self._reset_data()
with self.mock_mail_gateway(), mute_logger('odoo.addons.mail.models.mail_mail'):
mail.send(raise_exception=False)
self.assertFalse(self._mails[0]['email_from'])
self.assertIn(
'You must either provide a sender address explicitly or configure using the combination of `mail.catchall.domain` and `mail.default.from` ICPs',
mail.failure_reason)
self.assertFalse(mail.failure_type, 'Mail: void from: no failure type, should be updated')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'unknown', 'Mail: void from: unknown failure type, should be updated')
self.assertEqual(notification.notification_status, 'exception')
# MailServer.send_email(): _prepare_email_message: unexpected ASCII
# Force catchall domain to void otherwise bounce is set to postmaster-odoo@domain
self.env['ir.config_parameter'].set_param('mail.catchall.domain', '')
self._reset_data()
mail.write({'email_from': 'strange@example¢¡.com'})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertEqual(self._mails[0]['email_from'], 'strange@example¢¡.com')
self.assertIn('Codepoint U+00A2 at position', mail.failure_reason)
self.assertFalse(mail.failure_type, 'Mail: bugged from (ascii): no failure type, should be updated')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'unknown', 'Mail: bugged from (ascii): unknown failure type, should be updated')
self.assertEqual(notification.notification_status, 'exception')
# MailServer.send_email(): _prepare_email_message: unexpected ASCII based on catchall domain
self.env['ir.config_parameter'].set_param('mail.catchall.domain', 'domain¢¡.com')
self._reset_data()
mail.write({'email_from': 'test.user@example.com'})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertEqual(self._mails[0]['email_from'], 'test.user@example.com')
self.assertIn('Codepoint U+00A2 at position', mail.failure_reason)
self.assertFalse(mail.failure_type, 'Mail: bugged catchall domain (ascii): no failure type, should be updated')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'unknown', 'Mail: bugged catchall domain (ascii): unknown failure type, should be updated')
self.assertEqual(notification.notification_status, 'exception')
# MailServer.send_email(): _prepare_email_message: Malformed 'Return-Path' or 'From' address
self.env['ir.config_parameter'].set_param('mail.catchall.domain', '')
self._reset_data()
mail.write({'email_from': 'robert'})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertEqual(self._mails[0]['email_from'], 'robert')
self.assertIn("Malformed 'Return-Path' or 'From' address: robert", mail.failure_reason)
self.assertFalse(mail.failure_type, 'Mail: bugged from (ascii): no failure type, should be updated')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'unknown', 'Mail: bugged from (ascii): unknown failure type, should be updated')
self.assertEqual(notification.notification_status, 'exception')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_mail_send_exceptions_recipients_emails(self):
""" Test various use case with exceptions and errors and see how they are
managed and stored at mail and notification level. """
mail, notification = self.test_mail, self.test_notification
self.env['ir.config_parameter'].set_param('mail.catchall.domain', self.alias_domain)
self.env['ir.config_parameter'].set_param('mail.default.from', self.default_from)
# MailServer.send_email(): _prepare_email_message: missing To
for email_to in self.emails_falsy:
self._reset_data()
mail.write({'email_to': email_to})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertIn('Error without exception. Probably due do sending an email without computed recipients.', mail.failure_reason)
self.assertFalse(mail.failure_type, 'Mail: missing email_to: no failure type, should be updated')
self.assertEqual(mail.state, 'exception')
if email_to == ' ':
self.assertEqual(notification.failure_type, 'mail_email_missing')
self.assertEqual(notification.notification_status, 'exception')
else:
self.assertEqual(notification.failure_type, False, 'Mail: missing email_to: notification is wrongly set as sent')
self.assertEqual(notification.notification_status, 'sent', 'Mail: missing email_to: notification is wrongly set as sent')
# MailServer.send_email(): _prepare_email_message: invalid To
for email_to, failure_type in zip(self.emails_invalid,
['mail_email_missing', 'mail_email_missing']):
self._reset_data()
mail.write({'email_to': email_to})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertIn('Error without exception. Probably due do sending an email without computed recipients.', mail.failure_reason)
self.assertFalse(mail.failure_type, 'Mail: invalid email_to: no failure type, should be updated')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, failure_type, 'Mail: invalid email_to: missing instead of invalid')
self.assertEqual(notification.notification_status, 'exception')
# MailServer.send_email(): _prepare_email_message: invalid To (ascii)
for email_to in self.emails_invalid_ascii:
self._reset_data()
mail.write({'email_to': email_to})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertIn('Codepoint U+00A2 at position', mail.failure_reason)
self.assertFalse(mail.failure_type, 'Mail: invalid (ascii) recipient partner: no failure type, should be updated')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'unknown', 'Mail: invalid (ascii) recipient partner: unknown failure type, should be updated')
self.assertEqual(notification.notification_status, 'exception')
# MailServer.send_email(): _prepare_email_message: ok To (ascii or just ok)
for email_to in self.emails_valid:
self._reset_data()
mail.write({'email_to': email_to})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertFalse(mail.failure_reason)
self.assertFalse(mail.failure_type)
self.assertEqual(mail.state, 'sent')
self.assertFalse(notification.failure_type)
self.assertEqual(notification.notification_status, 'sent')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_mail_send_exceptions_recipients_partners(self):
""" Test various use case with exceptions and errors and see how they are
managed and stored at mail and notification level. """
mail, notification = self.test_mail, self.test_notification
mail.write({'email_from': 'test.user@test.example.com', 'email_to': False})
partners_falsy = self.env['res.partner'].create([
{'name': 'Name %s' % email, 'email': email}
for email in self.emails_falsy
])
partners_invalid = self.env['res.partner'].create([
{'name': 'Name %s' % email, 'email': email}
for email in self.emails_invalid
])
partners_invalid_ascii = self.env['res.partner'].create([
{'name': 'Name %s' % email, 'email': email}
for email in self.emails_invalid_ascii
])
partners_valid = self.env['res.partner'].create([
{'name': 'Name %s' % email, 'email': email}
for email in self.emails_valid
])
# void values
for partner in partners_falsy:
self._reset_data()
mail.write({'recipient_ids': [(5, 0), (4, partner.id)]})
notification.write({'res_partner_id': partner.id})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertEqual(mail.failure_reason, 'Error without exception. Probably due do sending an email without computed recipients.')
self.assertFalse(mail.failure_type, 'Mail: void recipient partner: no failure type, should be updated')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'mail_email_invalid', 'Mail: void recipient partner: should be missing, not invalid')
self.assertEqual(notification.notification_status, 'exception')
# wrong values
for partner in partners_invalid:
self._reset_data()
mail.write({'recipient_ids': [(5, 0), (4, partner.id)]})
notification.write({'res_partner_id': partner.id})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertEqual(mail.failure_reason, 'Error without exception. Probably due do sending an email without computed recipients.')
self.assertFalse(mail.failure_type, 'Mail: invalid recipient partner: no failure type, should be updated')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'mail_email_invalid')
self.assertEqual(notification.notification_status, 'exception')
# ascii ko
for partner in partners_invalid_ascii:
self._reset_data()
mail.write({'recipient_ids': [(5, 0), (4, partner.id)]})
notification.write({'res_partner_id': partner.id})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertIn('Codepoint U+00A2 at position', mail.failure_reason)
self.assertFalse(mail.failure_type, 'Mail: invalid (ascii) recipient partner: no failure type, should be updated')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'unknown', 'Mail: invalid (ascii) recipient partner: unknown failure type, should be updated')
self.assertEqual(notification.notification_status, 'exception')
# ascii ok or just ok
for partner in partners_valid:
self._reset_data()
mail.write({'recipient_ids': [(5, 0), (4, partner.id)]})
notification.write({'res_partner_id': partner.id})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertFalse(mail.failure_reason)
self.assertFalse(mail.failure_type)
self.assertEqual(mail.state, 'sent')
self.assertFalse(notification.failure_type)
self.assertEqual(notification.notification_status, 'sent')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_mail_send_exceptions_recipients_partners_mixed(self):
""" Test various use case with exceptions and errors and see how they are
managed and stored at mail and notification level. """
mail, notification = self.test_mail, self.test_notification
mail.write({'email_to': 'test@example.com'})
partners_falsy = self.env['res.partner'].create([
{'name': 'Name %s' % email, 'email': email}
for email in self.emails_falsy
])
partners_invalid = self.env['res.partner'].create([
{'name': 'Name %s' % email, 'email': email}
for email in self.emails_invalid
])
partners_valid = self.env['res.partner'].create([
{'name': 'Name %s' % email, 'email': email}
for email in self.emails_valid
])
# valid to, missing email for recipient or wrong email for recipient
for partner in partners_falsy + partners_invalid:
self._reset_data()
mail.write({'recipient_ids': [(5, 0), (4, partner.id)]})
notification.write({'res_partner_id': partner.id})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertFalse(mail.failure_reason, 'Mail: at least one valid recipient, mail is sent to avoid send loops and spam')
self.assertFalse(mail.failure_type, 'Mail: at least one valid recipient, mail is sent to avoid send loops and spam')
self.assertEqual(mail.state, 'sent', 'Mail: at least one valid recipient, mail is sent to avoid send loops and spam')
self.assertEqual(notification.failure_type, 'mail_email_invalid', 'Mail: void email considered as invalid')
self.assertEqual(notification.notification_status, 'exception')
# update to have valid partner and invalid partner
mail.write({'recipient_ids': [(5, 0), (4, partners_valid[1].id), (4, partners_falsy[0].id)]})
notification.write({'res_partner_id': partners_valid[1].id})
notification2 = notification.create({
'is_read': False,
'mail_mail_id': mail.id,
'mail_message_id': self.test_message.id,
'notification_type': 'email',
'res_partner_id': partners_falsy[0].id,
})
# missing to / invalid to
for email_to in self.emails_falsy + self.emails_invalid:
self._reset_data()
notification2.write({'failure_type': False, 'notification_status': 'ready'})
mail.write({'email_to': email_to})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertFalse(mail.failure_reason, 'Mail: at least one valid recipient, mail is sent to avoid send loops and spam')
self.assertFalse(mail.failure_type, 'Mail: at least one valid recipient, mail is sent to avoid send loops and spam')
self.assertEqual(mail.state, 'sent', 'Mail: at least one valid recipient, mail is sent to avoid send loops and spam')
self.assertFalse(notification.failure_type)
self.assertEqual(notification.notification_status, 'sent')
self.assertEqual(notification2.failure_type, 'mail_email_invalid')
self.assertEqual(notification2.notification_status, 'exception')
# buggy to (ascii)
for email_to in self.emails_invalid_ascii:
self._reset_data()
notification2.write({'failure_type': False, 'notification_status': 'ready'})
mail.write({'email_to': email_to})
with self.mock_mail_gateway():
mail.send(raise_exception=False)
self.assertIn('Codepoint U+00A2 at position', mail.failure_reason)
self.assertFalse(mail.failure_type, 'Mail: at least one valid recipient, mail is sent to avoid send loops and spam')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'unknown')
self.assertEqual(notification.notification_status, 'exception')
self.assertEqual(notification2.failure_type, 'unknown')
self.assertEqual(notification2.notification_status, 'exception')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_mail_send_exceptions_raise_management(self):
""" Test various use case with exceptions and errors and see how they are
managed and stored at mail and notification level. """
mail, notification = self.test_mail, self.test_notification
mail.write({'email_from': 'test.user@test.example.com', 'email_to': 'test@example.com'})
# SMTP connecting issues
with self.mock_mail_gateway():
_connect_current = self.connect_mocked.side_effect
# classic errors that may be raised during sending, just to test their current support
for error, msg in [
(smtplib.SMTPServerDisconnected('SMTPServerDisconnected'), 'SMTPServerDisconnected'),
(smtplib.SMTPResponseException('code', 'SMTPResponseException'), 'code\nSMTPResponseException'),
(smtplib.SMTPNotSupportedError('SMTPNotSupportedError'), 'SMTPNotSupportedError'),
(smtplib.SMTPException('SMTPException'), 'SMTPException'),
(SSLError('SSLError'), 'SSLError'),
(gaierror('gaierror'), 'gaierror'),
(timeout('timeout'), 'timeout')]:
def _connect(*args, **kwargs):
raise error
self.connect_mocked.side_effect = _connect
mail.send(raise_exception=False)
self.assertFalse(mail.failure_type)
self.assertEqual(mail.failure_reason, msg)
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'mail_smtp')
self.assertEqual(notification.notification_status, 'exception')
self._reset_data()
self.connect_mocked.side_effect = _connect_current
# SMTP sending issues
with self.mock_mail_gateway():
_send_current = self.send_email_mocked.side_effect
self._reset_data()
mail.write({'email_to': 'test@example.com'})
# should always raise for those errors, even with raise_exception=False
for error, error_class in [
(smtplib.SMTPServerDisconnected("Some exception"), smtplib.SMTPServerDisconnected),
(MemoryError("Some exception"), MemoryError)]:
def _send_email(*args, **kwargs):
raise error
self.send_email_mocked.side_effect = _send_email
with self.assertRaises(error_class):
mail.send(raise_exception=False)
self.assertFalse(mail.failure_reason, 'SMTPServerDisconnected/MemoryError during Send raises and lead to a rollback')
self.assertFalse(mail.failure_type, 'SMTPServerDisconnected/MemoryError during Send raises and lead to a rollback')
self.assertEqual(mail.state, 'outgoing', 'SMTPServerDisconnected/MemoryError during Send raises and lead to a rollback')
self.assertFalse(notification.failure_type, 'SMTPServerDisconnected/MemoryError during Send raises and lead to a rollback')
self.assertEqual(notification.notification_status, 'ready', 'SMTPServerDisconnected/MemoryError during Send raises and lead to a rollback')
# MailDeliveryException: should be catched; other issues are sub-catched under
# a MailDeliveryException and are catched
for error, msg in [
(MailDeliveryException("Some exception"), 'Some exception'),
(ValueError("Unexpected issue"), 'Unexpected issue')]:
def _send_email(*args, **kwargs):
raise error
self.send_email_mocked.side_effect = _send_email
self._reset_data()
mail.send(raise_exception=False)
self.assertEqual(mail.failure_reason, msg)
self.assertFalse(mail.failure_type, 'Mail: unlogged failure type to fix')
self.assertEqual(mail.state, 'exception')
self.assertEqual(notification.failure_type, 'unknown', 'Mail: generic failure type')
self.assertEqual(notification.notification_status, 'exception')
self.send_email_mocked.side_effect = _send_current
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_mail_mail_send_server(self):
"""Test that the mails are send in batch.
@@ -133,6 +523,7 @@ class TestMailMail(TestMailCommon, MockSmtplibCase):
self.assert_email_sent_smtp(message_from='user_1@test_2.com', emails_count=5, from_filter=self.server_domain.from_filter)
@tagged('mail_mail')
class TestMailMailRace(common.TransactionCase):
@mute_logger('odoo.addons.mail.models.mail_mail')
@@ -18,7 +18,6 @@ class TestMessageValues(TestMailCommon):
def setUpClass(cls):
super(TestMessageValues, cls).setUpClass()
cls._init_mail_gateway()
cls.alias_record = cls.env['mail.test.container'].with_context(cls._test_context).create({
'name': 'Pigs',
'alias_name': 'pigs',
@@ -47,8 +47,6 @@ class TestMultiCompanySetup(TestMailCommon, TestRecipients):
'message_id': '<123456-openerp-%s-mail.test.gateway@%s>' % (cls.test_record.id, socket.gethostname()),
})
cls._init_mail_gateway()
@users('employee')
def test_notify_reply_to_computation(self):
test_record = self.env['mail.test.gateway'].browse(self.test_record.ids)
@@ -103,6 +103,7 @@ class TestChatterTweaks(TestMailCommon, TestRecipients):
self.assertTrue(record.name)
@tagged('mail_thread')
class TestDiscuss(TestMailCommon, TestRecipients):
@classmethod
@@ -239,7 +240,12 @@ class TestDiscuss(TestMailCommon, TestRecipients):
partner_ids=[self.user_employee.partner_id.id]
)
self.assertFalse(message.has_error)
with self.mock_mail_gateway(sim_error='connect_smtp_notfound'):
with self.mock_mail_gateway():
def _connect(*args, **kwargs):
raise Exception("Some exception")
self.connect_mocked.side_effect = _connect
self.user_admin.notification_type = 'email'
message2 = self.test_record.with_user(self.user_employee).message_post(
body='Test', message_type='comment', subtype_xmlid='mail.mt_comment',
@@ -34,8 +34,10 @@ class TestMailResend(TestMailCommon):
def test_mail_resend_workflow(self):
with self.assertSinglePostNotifications(
[{'partner': partner, 'type': 'email', 'status': 'exception'} for partner in self.partners],
message_info={'message_type': 'notification'},
sim_error='connect_failure'):
message_info={'message_type': 'notification'}):
def _connect(*args, **kwargs):
raise Exception("Some exception")
self.connect_mocked.side_effect = _connect
message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype_xmlid='mail.mt_comment', message_type='notification')
wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({})
+543 -305
View File
@@ -6,6 +6,7 @@ import base64
from unittest.mock import patch
from odoo import tools
from odoo.addons.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.addons.test_mail.tests.common import TestMailCommon, TestRecipients
@@ -19,12 +20,29 @@ from markupsafe import escape
@tagged('mail_post')
class TestMessagePost(TestMailCommon, TestRecipients):
class TestMessagePostCommon(TestMailCommon, TestRecipients):
@classmethod
def setUpClass(cls):
super(TestMessagePost, cls).setUpClass()
cls._create_portal_user()
super(TestMessagePostCommon, cls).setUpClass()
# portal user, notably for ACLS / notifications
cls.user_portal = cls._create_portal_user()
cls.partner_portal = cls.user_portal.partner_id
# another standard employee to test follow and notifications between two
# users (and not admin / user)
cls.user_employee_2 = mail_new_test_user(
cls.env, login='employee2',
groups='base.group_user',
company_id=cls.company_admin.id,
email='eglantine@example.com', # check: use a formatted email
name='Eglantine Employee2',
notification_type='email',
signature='--\nEglantine',
)
cls.partner_employee_2 = cls.user_employee_2.partner_id
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({
'name': 'Test',
'email_from': 'ignasse@example.com'
@@ -45,8 +63,22 @@ class TestMessagePost(TestMailCommon, TestRecipients):
})
cls.user_admin.write({'notification_type': 'email'})
def setUp(self):
super(TestMessagePostCommon, self).setUp()
# send tracking and messages + patch registry to simulate a ready environment
# purpose is to avoid nondeterministic tests, notably because tracking is
# accumulated and sent at flush -> we want to test only the result of a
# given test, not setup + test
self.flush_tracking()
# see ``_message_auto_subscribe_notify``
self.patch(self.env.registry, 'ready', True)
@tagged('mail_post')
class TestMailNotifyAPI(TestMessagePostCommon):
@users('employee')
def test_notify_email_layouts(self):
def test_email_notifiction_layouts(self):
self.user_employee.write({'notification_type': 'email'})
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
test_message = self.env['mail.message'].browse(self.test_message.ids)
@@ -74,12 +106,13 @@ class TestMessagePost(TestMailCommon, TestRecipients):
self.assertTrue(user_email)
@users('employee')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_notify_by_mail_add_signature(self):
self.test_track = self.env['mail.test.track'].with_context(self._test_context).with_user(self.user_employee).create({
test_track = self.env['mail.test.track'].with_context(self._test_context).with_user(self.user_employee).create({
'name': 'Test',
'email_from': 'ignasse@example.com'
})
self.test_track.user_id = self.env.user
test_track.user_id = self.env.user
signature = self.env.user.signature
@@ -87,7 +120,7 @@ class TestMessagePost(TestMailCommon, TestRecipients):
self.assertIn("record.user_id.sudo().signature", template.arch)
with self.mock_mail_gateway():
self.test_track.message_post(
test_track.message_post(
body="Test body",
email_add_signature=True,
email_layout_xmlid="mail.mail_notification_paynow",
@@ -99,12 +132,12 @@ class TestMessagePost(TestMailCommon, TestRecipients):
self.assertEqual(found_mail.body_html.count(signature), 1)
with self.mock_mail_gateway():
self.test_track.message_post(
test_track.message_post(
body="Test body",
email_add_signature=False,
email_layout_xmlid="mail.mail_notification_paynow",
mail_auto_delete=False,
partner_ids=[self.partner_1.id, self.partner_2.id]
partner_ids=[self.partner_1.id, self.partner_2.id],
)
found_mail = self._new_mails
self.assertNotIn(signature, found_mail.body_html)
@@ -112,18 +145,22 @@ class TestMessagePost(TestMailCommon, TestRecipients):
@users('employee')
def test_notify_by_email_add_signature_no_author_user_or_no_user(self):
self.test_message.author_id = self.env['res.partner'].sudo().create({
'name': 'Steve',
}).id
template_values = self.test_record._notify_by_email_prepare_rendering_context(self.test_message, {})
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
test_message = self.env['mail.message'].browse(self.test_message.ids)
test_message.write({
'author_id': self.env['res.partner'].sudo().create({
'name': 'Steve',
}).id
})
template_values = test_record._notify_by_email_prepare_rendering_context(test_message, {})
self.assertNotEqual(escape(template_values['signature']), escape('<p>-- <br/>Steve</p>'))
self.test_message.author_id = None
template_values = self.test_record._notify_by_email_prepare_rendering_context(self.test_message, {})
template_values = test_record._notify_by_email_prepare_rendering_context(test_message, {})
self.assertEqual(template_values['signature'], '')
@users('employee')
def test_notify_by_email_prepare_rendering_contextt(self):
def test_notify_by_email_prepare_rendering_context(self):
""" Verify that the template context company value is right
after switching the env company or if a company_id is set
on mail record.
@@ -225,300 +262,495 @@ class TestMessagePost(TestMailCommon, TestRecipients):
self.assertFalse(partner_info['has_button_access'])
self.assertFalse(emp_info['has_button_access'])
@tagged('mail_post', 'mail_notify')
class TestMessageNotify(TestMessagePostCommon):
@users('employee')
@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.assertSinglePostNotifications([{'partner': self.partner_employee, 'type': 'inbox'}], {'content': 'Body'}):
self.test_record.message_post(
body='Body', message_type='comment', subtype_xmlid='mail.mt_comment',
partner_ids=[self.user_employee.partner_id.id])
self.test_record.message_subscribe([self.partner_1.id])
with self.assertSinglePostNotifications([
{'partner': self.partner_employee, 'type': 'inbox'},
{'partner': self.partner_1, 'type': 'email'}], {'content': 'NewBody'}):
self.test_record.message_post(
body='NewBody', message_type='comment', subtype_xmlid='mail.mt_comment',
partner_ids=[self.user_employee.partner_id.id])
with self.assertSinglePostNotifications([
{'partner': self.partner_1, 'type': 'email'},
{'partner': self.partner_portal, 'type': 'email'}], {'content': 'ToPortal'}):
self.test_record.message_post(
body='ToPortal', message_type='comment', subtype_xmlid='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.assertSinglePostNotifications([{'partner': self.partner_employee, 'type': 'inbox'}], {'content': 'Test'}):
self.test_record.message_post(
body='Test', message_type='comment', subtype_xmlid='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.assertPostNotifications([{'content': 'Test', 'notif': []}]):
self.test_record.message_post(
body='Test', message_type='comment', subtype_xmlid='mail.mt_comment')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_notifications(self):
_body, _subject = '<p>Test Body</p>', 'Test Subject'
# subscribe second employee to the group to test notifications
self.test_record.message_subscribe(partner_ids=[self.user_admin.partner_id.id])
with self.assertSinglePostNotifications(
[{'partner': self.partner_1, 'type': 'email'},
{'partner': self.partner_2, 'type': 'email'},
{'partner': self.partner_admin, 'type': 'email'}],
{'content': _body},
mail_unlink_sent=True):
msg = self.test_record.with_user(self.user_employee).message_post(
body=_body, subject=_subject,
message_type='comment', subtype_xmlid='mail.mt_comment',
partner_ids=[self.partner_1.id, self.partner_2.id]
)
# message content
self.assertEqual(msg.subject, _subject)
self.assertEqual(msg.body, _body)
self.assertEqual(msg.partner_ids, self.partner_1 | self.partner_2)
self.assertEqual(msg.notified_partner_ids, self.user_admin.partner_id | self.partner_1 | self.partner_2)
# notifications emails should have been deleted
self.assertFalse(self.env['mail.mail'].sudo().search([('mail_message_id', '=', msg.id)]),
'message_post: mail.mail notifications should have been auto-deleted')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_notifications_keep_emails(self):
self.test_record.message_subscribe(partner_ids=[self.user_admin.partner_id.id])
msg = self.test_record.with_user(self.user_employee).message_post(
body='Test', subject='Test',
message_type='comment', subtype_xmlid='mail.mt_comment',
partner_ids=[self.partner_1.id, self.partner_2.id],
mail_auto_delete=False
)
# notifications emails should not have been deleted: one for customers, one for user
self.assertEqual(len(self.env['mail.mail'].sudo().search([('mail_message_id', '=', msg.id)])), 2)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_notifications_emails_tweak(self):
pass
# we should check _notification_groups behavior, for emails and buttons
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_attachments(self):
_attachments = [
('List1', b'My first attachment'),
('List2', b'My second attachment')
]
_attach_1 = self.env['ir.attachment'].with_user(self.user_employee).create({
'name': 'Attach1',
'datas': 'bWlncmF0aW9uIHRlc3Q=',
'res_model': 'mail.compose.message', 'res_id': 0})
_attach_2 = self.env['ir.attachment'].with_user(self.user_employee).create({
'name': 'Attach2',
'datas': 'bWlncmF0aW9uIHRlc3Q=',
'res_model': 'mail.compose.message', 'res_id': 0})
def test_notify(self):
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
with self.mock_mail_gateway():
msg = self.test_record.with_user(self.user_employee).message_post(
body='Test', subject='Test',
message_type='comment', subtype_xmlid='mail.mt_comment',
attachment_ids=[_attach_1.id, _attach_2.id],
partner_ids=[self.partner_1.id],
attachments=_attachments,
)
# message attachments
self.assertEqual(len(msg.attachment_ids), 4)
self.assertEqual(set(msg.attachment_ids.mapped('res_model')), set([self.test_record._name]))
self.assertEqual(set(msg.attachment_ids.mapped('res_id')), set([self.test_record.id]))
self.assertEqual(set([base64.b64decode(x) for x in msg.attachment_ids.mapped('datas')]),
set([b'migration test', _attachments[0][1], _attachments[1][1]]))
self.assertTrue(set([_attach_1.id, _attach_2.id]).issubset(msg.attachment_ids.ids),
'message_post: mail.message attachments duplicated')
# notification email attachments
self.assertSentEmail(self.user_employee.partner_id, [self.partner_1])
# self.assertEqual(len(self._mails), 1)
self.assertEqual(len(self._mails[0]['attachments']), 4)
self.assertIn(('List1', b'My first attachment', 'application/octet-stream'), self._mails[0]['attachments'])
self.assertIn(('List2', b'My second attachment', 'application/octet-stream'), self._mails[0]['attachments'])
self.assertIn(('Attach1', b'migration test', 'application/octet-stream'), self._mails[0]['attachments'])
self.assertIn(('Attach2', b'migration test', 'application/octet-stream'), self._mails[0]['attachments'])
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_answer(self):
with self.mock_mail_gateway():
parent_msg = self.test_record.with_user(self.user_employee).message_post(
body='<p>Test</p>', subject='Test Subject',
message_type='comment', subtype_xmlid='mail.mt_comment')
self.assertEqual(parent_msg.partner_ids, self.env['res.partner'])
self.assertNotSentEmail()
with self.assertPostNotifications([{'content': '<p>Test Answer</p>', 'notif': [{'partner': self.partner_1, 'type': 'email'}]}]):
msg = self.test_record.with_user(self.user_employee).message_post(
body='<p>Test Answer</p>',
message_type='comment', subtype_xmlid='mail.mt_comment',
partner_ids=[self.partner_1.id],
parent_id=parent_msg.id)
self.assertEqual(msg.parent_id.id, parent_msg.id)
self.assertEqual(msg.partner_ids, self.partner_1)
self.assertEqual(parent_msg.partner_ids, self.env['res.partner'])
# check notification emails: references
self.assertSentEmail(self.user_employee.partner_id, [self.partner_1], ref_content='openerp-%d-mail.test.simple' % self.test_record.id)
# self.assertTrue(all('openerp-%d-mail.test.simple' % self.test_record.id in m['references'] for m in self._mails))
new_msg = self.test_record.with_user(self.user_employee).message_post(
body='<p>Test Answer Bis</p>',
message_type='comment', subtype_xmlid='mail.mt_comment',
parent_id=msg.id)
self.assertEqual(new_msg.parent_id.id, parent_msg.id, 'message_post: flatten error')
self.assertEqual(new_msg.partner_ids, self.env['res.partner'])
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_email_with_multiline_subject(self):
_body, _body_alt, _subject = '<p>Test Body</p>', 'Test Body', '1st line\n2nd line'
msg = self.test_record.with_user(self.user_employee).message_post(
body=_body, subject=_subject,
message_type='comment', subtype_xmlid='mail.mt_comment',
partner_ids=[self.partner_1.id, self.partner_2.id]
)
self.assertEqual(msg.subject, '1st line 2nd line')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_portal_ok(self):
self.test_record.message_subscribe((self.partner_1 | self.user_employee.partner_id).ids)
with self.assertPostNotifications([{'content': '<p>Test</p>', 'notif': [
{'partner': self.partner_employee, 'type': 'inbox'},
{'partner': self.partner_1, 'type': 'email'}]}
]), patch.object(MailTestSimple, 'check_access_rights', return_value=True):
new_msg = self.test_record.with_user(self.user_portal).message_post(
body='<p>Test</p>', subject='Subject',
message_type='comment', subtype_xmlid='mail.mt_comment')
self.assertEqual(new_msg.sudo().notified_partner_ids, (self.partner_1 | self.user_employee.partner_id))
def test_post_portal_crash(self):
with self.assertRaises(AccessError):
self.test_record.with_user(self.user_portal).message_post(
body='<p>Test</p>', subject='Subject',
message_type='comment', subtype_xmlid='mail.mt_comment')
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_thread')
def test_post_internal(self):
self.test_record.message_subscribe([self.user_admin.partner_id.id])
msg = self.test_record.with_user(self.user_employee).message_post(
body='My Body', subject='My Subject',
message_type='comment', subtype_xmlid='mail.mt_note')
self.assertEqual(msg.partner_ids, self.env['res.partner'])
self.assertEqual(msg.notified_partner_ids, self.env['res.partner'])
self.format_and_process(
MAIL_TEMPLATE_PLAINTEXT, self.user_admin.email, 'not_my_businesss@example.com',
msg_id='<1198923581.41972151344608186800.JavaMail.diff1@agrolait.com>',
extra='In-Reply-To:\r\n\t%s\n' % msg.message_id,
target_model='mail.test.simple')
reply = self.test_record.message_ids - msg
self.assertTrue(reply)
self.assertEqual(reply.subtype_id, self.env.ref('mail.mt_note'))
self.assertEqual(reply.notified_partner_ids, self.user_employee.partner_id)
self.assertEqual(reply.parent_id, msg)
def test_post_log(self):
new_note = self.test_record.with_user(self.user_employee)._message_log(
body='<p>Labrador</p>',
)
self.assertEqual(new_note.subtype_id, self.env.ref('mail.mt_note'))
self.assertEqual(new_note.body, '<p>Labrador</p>')
self.assertEqual(new_note.author_id, self.user_employee.partner_id)
self.assertEqual(new_note.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
self.assertEqual(new_note.notified_partner_ids, self.env['res.partner'])
def test_post_log_with_view(self):
new_note = self.test_record.with_user(self.user_employee)._message_log_with_view(
'test_mail.mail_template_simple_test', values={'partner': self.user_employee.partner_id}
)
self.assertEqual(new_note.subtype_id, self.env.ref('mail.mt_note'))
self.assertTrue('<p>Hello %s,</p>' % self.user_employee.name in new_note.body)
self.assertEqual(new_note.author_id, self.user_employee.partner_id)
self.assertEqual(new_note.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
self.assertEqual(new_note.notified_partner_ids, self.env['res.partner'])
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_notify(self):
self.user_employee.write({'notification_type': 'inbox'})
with self.mock_mail_gateway():
new_notification = self.test_record.message_notify(
subject='This should be a subject',
new_notification = test_record.message_notify(
body='<p>You have received a notification</p>',
partner_ids=[self.partner_1.id, self.partner_admin.id, self.user_employee.partner_id.id],
partner_ids=[self.partner_1.id, self.partner_admin.id, self.partner_employee_2.id],
subject='This should be a subject',
)
self.assertEqual(new_notification.subtype_id, self.env.ref('mail.mt_note'))
self.assertEqual(new_notification.message_type, 'user_notification')
self.assertEqual(new_notification.body, '<p>You have received a notification</p>')
self.assertEqual(new_notification.author_id, self.env.user.partner_id)
self.assertEqual(new_notification.email_from, formataddr((self.env.user.name, self.env.user.email)))
self.assertEqual(new_notification.notified_partner_ids, self.partner_1 | self.user_employee.partner_id | self.partner_admin)
self.assertMessageFields(
new_notification,
{'author_id': self.partner_employee,
'body': '<p>You have received a notification</p>',
'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)),
'message_type': 'user_notification',
'model': test_record._name,
'notified_partner_ids': self.partner_1 | self.partner_employee_2 | self.partner_admin,
'res_id': test_record.id,
'subtype_id': self.env.ref('mail.mt_note'),
}
)
self.assertNotIn(new_notification, self.test_record.message_ids)
admin_mails = [x for x in self._mails if self.partner_admin.name in x.get('email_to')[0]]
admin_mails = [mail for mail in self._mails if self.partner_admin.name in mail.get('email_to')[0]]
self.assertEqual(len(admin_mails), 1, 'There should be exactly one email sent to admin')
admin_mail = admin_mails[0].get('body')
admin_access_link = admin_mail[admin_mail.index('model='):admin_mail.index('/>') - 1] if 'model=' in admin_mail else None
admin_mail_body = admin_mails[0].get('body')
admin_access_link = admin_mail_body[admin_mail_body.index('model='):admin_mail_body.index('/>') - 1] if 'model=' in admin_mail_body else None
self.assertIsNotNone(admin_access_link, 'The email sent to admin should contain an access link')
self.assertIn('model=%s' % self.test_record._name, admin_access_link, 'The access link should contain a valid model argument')
self.assertIn('res_id=%d' % self.test_record.id, admin_access_link, 'The access link should contain a valid res_id argument')
partner_mails = [x for x in self._mails if self.partner_1.name in x.get('email_to')[0]]
self.assertEqual(len(partner_mails), 1, 'There should be exactly one email sent to partner')
partner_mail = partner_mails[0].get('body')
self.assertNotIn('/mail/view?model=', partner_mail, 'The email sent to admin should not contain an access link')
# todo xdo add test message_notify on thread with followers and stuff
partner_mail_body = partner_mails[0].get('body')
self.assertNotIn('/mail/view?model=', partner_mail_body, 'The email sent to customer should not contain an access link')
@users('employee')
def test_notify_batch(self):
""" Test notify in batch. Currently not supported. """
test_records, _partners = self._create_records_for_batch('mail.test.simple', 10)
with self.assertRaises(ValueError):
test_records.message_notify(
body='<p>Nice notification content</p>',
partner_ids=self.partner_employee_2.ids,
subject='Notify Subject',
)
@users('employee')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_notify_from_user_id(self):
""" Test notify coming from user_id assignment. """
test_record = self.env['mail.test.track'].create({
'company_id': self.env.user.company_id.id,
'email_from': self.env.user.email_formatted,
'name': 'Test UserId Track',
'user_id': False,
})
self.flush_tracking()
with self.mock_mail_gateway(), self.mock_mail_app():
test_record.write({'user_id': self.user_employee_2.id})
self.flush_tracking()
self.assertEqual(len(self._new_msgs), 2, 'Should have 2 messages: tracking and assignment')
assign_notif = self._new_msgs.filtered(lambda msg: msg.message_type == 'user_notification')
self.assertTrue(assign_notif)
self.assertMessageFields(
assign_notif,
{'author_id': self.partner_employee,
'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)),
'model': test_record._name,
'notified_partner_ids': self.partner_employee_2,
'res_id': test_record.id,
'subtype_id': self.env.ref('mail.mt_note'),
}
)
self.assertIn('Dear %s' % self.partner_employee_2.name, assign_notif.body)
@users('employee')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_notify_from_user_id_batch(self):
""" Test notify coming from user_id assignment. """
test_records, _ = self._create_records_for_batch(
'mail.test.track', 10, {
'company_id': self.env.user.company_id.id,
'email_from': self.env.user.email_formatted,
'user_id': False,
}
)
test_records = self.env['mail.test.track'].browse(test_records.ids)
self.flush_tracking()
with self.mock_mail_gateway(), self.mock_mail_app():
test_records.write({'user_id': self.user_employee_2.id})
self.flush_tracking()
self.assertEqual(len(self._new_msgs), 20, 'Should have 20 messages: 10 tracking and 10 assignments')
for test_record in test_records:
assign_notif = self._new_msgs.filtered(lambda msg: msg.message_type == 'user_notification' and msg.res_id == test_record.id)
self.assertTrue(assign_notif)
self.assertMessageFields(
assign_notif,
{'author_id': self.partner_employee,
'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)),
'model': test_record._name,
'notified_partner_ids': self.partner_employee_2,
'res_id': test_record.id,
'subtype_id': self.env.ref('mail.mt_note'),
}
)
@users('employee')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_notify_thread(self):
""" Test notify on ``mail.thread`` model, which is pushing a message to
people without having a document. """
with self.mock_mail_gateway():
new_notification = self.env['mail.thread'].message_notify(
body='<p>You have received a notification</p>',
partner_ids=[self.partner_1.id, self.partner_admin.id, self.partner_employee_2.id],
subject='This should be a subject',
)
self.assertMessageFields(
new_notification,
{'author_id': self.partner_employee,
'body': '<p>You have received a notification</p>',
'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)),
'message_type': 'user_notification',
'model': False,
'res_id': False,
'notified_partner_ids': self.partner_1 | self.partner_employee_2 | self.partner_admin,
'subtype_id': self.env.ref('mail.mt_note'),
}
)
@tagged('mail_post')
class TestMessagePost(TestMessagePostCommon):
def test_initial_values(self):
self.assertFalse(self.test_record.message_ids)
self.assertFalse(self.test_record.message_follower_ids)
self.assertFalse(self.test_record.message_partner_ids)
@users('employee')
def test_message_log(self):
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
test_record.message_subscribe(self.partner_employee_2.ids)
new_note = test_record._message_log(
body='<p>Labrador</p>',
)
self.assertMessageFields(
new_note,
{'author_id': self.partner_employee,
'body': '<p>Labrador</p>',
'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)),
'is_internal': True,
'message_type': 'notification',
'model': test_record._name,
'notified_partner_ids': self.env['res.partner'],
'reply_to': formataddr((self.company_admin.name, '%s@%s' % (self.alias_catchall, self.alias_domain))),
'res_id': test_record.id,
'subtype_id': self.env.ref('mail.mt_note'),
}
)
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('employee')
def test_message_post(self):
self.user_employee_2.write({'notification_type': 'inbox'})
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
with self.assertSinglePostNotifications(
[{'partner': self.partner_employee_2, 'type': 'inbox'}],
{'content': 'Body'}
):
new_message = test_record.message_post(
body='Body',
message_type='comment',
subtype_xmlid='mail.mt_comment',
partner_ids=[self.partner_employee_2.id],
)
self.assertMessageFields(
new_message,
{'author_id': self.partner_employee,
'body': '<p>Body</p>',
'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)),
'is_internal': False,
'message_type': 'comment',
'model': test_record._name,
'notified_partner_ids': self.partner_employee_2,
'reply_to': formataddr(("%s %s" % (self.company_admin.name, test_record.name), '%s@%s' % (self.alias_catchall, self.alias_domain))),
'res_id': test_record.id,
'subtype_id': self.env.ref('mail.mt_comment'),
}
)
self.assertEqual(test_record.message_partner_ids, self.partner_employee)
test_record.message_subscribe(self.partner_1.ids)
with self.assertSinglePostNotifications(
[{'partner': self.partner_employee_2, 'type': 'inbox'},
{'partner': self.partner_1, 'type': 'email'}],
message_info={'content': 'NewBody'},
mail_unlink_sent=True
):
new_message = test_record.message_post(
body='NewBody',
message_type='comment',
subtype_xmlid='mail.mt_comment',
partner_ids=[self.partner_employee_2.id],
)
self.assertMessageFields(
new_message,
{'notified_partner_ids': self.partner_1 + self.partner_employee_2}
)
# notifications emails should have been deleted
self.assertFalse(self.env['mail.mail'].sudo().search_count([('mail_message_id', '=', new_message.id)]))
with self.assertSinglePostNotifications(
[{'partner': self.partner_1, 'type': 'email'},
{'partner': self.partner_portal, 'type': 'email'}],
{'content': 'ToPortal'}
):
test_record.message_post(
body='ToPortal',
message_type='comment',
subtype_xmlid='mail.mt_comment',
partner_ids=self.partner_portal.ids,
)
@users('employee')
def test_message_post_inactive_follower(self):
""" Test posting with inactive followers does not notify them (e.g. odoobot) """
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
test_record._message_subscribe(self.user_employee_2.partner_id.ids)
self.user_employee_2.write({'active': False})
self.partner_employee_2.write({'active': False})
with self.assertPostNotifications([{'content': 'Test', 'notif': []}]):
test_record.message_post(
body='Test',
message_type='comment',
subtype_xmlid='mail.mt_comment',
)
@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)],
@users('employee')
def test_message_post_keep_emails(self):
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
test_record.message_subscribe(partner_ids=self.partner_employee_2.ids)
with self.mock_mail_gateway(mail_unlink_sent=True):
msg = test_record.message_post(
body='Test',
mail_auto_delete=False,
message_type='comment',
partner_ids=[self.partner_1.id, self.partner_2.id],
subject='Test',
subtype_xmlid='mail.mt_comment',
)
# notifications emails should not have been deleted: one for customers, one for user
self.assertEqual(self.env['mail.mail'].sudo().search_count([('mail_message_id', '=', msg.id)]), 2)
@mute_logger('odoo.addons.mail.models.mail_mail')
@users('employee')
def test_message_post_w_attachments(self):
_attachments = [
('List1', b'My first attachment'),
('List2', b'My second attachment'),
]
_attachment_records = self.env['ir.attachment'].create(
self._generate_attachments_data(3, res_id=0, res_model='mail.compose.message')
)
_attachment_records[1].write({'mimetype': 'image/png'}) # to test message_main_attachment heuristic
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
self.assertFalse(test_record.message_main_attachment_id)
with self.mock_mail_gateway():
msg = test_record.message_post(
attachments=_attachments,
attachment_ids=_attachment_records.ids,
body='Test',
message_type='comment',
partner_ids=[self.partner_1.id],
subject='Test',
subtype_xmlid='mail.mt_comment',
)
# updated message main attachment
self.assertEqual(test_record.message_main_attachment_id, _attachment_records[1],
'MailThread: main attachment should be set to image/png')
# message attachments
self.assertEqual(len(msg.attachment_ids), 5)
self.assertEqual(set(msg.attachment_ids.mapped('res_model')), set([self.test_record._name]))
self.assertEqual(set(msg.attachment_ids.mapped('res_id')), set([self.test_record.id]))
self.assertEqual(set(base64.b64decode(x) for x in msg.attachment_ids.mapped('datas')),
set([b'AttContent_00', b'AttContent_01', b'AttContent_02', _attachments[0][1], _attachments[1][1]]))
self.assertTrue(set(_attachment_records.ids).issubset(msg.attachment_ids.ids),
'message_post: mail.message attachments duplicated')
# notification email attachments
self.assertEqual(len(self._mails), 1)
self.assertSentEmail(
self.user_employee.partner_id, [self.partner_1],
attachments=[('List1', b'My first attachment', 'application/octet-stream'),
('List2', b'My second attachment', 'application/octet-stream'),
('AttFileName_00.txt', b'AttContent_00', 'text/plain'),
('AttFileName_01.txt', b'AttContent_01', 'image/png'),
('AttFileName_02.txt', b'AttContent_02', 'text/plain'),
]
)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_multiline_subject(self):
with self.mock_mail_gateway():
msg = self.test_record.with_user(self.user_employee).message_post(
body='<p>Test Body</p>',
partner_ids=[self.partner_1.id, self.partner_2.id],
subject='1st line\n2nd line',
)
self.assertEqual(msg.subject, '1st line 2nd line')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_portal_acls(self):
self.test_record.message_subscribe((self.partner_1 | self.user_employee.partner_id).ids)
with self.assertPostNotifications(
[{'content': '<p>Test</p>', 'notif': [
{'partner': self.partner_employee, 'type': 'inbox'},
{'partner': self.partner_1, 'type': 'email'}]}
]
), patch.object(MailTestSimple, 'check_access_rights', return_value=True):
new_msg = self.test_record.with_user(self.user_portal).message_post(
body='<p>Test</p>',
message_type='comment',
subject='Subject',
subtype_xmlid='mail.mt_comment',
)
self.assertEqual(new_msg.sudo().notified_partner_ids, (self.partner_1 | self.user_employee.partner_id))
with self.assertRaises(AccessError):
self.test_record.with_user(self.user_portal).message_post(
body='<p>Test</p>',
message_type='comment',
subject='Subject',
subtype_xmlid='mail.mt_comment',
)
@mute_logger('odoo.addons.mail.models.mail_mail')
@users('employee')
def test_post_answer(self):
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
with self.mock_mail_gateway():
parent_msg = test_record.message_post(
body='<p>Test</p>',
message_type='comment',
subject='Test Subject',
subtype_xmlid='mail.mt_comment',
)
self.assertFalse(parent_msg.partner_ids)
self.assertNotSentEmail()
with self.assertPostNotifications(
[{'content': '<p>Test Answer</p>', 'notif': [{'partner': self.partner_1, 'type': 'email'}]}]
):
msg = test_record.message_post(
body='<p>Test Answer</p>',
message_type='comment',
parent_id=parent_msg.id,
partner_ids=[self.partner_1.id],
subtype_xmlid='mail.mt_comment',
)
self.assertEqual(msg.parent_id, parent_msg)
self.assertEqual(msg.partner_ids, self.partner_1)
self.assertFalse(parent_msg.partner_ids)
# check notification emails: references
self.assertSentEmail(
self.user_employee.partner_id, [self.partner_1],
ref_content='openerp-%d-mail.test.simple' % self.test_record.id
)
with self.mock_mail_gateway():
new_msg = test_record.message_post(
body='<p>Test Answer Bis</p>',
message_type='comment',
parent_id=msg.id,
subtype_xmlid='mail.mt_comment',
)
self.assertEqual(new_msg.parent_id, parent_msg, 'message_post: flatten error')
self.assertFalse(new_msg.partner_ids)
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_thread')
@users('employee')
def test_post_internal(self):
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
test_record.message_subscribe([self.user_admin.partner_id.id])
with self.mock_mail_gateway():
msg = test_record.message_post(
body='My Body',
message_type='comment',
subject='My Subject',
subtype_xmlid='mail.mt_note',
)
self.assertFalse(msg.is_internal,
'Notes are not "internal" but replies will be. Subtype being internal should be sufficient from ACLs point of view.')
self.assertFalse(msg.partner_ids)
self.assertFalse(msg.notified_partner_ids)
self.format_and_process(
MAIL_TEMPLATE_PLAINTEXT, self.user_admin.email, 'not_my_businesss@example.com',
msg_id='<1198923581.41972151344608186800.JavaMail.diff1@agrolait.example.com>',
extra='In-Reply-To:\r\n\t%s\n' % msg.message_id,
target_model='mail.test.simple')
reply = test_record.message_ids - msg
self.assertTrue(reply)
self.assertTrue(reply.is_internal)
self.assertEqual(reply.notified_partner_ids, self.user_employee.partner_id)
self.assertEqual(reply.parent_id, msg)
self.assertEqual(reply.subtype_id, self.env.ref('mail.mt_note'))
@tagged('mail_post')
class TestMessagePostHelpers(TestMessagePostCommon):
@classmethod
def setUpClass(cls):
super(TestMessagePostHelpers, cls).setUpClass()
# ensure employee can create partners, necessary for templates
cls.user_employee.write({
'groups_id': [(4, cls.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
}]
@users('employee')
def test_message_log_with_view(self):
test_record = self.env['mail.test.simple'].browse(self.test_record.ids)
new_note = test_record._message_log_with_view(
'test_mail.mail_template_simple_test',
values={'partner': self.user_employee.partner_id}
)
self.assertMessageFields(
new_note,
{'author_id': self.partner_employee,
'body': '<p>Hello %s,</p>' % self.user_employee.name,
'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)),
'is_internal': True,
'message_type': 'notification',
'model': test_record._name,
'notified_partner_ids': self.env['res.partner'],
'reply_to': formataddr((self.company_admin.name, '%s@%s' % (self.alias_catchall, self.alias_domain))),
'res_id': test_record.id,
'subtype_id': self.env.ref('mail.mt_note'),
}
)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_message_post_w_template(self):
test_record = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
_attachments = self._generate_attachments_data(count=2, res_id=0, res_model='mail.template')
email_1 = 'test1@example.com'
email_2 = 'test2@example.com'
email_3 = self.partner_1.email
self._create_template('mail.test.simple', {
'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])],
template = self._create_template('mail.test.simple', {
'attachment_ids': [(0, 0, attach_vals) for attach_vals in _attachments],
'partner_to': '%s,%s' % (self.partner_2.id, self.user_admin.partner_id.id),
'email_to': '%s, %s' % (email_1, email_2),
'email_cc': '%s' % email_3,
})
template.attachment_ids.write({'res_id': template.id})
# admin should receive emails
self.user_admin.write({'notification_type': 'email'})
# Force the attachments of the template to be in the natural order.
@@ -534,25 +766,35 @@ class TestMessagePost(TestMailCommon, TestRecipients):
[r],
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')])
attachments=[
('AttFileName_00.txt', b'AttContent_00', 'text/plain'),
('AttFileName_01.txt', b'AttContent_01', 'text/plain'),
]
)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_post_post_w_template_mass_mode(self):
def test_message_post_w_template_mass_mode(self):
test_record = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
self.user_employee.write({
'groups_id': [(4, self.env.ref('base.group_partner_manager').id)],
})
self._create_template('mail.test.simple', {
'partner_to': '%s,%s' % (self.partner_2.id, self.user_admin.partner_id.id),
'email_to': 'test@example.com',
'email_cc': self.partner_1.email,
_attachments = self._generate_attachments_data(count=2, res_id=0, res_model='mail.template')
template = self._create_template('mail.test.simple', {
'attachment_ids': [(0, 0, attach_vals) for attach_vals in _attachments],
# After the HTML sanitizer, it will become "<p>Body for: <t t-out="object.name" /><a href="">link</a></p>"
'body_html': 'Body for: <t t-out="object.name" /><script>test</script><a href="javascript:alert(1)">link</a>',
'email_to': 'test@example.com',
'email_cc': self.partner_1.email,
'partner_to': '%s,%s' % (self.partner_2.id, self.user_admin.partner_id.id),
})
template.attachment_ids.write({'res_id': template.id})
with self.mock_mail_gateway():
test_record.with_user(self.user_employee).message_post_with_template(self.email_template.id, composition_mode='mass_mail')
test_record.with_user(self.user_employee).message_post_with_template(
template.id,
composition_mode='mass_mail'
)
new_partner = self.env['res.partner'].search([('email', '=', 'test@example.com')])
@@ -561,21 +803,15 @@ class TestMessagePost(TestMailCommon, TestRecipients):
[new_partner],
subject='About %s' % test_record.name,
body_content=test_record.name,
attachments=[])
attachments=[
('AttFileName_00.txt', b'AttContent_00', 'text/plain'),
('AttFileName_01.txt', b'AttContent_01', 'text/plain'),
]
)
@tagged('mail_post', 'post_install', '-at_install')
class TestMessagePostGlobal(TestMailCommon, TestRecipients):
@classmethod
def setUpClass(cls):
super(TestMessagePostGlobal, cls).setUpClass()
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({
'email_from': 'ignasse@example.com',
'name': 'Test',
})
cls._reset_mail_context(cls.test_record)
cls.user_admin.write({'notification_type': 'email'})
class TestMessagePostGlobal(TestMessagePostCommon):
@users('employee')
def test_message_post_return(self):
@@ -586,8 +822,7 @@ class TestMessagePostGlobal(TestMailCommon, TestRecipients):
message_id = call_kw(self.env['mail.test.simple'],
'message_post',
[test_record.id],
{'body': 'test'}
)
{'body': 'test'})
self.assertTrue(isinstance(message_id, int))
@@ -631,6 +866,7 @@ class TestMessagePostLang(TestMailCommon, TestRecipients):
cls.partner_2.write({'lang': 'es_ES'})
@users('employee')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_composer_lang_template(self):
test_records = self.test_records.with_user(self.env.user)
test_template = self.test_template.with_user(self.env.user)
@@ -659,6 +895,7 @@ class TestMessagePostLang(TestMailCommon, TestRecipients):
self.assertEqual('EnglishSubject for %s' % record.name, customer_email['subject'], 'Fixme: this should be translated')
@users('employee')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_layout_email_lang_context(self):
test_records = self.test_records.with_user(self.env.user).with_context(lang='es_ES')
test_records[1].message_subscribe(self.partner_2.ids)
@@ -687,6 +924,7 @@ class TestMessagePostLang(TestMailCommon, TestRecipients):
self.assertIn('Hello', body, 'Body of posted message should be present')
@users('employee')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_layout_email_lang_template(self):
test_records = self.test_records.with_user(self.env.user)
test_template = self.test_template.with_user(self.env.user)
+258 -108
View File
@@ -1,70 +1,41 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
from odoo.addons.base.tests.common import TransactionCaseWithUserDemo
from odoo.tests.common import users, warmup
from odoo.addons.mail.tests.common import MailCommon
from odoo.tests.common import users, warmup, Form
from odoo.tests import tagged
from odoo.tools import mute_logger, formataddr
@tagged('mail_performance')
class BaseMailPerformance(TransactionCaseWithUserDemo):
class BaseMailPerformance(MailCommon, TransactionCaseWithUserDemo):
@classmethod
def setUpClass(cls):
super(BaseMailPerformance, cls).setUpClass()
cls._quick_create_ctx = {
'no_reset_password': True,
'mail_create_nolog': True,
'mail_create_nosubscribe': True,
'mail_notrack': True,
'mail_channel_nosubscribe': True,
}
cls.user_employee = cls.env['res.users'].with_context(cls._quick_create_ctx).create({
'country_id': cls.env.ref('base.be').id,
'email': 'e.e@example.com',
'groups_id': [(6, 0, [cls.env.ref('base.group_user').id, cls.env.ref('base.group_partner_manager').id])],
'login': 'employee',
'name': 'Ernest Employee',
'notification_type': 'inbox',
'signature': '--\nErnest',
})
cls.admin = cls.env.user
cls.admin.write({
'country_id': cls.env.ref('base.be').id,
})
# creating partners is required notably witn template usage
cls.user_employee.write({'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)]})
cls.customer = cls.env['res.partner'].with_context(cls._quick_create_ctx).create({
cls.customer = cls.env['res.partner'].with_context(cls._test_context).create({
'country_id': cls.env.ref('base.be').id,
'email': 'customer.test@example.com',
'name': 'Test Customer',
'mobile': '0456123456',
})
cls.test_attachments_vals = cls._generate_attachments_data(
cls, 3, res_model='mail.compose.message', res_id=0
)
def setUp(self):
super(BaseMailPerformance, self).setUp()
# patch registry to simulate a ready environment
self.patch(self.env.registry, 'ready', True)
self._flush_tracking()
def _init_mail_gateway(self):
# setup mail gateway
self.alias_domain = 'example.com'
self.alias_catchall = 'catchall.test'
self.alias_bounce = 'bounce.test'
self.default_from = 'notifications'
self.env['ir.config_parameter'].set_param('mail.bounce.alias', self.alias_bounce)
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)
self.env['ir.config_parameter'].set_param('mail.default.from', self.default_from)
def _flush_tracking(self):
""" Force the creation of tracking values notably, and ensure tests are
reproducible. """
self.env['base'].flush()
self.cr.flush()
self.flush_tracking()
@tagged('mail_performance')
@@ -113,8 +84,6 @@ class TestBaseMailPerformance(BaseMailPerformance):
}
])
self._init_mail_gateway()
@users('__system__', 'demo')
@warmup
def test_read_mail(self):
@@ -215,7 +184,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
def setUp(self):
super(TestMailAPIPerformance, self).setUp()
self.user_test = self.env['res.users'].with_context(self._quick_create_ctx).create({
self.user_test = self.env['res.users'].with_context(self._test_context).create({
'name': 'Paulette Testouille',
'login': 'paul',
'email': 'user.test.paulette@example.com',
@@ -223,13 +192,11 @@ class TestMailAPIPerformance(BaseMailPerformance):
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self._init_mail_gateway()
# automatically follow activities, for backward compatibility concerning query count
self.env.ref('mail.mt_activities').write({'default': True})
def _create_test_records(self):
self.test_record_full = self.env['mail.test.ticket'].with_context(self._quick_create_ctx).create({
self.test_record_full = self.env['mail.test.ticket'].with_context(self._test_context).create({
'name': 'TestRecord',
'customer_id': self.customer.id,
'user_id': self.user_test.id,
@@ -243,6 +210,10 @@ class TestMailAPIPerformance(BaseMailPerformance):
'email_from': '{{ object.user_id.email_formatted }}',
'partner_to': '{{ object.customer_id.id }}',
'email_to': '{{ ("%s Customer <%s>" % (object.name, object.email_from)) }}',
'attachment_ids': [
(0, 0, dict(attachment, res_model='mail.template'))
for attachment in self.test_attachments_vals
],
})
@users('__system__', 'employee')
@@ -268,9 +239,9 @@ class TestMailAPIPerformance(BaseMailPerformance):
'res_id': record.id,
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
})
#read activity_type to normalize cache between enterprise and community
#voip module read activity_type during create leading to one less query in enterprise on action_feedback
category = activity.activity_type_id.category
# read activity_type to normalize cache between enterprise and community
# voip module read activity_type during create leading to one less query in enterprise on action_feedback
_category = activity.activity_type_id.category
with self.assertQueryCount(__system__=17, employee=19):
activity.action_feedback(feedback='Zizisse Done !')
@@ -283,9 +254,9 @@ class TestMailAPIPerformance(BaseMailPerformance):
with self.assertQueryCount(__system__=8, employee=8):
activity = record.action_start('Test Start')
#read activity_type to normalize cache between enterprise and community
#voip module read activity_type during create leading to one less query in enterprise on action_close
category = activity.activity_type_id.category
# read activity_type to normalize cache between enterprise and community
# voip module read activity_type during create leading to one less query in enterprise on action_close
_category = activity.activity_type_id.category
record.write({'name': 'Dupe write'})
@@ -296,7 +267,38 @@ class TestMailAPIPerformance(BaseMailPerformance):
@users('__system__', 'employee')
@warmup
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail')
@mute_logger('odoo.models.unlink')
def test_adv_activity_mixin_w_attachments(self):
record = self.env['mail.test.activity'].create({'name': 'Test'})
attachments = self.env['ir.attachment'].create([
dict(values,
res_model='mail.activity',
res_id=0)
for values in self.test_attachments_vals
])
with self.assertQueryCount(__system__=8, employee=8):
activity = record.action_start('Test Start')
#read activity_type to normalize cache between enterprise and community
#voip module read activity_type during create leading to one less query in enterprise on action_close
_category = activity.activity_type_id.category
record.write({'name': 'Dupe write'})
with self.assertQueryCount(__system__=20, employee=24):
record.action_close('Dupe feedback', attachment_ids=attachments.ids)
# notifications
message = record.message_ids[0]
self.assertEqual(
set(message.attachment_ids.mapped('name')),
set(['AttFileName_00.txt', 'AttFileName_01.txt', 'AttFileName_02.txt'])
)
@users('__system__', 'employee')
@warmup
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.tests')
def test_mail_composer(self):
self._create_test_records()
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
@@ -311,12 +313,64 @@ class TestMailAPIPerformance(BaseMailPerformance):
'partner_ids': [(4, customer_id)],
})
with self.assertQueryCount(__system__=32, employee=38):
with self.assertQueryCount(__system__=33, employee=39):
composer._action_send_mail()
@users('__system__', 'employee')
@warmup
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail')
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.tests')
def test_mail_composer_attachments(self):
self._create_test_records()
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
customer = self.env['res.partner'].browse(self.customer.ids)
attachments = self.env['ir.attachment'].with_user(self.env.user).create(self.test_attachments_vals)
with self.assertQueryCount(__system__=3, employee=3):
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': test_record._name,
'default_res_id': test_record.id,
}).create({
'attachment_ids': attachments.ids,
'body': '<p>Test Body</p>',
'partner_ids': [(4, customer.id)],
})
with self.assertQueryCount(__system__=36, employee=44):
composer._action_send_mail()
@users('__system__', 'employee')
@warmup
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.tests')
def test_mail_composer_form_attachments(self):
self._create_test_records()
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
customer = self.env['res.partner'].browse(self.customer.ids)
attachments = self.env['ir.attachment'].with_user(self.env.user).create(self.test_attachments_vals)
with self.assertQueryCount(__system__=15, employee=15):
composer_form = Form(
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': test_record._name,
'default_res_id': test_record.id,
})
)
composer_form.body = '<p>Test Body</p>'
composer_form.partner_ids.add(customer)
for attachment in attachments:
composer_form.attachment_ids.add(attachment)
composer = composer_form.save()
with self.assertQueryCount(__system__=44, employee=52):
composer._action_send_mail()
# notifications
message = test_record.message_ids[0]
self.assertEqual(message.attachment_ids, attachments)
self.assertEqual(message.notified_partner_ids, customer + self.user_test.partner_id)
@users('__system__', 'employee')
@warmup
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.tests')
def test_mail_composer_nodelete(self):
self._create_test_records()
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
@@ -332,16 +386,19 @@ class TestMailAPIPerformance(BaseMailPerformance):
'partner_ids': [(4, customer_id)],
})
with self.assertQueryCount(__system__=25, employee=31):
with self.assertQueryCount(__system__=26, employee=32):
composer._action_send_mail()
@users('__system__', 'employee')
@warmup
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.tests')
def test_mail_composer_w_template(self):
self._create_test_records()
self.test_template_full.write({'attachment_ids': [(5, 0)]})
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
test_template = self.env['mail.template'].browse(self.test_template_full.id)
with self.assertQueryCount(__system__=11, employee=12):
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
@@ -351,19 +408,121 @@ class TestMailAPIPerformance(BaseMailPerformance):
}).create({})
composer._onchange_template_id_wrapper()
with self.assertQueryCount(__system__=32, employee=38):
with self.assertQueryCount(__system__=33, employee=39):
composer._action_send_mail()
# notifications
message = test_record.message_ids[0]
self.assertFalse(message.attachment_ids)
# remove created partner to ensure tests are the same each run
self.env['res.partner'].sudo().search([('email', '=', 'nopartner.test@example.com')]).unlink()
@users('__system__', 'employee')
@warmup
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.tests')
def test_mail_composer_w_template_attachments(self):
self._create_test_records()
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
test_template = self.env['mail.template'].browse(self.test_template_full.id)
with self.assertQueryCount(__system__=12, employee=13):
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': test_record._name,
'default_res_id': test_record.id,
'default_template_id': test_template.id,
}).create({})
composer._onchange_template_id_wrapper()
with self.assertQueryCount(__system__=41, employee=52):
composer._action_send_mail()
# notifications
message = test_record.message_ids[0]
self.assertEqual(
set(message.attachment_ids.mapped('name')),
set(['AttFileName_00.txt', 'AttFileName_01.txt', 'AttFileName_02.txt'])
)
# remove created partner to ensure tests are the same each run
self.env['res.partner'].sudo().search([('email', '=', 'nopartner.test@example.com')]).unlink()
@users('__system__', 'employee')
@warmup
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.tests')
def test_mail_composer_w_template_form(self):
self._create_test_records()
self.test_template_full.write({'attachment_ids': [(5, 0)]})
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
test_template = self.env['mail.template'].browse(self.test_template_full.id)
customer = self.env['res.partner'].browse(self.customer.ids)
with self.assertQueryCount(__system__=33, employee=33):
composer_form = Form(
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': test_record._name,
'default_res_id': test_record.id,
'default_template_id': test_template.id,
})
)
composer = composer_form.save()
with self.assertQueryCount(__system__=41, employee=47):
composer._action_send_mail()
# notifications
new_partner = self.env['res.partner'].sudo().search([('email', '=', 'nopartner.test@example.com')])
message = test_record.message_ids[0]
self.assertFalse(message.attachment_ids)
self.assertEqual(message.notified_partner_ids, customer + self.user_test.partner_id + new_partner)
# remove created partner to ensure tests are the same each run
new_partner.unlink()
@users('__system__', 'employee')
@warmup
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.tests')
def test_mail_composer_w_template_form_attachments(self):
self._create_test_records()
test_record = self.env['mail.test.ticket'].browse(self.test_record_full.id)
test_template = self.env['mail.template'].browse(self.test_template_full.id)
customer = self.env['res.partner'].browse(self.customer.ids)
with self.assertQueryCount(__system__=34, employee=34):
composer_form = Form(
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': test_record._name,
'default_res_id': test_record.id,
'default_template_id': test_template.id,
})
)
composer = composer_form.save()
with self.assertQueryCount(__system__=53, employee=70):
composer._action_send_mail()
# notifications
new_partner = self.env['res.partner'].sudo().search([('email', '=', 'nopartner.test@example.com')])
message = test_record.message_ids[0]
self.assertEqual(
set(message.attachment_ids.mapped('name')),
set(['AttFileName_00.txt', 'AttFileName_01.txt', 'AttFileName_02.txt'])
)
self.assertEqual(message.notified_partner_ids, customer + self.user_test.partner_id + new_partner)
# remove created partner to ensure tests are the same each run
new_partner.unlink()
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'employee')
@warmup
def test_message_assignation_email(self):
self.user_test.write({'notification_type': 'email'})
record = self.env['mail.test.track'].create({'name': 'Test'})
with self.assertQueryCount(__system__=38, employee=39):
with self.assertQueryCount(__system__=39, employee=40):
record.write({
'user_id': self.user_test.id,
})
@@ -416,7 +575,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
def test_message_post_one_email_notification(self):
record = self.env['mail.test.simple'].create({'name': 'Test'})
with self.assertQueryCount(__system__=29, employee=32):
with self.assertQueryCount(__system__=30, employee=33):
record.message_post(
body='<p>Test Post Performances with an email ping</p>',
partner_ids=self.customer.ids,
@@ -511,7 +670,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
def setUp(self):
super(TestMailComplexPerformance, self).setUp()
self.user_portal = self.env['res.users'].with_context(self._quick_create_ctx).create({
self.user_portal = self.env['res.users'].with_context(self._test_context).create({
'name': 'Olivia Portal',
'login': 'port',
'email': 'p.p@example.com',
@@ -525,16 +684,14 @@ class TestMailComplexPerformance(BaseMailPerformance):
'customer_id': self.customer.id,
'alias_name': 'test-alias',
})
Partners = self.env['res.partner'].with_context(self._quick_create_ctx)
Partners = self.env['res.partner'].with_context(self._test_context)
self.partners = self.env['res.partner']
for x in range(0, 10):
self.partners |= Partners.create({'name': 'Test %s' % x, 'email': 'test%s@example.com' % x})
self.container.message_subscribe(self.partners.ids, subtype_ids=[
self.env.ref('mail.mt_comment').id,
self.env.ref('test_mail.st_mail_test_container_child_full').id]
)
self._init_mail_gateway()
self.env.ref('test_mail.st_mail_test_container_child_full').id
])
# `test_complex_mail_mail_send`
self.container.flush()
@@ -557,11 +714,13 @@ class TestMailComplexPerformance(BaseMailPerformance):
'recipient_ids': [(4, pid) for pid in self.partners.ids],
})
mail_ids = mail.ids
with self.assertQueryCount(__system__=8, employee=8):
with self.assertQueryCount(__system__=9, employee=9):
self.env['mail.mail'].sudo().browse(mail_ids).send()
self.assertEqual(mail.body_html, '<p>Test</p>')
self.assertEqual(mail.reply_to, formataddr(('%s %s' % (self.env.company.name, self.container.name), 'test-alias@example.com')))
self.assertEqual(mail.reply_to, formataddr(
('%s %s' % (self.env.company.name, self.container.name), 'test-alias@%s' % self.alias_domain)
))
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('__system__', 'employee')
@@ -570,7 +729,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
self.container.message_subscribe(self.user_portal.partner_id.ids)
record = self.container.with_user(self.env.user)
with self.assertQueryCount(__system__=53, employee=54): # about 20 (19?) queries per additional customer group
with self.assertQueryCount(__system__=54, employee=55): # about 20 (19?) queries per additional customer group
record.message_post(
body='<p>Test Post Performances</p>',
message_type='comment',
@@ -587,7 +746,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
record = self.container.with_user(self.env.user)
template_id = self.env.ref('test_mail.mail_test_container_tpl').id
with self.assertQueryCount(__system__=61, employee=62): # about 20 (19 ?) queries per additional customer group
with self.assertQueryCount(__system__=62, employee=63): # about 20 (19 ?) queries per additional customer group
record.message_post_with_template(template_id, message_type='comment', composition_mode='comment')
self.assertEqual(record.message_ids[0].body, '<p>Adding stuff on %s</p>' % record.name)
@@ -650,7 +809,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
})
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id)
with self.assertQueryCount(__system__=38, employee=39):
with self.assertQueryCount(__system__=39, employee=40):
rec.write({'user_id': self.user_portal.id})
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id)
# write tracking message
@@ -670,7 +829,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
customer_id = self.customer.id
user_id = self.user_portal.id
with self.assertQueryCount(__system__=89, employee=90):
with self.assertQueryCount(__system__=91, employee=92):
rec = self.env['mail.test.ticket'].create({
'name': 'Test',
'container_id': container_id,
@@ -699,7 +858,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id)
self.assertEqual(len(rec1.message_ids), 1)
with self.assertQueryCount(__system__=58, employee=58):
with self.assertQueryCount(__system__=59, employee=59):
rec.write({
'name': 'Test2',
'container_id': self.container.id,
@@ -736,7 +895,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id)
with self.assertQueryCount(__system__=65, employee=65):
with self.assertQueryCount(__system__=66, employee=66):
rec.write({
'name': 'Test2',
'container_id': container_id,
@@ -769,7 +928,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id)
with self.assertQueryCount(__system__=28, employee=29):
with self.assertQueryCount(__system__=29, employee=30):
rec.write({
'name': 'Test2',
'customer_id': customer_id,
@@ -922,7 +1081,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
messages.flush()
messages.invalidate_cache()
with self.assertQueryCount(employee=13):
with self.assertQueryCount(employee=13): # tm only: 12
res = messages.message_format()
self.assertEqual(len(res), 6)
@@ -934,96 +1093,87 @@ class TestMailHeavyPerformancePost(BaseMailPerformance):
super(TestMailHeavyPerformancePost, self).setUp()
# record
self.record = self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({
self.record_container = self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({
'name': 'Test record',
'customer_id': self.customer.id,
'alias_name': 'test-alias',
})
# followers
self.user_follower_email = self.env['res.users'].with_context(self._quick_create_ctx).create({
self.user_follower_email = self.env['res.users'].with_context(self._test_context).create({
'name': 'user_follower_email',
'login': 'user_follower_email',
'email': 'user_follower_email@example.com',
'notification_type': 'email',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self.user_follower_inbox = self.env['res.users'].with_context(self._quick_create_ctx).create({
self.user_follower_inbox = self.env['res.users'].with_context(self._test_context).create({
'name': 'user_follower_inbox',
'login': 'user_follower_inbox',
'email': 'user_follower_inbox@example.com',
'notification_type': 'inbox',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self.partner_follower = self.env['res.partner'].with_context(self._quick_create_ctx).create({
self.partner_follower = self.env['res.partner'].with_context(self._test_context).create({
'name': 'partner_follower',
'email': 'partner_follower@example.com',
})
self.record.message_subscribe([
self.record_container.message_subscribe([
self.partner_follower.id,
self.user_follower_inbox.partner_id.id,
self.user_follower_email.partner_id.id
])
# partner_ids
self.user_inbox = self.env['res.users'].with_context(self._quick_create_ctx).create({
self.user_inbox = self.env['res.users'].with_context(self._test_context).create({
'name': 'user_inbox',
'login': 'user_inbox',
'email': 'user_inbox@example.com',
'notification_type': 'inbox',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self.user_email = self.env['res.users'].with_context(self._quick_create_ctx).create({
self.user_email = self.env['res.users'].with_context(self._test_context).create({
'name': 'user_email',
'login': 'user_email',
'email': 'user_email@example.com',
'notification_type': 'email',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self.partner = self.env['res.partner'].with_context(self._quick_create_ctx).create({
self.partner = self.env['res.partner'].with_context(self._test_context).create({
'name': 'partner',
'email': 'partner@example.com',
})
self.vals = [{
'datas': base64.b64encode(bytes("attachement content %s" % i, 'utf-8')),
'name': 'fileText_test%s.txt' % i,
'mimetype': 'text/plain',
'res_model': 'mail.compose.message',
'res_id': 0,
} for i in range(3)]
self._init_mail_gateway()
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('employee')
@warmup
def test_complete_message_post(self):
# aims to cover as much features of message_post as possible
partner_ids = [self.user_inbox.partner_id.id, self.user_email.partner_id.id, self.partner.id]
record = self.record.with_user(self.env.user)
attachements = [ # not linear on number of attachements
recipients = self.user_inbox.partner_id + self.user_email.partner_id + self.partner
record_container = self.record_container.with_user(self.env.user)
attachments_vals = [ # not linear on number of attachments_vals
('attach tuple 1', "attachement tupple content 1"),
('attach tuple 2', "attachement tupple content 2", {'cid': 'cid1'}),
('attach tuple 3', "attachement tupple content 3", {'cid': 'cid2'}),
]
self.attachements = self.env['ir.attachment'].with_user(self.env.user).create(self.vals)
attachement_ids = self.attachements.ids
with self.assertQueryCount(employee=67):
attachments = self.env['ir.attachment'].with_user(self.env.user).create(self.test_attachments_vals)
with self.assertQueryCount(employee=68):
self.cr.sql_log = self.warm and self.cr.sql_log_count
record.with_context({}).message_post(
record_container.with_context({}).message_post(
body='<p>Test body <img src="cid:cid1"> <img src="cid:cid2"></p>',
subject='Test Subject',
message_type='notification',
subtype_xmlid=None,
partner_ids=partner_ids,
partner_ids=recipients.ids,
parent_id=False,
attachments=attachements,
attachment_ids=attachement_ids,
attachments=attachments_vals,
attachment_ids=attachments.ids,
email_add_signature=True,
model_description=False,
mail_auto_delete=True
)
self.cr.sql_log = False
self.assertTrue(record.message_ids[0].body.startswith('<p>Test body <img src="/web/image/'))
self.assertEqual(self.attachements.mapped('res_model'), [record._name for i in range(3)])
self.assertEqual(self.attachements.mapped('res_id'), [record.id for i in range(3)])
# self.assertEqual(record.message_ids[0].notified_partner_ids, [])
new_message = record_container.message_ids[0]
self.assertEqual(attachments.mapped('res_model'), [record_container._name for i in range(3)])
self.assertEqual(attachments.mapped('res_id'), [record_container.id for i in range(3)])
self.assertTrue(new_message.body.startswith('<p>Test body <img src="/web/image/'))
self.assertEqual(new_message.notified_partner_ids, recipients)
+3
View File
@@ -15,6 +15,8 @@ real applications. """,
'test_mass_mailing',
'mail',
'mail_bot',
'portal',
'rating',
# 'snailmail',
'mass_mailing',
'mass_mailing_sms',
@@ -23,6 +25,7 @@ real applications. """,
],
'data': [
'security/ir.model.access.csv',
'security/ir_rule_data.xml',
],
'demo': [
],
+2 -1
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import test_mail_models
from . import test_mail_models_mail
from . import test_mail_models_sms
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class MailTestRating(models.Model):
""" A model inheriting from mail.thread with some fields used for SMS
gateway, like a partner, a specific mobile phone, ... """
_description = 'Rating Model (ticket-like)'
_name = 'mail.test.rating'
_inherit = [
'mail.thread',
'mail.activity.mixin',
'rating.mixin',
'portal.mixin',
]
_mailing_enabled = True
_order = 'name asc, id asc'
name = fields.Char()
subject = fields.Char()
company_id = fields.Many2one('res.company', 'Company')
customer_id = fields.Many2one('res.partner', 'Customer')
email_from = fields.Char(compute='_compute_email_from', precompute=True, readonly=False, store=True)
mobile_nbr = fields.Char(compute='_compute_mobile_nbr', precompute=True, readonly=False, store=True)
phone_nbr = fields.Char(compute='_compute_phone_nbr', precompute=True, readonly=False, store=True)
user_id = fields.Many2one('res.users', 'Responsible', tracking=1)
@api.depends('customer_id')
def _compute_email_from(self):
for rating in self:
if rating.customer_id.email_normalized:
rating.email_from = rating.customer_id.email_normalized
elif not rating.email_from:
rating.email_from = False
@api.depends('customer_id')
def _compute_mobile_nbr(self):
for rating in self:
if rating.customer_id.mobile:
rating.mobile_nbr = rating.customer_id.mobile
elif not rating.mobile_nbr:
rating.mobile_nbr = False
@api.depends('customer_id')
def _compute_phone_nbr(self):
for rating in self:
if rating.customer_id.phone:
rating.phone_nbr = rating.customer_id.phone
elif not rating.phone_nbr:
rating.phone_nbr = False
def rating_get_partner_id(self):
return self.customer_id
@@ -1,4 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_test_rating_all,mail.test.rating.all,model_mail_test_rating,,0,0,0,0
access_mail_test_rating_portal,mail.test.rating.portal,model_mail_test_rating,base.group_portal,1,0,0,0
access_mail_test_rating_user,mail.test.rating.user,model_mail_test_rating,base.group_user,1,1,1,1
access_mail_test_sms_all,mail.test.sms.all,model_mail_test_sms,,0,0,0,0
access_mail_test_sms_user,mail.test.sms.user,model_mail_test_sms,base.group_user,1,1,1,1
access_mail_test_sms_bl_all,mail.test.sms.bl.all,model_mail_test_sms_bl,,0,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mail_test_rating_all mail.test.rating.all model_mail_test_rating 0 0 0 0
3 access_mail_test_rating_portal mail.test.rating.portal model_mail_test_rating base.group_portal 1 0 0 0
4 access_mail_test_rating_user mail.test.rating.user model_mail_test_rating base.group_user 1 1 1 1
5 access_mail_test_sms_all mail.test.sms.all model_mail_test_sms 0 0 0 0
6 access_mail_test_sms_user mail.test.sms.user model_mail_test_sms base.group_user 1 1 1 1
7 access_mail_test_sms_bl_all mail.test.sms.bl.all model_mail_test_sms_bl 0 0 0 0
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="mail_test_rating_rule_mc" model="ir.rule">
<field name="name">TestRating: Multi Company</field>
<field name="model_id" ref="test_mail_full.model_mail_test_rating"/>
<field eval="True" name="global"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>
<record id="mail_test_rating_rule_portal" model="ir.rule">
<field name="name">TestRating: Portal should follow</field>
<field name="model_id" ref="test_mail_full.model_mail_test_rating"/>
<field name="domain_force">[('message_partner_ids', 'in', [user.partner_id.id])]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
</record>
</odoo>
+2
View File
@@ -2,10 +2,12 @@
from . import common
from . import test_odoobot
from . import test_mail_performance
from . import test_phone_blacklist
from . import test_mass_mailing
from . import test_mass_mailing_statistics
from . import test_mass_sms
from . import test_rating
from . import test_sms_composer
from . import test_sms_management
from . import test_sms_performance
@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.mail.tests.common import mail_new_test_user
from odoo.addons.test_mail.tests.test_performance import BaseMailPerformance
from odoo.tests.common import users, warmup
from odoo.tests import tagged
from odoo.tools import mute_logger
@tagged('mail_performance')
class TestMailPerformance(BaseMailPerformance):
@classmethod
def setUpClass(cls):
super(TestMailPerformance, cls).setUpClass()
# users / followers
cls.user_emp_email = mail_new_test_user(
cls.env,
company_id=cls.user_admin.company_id.id,
company_ids=[(4, cls.user_admin.company_id.id)],
email='user.emp.email@test.example.com',
login='user_emp_email',
groups='base.group_user,base.group_partner_manager',
name='Emmanuel Email',
notification_type='email',
signature='--\nEmmanuel',
)
cls.user_portal = mail_new_test_user(
cls.env,
company_id=cls.user_admin.company_id.id,
company_ids=[(4, cls.user_admin.company_id.id)],
email='user.portal@test.example.com',
login='user_portal',
groups='base.group_portal',
name='Paul Portal',
)
cls.customers = cls.env['res.partner'].create([
{'country_id': cls.env.ref('base.be').id,
'email': 'customer.full.test.1@example.com',
'name': 'Test Full Customer 1',
'mobile': '0456112233',
'phone': '0456112233',
},
{'country_id': cls.env.ref('base.be').id,
'email': 'customer.full.test.2@example.com',
'name': 'Test Full Customer 2',
'mobile': '0456223344',
'phone': '0456112233',
},
])
# record
cls.record_container = cls.env['mail.test.container.mc'].create({
'alias_name': 'test-alias',
'customer_id': cls.customer.id,
'name': 'Test Container',
})
cls.record_ticket = cls.env['mail.test.ticket.mc'].create({
'email_from': 'email.from@test.example.com',
'container_id': cls.record_container.id,
'customer_id': False,
'name': 'Test Ticket',
'user_id': cls.user_emp_email.id,
})
cls.record_ticket.message_subscribe(cls.customers.ids + cls.user_admin.partner_id.ids + cls.user_portal.partner_id.ids)
def test_initial_values(self):
""" Simply ensure some values through all tests """
record_ticket = self.env['mail.test.ticket.mc'].browse(self.record_ticket.ids)
self.assertEqual(record_ticket.message_partner_ids,
self.user_emp_email.partner_id + self.user_admin.partner_id + self.customers + self.user_portal.partner_id)
self.assertEqual(len(record_ticket.message_ids), 1)
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('employee')
@warmup
def test_message_post_w_followers(self):
""" Aims to cover as much features of message_post as possible """
record_ticket = self.env['mail.test.ticket.mc'].browse(self.record_ticket.ids)
attachments = self.env['ir.attachment'].create(self.test_attachments_vals)
with self.assertQueryCount(employee=102): # tmf: 93 / com: 100
new_message = record_ticket.message_post(
attachment_ids=attachments.ids,
body='<p>Test Content</p>',
message_type='comment',
subject='Test Subject',
subtype_xmlid='mail.mt_comment',
)
self.assertEqual(
new_message.notified_partner_ids,
self.user_emp_email.partner_id + self.user_admin.partner_id + self.customers + self.user_portal.partner_id
)
@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestMailFullRecipients
from odoo.tests import tagged
from odoo.tests.common import users
from odoo.tools import mute_logger
@tagged('rating')
class TestRatingFlow(TestMailFullCommon, TestMailFullRecipients):
@classmethod
def setUpClass(cls):
super(TestRatingFlow, cls).setUpClass()
cls.record_rating = cls.env['mail.test.rating'].create({
'customer_id': cls.partner_1.id,
'name': 'Test Rating',
'user_id': cls.user_admin.id,
})
def test_initial_values(self):
record_rating = self.env['mail.test.rating'].browse(self.record_rating.ids)
self.assertFalse(record_rating.rating_ids)
self.assertEqual(record_rating.message_partner_ids, self.partner_admin)
self.assertEqual(len(record_rating.message_ids), 1)
@users('employee')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_rating_prepare(self):
record_rating = self.env['mail.test.rating'].browse(self.record_rating.ids)
# prepare rating token
access_token = record_rating.rating_get_access_token()
record_rating.invalidate_cache(fnames=['rating_ids'])
# check rating creation
rating = record_rating.rating_ids
self.assertEqual(rating.access_token, access_token)
self.assertFalse(rating.consumed)
self.assertFalse(rating.is_internal)
self.assertEqual(rating.partner_id, self.partner_1)
self.assertEqual(rating.rated_partner_id, self.user_admin.partner_id)
self.assertFalse(rating.rating)
@users('employee')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_rating_rating_apply(self):
record_rating = self.env['mail.test.rating'].browse(self.record_rating.ids)
# prepare rating token
access_token = record_rating.rating_get_access_token()
record_rating.invalidate_cache(fnames=['rating_ids'])
# apply a rate
with self.mock_mail_gateway(mail_unlink_sent=False), self.mock_mail_app():
record_rating.rating_apply(5, token=access_token, feedback='Top Feedback')
message = record_rating.message_ids[0]
rating = record_rating.rating_ids
# check posted message
self.assertIn('Top Feedback', message.body)
self.assertIn('rating/static/src/img/rating_5.png', message.body)
self.assertEqual(message.author_id, self.partner_1)
self.assertFalse(message.rating_ids)
self.assertEqual(message.notified_partner_ids, self.partner_admin)
self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
# check rating update
self.assertTrue(rating.consumed)
self.assertEqual(rating.feedback, 'Top Feedback')
self.assertFalse(rating.message_id)
self.assertEqual(rating.rating, 5)
@@ -14,14 +14,14 @@ class TestSMSPerformance(BaseMailPerformance, sms_common.SMSCase):
def setUp(self):
super(TestSMSPerformance, self).setUp()
self.test_record = self.env['mail.test.sms'].with_context(self._quick_create_ctx).create({
self.test_record = self.env['mail.test.sms'].with_context(self._test_context).create({
'name': 'Test',
'customer_id': self.customer.id,
'phone_nbr': '0456999999',
})
# prepare recipients to test for more realistic workload
self.partners = self.env['res.partner'].with_context(self._quick_create_ctx).create([
self.partners = self.env['res.partner'].with_context(self._test_context).create([
{'name': 'Test %s' % x,
'email': 'test%s@example.com' % x,
'mobile': '0456%s%s0000' % (x, x),
@@ -29,8 +29,6 @@ class TestSMSPerformance(BaseMailPerformance, sms_common.SMSCase):
} for x in range(0, 10)
])
self._init_mail_gateway()
@mute_logger('odoo.addons.sms.models.sms_sms')
@users('employee')
@warmup
@@ -87,13 +85,13 @@ class TestSMSMassPerformance(BaseMailPerformance, sms_common.MockSMS):
records = self.env['mail.test.sms']
partners = self.env['res.partner']
for x in range(50):
partners += self.env['res.partner'].with_context(**self._quick_create_ctx).create({
partners += self.env['res.partner'].with_context(**self._test_context).create({
'name': 'Partner_%s' % (x),
'email': '_test_partner_%s@example.com' % (x),
'country_id': be_country_id,
'mobile': '047500%02d%02d' % (x, x)
})
records += self.env['mail.test.sms'].with_context(**self._quick_create_ctx).create({
records += self.env['mail.test.sms'].with_context(**self._test_context).create({
'name': 'Test_%s' % (x),
'customer_id': partners[x].id,
})
@@ -2,20 +2,17 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.test_mass_mailing.tests.common import TestMassMailCommon
from odoo.addons.base.tests.common import MockSmtplibCase
from odoo.tests import tagged
from odoo.tests.common import users
from odoo.tools import mute_logger
@tagged('mass_mailing')
class TestMassMailingServer(TestMassMailCommon, MockSmtplibCase):
class TestMassMailingServer(TestMassMailCommon):
@classmethod
def setUpClass(cls):
super(TestMassMailingServer, cls).setUpClass()
cls._init_mail_gateway()
cls.env['ir.mail_server'].search([]).unlink()
cls._init_mail_servers()
cls.recipients = cls._create_mailing_test_records(model='mailing.test.optout', count=8)
@@ -2,39 +2,25 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.mail.tests.common import mail_new_test_user
from odoo.tests.common import TransactionCase, users, warmup
from odoo.addons.test_mail.tests.test_performance import BaseMailPerformance
from odoo.tests.common import users, warmup
from odoo.tests import tagged
from odoo.tools import mute_logger
class TestMassMailPerformanceBase(TransactionCase):
class TestMassMailPerformanceBase(BaseMailPerformance):
def setUp(self):
super(TestMassMailPerformanceBase, self).setUp()
@classmethod
def setUpClass(cls):
super(TestMassMailPerformanceBase, cls).setUpClass()
self.user_employee = mail_new_test_user(
self.env, login='emp',
groups='base.group_user',
name='Ernest Employee', notification_type='inbox')
self.user_marketing = mail_new_test_user(
self.env, login='marketing',
cls.user_marketing = mail_new_test_user(
cls.env,
groups='base.group_user,mass_mailing.group_mass_mailing_user',
name='Martial Marketing', signature='--\nMartial')
# setup mail gateway
self.alias_domain = 'example.com'
self.alias_catchall = 'catchall.test'
self.alias_bounce = 'bounce.test'
self.default_from = 'notifications'
self.env['ir.config_parameter'].set_param('mail.bounce.alias', self.alias_bounce)
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)
self.env['ir.config_parameter'].set_param('mail.default.from', self.default_from)
# patch registry to simulate a ready environment
self.patch(self.env.registry, 'ready', True)
login='marketing',
name='Martial Marketing',
signature='--\nMartial'
)
@tagged('mail_performance')
class TestMassMailPerformance(TestMassMailPerformanceBase):
@@ -60,8 +46,8 @@ class TestMassMailPerformance(TestMassMailPerformanceBase):
'mailing_domain': [('id', 'in', self.mm_recs.ids)],
})
# runbot needs +101 compared to local (1568)
with self.assertQueryCount(__system__=1670, marketing=1671):
# runbot needs +101 compared to local (1570)
with self.assertQueryCount(__system__=1672, marketing=1673):
mailing.action_send_mail()
self.assertEqual(mailing.sent, 50)
@@ -100,8 +86,8 @@ class TestMassMailBlPerformance(TestMassMailPerformanceBase):
'mailing_domain': [('id', 'in', self.mm_recs.ids)],
})
# runbot needs +125 compared to local (1834)
with self.assertQueryCount(__system__=1960, marketing=1961):
# runbot needs +125 compared to local (1836)
with self.assertQueryCount(__system__=1962, marketing=1963):
mailing.action_send_mail()
self.assertEqual(mailing.sent, 50)
+14 -12
View File
@@ -252,14 +252,14 @@ class MockSmtplibCase:
self.testing_smtp_session = TestingSMTPSession()
IrMailServer = self.env['ir.mail_server']
connect = IrMailServer.connect
find_mail_server = IrMailServer._find_mail_server
connect_origin = IrMailServer.connect
find_mail_server_origin = IrMailServer._find_mail_server
with patch.object(type(IrMailServer), '_is_test_mode', lambda self: False), \
patch('smtplib.SMTP_SSL', side_effect=lambda *args, **kwargs: self.testing_smtp_session), \
with patch('smtplib.SMTP_SSL', side_effect=lambda *args, **kwargs: self.testing_smtp_session), \
patch('smtplib.SMTP', side_effect=lambda *args, **kwargs: self.testing_smtp_session), \
patch.object(type(IrMailServer), 'connect', side_effect=connect) as connect_mocked, \
patch.object(type(IrMailServer), '_find_mail_server', side_effect=find_mail_server) as find_mail_server_mocked:
patch.object(type(IrMailServer), '_is_test_mode', lambda self: False), \
patch.object(type(IrMailServer), 'connect', wraps=IrMailServer, side_effect=connect_origin) as connect_mocked, \
patch.object(type(IrMailServer), '_find_mail_server', side_effect=find_mail_server_origin) as find_mail_server_mocked:
self.connect_mocked = connect_mocked
self.find_mail_server_mocked = find_mail_server_mocked
yield
@@ -298,14 +298,16 @@ class MockSmtplibCase:
)
@classmethod
def _init_mail_servers(cls):
cls.env['ir.config_parameter'].sudo().set_param('mail.catchall.domain', 'test.com')
cls.env['ir.config_parameter'].sudo().set_param('mail.default.from', 'notifications')
cls.env['ir.config_parameter'].sudo().set_param('mail.bounce.alias', 'bounce')
cls.alias_bounce = 'bounce'
def _init_mail_config(cls):
cls.alias_bounce = 'bounce.test'
cls.alias_domain = 'test.com'
cls.default_from = 'notifications'
cls.env['ir.config_parameter'].sudo().set_param('mail.catchall.domain', cls.alias_domain)
cls.env['ir.config_parameter'].sudo().set_param('mail.default.from', cls.default_from)
cls.env['ir.config_parameter'].sudo().set_param('mail.bounce.alias', cls.alias_bounce)
@classmethod
def _init_mail_servers(cls):
cls.env['ir.mail_server'].search([]).unlink()
ir_mail_server_values = {
@@ -15,6 +15,7 @@ from odoo.tools import mute_logger
class TestIrMailServer(TransactionCase, MockSmtplibCase):
def setUp(self):
self._init_mail_config()
self._init_mail_servers()
def _build_email(self, mail_from, return_path=None):
@@ -231,7 +232,7 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
self.assertEqual(len(self.emails), 1)
self.assert_email_sent_smtp(
smtp_from='bounce@xn--9caaaaaaa.com',
smtp_from='bounce.test@xn--9caaaaaaa.com',
smtp_to_list=['dest@xn--example--i1a.com'],
message_from='test@=?utf-8?b?w6nDqcOpw6nDqcOpw6k=?=.com',
from_filter=False,
@@ -322,7 +323,7 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
)
@mute_logger('odoo.models.unlink')
@patch.dict("odoo.tools.config.options", {"from_filter": "test.com"})
@patch.dict("odoo.tools.config.options", {"from_filter": "test.com", "smtp_server": "example.com"})
def test_mail_server_binary_arguments_domain(self):
"""Test the configuration provided in the odoo-bin arguments.
@@ -374,7 +375,7 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
)
@mute_logger('odoo.models.unlink')
@patch.dict("odoo.tools.config.options", {"from_filter": "test.com"})
@patch.dict("odoo.tools.config.options", {"from_filter": "test.com", "smtp_server": "example.com"})
def test_mail_server_binary_arguments_domain_smtp_session(self):
"""Test the configuration provided in the odoo-bin arguments.
@@ -417,7 +418,7 @@ class TestIrMailServer(TransactionCase, MockSmtplibCase):
)
@mute_logger('odoo.models.unlink')
@patch.dict('odoo.tools.config.options', {'from_filter': 'test.com'})
@patch.dict('odoo.tools.config.options', {'from_filter': 'test.com', 'smtp_server': 'example.com'})
def test_mail_server_mail_default_from_filter(self):
"""Test that the config parameter "mail.default.from_filter" overwrite the odoo-bin
argument "--from-filter"