[MERGE] mail: fixes and improved tests

[FIX] mail_followers: notification emails did not have 'references' correctly filled with their parent message message_id,
[FIX] mail_mail: emails sent to partners now use the 'Name <email>' format for email_to, instead of just 'email'
[FIX] mail_compose_message: fixed mass_mail subscribing the author to all documents,
[FIX] mail_compose_message: fixed access rule on mail_compose_message not taking into account the wizard creation in mass_mail_mode (model but no res_id -> could lead to crash),
[TEST] mail: cleaned a bit the tests; mail gateways related tests are now in a dedicated file; tests were added that helped triggering the previous errors

bzr revid: tde@openerp.com-20130320145225-1hhp383wt507p3q3
This commit is contained in:
Thibault Delavallée 2013-03-20 15:52:25 +01:00
commit 1b2579f777
7 changed files with 639 additions and 340 deletions

View File

@ -161,11 +161,16 @@ class mail_notification(osv.Model):
else: else:
email_from = msg.email_from email_from = msg.email_from
references = False
if msg.parent_id:
references = msg.parent_id.message_id
mail_values = { mail_values = {
'mail_message_id': msg.id, 'mail_message_id': msg.id,
'auto_delete': True, 'auto_delete': True,
'body_html': body_html, 'body_html': body_html,
'email_from': email_from, 'email_from': email_from,
'references': references,
} }
email_notif_id = mail_mail.create(cr, uid, mail_values, context=context) email_notif_id = mail_mail.create(cr, uid, mail_values, context=context)
try: try:

View File

@ -228,7 +228,7 @@ class mail_mail(osv.Model):
subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context) subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context)
reply_to = self.send_get_mail_reply_to(cr, uid, mail, partner=partner, context=context) reply_to = self.send_get_mail_reply_to(cr, uid, mail, partner=partner, context=context)
body_alternative = tools.html2plaintext(body) body_alternative = tools.html2plaintext(body)
email_to = [partner.email] if partner else tools.email_split(mail.email_to) email_to = ['%s <%s>' % (partner.name, partner.email)] if partner else tools.email_split(mail.email_to)
return { return {
'body': body, 'body': body,
'body_alternative': body_alternative, 'body_alternative': body_alternative,

View File

@ -19,11 +19,12 @@
# #
############################################################################## ##############################################################################
from . import test_mail_message, test_mail_features, test_message_read, test_invite from . import test_mail_message, test_mail_features, test_mail_gateway, test_message_read, test_invite
checks = [ checks = [
test_mail_message, test_mail_message,
test_mail_features, test_mail_features,
test_mail_gateway,
test_message_read, test_message_read,
test_invite, test_invite,
] ]

View File

@ -70,9 +70,9 @@ class TestMailBase(common.TransactionCase):
# Test users to use through the various tests # Test users to use through the various tests
self.user_raoul_id = self.res_users.create(cr, uid, self.user_raoul_id = self.res_users.create(cr, uid,
{'name': 'Raoul Grosbedon', 'signature': 'Raoul', 'email': 'raoul@raoul.fr', 'login': 'raoul', 'groups_id': [(6, 0, [self.group_employee_id])]}) {'name': 'Raoul Grosbedon', 'signature': 'SignRaoul', 'email': 'raoul@raoul.fr', 'login': 'raoul', 'groups_id': [(6, 0, [self.group_employee_id])]})
self.user_bert_id = self.res_users.create(cr, uid, self.user_bert_id = self.res_users.create(cr, uid,
{'name': 'Bert Tartignole', 'signature': 'Bert', 'email': 'bert@bert.fr', 'login': 'bert', 'groups_id': [(6, 0, [])]}) {'name': 'Bert Tartignole', 'signature': 'SignBert', 'email': 'bert@bert.fr', 'login': 'bert', 'groups_id': [(6, 0, [])]})
self.user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id) self.user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id)
self.user_bert = self.res_users.browse(cr, uid, self.user_bert_id) self.user_bert = self.res_users.browse(cr, uid, self.user_bert_id)
self.user_admin = self.res_users.browse(cr, uid, uid) self.user_admin = self.res_users.browse(cr, uid, uid)

View File

@ -20,183 +20,12 @@
############################################################################## ##############################################################################
from openerp.addons.mail.tests.test_mail_base import TestMailBase from openerp.addons.mail.tests.test_mail_base import TestMailBase
from openerp.tools.mail import html_sanitize, append_content_to_html from openerp.tools.mail import html_sanitize
MAIL_TEMPLATE = """Return-Path: <whatever-2a840@postmaster.twitter.com>
To: {to}
Received: by mail1.openerp.com (Postfix, from userid 10002)
id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
From: Sylvie Lelitre <sylvie.lelitre@agrolait.com>
Subject: {subject}
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_Part_4200734_24778174.1344608186754"
Date: Fri, 10 Aug 2012 14:16:26 +0000
Message-ID: <1198923581.41972151344608186760.JavaMail@agrolait.com>
{extra}
------=_Part_4200734_24778174.1344608186754
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Please call me as soon as possible this afternoon!
--
Sylvie
------=_Part_4200734_24778174.1344608186754
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>=20
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8" />
</head>=20
<body style=3D"margin: 0; padding: 0; background: #ffffff;-webkit-text-size-adjust: 100%;">=20
<p>Please call me as soon as possible this afternoon!</p>
<p>--<br/>
Sylvie
<p>
</body>
</html>
------=_Part_4200734_24778174.1344608186754--
"""
MAIL_TEMPLATE_PLAINTEXT = """Return-Path: <whatever-2a840@postmaster.twitter.com>
To: {to}
Received: by mail1.openerp.com (Postfix, from userid 10002)
id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
From: Sylvie Lelitre <sylvie.lelitre@agrolait.com>
Subject: {subject}
MIME-Version: 1.0
Content-Type: text/plain
Date: Fri, 10 Aug 2012 14:16:26 +0000
Message-ID: {msg_id}
{extra}
Please call me as soon as possible this afternoon!
--
Sylvie
"""
class test_mail(TestMailBase): class test_mail(TestMailBase):
def _mock_send_get_mail_body(self, *args, **kwargs): def test_00_followers_function_field(self):
# def _send_get_mail_body(self, cr, uid, mail, partner=None, context=None)
body = append_content_to_html(args[2].body_html, kwargs.get('partner').name if kwargs.get('partner') else 'No specific partner', plaintext=False)
return body
def setUp(self):
super(test_mail, self).setUp()
# Mock send_get_mail_body to test its functionality without other addons override
self._send_get_mail_body = self.registry('mail.mail').send_get_mail_body
self.registry('mail.mail').send_get_mail_body = self._mock_send_get_mail_body
def tearDown(self):
# Remove mocks
self.registry('mail.mail').send_get_mail_body = self._send_get_mail_body
super(test_mail, self).tearDown()
def test_00_message_process(self):
""" Testing incoming emails processing. """
cr, uid, user_raoul = self.cr, self.uid, self.user_raoul
# groups@.. will cause the creation of new mail groups
self.mail_group_model_id = self.ir_model.search(cr, uid, [('model', '=', 'mail.group')])[0]
self.mail_alias.create(cr, uid, {'alias_name': 'groups', 'alias_model_id': self.mail_group_model_id})
# Incoming mail creates a new mail_group "frogs"
self.assertEqual(self.mail_group.search(cr, uid, [('name', '=', 'frogs')]), [])
mail_frogs = MAIL_TEMPLATE.format(to='groups@example.com, other@gmail.com', subject='frogs', extra='')
self.mail_thread.message_process(cr, uid, None, mail_frogs)
frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'frogs')])
self.assertTrue(len(frog_groups) == 1)
# Previously-created group can be emailed now - it should have an implicit alias group+frogs@...
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
group_messages = frog_group.message_ids
self.assertTrue(len(group_messages) == 1, 'New group should only have the original message')
mail_frog_news = MAIL_TEMPLATE.format(to='Friendly Frogs <group+frogs@example.com>', subject='news', extra='')
self.mail_thread.message_process(cr, uid, None, mail_frog_news)
frog_group.refresh()
self.assertTrue(len(frog_group.message_ids) == 2, 'Group should contain 2 messages now')
# Even with a wrong destination, a reply should end up in the correct thread
mail_reply = MAIL_TEMPLATE.format(to='erroneous@example.com>', subject='Re: news',
extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
self.mail_thread.message_process(cr, uid, None, mail_reply)
frog_group.refresh()
self.assertTrue(len(frog_group.message_ids) == 3, 'Group should contain 3 messages now')
# No model passed and no matching alias must raise
mail_spam = MAIL_TEMPLATE.format(to='noone@example.com', subject='spam', extra='')
self.assertRaises(Exception,
self.mail_thread.message_process,
cr, uid, None, mail_spam)
# plain text content should be wrapped and stored as html
test_msg_id = '<deadcafe.1337@smtp.agrolait.com>'
mail_text = MAIL_TEMPLATE_PLAINTEXT.format(to='groups@example.com', subject='frogs', extra='', msg_id=test_msg_id)
self.mail_thread.message_process(cr, uid, None, mail_text)
new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0])
self.assertEqual(new_mail.body, '<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>',
'plaintext mail incorrectly parsed')
# Do: post a new message, with a known partner
test_msg_id = '<deadcafe.1337-2@smtp.agrolait.com>'
TEMPLATE_MOD = MAIL_TEMPLATE_PLAINTEXT.replace('Sylvie Lelitre <sylvie.lelitre@agrolait.com>', user_raoul.email)
mail_new = TEMPLATE_MOD.format(to='Friendly Frogs <group+frogs@example.com>', subject='extra news', extra='', msg_id=test_msg_id)
self.mail_thread.message_process(cr, uid, None, mail_new)
new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0])
# Test: author_id set, not email_from
self.assertEqual(new_mail.author_id, user_raoul.partner_id, 'message process wrong author found')
self.assertEqual(new_mail.email_from, user_raoul.email, 'message process wrong email_from')
# Do: post a new message, with a unknown partner
test_msg_id = '<deadcafe.1337-3@smtp.agrolait.com>'
TEMPLATE_MOD = MAIL_TEMPLATE_PLAINTEXT.replace('Sylvie Lelitre <sylvie.lelitre@agrolait.com>', '_abcd_')
mail_new = TEMPLATE_MOD.format(to='Friendly Frogs <group+frogs@example.com>', subject='super news', extra='', msg_id=test_msg_id)
self.mail_thread.message_process(cr, uid, None, mail_new)
new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0])
# Test: author_id set, not email_from
self.assertFalse(new_mail.author_id, 'message process shnould not have found a partner for _abcd_ email address')
self.assertIn('_abcd_', new_mail.email_from, 'message process should set en email_from when not finding a partner_id')
def test_05_thread_parent_resolution(self):
"""Verify parent/child relationships are correctly established when processing incoming mails"""
cr, uid = self.cr, self.uid
group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
msg1 = group_pigs.message_post(body='My Body', subject='1')
msg2 = group_pigs.message_post(body='My Body', subject='2')
msg1, msg2 = self.mail_message.browse(cr, uid, [msg1, msg2])
self.assertTrue(msg1.message_id, "New message should have a proper message_id")
# Reply to msg1, make sure the reply is properly attached using the various reply identification mechanisms
# 1. In-Reply-To header
reply_msg = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: 1',
extra='In-Reply-To: %s' % msg1.message_id)
self.mail_group.message_process(cr, uid, None, reply_msg)
# 2. References header
reply_msg2 = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: Re: 1',
extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id)
self.mail_group.message_process(cr, uid, None, reply_msg2)
# 3. Subject contains [<ID>] + model passed to message+process -> only attached to group, not to mail
reply_msg3 = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com',
extra='', subject='Re: [%s] 1' % self.group_pigs_id)
self.mail_group.message_process(cr, uid, 'mail.group', reply_msg3)
group_pigs.refresh()
msg1.refresh()
self.assertEqual(5, len(group_pigs.message_ids), 'group should contain 5 messages')
self.assertEqual(2, len(msg1.child_ids), 'msg1 should have 2 children now')
def test_10_followers_function_field(self):
""" Tests designed for the many2many function field 'follower_ids'. """ Tests designed for the many2many function field 'follower_ids'.
We will test to perform writes using the many2many commands 0, 3, 4, We will test to perform writes using the many2many commands 0, 3, 4,
5 and 6. """ 5 and 6. """
@ -253,7 +82,7 @@ class test_mail(TestMailBase):
follower_ids = set([follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)]) follower_ids = set([follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)])
self.assertEqual(follower_ids, set([partner_bert_id, user_admin.partner_id.id]), 'Bert and Admin should be the followers of dummy mail.group data') self.assertEqual(follower_ids, set([partner_bert_id, user_admin.partner_id.id]), 'Bert and Admin should be the followers of dummy mail.group data')
def test_11_message_followers_and_subtypes(self): def test_05_message_followers_and_subtypes(self):
""" Tests designed for the subscriber API as well as message subtypes """ """ Tests designed for the subscriber API as well as message subtypes """
cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs
# Data: message subtypes # Data: message subtypes
@ -305,7 +134,7 @@ class test_mail(TestMailBase):
self.assertTrue(subtype_data['mt_mg_nodef']['followed'], 'Admin should follow mt_mg_nodef in pigs') self.assertTrue(subtype_data['mt_mg_nodef']['followed'], 'Admin should follow mt_mg_nodef in pigs')
self.assertTrue(subtype_data['mt_all_nodef']['followed'], 'Admin should follow mt_all_nodef in pigs') self.assertTrue(subtype_data['mt_all_nodef']['followed'], 'Admin should follow mt_all_nodef in pigs')
def test_20_message_quote_context(self): def test_10_message_quote_context(self):
""" Tests designed for message_post. """ """ Tests designed for message_post. """
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
@ -323,236 +152,391 @@ class test_mail(TestMailBase):
self.assertNotIn('First answer, should not be displayed', result, 'Old answer should not be in quote.') self.assertNotIn('First answer, should not be displayed', result, 'Old answer should not be in quote.')
self.assertNotIn('My answer I am propagating', result, 'Thread header content should be in quote.') self.assertNotIn('My answer I am propagating', result, 'Thread header content should be in quote.')
def test_21_message_post(self): def test_20_message_post(self):
""" Tests designed for message_post. """ """ Tests designed for message_post. """
cr, uid, user_raoul, group_pigs = self.cr, self.uid, self.user_raoul, self.group_pigs cr, uid, user_raoul, group_pigs = self.cr, self.uid, self.user_raoul, self.group_pigs
self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'})
# --------------------------------------------------
# Data creation
# --------------------------------------------------
# 0 - Update existing users-partners
self.res_users.write(cr, uid, [uid], {'email': 'a@a'})
self.res_users.write(cr, uid, [self.user_raoul_id], {'email': 'r@r'})
# 1 - Bert Tartopoils, with email, should receive emails for comments and emails # 1 - Bert Tartopoils, with email, should receive emails for comments and emails
p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'}) p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
# 2 - Carine Poilvache, with email, should never receive emails # 2 - Carine Poilvache, with email, should receive emails for emails
p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'}) p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'})
# 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message # 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message
p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'notification_email_send': 'all'}) p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'email': 'd@d', 'notification_email_send': 'all'})
# 4 - Attachments
# Subscribe Raoul, #1, #2 attach1_id = self.ir_attachment.create(cr, user_raoul.id, {
group_pigs.message_subscribe([self.partner_raoul_id, p_b_id, p_c_id]) 'name': 'Attach1', 'datas_fname': 'Attach1',
'datas': 'bWlncmF0aW9uIHRlc3Q=',
# Mail data 'res_model': 'mail.compose.message', 'res_id': 0})
attach2_id = self.ir_attachment.create(cr, user_raoul.id, {
'name': 'Attach2', 'datas_fname': 'Attach2',
'datas': 'bWlncmF0aW9uIHRlc3Q=',
'res_model': 'mail.compose.message', 'res_id': 0})
attach3_id = self.ir_attachment.create(cr, user_raoul.id, {
'name': 'Attach3', 'datas_fname': 'Attach3',
'datas': 'bWlncmF0aW9uIHRlc3Q=',
'res_model': 'mail.compose.message', 'res_id': 0})
# 5 - Mail data
_subject = 'Pigs' _subject = 'Pigs'
_mail_subject = 'Re: %s' % (group_pigs.name) _mail_subject = 'Re: %s' % (group_pigs.name)
_body1 = '<p>Pigs rules</p>' _body1 = '<p>Pigs rules</p>'
_mail_body1 = '<p>Pigs rules</p>\n<div><p>Raoul</p></div>\n' _body2 = '<html>Pigs rocks</html>'
_mail_bodyalt1 = 'Pigs rules\nRaoul\n' _attachments = [
_body2 = '<html>Pigs rules</html>' ('List1', 'My first attachment'),
_mail_body2 = '<div><p>Pigs rules</p></div>\n<div><p>Raoul</p></div>' ('List2', 'My second attachment')
_mail_bodyalt2 = 'Pigs rules\nRaoul' ]
_attachments = [('First', 'My first attachment'), ('Second', 'My second attachment')]
# ---------------------------------------- # --------------------------------------------------
# CASE1: post comment, body and subject specified # CASE1: post comment + partners + attachments
# ---------------------------------------- # --------------------------------------------------
# 1. Post a new comment on Pigs # Data: set alias_domain to see emails with alias
self.registry('ir.config_parameter').set_param(self.cr, self.uid, 'mail.catchall.domain', 'schlouby.fr')
# Do: subscribe Raoul
new_follower_ids = [self.partner_raoul_id]
group_pigs.message_subscribe(new_follower_ids)
# Test: group followers = Raoul + uid
group_fids = [follower.id for follower in group_pigs.message_follower_ids]
test_fids = new_follower_ids + [self.partner_admin_id]
self.assertEqual(set(test_fids), set(group_fids),
'message_subscribe: incorrect followers after subscribe')
# Do: Raoul message_post on Pigs
self._init_mock_build_email() self._init_mock_build_email()
msg1_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id, body=_body1, subject=_subject, type='comment', subtype='mt_comment') msg1_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id,
message1 = self.mail_message.browse(cr, uid, msg1_id) body=_body1, subject=_subject, partner_ids=[p_b_id, p_c_id],
attachment_ids=[attach1_id, attach2_id], attachments=_attachments,
type='comment', subtype='mt_comment')
msg = self.mail_message.browse(cr, uid, msg1_id)
msg_message_id = msg.message_id
msg_pids = [partner.id for partner in msg.notified_partner_ids]
msg_aids = [attach.id for attach in msg.attachment_ids]
sent_emails = self._build_email_kwargs_list sent_emails = self._build_email_kwargs_list
# Test: mail.mail notifications have been deleted
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg1_id)]), 'mail.mail notifications should have been auto-deleted!') # Test: mail_message: subject and body not modified
# Test: mail_message: subject is _subject, body is _body1 (no formatting done) self.assertEqual(_subject, msg.subject, 'message_post: mail.message subject incorrect')
self.assertEqual(message1.subject, _subject, 'mail.message subject incorrect') self.assertEqual(_body1, msg.body, 'message_post: mail.message body incorrect')
self.assertEqual(message1.body, _body1, 'mail.message body incorrect') # Test: mail_message: notified_partner_ids = group followers + partner_ids - author
# Test: sent_email: email send by server: correct subject, body, body_alternative
self.assertEqual(len(sent_emails), 2, 'sent_email number of sent emails incorrect')
for sent_email in sent_emails:
self.assertEqual(sent_email['subject'], _subject, 'sent_email subject incorrect')
self.assertTrue(sent_email['body'] in [_mail_body1 + '\nBert Tartopoils\n', _mail_body1 + '\nAdministrator\n'],
'sent_email body incorrect')
# the html2plaintext uses etree or beautiful soup, so the result may be slighly different
# depending if you have installed beautiful soup.
self.assertTrue(sent_email['body_alternative'] in [_mail_bodyalt1 + '\nBert Tartopoils\n', _mail_bodyalt1 + '\nAdministrator\n'],
'sent_email body_alternative is incorrect')
# Test: mail_message: notified_partner_ids = group followers
message_pids = set([partner.id for partner in message1.notified_partner_ids])
test_pids = set([self.partner_admin_id, p_b_id, p_c_id]) test_pids = set([self.partner_admin_id, p_b_id, p_c_id])
self.assertEqual(test_pids, message_pids, 'mail.message notified partners incorrect') self.assertEqual(test_pids, set(msg_pids), 'message_post: mail.message notified partners incorrect')
# Test: mail_message: attachments (4, attachment_ids + attachments)
test_aids = set([attach1_id, attach2_id])
msg_attach_names = set([attach.name for attach in msg.attachment_ids])
test_attach_names = set(['Attach1', 'Attach2', 'List1', 'List2'])
self.assertEqual(len(msg_aids), 4,
'message_post: mail.message wrong number of attachments')
self.assertEqual(msg_attach_names, test_attach_names,
'message_post: mail.message attachments incorrectly added')
self.assertTrue(test_aids.issubset(set(msg_aids)),
'message_post: mail.message attachments duplicated')
for attach in msg.attachment_ids:
self.assertEqual(attach.res_model, 'mail.group',
'message_post: mail.message attachments were not linked to the document')
self.assertEqual(attach.res_id, group_pigs.id,
'message_post: mail.message attachments were not linked to the document')
if 'List' in attach.name:
self.assertIn((attach.name, attach.datas.decode('base64')), _attachments,
'message_post: mail.message attachment name / data incorrect')
dl_attach = self.mail_message.download_attachment(cr, user_raoul.id, id_message=msg.id, attachment_id=attach.id)
self.assertIn((dl_attach['filename'], dl_attach['base64'].decode('base64')), _attachments,
'message_post: mail.message download_attachment is incorrect')
# Test: followers: same as before (author was already subscribed)
group_pigs.refresh()
group_fids = [follower.id for follower in group_pigs.message_follower_ids]
test_fids = new_follower_ids + [self.partner_admin_id]
self.assertEqual(set(test_fids), set(group_fids),
'message_post: wrong followers after posting')
# Test: mail_mail: notifications have been deleted
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg1_id)]),
'message_post: mail.mail notifications should have been auto-deleted!')
# Test: notifications emails: to a and b, c is email only, r is author
test_emailto = ['Administrator <a@a>', 'Bert Tartopoils <b@b>']
self.assertEqual(len(sent_emails), 2,
'message_post: notification emails wrong number of send emails')
self.assertEqual(set([m['email_to'][0] for m in sent_emails]), set(test_emailto),
'message_post: notification emails wrong recipients (email_to)')
for sent_email in sent_emails:
self.assertEqual(sent_email['email_from'], 'Raoul Grosbedon <raoul@schlouby.fr>',
'message_post: notification email wrong email_from: should use alias of sender')
self.assertEqual(len(sent_email['email_to']), 1,
'message_post: notification email sent to more than one email address instead of a precise partner')
self.assertIn(sent_email['email_to'][0], test_emailto,
'message_post: notification email email_to incorrect')
self.assertEqual(sent_email['reply_to'], 'Followers of Pigs <group+pigs@schlouby.fr>',
'message_post: notification email reply_to incorrect')
self.assertEqual(_subject, sent_email['subject'],
'message_post: notification email subject incorrect')
self.assertIn(_body1, sent_email['body'],
'message_post: notification email body incorrect')
self.assertIn(user_raoul.signature, sent_email['body'],
'message_post: notification email body should contain the sender signature')
self.assertIn('Pigs rules', sent_email['body_alternative'],
'message_post: notification email body alternative should contain the body')
self.assertNotIn('<p>', sent_email['body_alternative'],
'message_post: notification email body alternative still contains html')
self.assertIn(user_raoul.signature, sent_email['body_alternative'],
'message_post: notification email body alternative should contain the sender signature')
self.assertFalse(sent_email['references'],
'message_post: references should be False when sending a message that is not a reply')
# Test: notification linked to this message = group followers = notified_partner_ids # Test: notification linked to this message = group followers = notified_partner_ids
notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', msg1_id)]) notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', msg1_id)])
notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)]) notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)])
self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect') self.assertEqual(notif_pids, test_pids,
# Test: sent_email: email_to should contain b@b, not c@c (pref email), not a@a (writer) 'message_post: mail.message created mail.notification incorrect')
for sent_email in sent_emails:
self.assertTrue(set(sent_email['email_to']).issubset(set(['a@a', 'b@b'])), 'sent_email email_to is incorrect')
# ---------------------------------------- # --------------------------------------------------
# CASE2: post an email with attachments, parent_id, partner_ids, parent notification # CASE2: reply + parent_id + parent notification
# ---------------------------------------- # --------------------------------------------------
# 1. Post a new email comment on Pigs # Data: remove alias_domain to see emails with alias
param_ids = self.registry('ir.config_parameter').search(cr, uid, [('key', '=', 'mail.catchall.domain')])
self.registry('ir.config_parameter').unlink(cr, uid, param_ids)
# Do: Raoul message_post on Pigs
self._init_mock_build_email() self._init_mock_build_email()
msg2_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id, body=_body2, type='email', subtype='mt_comment', msg2_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id,
partner_ids=[p_d_id], parent_id=msg1_id, attachments=_attachments, body=_body2, type='email', subtype='mt_comment',
context={'mail_post_autofollow': True}) partner_ids=[p_d_id], parent_id=msg1_id, attachment_ids=[attach3_id],
message2 = self.mail_message.browse(cr, uid, msg2_id) context={'mail_post_autofollow': True})
msg = self.mail_message.browse(cr, uid, msg2_id)
msg_pids = [partner.id for partner in msg.notified_partner_ids]
msg_aids = [attach.id for attach in msg.attachment_ids]
sent_emails = self._build_email_kwargs_list sent_emails = self._build_email_kwargs_list
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg2_id)]), 'mail.mail notifications should have been auto-deleted!')
# Test: mail_message: subject is False, body is _body2 (no formatting done), parent_id is msg_id # Test: mail_message: subject is False, body, parent_id is msg_id
self.assertEqual(message2.subject, False, 'mail.message subject incorrect') self.assertEqual(msg.subject, False, 'message_post: mail.message subject incorrect')
self.assertEqual(message2.body, html_sanitize(_body2), 'mail.message body incorrect') self.assertEqual(msg.body, html_sanitize(_body2), 'message_post: mail.message body incorrect')
self.assertEqual(message2.parent_id.id, msg1_id, 'mail.message parent_id incorrect') self.assertEqual(msg.parent_id.id, msg1_id, 'message_post: mail.message parent_id incorrect')
# Test: sent_email: email send by server: correct automatic subject, body, body_alternative
self.assertEqual(len(sent_emails), 3, 'sent_email number of sent emails incorrect')
for sent_email in sent_emails:
self.assertEqual(sent_email['subject'], _mail_subject, 'sent_email subject incorrect')
self.assertIn(_mail_body2, sent_email['body'], 'sent_email body incorrect')
self.assertIn(_mail_bodyalt2, sent_email['body_alternative'], 'sent_email body_alternative incorrect')
# Test: mail_message: notified_partner_ids = group followers # Test: mail_message: notified_partner_ids = group followers
message_pids = set([partner.id for partner in message2.notified_partner_ids]) test_pids = [self.partner_admin_id, p_d_id]
test_pids = set([self.partner_admin_id, p_b_id, p_c_id, p_d_id]) self.assertEqual(set(test_pids), set(msg_pids), 'message_post: mail.message partners incorrect')
self.assertEqual(message_pids, test_pids, 'mail.message partners incorrect') # Test: mail_message: notifications linked to this message = group followers = notified_partner_ids
# Test: notifications linked to this message = group followers = notified_partner_ids
notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', msg2_id)]) notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', msg2_id)])
notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)]) notif_pids = [notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)]
self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect') self.assertEqual(set(test_pids), set(notif_pids), 'message_post: mail.message notification partners incorrect')
# Test: sent_email: email_to should contain b@b, c@c, not a@a (writer)
# Test: mail_mail: notifications deleted
self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg2_id)]), 'mail.mail notifications should have been auto-deleted!')
# Test: emails send by server (to a, b, c, d)
test_emailto = [u'Administrator <a@a>', u'Bert Tartopoils <b@b>', u'Carine Poilvache <c@c>', u'D\xe9d\xe9 Grosbedon <d@d>']
# self.assertEqual(len(sent_emails), 3, 'sent_email number of sent emails incorrect')
for sent_email in sent_emails: for sent_email in sent_emails:
self.assertTrue(set(sent_email['email_to']).issubset(set(['a@a', 'b@b', 'c@c'])), 'sent_email email_to incorrect') self.assertEqual(sent_email['email_from'], 'Raoul Grosbedon <r@r>',
# Test: attachments 'message_post: notification email wrong email_from: should use email of sender when no alias domain set')
for attach in message2.attachment_ids: self.assertEqual(len(sent_email['email_to']), 1,
self.assertEqual(attach.res_model, 'mail.group', 'mail.message attachment res_model incorrect') 'message_post: notification email sent to more than one email address instead of a precise partner')
self.assertEqual(attach.res_id, self.group_pigs_id, 'mail.message attachment res_id incorrect') self.assertIn(sent_email['email_to'][0], test_emailto,
self.assertIn((attach.name, attach.datas.decode('base64')), _attachments, 'message_post: notification email email_to incorrect')
'mail.message attachment name / data incorrect') self.assertEqual(sent_email['reply_to'], 'Followers of Pigs <r@r>',
# Test: download attachments 'message_post: notification email reply_to incorrect: should name Followers of Pigs, and have raoul email')
for attach in message2.attachment_ids: self.assertEqual(_mail_subject, sent_email['subject'],
dl_attach = self.mail_message.download_attachment(cr, user_raoul.id, id_message=message2.id, attachment_id=attach.id) 'message_post: notification email subject incorrect')
self.assertIn((dl_attach['filename'], dl_attach['base64'].decode('base64')), _attachments, 'mail.message download_attachment is incorrect') self.assertIn(html_sanitize(_body2), sent_email['body'],
'message_post: notification email does not contain the body')
self.assertIn(user_raoul.signature, sent_email['body'],
'message_post: notification email body should contain the sender signature')
self.assertIn('Pigs rocks', sent_email['body_alternative'],
'message_post: notification email body alternative should contain the body')
self.assertNotIn('<p>', sent_email['body_alternative'],
'message_post: notification email body alternative still contains html')
self.assertIn(user_raoul.signature, sent_email['body_alternative'],
'message_post: notification email body alternative should contain the sender signature')
self.assertIn(msg_message_id, sent_email['references'],
'message_post: notification email references lacks parent message message_id')
# Test: attachments + download
for attach in msg.attachment_ids:
self.assertEqual(attach.res_model, 'mail.group',
'message_post: mail.message attachment res_model incorrect')
self.assertEqual(attach.res_id, self.group_pigs_id,
'message_post: mail.message attachment res_id incorrect')
# 2. Dédé has been notified -> should also have been notified of the parent message # Test: Dédé has been notified -> should also have been notified of the parent message
message1.refresh() msg = self.mail_message.browse(cr, uid, msg1_id)
message_pids = set([partner.id for partner in message1.notified_partner_ids]) msg_pids = set([partner.id for partner in msg.notified_partner_ids])
test_pids = set([self.partner_admin_id, p_b_id, p_c_id, p_d_id]) test_pids = set([self.partner_admin_id, p_b_id, p_c_id, p_d_id])
self.assertEqual(test_pids, message_pids, 'mail.message parent notification not created') self.assertEqual(test_pids, msg_pids, 'message_post: mail.message parent notification not created')
# 3. Reply to the last message, check that its parent will be the first message # Do: reply to last message
msg3_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id, body='Test', parent_id=msg2_id) msg3_id = self.mail_group.message_post(cr, user_raoul.id, self.group_pigs_id, body='Test', parent_id=msg2_id)
message = self.mail_message.browse(cr, uid, msg3_id) msg = self.mail_message.browse(cr, uid, msg3_id)
self.assertEqual(message.parent_id.id, msg1_id, 'message_post did not flatten the thread structure') # Test: check that its parent will be the first message
self.assertEqual(msg.parent_id.id, msg1_id, 'message_post did not flatten the thread structure')
def test_25_message_compose_wizard(self): def test_25_message_compose_wizard(self):
""" Tests designed for the mail.compose.message wizard. """ """ Tests designed for the mail.compose.message wizard. """
cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs cr, uid, user_raoul, group_pigs = self.cr, self.uid, self.user_raoul, self.group_pigs
mail_compose = self.registry('mail.compose.message') mail_compose = self.registry('mail.compose.message')
self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'})
group_bird_id = self.mail_group.create(cr, uid, {'name': 'Bird', 'description': 'Bird resistance'}, {'mail_create_nolog': True})
group_bird = self.mail_group.browse(cr, uid, group_bird_id)
# Mail data # --------------------------------------------------
# Data creation
# --------------------------------------------------
# 0 - Update existing users-partners
self.res_users.write(cr, uid, [uid], {'email': 'a@a'})
self.res_users.write(cr, uid, [self.user_raoul_id], {'email': 'r@r'})
# 1 - Bert Tartopoils, with email, should receive emails for comments and emails
p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
# 2 - Carine Poilvache, with email, should receive emails for emails
p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'})
# 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message
p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'email': 'd@d', 'notification_email_send': 'all'})
# 4 - Create a Bird mail.group, that will be used to test mass mailing
group_bird_id = self.mail_group.create(cr, uid,
{
'name': 'Bird',
'description': 'Bird resistance',
}, context={'mail_create_nolog': True})
group_bird = self.mail_group.browse(cr, uid, group_bird_id)
# 5 - Mail data
_subject = 'Pigs' _subject = 'Pigs'
_body = 'Pigs <b>rule</b>' _body = 'Pigs <b>rule</b>'
_reply_subject = 'Re: Pigs' _reply_subject = 'Re: %s' % _subject
_attachments = [ _attachments = [
{'name': 'First', 'datas_fname': 'first.txt', 'datas': 'My first attachment'.encode('base64')}, {'name': 'First', 'datas_fname': 'first.txt', 'datas': 'My first attachment'.encode('base64')},
{'name': 'Second', 'datas_fname': 'second.txt', 'datas': 'My second attachment'.encode('base64')} {'name': 'Second', 'datas_fname': 'second.txt', 'datas': 'My second attachment'.encode('base64')}
] ]
_attachments_test = [('first.txt', 'My first attachment'), ('second.txt', 'My second attachment')] _attachments_test = [('first.txt', 'My first attachment'), ('second.txt', 'My second attachment')]
# 6 - Subscribe Bert to Pigs
# 1 - Bert Tartopoils, with email, should receive emails for comments and emails
p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
# 2 - Carine Poilvache, with email, should never receive emails
p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'})
# 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message
p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'notification_email_send': 'all'})
# Subscribe #1
group_pigs.message_subscribe([p_b_id]) group_pigs.message_subscribe([p_b_id])
# ---------------------------------------- # --------------------------------------------------
# CASE1: comment on group_pigs # CASE1: wizard + partners + context keys
# ---------------------------------------- # --------------------------------------------------
# 1. Comment group_pigs with body_text and subject # Do: Raoul wizard-composes on Pigs with auto-follow for partners, not for author
compose_id = mail_compose.create(cr, uid, compose_id = mail_compose.create(cr, user_raoul.id,
{'subject': _subject, 'body': _body, 'partner_ids': [(4, p_c_id), (4, p_d_id)]}, {
{'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id, 'subject': _subject,
'default_content_subtype': 'plaintext'}) 'body': _body,
'partner_ids': [(4, p_c_id), (4, p_d_id)],
}, context={
'default_composition_mode': 'comment',
'default_model': 'mail.group',
'default_res_id': self.group_pigs_id,
})
compose = mail_compose.browse(cr, uid, compose_id) compose = mail_compose.browse(cr, uid, compose_id)
# Test: mail.compose.message: composition_mode, model, res_id
self.assertEqual(compose.composition_mode, 'comment', 'mail.compose.message incorrect composition_mode')
self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model')
self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id')
# 2. Post the comment, get created message # Test: mail.compose.message: composition_mode, model, res_id
mail_compose.send_mail(cr, uid, [compose_id], {'mail_post_autofollow': True}) self.assertEqual(compose.composition_mode, 'comment', 'compose wizard: mail.compose.message incorrect composition_mode')
self.assertEqual(compose.model, 'mail.group', 'compose wizard: mail.compose.message incorrect model')
self.assertEqual(compose.res_id, self.group_pigs_id, 'compose wizard: mail.compose.message incorrect res_id')
# Do: Post the comment
mail_compose.send_mail(cr, user_raoul.id, [compose_id], {'mail_post_autofollow': True, 'mail_create_nosubscribe': True})
group_pigs.refresh() group_pigs.refresh()
message = group_pigs.message_ids[0] message = group_pigs.message_ids[0]
# Test: mail.message: subject, body inside pre
self.assertEqual(message.subject, _subject, 'mail.message incorrect subject') # Test: mail.group: followers (c and d added by auto follow key; raoul not added by nosubscribe key)
self.assertEqual(message.body, '<p>%s</p>' % _body, 'mail.message incorrect body') pigs_pids = [p.id for p in group_pigs.message_follower_ids]
# Test: mail.message: notified_partner_ids = entries in mail.notification: group_pigs fans (a, b) + mail.compose.message partner_ids (c, d) test_pids = [self.partner_admin_id, p_b_id, p_c_id, p_d_id]
self.assertEqual(set(pigs_pids), set(test_pids),
'compose wizard: mail_post_autofollow and mail_create_nosubscribe context keys not correctly taken into account')
# Test: mail.message: subject, body inside p
self.assertEqual(message.subject, _subject, 'compose wizard: mail.message incorrect subject')
self.assertEqual(message.body, '<p>%s</p>' % _body, 'compose wizard: mail.message incorrect body')
# Test: mail.message: notified_partner_ids = admin + bert (followers) + c + d (recipients)
msg_pids = [partner.id for partner in message.notified_partner_ids] msg_pids = [partner.id for partner in message.notified_partner_ids]
test_pids = [p_b_id, p_c_id, p_d_id] test_pids = [self.partner_admin_id, p_b_id, p_c_id, p_d_id]
notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)]) self.assertEqual(set(msg_pids), set(test_pids),
self.assertEqual(len(notif_ids), 3, 'mail.message: too much notifications created') 'compose wizard: mail.message notified_partner_ids incorrect')
self.assertEqual(set(msg_pids), set(test_pids), 'mail.message notified_partner_ids incorrect')
# ---------------------------------------- # --------------------------------------------------
# CASE2: reply to last comment with attachments # CASE2: reply + attachments
# ---------------------------------------- # --------------------------------------------------
# 1. Update last comment subject, reply with attachments # Do: Reply with attachments
message.write({'subject': _subject}) compose_id = mail_compose.create(cr, user_raoul.id,
compose_id = mail_compose.create(cr, uid, {
{'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])]}, 'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])]
{'default_composition_mode': 'reply', 'default_model': 'mail.thread', 'default_res_id': self.group_pigs_id, 'default_parent_id': message.id}) }, context={
'default_composition_mode': 'reply',
'default_model': 'mail.thread',
'default_res_id': self.group_pigs_id,
'default_parent_id': message.id
})
compose = mail_compose.browse(cr, uid, compose_id) compose = mail_compose.browse(cr, uid, compose_id)
# Test: model, res_id, parent_id
self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model') # Test: mail.compose.message: model, res_id, parent_id
self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id') self.assertEqual(compose.model, 'mail.group', 'compose wizard: mail.compose.message incorrect model')
self.assertEqual(compose.parent_id.id, message.id, 'mail.compose.message incorrect parent_id') self.assertEqual(compose.res_id, self.group_pigs_id, 'compose wizard: mail.compose.message incorrect res_id')
# Test: mail.message: subject as Re:.., body in html, parent_id self.assertEqual(compose.parent_id.id, message.id, 'compose wizard: mail.compose.message incorrect parent_id')
self.assertEqual(compose.subject, _reply_subject, 'mail.message incorrect subject')
# self.assertIn('Administrator wrote:<blockquote><pre>Pigs rules</pre></blockquote>', compose.body, 'mail.message body is incorrect') # Test: mail.compose.message: subject as Re:.., body, parent_id
self.assertEqual(compose.parent_id and compose.parent_id.id, message.id, 'mail.message parent_id incorrect') self.assertEqual(compose.subject, _reply_subject, 'compose wizard: mail.compose.message incorrect subject')
# Test: mail.message: attachments self.assertFalse(compose.body, 'compose wizard: mail.compose.message body should not contain parent message body')
self.assertEqual(compose.parent_id and compose.parent_id.id, message.id, 'compose wizard: mail.compose.message parent_id incorrect')
# Test: mail.compose.message: attachments
for attach in compose.attachment_ids: for attach in compose.attachment_ids:
self.assertIn((attach.datas_fname, attach.datas.decode('base64')), _attachments_test, 'mail.message attachment name / data incorrect') self.assertIn((attach.datas_fname, attach.datas.decode('base64')), _attachments_test,
'compose wizard: mail.message attachment name / data incorrect')
# ---------------------------------------- # --------------------------------------------------
# CASE3: mass_mail on Pigs and Bird # CASE3: mass_mail on Pigs and Bird
# ---------------------------------------- # --------------------------------------------------
# 1. mass_mail on pigs and bird # Do: Compose in mass_mail_mode on pigs and bird
compose_id = mail_compose.create(cr, uid, compose_id = mail_compose.create(cr, user_raoul.id,
{'subject': _subject, 'body': '${object.description}', 'partner_ids': [(4, p_c_id), (4, p_d_id)]}, {
{'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', 'default_res_id': False, 'subject': _subject,
'active_ids': [self.group_pigs_id, group_bird_id]}) 'body': '${object.description}',
'partner_ids': [(4, p_c_id), (4, p_d_id)],
}, context={
'default_composition_mode': 'mass_mail',
'default_model': 'mail.group',
'default_res_id': False,
'active_ids': [self.group_pigs_id, group_bird_id],
})
compose = mail_compose.browse(cr, uid, compose_id) compose = mail_compose.browse(cr, uid, compose_id)
# 2. Post the comment, get created message for each group # D: Post the comment, get created message for each group
mail_compose.send_mail(cr, uid, [compose_id], mail_compose.send_mail(cr, user_raoul.id, [compose_id], context={
context={'default_res_id': -1, 'active_ids': [self.group_pigs_id, group_bird_id]}) 'default_res_id': -1,
'active_ids': [self.group_pigs_id, group_bird_id]
})
group_pigs.refresh() group_pigs.refresh()
group_bird.refresh() group_bird.refresh()
message1 = group_pigs.message_ids[0] message1 = group_pigs.message_ids[0]
message2 = group_bird.message_ids[0] message2 = group_bird.message_ids[0]
# Test: Pigs and Bird did receive their message # Test: Pigs and Bird did receive their message
test_msg_ids = self.mail_message.search(cr, uid, [], limit=2) test_msg_ids = self.mail_message.search(cr, uid, [], limit=2)
self.assertIn(message1.id, test_msg_ids, 'Pigs did not receive its mass mailing message') self.assertIn(message1.id, test_msg_ids, 'compose wizard: Pigs did not receive its mass mailing message')
self.assertIn(message2.id, test_msg_ids, 'Bird did not receive its mass mailing message') self.assertIn(message2.id, test_msg_ids, 'compose wizard: Bird did not receive its mass mailing message')
# Test: mail.message: subject, body, subtype, notified partners (nobody + specific recipients) # Test: mail.message: subject, body, subtype, notified partners (nobody + specific recipients)
self.assertEqual(message1.subject, _subject, self.assertEqual(message1.subject, _subject,
'message_post: mail.message in mass mail subject incorrect') 'compose wizard: message_post: mail.message in mass mail subject incorrect')
self.assertEqual(message1.body, '<p>%s</p>' % group_pigs.description, self.assertEqual(message1.body, '<p>%s</p>' % group_pigs.description,
'message_post: mail.message in mass mail body incorrect') 'compose wizard: message_post: mail.message in mass mail body incorrect')
self.assertEqual(set([p.id for p in message1.notified_partner_ids]), set([p_c_id, p_d_id]), self.assertEqual(set([p.id for p in message1.notified_partner_ids]), set([p_c_id, p_d_id]),
'message_post: mail.message in mass mail incorrect notified partners') 'compose wizard: message_post: mail.message in mass mail incorrect notified partners')
self.assertEqual(message2.subject, _subject, self.assertEqual(message2.subject, _subject,
'message_post: mail.message in mass mail subject incorrect') 'compose wizard: message_post: mail.message in mass mail subject incorrect')
self.assertEqual(message2.body, '<p>%s</p>' % group_bird.description, self.assertEqual(message2.body, '<p>%s</p>' % group_bird.description,
'message_post: mail.message in mass mail body incorrect') 'compose wizard: message_post: mail.message in mass mail body incorrect')
self.assertEqual(set([p.id for p in message2.notified_partner_ids]), set([p_c_id, p_d_id]), self.assertEqual(set([p.id for p in message2.notified_partner_ids]), set([p_c_id, p_d_id]),
'message_post: mail.message in mass mail incorrect notified partners') 'compose wizard: message_post: mail.message in mass mail incorrect notified partners')
# Test: mail.group followers: author not added as follower in mass mail mode
pigs_pids = [p.id for p in group_pigs.message_follower_ids]
test_pids = [self.partner_admin_id, p_b_id, p_c_id, p_d_id]
self.assertEqual(set(pigs_pids), set(test_pids),
'compose wizard: mail_post_autofollow and mail_create_nosubscribe context keys not correctly taken into account')
bird_pids = [p.id for p in group_bird.message_follower_ids]
test_pids = [self.partner_admin_id]
self.assertEqual(set(bird_pids), set(test_pids),
'compose wizard: mail_post_autofollow and mail_create_nosubscribe context keys not correctly taken into account')
def test_30_needaction(self): def test_30_needaction(self):
""" Tests for mail.message needaction. """ """ Tests for mail.message needaction. """

View File

@ -0,0 +1,282 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.addons.mail.tests.test_mail_base import TestMailBase
MAIL_TEMPLATE = """Return-Path: <whatever-2a840@postmaster.twitter.com>
To: {to}
Received: by mail1.openerp.com (Postfix, from userid 10002)
id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
From: {email_from}
Subject: {subject}
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_Part_4200734_24778174.1344608186754"
Date: Fri, 10 Aug 2012 14:16:26 +0000
Message-ID: {msg_id}
{extra}
------=_Part_4200734_24778174.1344608186754
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Please call me as soon as possible this afternoon!
--
Sylvie
------=_Part_4200734_24778174.1344608186754
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>=20
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8" />
</head>=20
<body style=3D"margin: 0; padding: 0; background: #ffffff;-webkit-text-size-adjust: 100%;">=20
<p>Please call me as soon as possible this afternoon!</p>
<p>--<br/>
Sylvie
<p>
</body>
</html>
------=_Part_4200734_24778174.1344608186754--
"""
MAIL_TEMPLATE_PLAINTEXT = """Return-Path: <whatever-2a840@postmaster.twitter.com>
To: {to}
Received: by mail1.openerp.com (Postfix, from userid 10002)
id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
From: Sylvie Lelitre <sylvie.lelitre@agrolait.com>
Subject: {subject}
MIME-Version: 1.0
Content-Type: text/plain
Date: Fri, 10 Aug 2012 14:16:26 +0000
Message-ID: {msg_id}
{extra}
Please call me as soon as possible this afternoon!
--
Sylvie
"""
class TestMailgateway(TestMailBase):
def test_00_message_process(self):
""" Testing incoming emails processing. """
cr, uid, user_raoul = self.cr, self.uid, self.user_raoul
def format_and_process(template, to='groups@example.com, other@gmail.com', subject='Frogs',
extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>',
model=None):
self.assertEqual(self.mail_group.search(cr, uid, [('name', '=', subject)]), [])
mail = template.format(to=to, subject=subject, extra=extra, email_from=email_from, msg_id=msg_id)
self.mail_thread.message_process(cr, uid, model, mail)
return self.mail_group.search(cr, uid, [('name', '=', subject)])
# --------------------------------------------------
# Data creation
# --------------------------------------------------
# groups@.. will cause the creation of new mail groups
self.mail_group_model_id = self.ir_model.search(cr, uid, [('model', '=', 'mail.group')])[0]
alias_id = self.mail_alias.create(cr, uid, {
'alias_name': 'groups',
'alias_user_id': False,
'alias_model_id': self.mail_group_model_id})
# --------------------------------------------------
# Test1: new record creation
# --------------------------------------------------
# Do: incoming mail from an unknown partner on an alias creates a new mail_group "frogs"
self._init_mock_build_email()
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com')
sent_emails = self._build_email_kwargs_list
# Test: one group created by mailgateway administrator
self.assertTrue(len(frog_groups) == 1)
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
# Test: one message that is the incoming email
self.assertEqual(len(frog_group.message_ids), 1,
'message_process: newly created group should have the incoming email in message_ids')
msg = frog_group.message_ids[0]
self.assertEqual('Frogs', msg.subject,
'message_process: newly created group should have the incoming email as first message')
self.assertIn('Please call me as soon as possible this afternoon!', msg.body,
'message_process: newly created group should have the incoming email as first message')
self.assertEqual('email', msg.type,
'message_process: newly created group should have an email as first message')
self.assertEqual('Discussions', msg.subtype_id.name,
'message_process: newly created group should not have a log first message but an email')
# Test: message: unknown email address -> message has email_from, not author_id
self.assertFalse(msg.author_id,
'message_process: message on created group should not have an author_id')
self.assertIn('test.sylvie.lelitre@agrolait.com', msg.email_from,
'message_process: message on created group should have an email_from')
# Test: followers: nobody
self.assertEqual(len(frog_group.message_follower_ids), 0, 'message_process: newly create group should not have any follower')
# Test: sent emails: no-one
self.assertEqual(len(sent_emails), 0,
'message_process: should create emails without any follower added')
# Data: unlink group
frog_group.unlink()
# Do: incoming email from a known partner on an alias with known recipients, alias is owned by user that can create a group
self.mail_alias.write(cr, uid, [alias_id], {'alias_user_id': self.user_raoul_id})
p1id = self.res_partner.create(cr, uid, {'name': 'Sylvie Lelitre', 'email': 'test.sylvie.lelitre@agrolait.com'})
p2id = self.res_partner.create(cr, uid, {'name': 'Other Poilvache', 'email': 'other@gmail.com'})
self._init_mock_build_email()
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com')
sent_emails = self._build_email_kwargs_list
# Test: one group created by raoul
self.assertTrue(len(frog_groups) == 1)
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
# Test: one message that is the incoming email
self.assertEqual(len(frog_group.message_ids), 1,
'message_process: newly created group should have the incoming email in message_ids')
msg = frog_group.message_ids[0]
# Test: message: unknown email address -> message has email_from, not author_id
self.assertEqual(p1id, msg.author_id.id,
'message_process: message on created group should have Sylvie as author_id')
self.assertIn('Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', msg.email_from,
'message_process: message on created group should have have an email_from')
# Test: author (not recipient and and not raoul (as alias owner)) added as follower
frog_follower_ids = set([p.id for p in frog_group.message_follower_ids])
self.assertEqual(frog_follower_ids, set([p1id]),
'message_process: newly created group should have 1 follower (author, not creator, not recipients)')
# Test: sent emails: no-one, no bounce effet
self.assertEqual(len(sent_emails), 0,
'message_process: should not bounce incoming emails')
# Data: unlink group
frog_group.unlink()
# Do: incoming email from a known partner that is also an user that can create a mail.group
self.res_users.create(cr, uid, {'partner_id': p1id, 'login': 'sylvie', 'groups_id': [(6, 0, [self.group_employee_id])]})
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com')
# Test: one group created by Sylvie
self.assertTrue(len(frog_groups) == 1)
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
# Test: one message that is the incoming email
self.assertEqual(len(frog_group.message_ids), 1,
'message_process: newly created group should have the incoming email in message_ids')
# Test: author (and not recipient) added as follower
frog_follower_ids = set([p.id for p in frog_group.message_follower_ids])
self.assertEqual(frog_follower_ids, set([p1id]),
'message_process: newly created group should have 1 follower (author, not creator, not recipients)')
# Test: sent emails: no-one, no bounce effet
self.assertEqual(len(sent_emails), 0,
'message_process: should not bounce incoming emails')
# --------------------------------------------------
# Test2: discussion update
# --------------------------------------------------
# Do: even with a wrong destination, a reply should end up in the correct thread
frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other@gmail.com',
to='erroneous@example.com>', subject='Re: news',
extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
# Test: no group 'Re: news' created, still only 1 Frogs group
self.assertEqual(len(frog_groups), 0,
'message_process: reply on Frogs should not have created a new group with new subject')
frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
self.assertEqual(len(frog_groups), 1,
'message_process: reply on Frogs should not have created a duplicate group with old subject')
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
# Test: one new message
self.assertTrue(len(frog_group.message_ids) == 2, 'message_process: group should contain 2 messages after reply')
# Test: author (and not recipient) added as follower
frog_follower_ids = set([p.id for p in frog_group.message_follower_ids])
self.assertEqual(frog_follower_ids, set([p1id, p2id]),
'message_process: after reply, group should have 2 followers')
# --------------------------------------------------
# Test3: misc gateway features
# --------------------------------------------------
# Do: incoming email with model that does not accepts incoming emails must raise
self.assertRaises(AssertionError,
format_and_process,
MAIL_TEMPLATE, to='noone@example.com', subject='spam', extra='', model='res.country')
# Do: incoming email without model and without alias must raise
self.assertRaises(AssertionError,
format_and_process,
MAIL_TEMPLATE, to='noone@example.com', subject='spam', extra='')
# Do: incoming email with model that accepting incoming emails as fallback
frog_groups = format_and_process(MAIL_TEMPLATE, to='noone@example.com', subject='Spammy', extra='', model='mail.group')
self.assertEqual(len(frog_groups), 1,
'message_process: erroneous email but with a fallback model should have created a new mail.group')
# Do: incoming email in plaintext should be stored as html
frog_groups = format_and_process(MAIL_TEMPLATE_PLAINTEXT, to='groups@example.com', subject='Frogs Return', extra='', msg_id='<deadcafe.1337@smtp.agrolait.com>')
# Test: one group created with one message
self.assertTrue(len(frog_groups) == 1)
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
msg = frog_group.message_ids[0]
# Test: plain text content should be wrapped and stored as html
self.assertEqual(msg.body, '<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>',
'message_process: plaintext incoming email incorrectly parsed')
def test_10_thread_parent_resolution(self):
""" Testing parent/child relationships are correctly established when processing incoming mails """
cr, uid = self.cr, self.uid
def format(template, to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: 1',
extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>'):
return template.format(to=to, subject=subject, extra=extra, email_from=email_from, msg_id=msg_id)
group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
msg1 = group_pigs.message_post(body='My Body', subject='1')
msg2 = group_pigs.message_post(body='My Body', subject='2')
msg1, msg2 = self.mail_message.browse(cr, uid, [msg1, msg2])
self.assertTrue(msg1.message_id, "message_process: new message should have a proper message_id")
# Reply to msg1, make sure the reply is properly attached using the various reply identification mechanisms
# 0. Direct alias match
reply_msg1 = format(MAIL_TEMPLATE, to='Pretty Pigs <group+pigs@example.com>', extra='In-Reply-To: %s' % msg1.message_id)
self.mail_group.message_process(cr, uid, None, reply_msg1)
# 1. In-Reply-To header
reply_msg2 = format(MAIL_TEMPLATE, to='erroneous@example.com', extra='In-Reply-To: %s' % msg1.message_id)
self.mail_group.message_process(cr, uid, None, reply_msg2)
# 2. References header
reply_msg3 = format(MAIL_TEMPLATE, to='erroneous@example.com', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id)
self.mail_group.message_process(cr, uid, None, reply_msg3)
# 3. Subject contains [<ID>] + model passed to message+process -> only attached to group, but not to mail (not in msg1.child_ids)
reply_msg4 = format(MAIL_TEMPLATE, to='erroneous@example.com', extra='', subject='Re: [%s] 1' % self.group_pigs_id)
self.mail_group.message_process(cr, uid, 'mail.group', reply_msg4)
group_pigs.refresh()
msg1.refresh()
self.assertEqual(6, len(group_pigs.message_ids), 'message_process: group should contain 6 messages')
self.assertEqual(3, len(msg1.child_ids), 'message_process: msg1 should have 3 children now')
def test_20_private_discussion(self):
""" Testing private discussion between partners. """
pass

View File

@ -23,6 +23,7 @@ import base64
import re import re
from openerp import tools from openerp import tools
from openerp import SUPERUSER_ID
from openerp.osv import osv from openerp.osv import osv
from openerp.osv import fields from openerp.osv import fields
from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.safe_eval import safe_eval as eval
@ -126,6 +127,29 @@ class mail_compose_message(osv.TransientModel):
'partner_ids': lambda self, cr, uid, ctx={}: [], 'partner_ids': lambda self, cr, uid, ctx={}: [],
} }
def check_access_rule(self, cr, uid, ids, operation, context=None):
""" Access rules of mail.compose.message:
- create: if
- model, no res_id, I create a message in mass mail mode
- then: fall back on mail.message acces rules
"""
if isinstance(ids, (int, long)):
ids = [ids]
# Author condition (CREATE (mass_mail))
if operation == 'create' and uid != SUPERUSER_ID:
# read mail_compose_message.ids to have their values
message_values = {}
cr.execute('SELECT DISTINCT id, model, res_id FROM "%s" WHERE id = ANY (%%s) AND res_id = 0' % self._table, (ids,))
for id, rmod, rid in cr.fetchall():
message_values[id] = {'model': rmod, 'res_id': rid}
# remove from the set to check the ids that mail_compose_message accepts
author_ids = [mid for mid, message in message_values.iteritems()
if message.get('model') and not message.get('res_id')]
ids = list(set(ids) - set(author_ids))
return super(mail_compose_message, self).check_access_rule(cr, uid, ids, operation, context=context)
def _notify(self, cr, uid, newid, context=None): def _notify(self, cr, uid, newid, context=None):
""" Override specific notify method of mail.message, because we do """ Override specific notify method of mail.message, because we do
not want that feature in the wizard. """ not want that feature in the wizard. """
@ -218,8 +242,11 @@ class mail_compose_message(osv.TransientModel):
post_values.update(email_dict) post_values.update(email_dict)
# post the message # post the message
subtype = 'mail.mt_comment' subtype = 'mail.mt_comment'
if is_log or mass_mail_mode: if is_log: # log a note: subtype is False
subtype = False subtype = False
elif mass_mail_mode: # mass mail: is a log pushed to recipients, author not added
subtype = False
context = dict(context, mail_create_nosubscribe=True) # add context key to avoid subscribing the author
msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values) msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values)
# mass_mailing: notify specific partners, because subtype was False, and no-one was notified # mass_mailing: notify specific partners, because subtype was False, and no-one was notified
if mass_mail_mode and post_values['partner_ids']: if mass_mail_mode and post_values['partner_ids']: