1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Business Applications
5 # Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
24 from openerp.addons.mail.tests import test_mail_mockup
25 from openerp.tools.mail import html_sanitize
27 MAIL_TEMPLATE = """Return-Path: <whatever-2a840@postmaster.twitter.com>
29 Received: by mail1.openerp.com (Postfix, from userid 10002)
30 id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
31 From: Sylvie Lelitre <sylvie.lelitre@agrolait.com>
34 Content-Type: multipart/alternative;
35 boundary="----=_Part_4200734_24778174.1344608186754"
36 Date: Fri, 10 Aug 2012 14:16:26 +0000
37 Message-ID: <1198923581.41972151344608186760.JavaMail@agrolait.com>
39 ------=_Part_4200734_24778174.1344608186754
40 Content-Type: text/plain; charset=utf-8
41 Content-Transfer-Encoding: quoted-printable
43 Please call me as soon as possible this afternoon!
47 ------=_Part_4200734_24778174.1344608186754
48 Content-Type: text/html; charset=utf-8
49 Content-Transfer-Encoding: quoted-printable
51 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
54 <meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8" />
56 <body style=3D"margin: 0; padding: 0; background: #ffffff;-webkit-text-size-adjust: 100%;">=20
58 <p>Please call me as soon as possible this afternoon!</p>
65 ------=_Part_4200734_24778174.1344608186754--
68 MAIL_TEMPLATE_PLAINTEXT = """Return-Path: <whatever-2a840@postmaster.twitter.com>
70 Received: by mail1.openerp.com (Postfix, from userid 10002)
71 id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
72 From: Sylvie Lelitre <sylvie.lelitre@agrolait.com>
75 Content-Type: text/plain
76 Date: Fri, 10 Aug 2012 14:16:26 +0000
80 Please call me as soon as possible this afternoon!
87 class test_mail(test_mail_mockup.TestMailMockups):
89 def _mock_send_get_mail_body(self, *args, **kwargs):
90 # def _send_get_mail_body(self, cr, uid, mail, partner=None, context=None)
91 body = tools.append_content_to_html(args[2].body_html, kwargs.get('partner').name if kwargs.get('partner') else 'No specific partner')
95 super(test_mail, self).setUp()
96 cr, uid = self.cr, self.uid
97 self.ir_model = self.registry('ir.model')
98 self.mail_alias = self.registry('mail.alias')
99 self.mail_thread = self.registry('mail.thread')
100 self.mail_group = self.registry('mail.group')
101 self.mail_mail = self.registry('mail.mail')
102 self.mail_message = self.registry('mail.message')
103 self.mail_notification = self.registry('mail.notification')
104 self.mail_followers = self.registry('mail.followers')
105 self.mail_message_subtype = self.registry('mail.message.subtype')
106 self.res_users = self.registry('res.users')
107 self.res_partner = self.registry('res.partner')
109 # Find Employee group
110 group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
111 group_employee_id = group_employee_ref and group_employee_ref[1] or False
113 self.user_raoul_id = self.res_users.create(cr, uid,
114 {'name': 'Raoul Grosbedon', 'email': 'raoul@raoul.fr', 'login': 'raoul', 'groups_id': [(6, 0, [group_employee_id])]})
115 self.user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id)
116 self.user_admin = self.res_users.browse(cr, uid, uid)
118 # Mock send_get_mail_body to test its functionality without other addons override
119 self._send_get_mail_body = self.registry('mail.mail').send_get_mail_body
120 self.registry('mail.mail').send_get_mail_body = self._mock_send_get_mail_body
122 # groups@.. will cause the creation of new mail groups
123 self.mail_group_model_id = self.ir_model.search(cr, uid, [('model', '=', 'mail.group')])[0]
124 self.mail_alias.create(cr, uid, {'alias_name': 'groups',
125 'alias_model_id': self.mail_group_model_id})
126 # create a 'pigs' group that will be used through the various tests
127 self.group_pigs_id = self.mail_group.create(cr, uid,
128 {'name': 'Pigs', 'description': 'Fans of Pigs, unite !'})
129 self.group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
133 self.registry('mail.mail').send_get_mail_body = self._send_get_mail_body
134 super(test_mail, self).tearDown()
136 def test_00_message_process(self):
137 """ Testing incoming emails processing. """
138 cr, uid, user_raoul = self.cr, self.uid, self.user_raoul
139 # Incoming mail creates a new mail_group "frogs"
140 self.assertEqual(self.mail_group.search(cr, uid, [('name', '=', 'frogs')]), [])
141 mail_frogs = MAIL_TEMPLATE.format(to='groups@example.com, other@gmail.com', subject='frogs', extra='')
142 self.mail_thread.message_process(cr, uid, None, mail_frogs)
143 frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'frogs')])
144 self.assertTrue(len(frog_groups) == 1)
146 # Previously-created group can be emailed now - it should have an implicit alias group+frogs@...
147 frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
148 group_messages = frog_group.message_ids
149 self.assertTrue(len(group_messages) == 1, 'New group should only have the original message')
150 mail_frog_news = MAIL_TEMPLATE.format(to='Friendly Frogs <group+frogs@example.com>', subject='news', extra='')
151 self.mail_thread.message_process(cr, uid, None, mail_frog_news)
153 self.assertTrue(len(frog_group.message_ids) == 2, 'Group should contain 2 messages now')
155 # Even with a wrong destination, a reply should end up in the correct thread
156 mail_reply = MAIL_TEMPLATE.format(to='erroneous@example.com>', subject='Re: news',
157 extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
158 self.mail_thread.message_process(cr, uid, None, mail_reply)
160 self.assertTrue(len(frog_group.message_ids) == 3, 'Group should contain 3 messages now')
162 # No model passed and no matching alias must raise
163 mail_spam = MAIL_TEMPLATE.format(to='noone@example.com', subject='spam', extra='')
164 self.assertRaises(Exception,
165 self.mail_thread.message_process,
166 cr, uid, None, mail_spam)
168 # plain text content should be wrapped and stored as html
169 test_msg_id = '<deadcafe.1337@smtp.agrolait.com>'
170 mail_text = MAIL_TEMPLATE_PLAINTEXT.format(to='groups@example.com', subject='frogs', extra='', msg_id=test_msg_id)
171 self.mail_thread.message_process(cr, uid, None, mail_text)
172 new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0])
173 self.assertEqual(new_mail.body, '\n<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>\n',
174 'plaintext mail incorrectly parsed')
176 # Do: post a new message, with a known partner
177 test_msg_id = '<deadcafe.1337-2@smtp.agrolait.com>'
178 TEMPLATE_MOD = MAIL_TEMPLATE_PLAINTEXT.replace('Sylvie Lelitre <sylvie.lelitre@agrolait.com>', user_raoul.email)
179 mail_new = TEMPLATE_MOD.format(to='Friendly Frogs <group+frogs@example.com>', subject='extra news', extra='', msg_id=test_msg_id)
180 self.mail_thread.message_process(cr, uid, None, mail_new)
181 new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0])
182 # Test: author_id set, not email_from
183 self.assertEqual(new_mail.author_id, user_raoul.partner_id, 'message process wrong author found')
184 self.assertFalse(new_mail.email_from, 'message process should not set the email_from when an author is found')
186 # Do: post a new message, with a unknown partner
187 test_msg_id = '<deadcafe.1337-3@smtp.agrolait.com>'
188 TEMPLATE_MOD = MAIL_TEMPLATE_PLAINTEXT.replace('Sylvie Lelitre <sylvie.lelitre@agrolait.com>', '_abcd_')
189 mail_new = TEMPLATE_MOD.format(to='Friendly Frogs <group+frogs@example.com>', subject='super news', extra='', msg_id=test_msg_id)
190 self.mail_thread.message_process(cr, uid, None, mail_new)
191 new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0])
192 # Test: author_id set, not email_from
193 self.assertFalse(new_mail.author_id, 'message process shnould not have found a partner for _abcd_ email address')
194 self.assertIn('_abcd_', new_mail.email_from, 'message process should set en email_from when not finding a partner_id')
196 def test_10_followers_function_field(self):
197 """ Tests designed for the many2many function field 'follower_ids'.
198 We will test to perform writes using the many2many commands 0, 3, 4,
200 cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
202 # Data: create partner Bert Poilu
203 partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Poilu'})
204 # Data: create 'disturbing' values in mail.followers: same res_id, other res_model; same res_model, other res_id
205 group_dummy_id = self.mail_group.create(cr, uid,
206 {'name': 'Dummy group'})
207 self.mail_followers.create(cr, uid,
208 {'res_model': 'mail.thread', 'res_id': self.group_pigs_id, 'partner_id': partner_bert_id})
209 self.mail_followers.create(cr, uid,
210 {'res_model': 'mail.group', 'res_id': group_dummy_id, 'partner_id': partner_bert_id})
212 # Pigs just created: should be only Admin as follower
213 follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
214 self.assertEqual(follower_ids, set([user_admin.partner_id.id]), 'Admin should be the only Pigs fan')
216 # Subscribe Bert through a '4' command
217 group_pigs.write({'message_follower_ids': [(4, partner_bert_id)]})
219 follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
220 self.assertEqual(follower_ids, set([partner_bert_id, user_admin.partner_id.id]), 'Bert and Admin should be the only Pigs fans')
222 # Unsubscribe Bert through a '3' command
223 group_pigs.write({'message_follower_ids': [(3, partner_bert_id)]})
225 follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
226 self.assertEqual(follower_ids, set([user_admin.partner_id.id]), 'Admin should be the only Pigs fan')
228 # Set followers through a '6' command
229 group_pigs.write({'message_follower_ids': [(6, 0, [partner_bert_id])]})
231 follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
232 self.assertEqual(follower_ids, set([partner_bert_id]), 'Bert should be the only Pigs fan')
234 # Add a follower created on the fly through a '0' command
235 group_pigs.write({'message_follower_ids': [(0, 0, {'name': 'Patrick Fiori'})]})
236 partner_patrick_id = self.res_partner.search(cr, uid, [('name', '=', 'Patrick Fiori')])[0]
238 follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
239 self.assertEqual(follower_ids, set([partner_bert_id, partner_patrick_id]), 'Bert and Patrick should be the only Pigs fans')
241 # Finally, unlink through a '5' command
242 group_pigs.write({'message_follower_ids': [(5, 0)]})
244 follower_ids = set([follower.id for follower in group_pigs.message_follower_ids])
245 self.assertFalse(follower_ids, 'Pigs group should not have fans anymore')
247 # Test dummy data has not been altered
248 fol_obj_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.thread'), ('res_id', '=', self.group_pigs_id)])
249 follower_ids = set([follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)])
250 self.assertEqual(follower_ids, set([partner_bert_id]), 'Bert should be the follower of dummy mail.thread data')
251 fol_obj_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.group'), ('res_id', '=', group_dummy_id)])
252 follower_ids = set([follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)])
253 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')
255 def test_11_message_followers_and_subtypes(self):
256 """ Tests designed for the subscriber API as well as message subtypes """
257 cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
259 user_raoul_id = self.user_raoul_id
260 user_raoul = self.res_users.browse(cr, uid, user_raoul_id)
261 # Data: message subtypes
262 self.mail_message_subtype.create(cr, uid, {'name': 'mt_mg_def', 'default': True, 'res_model': 'mail.group'})
263 self.mail_message_subtype.create(cr, uid, {'name': 'mt_other_def', 'default': True, 'res_model': 'crm.lead'})
264 self.mail_message_subtype.create(cr, uid, {'name': 'mt_all_def', 'default': True, 'res_model': False})
265 mt_mg_nodef = self.mail_message_subtype.create(cr, uid, {'name': 'mt_mg_nodef', 'default': False, 'res_model': 'mail.group'})
266 mt_all_nodef = self.mail_message_subtype.create(cr, uid, {'name': 'mt_all_nodef', 'default': False, 'res_model': False})
267 default_group_subtypes = self.mail_message_subtype.search(cr, uid, [('default', '=', True), '|', ('res_model', '=', 'mail.group'), ('res_model', '=', False)])
269 # ----------------------------------------
270 # CASE1: test subscriptions with subtypes
271 # ----------------------------------------
273 # Do: Subscribe Raoul three times (niak niak) through message_subscribe_users
274 group_pigs.message_subscribe_users([user_raoul_id, user_raoul_id])
275 group_pigs.message_subscribe_users([user_raoul_id])
277 # Test: 2 followers (Admin and Raoul)
278 follower_ids = [follower.id for follower in group_pigs.message_follower_ids]
279 self.assertEqual(set(follower_ids), set([user_raoul.partner_id.id, user_admin.partner_id.id]), 'Admin and Raoul should be the only 2 Pigs fans')
280 # Test: Raoul follows default subtypes
281 fol_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id), ('partner_id', '=', user_raoul.partner_id.id)])
282 fol_obj = self.mail_followers.browse(cr, uid, fol_ids)[0]
283 fol_subtype_ids = set([subtype.id for subtype in fol_obj.subtype_ids])
284 self.assertEqual(set(fol_subtype_ids), set(default_group_subtypes), 'subscription subtypes are incorrect')
286 # Do: Unsubscribe Raoul twice through message_unsubscribe_users
287 group_pigs.message_unsubscribe_users([user_raoul_id, user_raoul_id])
289 # Test: 1 follower (Admin)
290 follower_ids = [follower.id for follower in group_pigs.message_follower_ids]
291 self.assertEqual(follower_ids, [user_admin.partner_id.id], 'Admin must be the only Pigs fan')
293 # Do: subscribe Admin with subtype_ids
294 group_pigs.message_subscribe_users([uid], [mt_mg_nodef, mt_all_nodef])
295 fol_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id), ('partner_id', '=', user_admin.partner_id.id)])
296 fol_obj = self.mail_followers.browse(cr, uid, fol_ids)[0]
297 fol_subtype_ids = set([subtype.id for subtype in fol_obj.subtype_ids])
298 self.assertEqual(set(fol_subtype_ids), set([mt_mg_nodef, mt_all_nodef]), 'subscription subtypes are incorrect')
300 # ----------------------------------------
301 # CASE2: test mail_thread fields
302 # ----------------------------------------
304 subtype_data = group_pigs._get_subscription_data(None, None)[group_pigs.id]['message_subtype_data']
305 self.assertEqual(set(subtype_data.keys()), set(['Discussions', 'mt_mg_def', 'mt_all_def', 'mt_mg_nodef', 'mt_all_nodef']), 'mail.group available subtypes incorrect')
306 self.assertFalse(subtype_data['Discussions']['followed'], 'Admin should not follow Discussions in pigs')
307 self.assertTrue(subtype_data['mt_mg_nodef']['followed'], 'Admin should follow mt_mg_nodef in pigs')
308 self.assertTrue(subtype_data['mt_all_nodef']['followed'], 'Admin should follow mt_all_nodef in pigs')
310 def test_20_message_quote_context(self):
311 """ Tests designed for message_post. """
312 cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
314 msg1_id = self.mail_message.create(cr, uid, {'body': 'Thread header about Zap Brannigan', 'subject': 'My subject'})
315 msg2_id = self.mail_message.create(cr, uid, {'body': 'First answer, should not be displayed', 'subject': 'Re: My subject', 'parent_id': msg1_id})
316 msg3_id = self.mail_message.create(cr, uid, {'body': 'Second answer', 'subject': 'Re: My subject', 'parent_id': msg1_id})
317 msg4_id = self.mail_message.create(cr, uid, {'body': 'Third answer', 'subject': 'Re: My subject', 'parent_id': msg1_id})
318 msg_new_id = self.mail_message.create(cr, uid, {'body': 'My answer I am propagating', 'subject': 'Re: My subject', 'parent_id': msg1_id})
320 result = self.mail_message.message_quote_context(cr, uid, msg_new_id, limit=3)
321 self.assertIn('Thread header about Zap Brannigan', result, 'Thread header content should be in quote.')
322 self.assertIn('Second answer', result, 'Answer should be in quote.')
323 self.assertIn('Third answer', result, 'Answer should be in quote.')
324 self.assertIn('expandable', result, 'Expandable should be present.')
325 self.assertNotIn('First answer, should not be displayed', result, 'Old answer should not be in quote.')
326 self.assertNotIn('My answer I am propagating', result, 'Thread header content should be in quote.')
328 def test_21_message_post(self):
329 """ Tests designed for message_post. """
330 cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
331 self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'})
332 # 1 - Bert Tartopoils, with email, should receive emails for comments and emails
333 p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
334 # 2 - Carine Poilvache, with email, should never receive emails
335 p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'})
336 # 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message
337 p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'notification_email_send': 'all'})
340 group_pigs.message_subscribe([p_b_id, p_c_id])
344 _mail_subject = '%s posted on %s' % (user_admin.name, group_pigs.name)
345 _body1 = 'Pigs rules'
346 _mail_body1 = 'Pigs rules\n<pre>Admin</pre>\n'
347 _mail_bodyalt1 = 'Pigs rules\nAdmin'
348 _body2 = '<html>Pigs rules</html>'
349 _mail_body2 = html_sanitize('<html>Pigs rules\n<pre>Admin</pre>\n</html>')
350 _mail_bodyalt2 = 'Pigs rules\nAdmin'
351 _attachments = [('First', 'My first attachment'), ('Second', 'My second attachment')]
353 # ----------------------------------------
354 # CASE1: post comment, body and subject specified
355 # ----------------------------------------
357 # 1. Post a new comment on Pigs
358 self._init_mock_build_email()
359 msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body1, subject=_subject, type='comment', subtype='mt_comment')
360 message = self.mail_message.browse(cr, uid, msg_id)
361 sent_emails = self._build_email_kwargs_list
362 # Test: mail.mail notifications have been deleted
363 self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id)]), 'mail.mail notifications should have been auto-deleted!')
364 # Test: mail_message: subject is _subject, body is _body1 (no formatting done)
365 self.assertEqual(message.subject, _subject, 'mail.message subject incorrect')
366 self.assertEqual(message.body, _body1, 'mail.message body incorrect')
367 # Test: sent_email: email send by server: correct subject, body, body_alternative
368 for sent_email in sent_emails:
369 self.assertEqual(sent_email['subject'], _subject, 'sent_email subject incorrect')
370 self.assertEqual(sent_email['body'], _mail_body1 + '\n<pre>Bert Tartopoils</pre>\n', 'sent_email body incorrect')
371 # the html2plaintext uses etree or beautiful soup, so the result may be slighly different
372 # depending if you have installed beautiful soup.
373 self.assertIn(sent_email['body_alternative'], _mail_bodyalt1 + '\nBert Tartopoils\n', 'sent_email body_alternative is incorrect')
374 # Test: mail_message: notified_partner_ids = group followers
375 message_pids = set([partner.id for partner in message.notified_partner_ids])
376 test_pids = set([p_b_id, p_c_id])
377 self.assertEqual(test_pids, message_pids, 'mail.message partners incorrect')
378 # Test: notification linked to this message = group followers = notified_partner_ids
379 notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)])
380 notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)])
381 self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect')
382 # Test: sent_email: email_to should contain b@b, not c@c (pref email), not a@a (writer)
383 for sent_email in sent_emails:
384 self.assertEqual(sent_email['email_to'], ['b@b'], 'sent_email email_to is incorrect')
386 # ----------------------------------------
387 # CASE2: post an email with attachments, parent_id, partner_ids
388 # ----------------------------------------
390 # 1. Post a new email comment on Pigs
391 self._init_mock_build_email()
392 msg_id2 = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body2, type='email', subtype='mt_comment',
393 partner_ids=[(6, 0, [p_d_id])], parent_id=msg_id, attachments=_attachments)
394 message = self.mail_message.browse(cr, uid, msg_id2)
395 sent_emails = self._build_email_kwargs_list
396 self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id2)]), 'mail.mail notifications should have been auto-deleted!')
397 # Test: mail_message: subject is False, body is _body2 (no formatting done), parent_id is msg_id
398 self.assertEqual(message.subject, False, 'mail.message subject incorrect')
399 self.assertEqual(message.body, html_sanitize(_body2), 'mail.message body incorrect')
400 self.assertEqual(message.parent_id.id, msg_id, 'mail.message parent_id incorrect')
401 # Test: sent_email: email send by server: correct automatic subject, body, body_alternative
402 self.assertEqual(len(sent_emails), 2, 'sent_email number of sent emails incorrect')
403 for sent_email in sent_emails:
404 self.assertEqual(sent_email['subject'], _mail_subject, 'sent_email subject incorrect')
405 self.assertIn(_mail_body2, sent_email['body'], 'sent_email body incorrect')
406 self.assertIn(_mail_bodyalt2, sent_email['body_alternative'], 'sent_email body_alternative incorrect')
407 # Test: mail_message: notified_partner_ids = group followers
408 message_pids = set([partner.id for partner in message.notified_partner_ids])
409 test_pids = set([p_b_id, p_c_id, p_d_id])
410 self.assertEqual(message_pids, test_pids, 'mail.message partners incorrect')
411 # Test: notifications linked to this message = group followers = notified_partner_ids
412 notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)])
413 notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)])
414 self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect')
415 # Test: sent_email: email_to should contain b@b, c@c, not a@a (writer)
416 for sent_email in sent_emails:
417 self.assertTrue(set(sent_email['email_to']).issubset(set(['b@b', 'c@c'])), 'sent_email email_to incorrect')
419 for attach in message.attachment_ids:
420 self.assertEqual(attach.res_model, 'mail.group', 'mail.message attachment res_model incorrect')
421 self.assertEqual(attach.res_id, self.group_pigs_id, 'mail.message attachment res_id incorrect')
422 self.assertIn((attach.name, attach.datas.decode('base64')), _attachments,
423 'mail.message attachment name / data incorrect')
425 # 3. Reply to the last message, check that its parent will be the first message
426 msg_id3 = self.mail_group.message_post(cr, uid, self.group_pigs_id, body='Test', parent_id=msg_id2)
427 message = self.mail_message.browse(cr, uid, msg_id3)
428 self.assertEqual(message.parent_id.id, msg_id, 'message_post did not flatten the thread structure')
430 def test_25_message_compose_wizard(self):
431 """ Tests designed for the mail.compose.message wizard. """
432 cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
433 mail_compose = self.registry('mail.compose.message')
434 self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'})
435 group_bird_id = self.mail_group.create(cr, uid, {'name': 'Bird', 'description': 'Bird resistance'})
436 group_bird = self.mail_group.browse(cr, uid, group_bird_id)
440 _body_text = 'Pigs rules'
441 _msg_reply = 'Re: Pigs'
442 _msg_body = '<pre>Pigs rules</pre>'
444 {'name': 'First', 'datas_fname': 'first.txt', 'datas': 'My first attachment'.encode('base64')},
445 {'name': 'Second', 'datas_fname': 'second.txt', 'datas': 'My second attachment'.encode('base64')}
447 _attachments_test = [('first.txt', 'My first attachment'), ('second.txt', 'My second attachment')]
449 # 1 - Bert Tartopoils, with email, should receive emails for comments and emails
450 p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'})
451 # 2 - Carine Poilvache, with email, should never receive emails
452 p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'})
453 # 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message
454 p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'notification_email_send': 'all'})
457 group_pigs.message_subscribe([p_b_id])
459 # ----------------------------------------
460 # CASE1: comment on group_pigs
461 # ----------------------------------------
463 # 1. Comment group_pigs with body_text and subject
464 compose_id = mail_compose.create(cr, uid,
465 {'subject': _subject, 'body_text': _body_text, 'partner_ids': [(4, p_c_id), (4, p_d_id)]},
466 {'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id})
467 compose = mail_compose.browse(cr, uid, compose_id)
468 # Test: mail.compose.message: composition_mode, model, res_id
469 self.assertEqual(compose.composition_mode, 'comment', 'mail.compose.message incorrect composition_mode')
470 self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model')
471 self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id')
473 # 2. Post the comment, get created message
474 mail_compose.send_mail(cr, uid, [compose_id])
476 message = group_pigs.message_ids[0]
477 # Test: mail.message: subject, body inside pre
478 self.assertEqual(message.subject, False, 'mail.message incorrect subject')
479 self.assertEqual(message.body, _msg_body, 'mail.message incorrect body')
480 # Test: mail.message: notified_partner_ids = entries in mail.notification: group_pigs fans (a, b) + mail.compose.message partner_ids (c, d)
481 msg_pids = [partner.id for partner in message.notified_partner_ids]
482 test_pids = [p_b_id, p_c_id, p_d_id]
483 notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)])
484 self.assertEqual(len(notif_ids), 3, 'mail.message: too much notifications created')
485 self.assertEqual(set(msg_pids), set(test_pids), 'mail.message notified_partner_ids incorrect')
487 # ----------------------------------------
488 # CASE2: reply to last comment with attachments
489 # ----------------------------------------
491 # 1. Update last comment subject, reply with attachments
492 message.write({'subject': _subject})
493 compose_id = mail_compose.create(cr, uid,
494 {'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])]},
495 {'default_composition_mode': 'reply', 'default_model': 'mail.thread', 'default_res_id': self.group_pigs_id, 'default_parent_id': message.id})
496 compose = mail_compose.browse(cr, uid, compose_id)
497 # Test: model, res_id, parent_id, content_subtype
498 self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model')
499 self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id')
500 self.assertEqual(compose.parent_id.id, message.id, 'mail.compose.message incorrect parent_id')
501 self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message incorrect content_subtype')
502 # Test: mail.message: subject as Re:.., body in html, parent_id
503 self.assertEqual(compose.subject, _msg_reply, 'mail.message incorrect subject')
504 # self.assertIn('Administrator wrote:<blockquote><pre>Pigs rules</pre></blockquote>', compose.body, 'mail.message body is incorrect')
505 self.assertEqual(compose.parent_id and compose.parent_id.id, message.id, 'mail.message parent_id incorrect')
506 # Test: mail.message: attachments
507 for attach in compose.attachment_ids:
508 self.assertEqual(attach.res_model, 'mail.group', 'mail.message attachment res_model incorrect')
509 self.assertEqual(attach.res_id, self.group_pigs_id, 'mail.message attachment res_id incorrect')
510 self.assertIn((attach.datas_fname, attach.datas.decode('base64')), _attachments_test, 'mail.message attachment name / data incorrect')
512 # ----------------------------------------
513 # CASE3: mass_mail on Pigs and Bird
514 # ----------------------------------------
516 # 1. mass_mail on pigs and bird
517 compose_id = mail_compose.create(cr, uid,
518 {'subject': _subject, 'body': '${object.description}'},
519 {'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', 'default_res_id': False,
520 'active_ids': [self.group_pigs_id, group_bird_id]})
521 compose = mail_compose.browse(cr, uid, compose_id)
522 # Test: content_subtype is html
523 self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message content_subtype incorrect')
525 # 2. Post the comment, get created message for each group
526 mail_compose.send_mail(cr, uid, [compose_id],
527 context={'default_res_id': -1, 'active_ids': [self.group_pigs_id, group_bird_id]})
530 message1 = group_pigs.message_ids[0]
531 message2 = group_bird.message_ids[0]
532 # Test: Pigs and Bird did receive their message
533 test_msg_ids = self.mail_message.search(cr, uid, [], limit=2)
534 self.assertIn(message1.id, test_msg_ids, 'Pigs did not receive its mass mailing message')
535 self.assertIn(message2.id, test_msg_ids, 'Bird did not receive its mass mailing message')
536 # Test: mail.message: subject, body
537 self.assertEqual(message1.subject, _subject, 'mail.message subject incorrect')
538 self.assertEqual(message1.body, group_pigs.description, 'mail.message body incorrect')
539 self.assertEqual(message2.subject, _subject, 'mail.message subject incorrect')
540 self.assertEqual(message2.body, group_bird.description, 'mail.message body incorrect')
542 def test_30_message_read(self):
543 """ Tests for message_read and expandables. """
544 cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
545 pigs_domain = [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)]
547 # Data: create a discussion in Pigs (2 messages, one with 2 and one with 3 answers)
548 msg_id0 = self.group_pigs.message_post(body='0', subtype='mt_comment')
549 msg_id1 = self.group_pigs.message_post(body='1', subtype='mt_comment')
550 msg_id2 = self.group_pigs.message_post(body='2', subtype='mt_comment')
551 msg_id3 = self.group_pigs.message_post(body='1-1', subtype='mt_comment', parent_id=msg_id1)
552 msg_id4 = self.group_pigs.message_post(body='2-1', subtype='mt_comment', parent_id=msg_id2)
553 msg_id5 = self.group_pigs.message_post(body='1-2', subtype='mt_comment', parent_id=msg_id1)
554 msg_id6 = self.group_pigs.message_post(body='2-2', subtype='mt_comment', parent_id=msg_id2)
555 msg_id7 = self.group_pigs.message_post(body='1-1-1', subtype='mt_comment', parent_id=msg_id3)
556 msg_id8 = self.group_pigs.message_post(body='2-1-1', subtype='mt_comment', parent_id=msg_id4)
557 msg_id9 = self.group_pigs.message_post(body='1-1-1', subtype='mt_comment', parent_id=msg_id3)
558 msg_id10 = self.group_pigs.message_post(body='2-1-1', subtype='mt_comment', parent_id=msg_id4)
559 msg_ids = [msg_id0, msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8, msg_id9, msg_id10]
561 # Test: read some specific ids
562 read_msg_list = self.mail_message.message_read(cr, uid, ids=msg_ids[2:4], domain=[('body', 'like', 'dummy')])
563 read_msg_ids = [msg.get('id') for msg in read_msg_list]
564 self.assertEqual(msg_ids[2:4], read_msg_ids, 'message_read with direct ids should read only the requested ids')
566 # Test: read messages of Pigs through a domain, being thread or not threaded
567 read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=200)
568 read_msg_ids = [msg.get('id') for msg in read_msg_list]
569 self.assertEqual(msg_ids, read_msg_ids, 'message_read flat with domain on Pigs should equal all messages of Pigs')
570 read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=200, thread_level=1)
571 read_msg_ids = [msg.get('id') for msg in read_msg_list]
572 self.assertEqual(msg_ids, read_msg_ids, 'message_read threaded with domain on Pigs should equal all messages of Pigs')
574 # ----------------------------------------
575 # CASE1: message_read with domain, threaded
576 # We simulate an entire flow, using the expandables to test them
577 # ----------------------------------------
579 # Do: read last message, threaded
580 read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=1, thread_level=1)
581 read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
582 # Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
583 self.assertEqual(len(read_msg_list), 4, 'message_read on last Pigs message should return 2 messages and 2 expandables')
584 self.assertEqual(set([msg_id2, msg_id10]), set(read_msg_ids), 'message_read on the last Pigs message should also get its parent')
585 self.assertEqual(read_msg_list[1].get('parent_id'), read_msg_list[0].get('id'), 'message_read should set the ancestor to the thread header')
586 # Data: get expandables
587 new_threads_exp, new_msg_exp = None, None
588 for msg in read_msg_list:
589 if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
590 new_threads_exp = msg
591 elif msg.get('type') == 'expandable':
594 # Do: fetch new messages in first thread, domain from expandable
595 self.assertIsNotNone(new_msg_exp, 'message_read on last Pigs message should have returned a new messages expandable')
596 domain = new_msg_exp.get('domain', [])
597 # Test: expandable, conditions in domain
598 self.assertIn(('id', 'child_of', msg_id2), domain, 'new messages expandable domain should contain a child_of condition')
599 self.assertIn(('id', '>=', msg_id4), domain, 'new messages expandable domain should contain an id greater than condition')
600 self.assertIn(('id', '<=', msg_id8), domain, 'new messages expandable domain should contain an id less than condition')
601 self.assertEqual(new_msg_exp.get('parent_id'), msg_id2, 'new messages expandable should have ancestor_id set to the thread header')
602 # Do: message_read with domain, thread_level=0, parent_id=msg_id2 (should be imposed by JS)
603 read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=200, thread_level=0, parent_id=msg_id2)
604 read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
605 # Test: other message in thread have been fetch
606 self.assertEqual(set([msg_id4, msg_id6, msg_id8]), set(read_msg_ids), 'message_read in Pigs thread should return all the previous messages')
608 # Do: fetch a new thread, domain from expandable
609 self.assertIsNotNone(new_threads_exp, 'message_read on last Pigs message should have returned a new threads expandable')
610 domain = new_threads_exp.get('domain', [])
611 # Test: expandable, conditions in domain
612 for condition in pigs_domain:
613 self.assertIn(condition, domain, 'new threads expandable domain should contain the message_read domain parameter')
614 self.assertFalse(new_threads_exp.get('parent_id'), 'new threads expandable should not have an ancestor_id')
615 # Do: message_read with domain, thread_level=1 (should be imposed by JS)
616 read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=1, thread_level=1)
617 read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
618 # Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
619 self.assertEqual(len(read_msg_list), 4, 'message_read on Pigs should return 2 messages and 2 expandables')
620 self.assertEqual(set([msg_id1, msg_id9]), set(read_msg_ids), 'message_read on a Pigs message should also get its parent')
621 self.assertEqual(read_msg_list[1].get('parent_id'), read_msg_list[0].get('id'), 'message_read should set the ancestor to the thread header')
622 # Data: get expandables
623 new_threads_exp, new_msg_exp = None, None
624 for msg in read_msg_list:
625 if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
626 new_threads_exp = msg
627 elif msg.get('type') == 'expandable':
630 # Do: fetch new messages in second thread, domain from expandable
631 self.assertIsNotNone(new_msg_exp, 'message_read on Pigs message should have returned a new messages expandable')
632 domain = new_msg_exp.get('domain', [])
633 # Test: expandable, conditions in domain
634 self.assertIn(('id', 'child_of', msg_id1), domain, 'new messages expandable domain should contain a child_of condition')
635 self.assertIn(('id', '>=', msg_id3), domain, 'new messages expandable domain should contain an id greater than condition')
636 self.assertIn(('id', '<=', msg_id7), domain, 'new messages expandable domain should contain an id less than condition')
637 self.assertEqual(new_msg_exp.get('parent_id'), msg_id1, 'new messages expandable should have ancestor_id set to the thread header')
638 # Do: message_read with domain, thread_level=0, parent_id=msg_id1 (should be imposed by JS)
639 read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=200, thread_level=0, parent_id=msg_id1)
640 read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
641 # Test: other message in thread have been fetch
642 self.assertEqual(set([msg_id3, msg_id5, msg_id7]), set(read_msg_ids), 'message_read on the last Pigs message should also get its parent')
644 # Test: fetch a new thread, domain from expandable
645 self.assertIsNotNone(new_threads_exp, 'message_read should have returned a new threads expandable')
646 domain = new_threads_exp.get('domain', [])
647 # Test: expandable, conditions in domain
648 for condition in pigs_domain:
649 self.assertIn(condition, domain, 'general expandable domain should contain the message_read domain parameter')
650 # Do: message_read with domain, thread_level=1 (should be imposed by JS)
651 read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=1, thread_level=1)
652 read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
653 # Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
654 self.assertEqual(len(read_msg_list), 1, 'message_read on Pigs should return 1 message because everything else has been fetched')
655 self.assertEqual([msg_id0], read_msg_ids, 'message_read after 2 More should return only 1 last message')
657 # ----------------------------------------
658 # CASE2: message_read with domain, flat
659 # ----------------------------------------
661 # Do: read 2 lasts message, flat
662 read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=2, thread_level=0)
663 read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
664 # Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is not set, 1 expandable
665 self.assertEqual(len(read_msg_list), 3, 'message_read on last Pigs message should return 2 messages and 1 expandable')
666 self.assertEqual(set([msg_id9, msg_id10]), set(read_msg_ids), 'message_read flat on Pigs last messages should only return those messages')
667 self.assertFalse(read_msg_list[0].get('parent_id'), 'message_read flat should set the ancestor as False')
668 self.assertFalse(read_msg_list[1].get('parent_id'), 'message_read flat should set the ancestor as False')
669 # Data: get expandables
670 new_threads_exp, new_msg_exp = None, None
671 for msg in read_msg_list:
672 if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
673 new_threads_exp = msg
675 # Do: fetch new messages, domain from expandable
676 self.assertIsNotNone(new_threads_exp, 'message_read flat on the 2 last Pigs messages should have returns a new threads expandable')
677 domain = new_threads_exp.get('domain', [])
678 # Test: expandable, conditions in domain
679 for condition in pigs_domain:
680 self.assertIn(condition, domain, 'new threads expandable domain should contain the message_read domain parameter')
681 # Do: message_read with domain, thread_level=0 (should be imposed by JS)
682 read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=20, thread_level=0)
683 read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
684 # Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
685 self.assertEqual(len(read_msg_list), 9, 'message_read on Pigs should return 9 messages and 0 expandable')
686 self.assertEqual([msg_id0, msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8], read_msg_ids,
687 'message_read, More on flat, should return all remaning messages')
689 def test_40_needaction(self):
690 """ Tests for mail.message needaction. """
691 cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
692 user_raoul = self.res_users.browse(cr, uid, self.user_raoul_id)
693 group_pigs_demo = self.mail_group.browse(cr, self.user_raoul_id, self.group_pigs_id)
694 na_admin_base = self.mail_message._needaction_count(cr, uid, domain=[])
695 na_demo_base = self.mail_message._needaction_count(cr, user_raoul.id, domain=[])
697 # Test: number of unread notification = needaction on mail.message
698 notif_ids = self.mail_notification.search(cr, uid, [
699 ('partner_id', '=', user_admin.partner_id.id),
702 na_count = self.mail_message._needaction_count(cr, uid, domain=[])
703 self.assertEqual(len(notif_ids), na_count, 'unread notifications count does not match needaction count')
705 # Do: post 2 message on group_pigs as admin, 3 messages as demo user
706 for dummy in range(2):
707 group_pigs.message_post(body='My Body', subtype='mt_comment')
708 for dummy in range(3):
709 group_pigs_demo.message_post(body='My Demo Body', subtype='mt_comment')
711 # Test: admin has 3 new notifications (from demo), and 3 new needaction
712 notif_ids = self.mail_notification.search(cr, uid, [
713 ('partner_id', '=', user_admin.partner_id.id),
716 self.assertEqual(len(notif_ids), na_admin_base + 3, 'Admin should have 3 new unread notifications')
717 na_admin = self.mail_message._needaction_count(cr, uid, domain=[])
718 na_admin_group = self.mail_message._needaction_count(cr, uid, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)])
719 self.assertEqual(na_admin, na_admin_base + 3, 'Admin should have 3 new needaction')
720 self.assertEqual(na_admin_group, 3, 'Admin should have 3 needaction related to Pigs')
721 # Test: demo has 0 new notifications (not a follower, not receiving its own messages), and 0 new needaction
722 notif_ids = self.mail_notification.search(cr, uid, [
723 ('partner_id', '=', user_raoul.partner_id.id),
726 self.assertEqual(len(notif_ids), na_demo_base + 0, 'Demo should have 0 new unread notifications')
727 na_demo = self.mail_message._needaction_count(cr, user_raoul.id, domain=[])
728 na_demo_group = self.mail_message._needaction_count(cr, user_raoul.id, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)])
729 self.assertEqual(na_demo, na_demo_base + 0, 'Demo should have 0 new needaction')
730 self.assertEqual(na_demo_group, 0, 'Demo should have 0 needaction related to Pigs')
732 def test_50_thread_parent_resolution(self):
733 """Verify parent/child relationships are correctly established when processing incoming mails"""
734 cr, uid = self.cr, self.uid
735 group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
736 msg1 = group_pigs.message_post(body='My Body', subject='1')
737 msg2 = group_pigs.message_post(body='My Body', subject='2')
738 msg1, msg2 = self.mail_message.browse(cr, uid, [msg1, msg2])
739 self.assertTrue(msg1.message_id, "New message should have a proper message_id")
741 # Reply to msg1, make sure the reply is properly attached using the various reply identification mechanisms
742 # 1. In-Reply-To header
743 reply_msg = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: 1',
744 extra='In-Reply-To: %s' % msg1.message_id)
745 self.mail_group.message_process(cr, uid, None, reply_msg)
747 # 2. References header
748 reply_msg2 = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: Re: 1',
749 extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id)
750 self.mail_group.message_process(cr, uid, None, reply_msg2)
752 # 3. Subject contains [<ID>] + model passed to message+process -> only attached to group, not to mail
753 reply_msg3 = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com',
754 extra='', subject='Re: [%s] 1' % self.group_pigs_id)
755 self.mail_group.message_process(cr, uid, 'mail.group', reply_msg3)
759 self.assertEqual(5, len(group_pigs.message_ids), 'group should contain 5 messages')
760 # TDE note: python test + debug because of the random error we see with the next assert
761 if len(msg1.child_ids) != 2:
762 msg_ids = self.mail_message.search(cr, uid, [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], limit=10)
763 for new_msg in self.mail_message.browse(cr, uid, msg_ids):
764 print new_msg.subject, '(id', new_msg.id, ')', 'parent_id:', new_msg.parent_id
765 print '\tchild_ids', [child.id for child in new_msg.child_ids]
766 self.assertEqual(2, len(msg1.child_ids), 'msg1 should have 2 children now')
768 def test_60_message_vote(self):
769 """ Test designed for the vote/unvote feature. """
770 cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs
771 # Data: post a message on Pigs
772 msg_id = group_pigs.message_post(body='My Body', subject='1')
773 msg = self.mail_message.browse(cr, uid, msg_id)
775 # Do: Admin vote for msg
776 self.mail_message.vote_toggle(cr, uid, [msg.id])
778 # Test: msg has Admin as voter
779 self.assertEqual(set(msg.vote_user_ids), set([user_admin]), 'mail_message vote: after voting, Admin should be in the voter')
780 # Do: Bert vote for msg
781 self.mail_message.vote_toggle(cr, user_raoul.id, [msg.id])
783 # Test: msg has Admin and Bert as voters
784 self.assertEqual(set(msg.vote_user_ids), set([user_admin, user_raoul]), 'mail_message vote: after voting, Admin and Bert should be in the voters')
785 # Do: Admin unvote for msg
786 self.mail_message.vote_toggle(cr, uid, [msg.id])
788 # Test: msg has Bert as voter
789 self.assertEqual(set(msg.vote_user_ids), set([user_raoul]), 'mail_message vote: after unvoting, Bert should be in the voter')
791 def test_70_message_favorite(self):
792 """ Tests for favorites. """
793 cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs
794 # Data: post a message on Pigs
795 msg_id = group_pigs.message_post(body='My Body', subject='1')
796 msg = self.mail_message.browse(cr, uid, msg_id)
798 # Do: Admin stars msg
799 self.mail_message.favorite_toggle(cr, uid, [msg.id])
801 # Test: msg starred by Admin
802 self.assertEqual(set(msg.favorite_user_ids), set([user_admin]), 'mail_message favorite: after starring, Admin should be in favorite_user_ids')
804 self.mail_message.favorite_toggle(cr, user_raoul.id, [msg.id])
806 # Test: msg starred by Admin and Raoul
807 self.assertEqual(set(msg.favorite_user_ids), set([user_admin, user_raoul]), 'mail_message favorite: after starring, Admin and Raoul should be in favorite_user_ids')
808 # Do: Admin unvote for msg
809 self.mail_message.favorite_toggle(cr, uid, [msg.id])
811 # Test: msg starred by Raoul
812 self.assertEqual(set(msg.favorite_user_ids), set([user_raoul]), 'mail_message favorite: after unstarring, Raoul should be in favorite_user_ids')