[MERGE] Forward-port of latest 7.0 bugfixes, up to rev. 9846 revid:dle@openerp.com...
[odoo/odoo.git] / addons / mail / tests / test_mail_gateway.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Business Applications
5 #    Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
6 #
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.
11 #
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.
16 #
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/>.
19 #
20 ##############################################################################
21
22 from openerp.addons.mail.tests.common import TestMail
23 from openerp.tools import mute_logger
24
25 MAIL_TEMPLATE = """Return-Path: <whatever-2a840@postmaster.twitter.com>
26 To: {to}
27 Received: by mail1.openerp.com (Postfix, from userid 10002)
28     id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
29 From: {email_from}
30 Subject: {subject}
31 MIME-Version: 1.0
32 Content-Type: multipart/alternative;
33     boundary="----=_Part_4200734_24778174.1344608186754"
34 Date: Fri, 10 Aug 2012 14:16:26 +0000
35 Message-ID: {msg_id}
36 {extra}
37 ------=_Part_4200734_24778174.1344608186754
38 Content-Type: text/plain; charset=utf-8
39 Content-Transfer-Encoding: quoted-printable
40
41 Please call me as soon as possible this afternoon!
42
43 --
44 Sylvie
45 ------=_Part_4200734_24778174.1344608186754
46 Content-Type: text/html; charset=utf-8
47 Content-Transfer-Encoding: quoted-printable
48
49 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
50 <html>
51  <head>=20
52   <meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8" />
53  </head>=20
54  <body style=3D"margin: 0; padding: 0; background: #ffffff;-webkit-text-size-adjust: 100%;">=20
55
56   <p>Please call me as soon as possible this afternoon!</p>
57
58   <p>--<br/>
59      Sylvie
60   <p>
61  </body>
62 </html>
63 ------=_Part_4200734_24778174.1344608186754--
64 """
65
66 MAIL_TEMPLATE_PLAINTEXT = """Return-Path: <whatever-2a840@postmaster.twitter.com>
67 To: {to}
68 Received: by mail1.openerp.com (Postfix, from userid 10002)
69     id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
70 From: Sylvie Lelitre <sylvie.lelitre@agrolait.com>
71 Subject: {subject}
72 MIME-Version: 1.0
73 Content-Type: text/plain
74 Date: Fri, 10 Aug 2012 14:16:26 +0000
75 Message-ID: {msg_id}
76 {extra}
77
78 Please call me as soon as possible this afternoon!
79
80 --
81 Sylvie
82 """
83
84 MAIL_MULTIPART_MIXED = """Return-Path: <ignasse.carambar@gmail.com>
85 X-Original-To: raoul@grosbedon.fr
86 Delivered-To: raoul@grosbedon.fr
87 Received: by mail1.grosbedon.com (Postfix, from userid 10002)
88     id E8166BFACA; Fri, 23 Aug 2013 13:18:01 +0200 (CEST)
89 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail1.grosbedon.com
90 X-Spam-Level: 
91 X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,FREEMAIL_FROM,
92     HTML_MESSAGE,RCVD_IN_DNSWL_LOW autolearn=unavailable version=3.3.1
93 Received: from mail-ie0-f173.google.com (mail-ie0-f173.google.com [209.85.223.173])
94     by mail1.grosbedon.com (Postfix) with ESMTPS id 9BBD7BFAAA
95     for <raoul@openerp.fr>; Fri, 23 Aug 2013 13:17:55 +0200 (CEST)
96 Received: by mail-ie0-f173.google.com with SMTP id qd12so575130ieb.4
97         for <raoul@grosbedon.fr>; Fri, 23 Aug 2013 04:17:54 -0700 (PDT)
98 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
99         d=gmail.com; s=20120113;
100         h=mime-version:date:message-id:subject:from:to:content-type;
101         bh=dMNHV52EC7GAa7+9a9tqwT9joy9z+1950J/3A6/M/hU=;
102         b=DGuv0VjegdSrEe36ADC8XZ9Inrb3Iu+3/52Bm+caltddXFH9yewTr0JkCRQaJgMwG9
103          qXTQgP8qu/VFEbCh6scu5ZgU1hknzlNCYr3LT+Ih7dAZVUEHUJdwjzUU1LFV95G2RaCd
104          /Lwff6CibuUvrA+0CBO7IRKW0Sn5j0mukYu8dbaKsm6ou6HqS8Nuj85fcXJfHSHp6Y9u
105          dmE8jBh3fHCHF/nAvU+8aBNSIzl1FGfiBYb2jCoapIuVFitKR4q5cuoodpkH9XqqtOdH
106          DG+YjEyi8L7uvdOfN16eMr7hfUkQei1yQgvGu9/5kXoHg9+Gx6VsZIycn4zoaXTV3Nhn
107          nu4g==
108 MIME-Version: 1.0
109 X-Received: by 10.50.124.65 with SMTP id mg1mr1144467igb.43.1377256674216;
110  Fri, 23 Aug 2013 04:17:54 -0700 (PDT)
111 Received: by 10.43.99.71 with HTTP; Fri, 23 Aug 2013 04:17:54 -0700 (PDT)
112 Date: Fri, 23 Aug 2013 13:17:54 +0200
113 Message-ID: <CAP76m_V4BY2F7DWHzwfjteyhW8L2LJswVshtmtVym+LUJ=rASQ@mail.gmail.com>
114 Subject: Test mail multipart/mixed
115 From: =?ISO-8859-1?Q?Raoul Grosbedon=E9e?= <ignasse.carambar@gmail.com>
116 To: Followers of ASUSTeK-Joseph-Walters <raoul@grosbedon.fr>
117 Content-Type: multipart/mixed; boundary=089e01536c4ed4d17204e49b8e96
118
119 --089e01536c4ed4d17204e49b8e96
120 Content-Type: multipart/alternative; boundary=089e01536c4ed4d16d04e49b8e94
121
122 --089e01536c4ed4d16d04e49b8e94
123 Content-Type: text/plain; charset=ISO-8859-1
124
125 Should create a multipart/mixed: from gmail, *bold*, with attachment.
126
127 -- 
128 Marcel Boitempoils.
129
130 --089e01536c4ed4d16d04e49b8e94
131 Content-Type: text/html; charset=ISO-8859-1
132
133 <div dir="ltr">Should create a multipart/mixed: from gmail, <b>bold</b>, with attachment.<br clear="all"><div><br></div>-- <br>Marcel Boitempoils.</div>
134
135 --089e01536c4ed4d16d04e49b8e94--
136 --089e01536c4ed4d17204e49b8e96
137 Content-Type: text/plain; charset=US-ASCII; name="test.txt"
138 Content-Disposition: attachment; filename="test.txt"
139 Content-Transfer-Encoding: base64
140 X-Attachment-Id: f_hkpb27k00
141
142 dGVzdAo=
143 --089e01536c4ed4d17204e49b8e96--"""
144
145
146 class TestMailgateway(TestMail):
147
148     def test_00_message_parse(self):
149         """ Testing incoming emails parsing """
150         cr, uid = self.cr, self.uid
151
152         res = self.mail_thread.message_parse(cr, uid, MAIL_TEMPLATE_PLAINTEXT)
153         self.assertIn('Please call me as soon as possible this afternoon!', res.get('body', ''),
154                       'message_parse: missing text in text/plain body after parsing')
155
156         res = self.mail_thread.message_parse(cr, uid, MAIL_TEMPLATE)
157         self.assertIn('<p>Please call me as soon as possible this afternoon!</p>', res.get('body', ''),
158                       'message_parse: missing html in multipart/alternative body after parsing')
159
160         res = self.mail_thread.message_parse(cr, uid, MAIL_MULTIPART_MIXED)
161         self.assertNotIn('Should create a multipart/mixed: from gmail, *bold*, with attachment', res.get('body', ''),
162                          'message_parse: text version should not be in body after parsing multipart/mixed')
163         self.assertIn('<div dir="ltr">Should create a multipart/mixed: from gmail, <b>bold</b>, with attachment.<br clear="all"><div><br></div>', res.get('body', ''),
164                       'message_parse: html version should be in body after parsing multipart/mixed')
165
166     @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')
167     def test_10_message_process(self):
168         """ Testing incoming emails processing. """
169         cr, uid, user_raoul = self.cr, self.uid, self.user_raoul
170
171         def format_and_process(template, to='groups@example.com, other@gmail.com', subject='Frogs',
172                                extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
173                                msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>',
174                                model=None):
175             self.assertEqual(self.mail_group.search(cr, uid, [('name', '=', subject)]), [])
176             mail = template.format(to=to, subject=subject, extra=extra, email_from=email_from, msg_id=msg_id)
177             self.mail_thread.message_process(cr, uid, model, mail)
178             return self.mail_group.search(cr, uid, [('name', '=', subject)])
179
180         # --------------------------------------------------
181         # Data creation
182         # --------------------------------------------------
183
184         # groups@.. will cause the creation of new mail groups
185         self.mail_group_model_id = self.ir_model.search(cr, uid, [('model', '=', 'mail.group')])[0]
186         alias_id = self.mail_alias.create(cr, uid, {
187             'alias_name': 'groups',
188             'alias_user_id': False,
189             'alias_model_id': self.mail_group_model_id,
190             'alias_parent_model_id': self.mail_group_model_id,
191             'alias_parent_thread_id': self.group_pigs_id,
192             'alias_contact': 'everyone'})
193
194         # --------------------------------------------------
195         # Test1: new record creation
196         # --------------------------------------------------
197
198         # Do: incoming mail from an unknown partner on an alias creates a new mail_group "frogs"
199         self._init_mock_build_email()
200         frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com')
201         sent_emails = self._build_email_kwargs_list
202         # Test: one group created by mailgateway administrator
203         self.assertEqual(len(frog_groups), 1, 'message_process: a new mail.group should have been created')
204         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
205         res = self.mail_group.perm_read(cr, uid, [frog_group.id], details=False)
206         self.assertEqual(res[0].get('create_uid'), uid,
207                          'message_process: group should have been created by uid as alias_user__id is False on the alias')
208         # Test: one message that is the incoming email
209         self.assertEqual(len(frog_group.message_ids), 1,
210                          'message_process: newly created group should have the incoming email in message_ids')
211         msg = frog_group.message_ids[0]
212         self.assertEqual('Frogs', msg.subject,
213                          'message_process: newly created group should have the incoming email as first message')
214         self.assertIn('Please call me as soon as possible this afternoon!', msg.body,
215                       'message_process: newly created group should have the incoming email as first message')
216         self.assertEqual('email', msg.type,
217                          'message_process: newly created group should have an email as first message')
218         self.assertEqual('Discussions', msg.subtype_id.name,
219                          'message_process: newly created group should not have a log first message but an email')
220         # Test: message: unknown email address -> message has email_from, not author_id
221         self.assertFalse(msg.author_id,
222                          'message_process: message on created group should not have an author_id')
223         self.assertIn('test.sylvie.lelitre@agrolait.com', msg.email_from,
224                       'message_process: message on created group should have an email_from')
225         # Test: followers: nobody
226         self.assertEqual(len(frog_group.message_follower_ids), 0, 'message_process: newly create group should not have any follower')
227         # Test: sent emails: no-one
228         self.assertEqual(len(sent_emails), 0,
229                          'message_process: should create emails without any follower added')
230         # Data: unlink group
231         frog_group.unlink()
232
233         # Do: incoming email from an unknown partner on a Partners only alias -> bounce
234         self._init_mock_build_email()
235         self.mail_alias.write(cr, uid, [alias_id], {'alias_contact': 'partners'})
236         frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other2@gmail.com')
237         # Test: no group created
238         self.assertTrue(len(frog_groups) == 0)
239         # Test: email bounced
240         sent_emails = self._build_email_kwargs_list
241         self.assertEqual(len(sent_emails), 1,
242                          'message_process: incoming email on Partners alias should send a bounce email')
243         self.assertIn('Frogs', sent_emails[0].get('subject'),
244                       'message_process: bounce email on Partners alias should contain the original subject')
245         self.assertIn('test.sylvie.lelitre@agrolait.com', sent_emails[0].get('email_to'),
246                       'message_process: bounce email on Partners alias should have original email sender as recipient')
247
248         # Do: incoming email from an unknown partner on a Followers only alias -> bounce
249         self._init_mock_build_email()
250         self.mail_alias.write(cr, uid, [alias_id], {'alias_contact': 'followers'})
251         frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other3@gmail.com')
252         # Test: no group created
253         self.assertTrue(len(frog_groups) == 0)
254         # Test: email bounced
255         sent_emails = self._build_email_kwargs_list
256         self.assertEqual(len(sent_emails), 1,
257                          'message_process: incoming email on Followers alias should send a bounce email')
258         self.assertIn('Frogs', sent_emails[0].get('subject'),
259                       'message_process: bounce email on Followers alias should contain the original subject')
260         self.assertIn('test.sylvie.lelitre@agrolait.com', sent_emails[0].get('email_to'),
261                       'message_process: bounce email on Followers alias should have original email sender as recipient')
262
263         # Do: incoming email from a known partner on a Partners alias -> ok (+ test on alias.user_id)
264         self.mail_alias.write(cr, uid, [alias_id], {'alias_user_id': self.user_raoul_id, 'alias_contact': 'partners'})
265         p1id = self.res_partner.create(cr, uid, {'name': 'Sylvie Lelitre', 'email': 'test.sylvie.lelitre@agrolait.com'})
266         p2id = self.res_partner.create(cr, uid, {'name': 'Other Poilvache', 'email': 'other4@gmail.com'})
267         self._init_mock_build_email()
268         frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other4@gmail.com')
269         sent_emails = self._build_email_kwargs_list
270         # Test: one group created by Raoul
271         self.assertEqual(len(frog_groups), 1, 'message_process: a new mail.group should have been created')
272         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
273         res = self.mail_group.perm_read(cr, uid, [frog_group.id], details=False)
274         self.assertEqual(res[0].get('create_uid'), self.user_raoul_id,
275                          'message_process: group should have been created by alias_user_id')
276         # Test: one message that is the incoming email
277         self.assertEqual(len(frog_group.message_ids), 1,
278                          'message_process: newly created group should have the incoming email in message_ids')
279         msg = frog_group.message_ids[0]
280         # Test: message: author found
281         self.assertEqual(p1id, msg.author_id.id,
282                          'message_process: message on created group should have Sylvie as author_id')
283         self.assertIn('Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', msg.email_from,
284                       'message_process: message on created group should have have an email_from')
285         # Test: author (not recipient and not Raoul (as alias owner)) added as follower
286         frog_follower_ids = set([p.id for p in frog_group.message_follower_ids])
287         self.assertEqual(frog_follower_ids, set([p1id]),
288                          'message_process: newly created group should have 1 follower (author, not creator, not recipients)')
289         # Test: sent emails: no-one, no bounce effet
290         sent_emails = self._build_email_kwargs_list
291         self.assertEqual(len(sent_emails), 0,
292                          'message_process: should not bounce incoming emails')
293         # Data: unlink group
294         frog_group.unlink()
295
296         # Do: incoming email from a not follower Partner on a Followers only alias -> bounce
297         self._init_mock_build_email()
298         self.mail_alias.write(cr, uid, [alias_id], {'alias_user_id': False, 'alias_contact': 'followers'})
299         frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other5@gmail.com')
300         # Test: no group created
301         self.assertTrue(len(frog_groups) == 0)
302         # Test: email bounced
303         sent_emails = self._build_email_kwargs_list
304         self.assertEqual(len(sent_emails), 1,
305                          'message_process: incoming email on Partners alias should send a bounce email')
306
307         # Do: incoming email from a parent document follower on a Followers only alias -> ok
308         self._init_mock_build_email()
309         self.mail_group.message_subscribe(cr, uid, [self.group_pigs_id], [p1id])
310         frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other6@gmail.com')
311         # Test: one group created by Raoul (or Sylvie maybe, if we implement it)
312         self.assertEqual(len(frog_groups), 1, 'message_process: a new mail.group should have been created')
313         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
314         # Test: one message that is the incoming email
315         self.assertEqual(len(frog_group.message_ids), 1,
316                          'message_process: newly created group should have the incoming email in message_ids')
317         # Test: author (and not recipient) added as follower
318         frog_follower_ids = set([p.id for p in frog_group.message_follower_ids])
319         self.assertEqual(frog_follower_ids, set([p1id]),
320                          'message_process: newly created group should have 1 follower (author, not creator, not recipients)')
321         # Test: sent emails: no-one, no bounce effet
322         sent_emails = self._build_email_kwargs_list
323         self.assertEqual(len(sent_emails), 0,
324                          'message_process: should not bounce incoming emails')
325
326         # --------------------------------------------------
327         # Test2: update-like alias
328         # --------------------------------------------------
329
330         # Do: Pigs alias is restricted, should bounce
331         self._init_mock_build_email()
332         self.mail_group.write(cr, uid, [frog_group.id], {'alias_name': 'frogs', 'alias_contact': 'followers', 'alias_force_thread_id': frog_group.id})
333         frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
334                                          msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
335                                          to='frogs@example.com>', subject='Re: news')
336         # Test: no group 'Re: news' created, still only 1 Frogs group
337         self.assertEqual(len(frog_groups), 0,
338                          'message_process: reply on Frogs should not have created a new group with new subject')
339         frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
340         self.assertEqual(len(frog_groups), 1,
341                          'message_process: reply on Frogs should not have created a duplicate group with old subject')
342         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
343         # Test: email bounced
344         sent_emails = self._build_email_kwargs_list
345         self.assertEqual(len(sent_emails), 1,
346                          'message_process: incoming email on Followers alias should send a bounce email')
347         self.assertIn('Re: news', sent_emails[0].get('subject'),
348                       'message_process: bounce email on Followers alias should contain the original subject')
349
350         # Do: Pigs alias is restricted, should accept Followers
351         self._init_mock_build_email()
352         self.mail_group.message_subscribe(cr, uid, [frog_group.id], [p2id])
353         frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
354                                          msg_id='<1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>',
355                                          to='frogs@example.com>', subject='Re: cats')
356         # Test: no group 'Re: news' created, still only 1 Frogs group
357         self.assertEqual(len(frog_groups), 0,
358                          'message_process: reply on Frogs should not have created a new group with new subject')
359         frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
360         self.assertEqual(len(frog_groups), 1,
361                          'message_process: reply on Frogs should not have created a duplicate group with old subject')
362         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
363         # Test: one new message
364         self.assertEqual(len(frog_group.message_ids), 2, 'message_process: group should contain 2 messages after reply')
365         # Test: sent emails: 1 (Sylvie copy of the incoming email, but no bounce)
366         sent_emails = self._build_email_kwargs_list
367         self.assertEqual(len(sent_emails), 1,
368                          'message_process: one email should have been generated')
369         self.assertIn('test.sylvie.lelitre@agrolait.com', sent_emails[0].get('email_to')[0],
370                       'message_process: email should be sent to Sylvie')
371         self.mail_group.message_unsubscribe(cr, uid, [frog_group.id], [p2id])
372
373         # --------------------------------------------------
374         # Test3: discussion and replies
375         # --------------------------------------------------
376
377         # Do: even with a wrong destination, a reply should end up in the correct thread
378         frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
379                                          msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
380                                          to='erroneous@example.com>', subject='Re: news',
381                                          extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
382         # Test: no group 'Re: news' created, still only 1 Frogs group
383         self.assertEqual(len(frog_groups), 0,
384                          'message_process: reply on Frogs should not have created a new group with new subject')
385         frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
386         self.assertEqual(len(frog_groups), 1,
387                          'message_process: reply on Frogs should not have created a duplicate group with old subject')
388         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
389         # Test: one new message
390         self.assertEqual(len(frog_group.message_ids), 3, 'message_process: group should contain 2 messages after reply')
391         # Test: author (and not recipient) added as follower
392         frog_follower_ids = set([p.id for p in frog_group.message_follower_ids])
393         self.assertEqual(frog_follower_ids, set([p1id, p2id]),
394                          'message_process: after reply, group should have 2 followers')
395
396         # Do: due to some issue, same email goes back into the mailgateway
397         frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
398                                          msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
399                                          subject='Re: news', extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
400         # Test: no group 'Re: news' created, still only 1 Frogs group
401         self.assertEqual(len(frog_groups), 0,
402                          'message_process: reply on Frogs should not have created a new group with new subject')
403         frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
404         self.assertEqual(len(frog_groups), 1,
405                          'message_process: reply on Frogs should not have created a duplicate group with old subject')
406         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
407         # Test: no new message
408         self.assertEqual(len(frog_group.message_ids), 3, 'message_process: message with already existing message_id should not have been duplicated')
409         # Test: message_id is still unique
410         msg_ids = self.mail_message.search(cr, uid, [('message_id', 'ilike', '<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>')])
411         self.assertEqual(len(msg_ids), 1,
412                          'message_process: message with already existing message_id should not have been duplicated')
413
414         # --------------------------------------------------
415         # Test4: email_from and partner finding
416         # --------------------------------------------------
417
418         # Data: extra partner with Raoul's email -> test the 'better author finding'
419         extra_partner_id = self.res_partner.create(cr, uid, {'name': 'A-Raoul', 'email': 'test_raoul@email.com'})
420
421         # Do: post a new message, with a known partner -> duplicate emails -> partner
422         format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik <test_raoul@email.com>',
423                            to='erroneous@example.com>', subject='Re: news (2)',
424                            msg_id='<1198923581.41972151344608186760.JavaMail.new1@agrolait.com>',
425                            extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
426         frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
427         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
428         # Test: author is A-Raoul (only existing)
429         self.assertEqual(frog_group.message_ids[0].author_id.id, extra_partner_id,
430                          'message_process: email_from -> author_id wrong')
431
432         # Do: post a new message, with a known partner -> duplicate emails -> user
433         frog_group.message_unsubscribe([extra_partner_id])
434         raoul_email = self.user_raoul.email
435         self.res_users.write(cr, uid, self.user_raoul_id, {'email': 'test_raoul@email.com'})
436         format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik <test_raoul@email.com>',
437                            to='erroneous@example.com>', subject='Re: news (3)',
438                            msg_id='<1198923581.41972151344608186760.JavaMail.new2@agrolait.com>',
439                            extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
440         frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
441         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
442         # Test: author is Raoul (user), not A-Raoul
443         self.assertEqual(frog_group.message_ids[0].author_id.id, self.partner_raoul_id,
444                          'message_process: email_from -> author_id wrong')
445
446         # Do: post a new message, with a known partner -> duplicate emails -> partner because is follower
447         frog_group.message_unsubscribe([self.partner_raoul_id])
448         frog_group.message_subscribe([extra_partner_id])
449         raoul_email = self.user_raoul.email
450         self.res_users.write(cr, uid, self.user_raoul_id, {'email': 'test_raoul@email.com'})
451         format_and_process(MAIL_TEMPLATE, email_from='Lombrik Lubrik <test_raoul@email.com>',
452                            to='erroneous@example.com>', subject='Re: news (3)',
453                            msg_id='<1198923581.41972151344608186760.JavaMail.new3@agrolait.com>',
454                            extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
455         frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
456         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
457         # Test: author is Raoul (user), not A-Raoul
458         self.assertEqual(frog_group.message_ids[0].author_id.id, extra_partner_id,
459                          'message_process: email_from -> author_id wrong')
460
461         self.res_users.write(cr, uid, self.user_raoul_id, {'email': raoul_email})
462
463         # --------------------------------------------------
464         # Test5: misc gateway features
465         # --------------------------------------------------
466
467         # Do: incoming email with model that does not accepts incoming emails must raise
468         self.assertRaises(ValueError,
469                           format_and_process,
470                           MAIL_TEMPLATE,
471                           to='noone@example.com', subject='spam', extra='', model='res.country',
472                           msg_id='<1198923581.41972151344608186760.JavaMail.new4@agrolait.com>')
473
474         # Do: incoming email without model and without alias must raise
475         self.assertRaises(ValueError,
476                           format_and_process,
477                           MAIL_TEMPLATE,
478                           to='noone@example.com', subject='spam', extra='',
479                           msg_id='<1198923581.41972151344608186760.JavaMail.new5@agrolait.com>')
480
481         # Do: incoming email with model that accepting incoming emails as fallback
482         frog_groups = format_and_process(MAIL_TEMPLATE,
483                                          to='noone@example.com',
484                                          subject='Spammy', extra='', model='mail.group',
485                                          msg_id='<1198923581.41972151344608186760.JavaMail.new6@agrolait.com>')
486         self.assertEqual(len(frog_groups), 1,
487                          'message_process: erroneous email but with a fallback model should have created a new mail.group')
488
489         # Do: incoming email in plaintext should be stored as  html
490         frog_groups = format_and_process(MAIL_TEMPLATE_PLAINTEXT,
491                                          to='groups@example.com', subject='Frogs Return', extra='',
492                                          msg_id='<deadcafe.1337@smtp.agrolait.com>')
493         # Test: one group created with one message
494         self.assertEqual(len(frog_groups), 1, 'message_process: a new mail.group should have been created')
495         frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
496         msg = frog_group.message_ids[0]
497         # Test: plain text content should be wrapped and stored as html
498         self.assertIn('<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>', msg.body,
499                       'message_process: plaintext incoming email incorrectly parsed')
500
501     @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')
502     def test_20_thread_parent_resolution(self):
503         """ Testing parent/child relationships are correctly established when processing incoming mails """
504         cr, uid = self.cr, self.uid
505
506         def format(template, to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: 1',
507                                 extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
508                                 msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>'):
509             return template.format(to=to, subject=subject, extra=extra, email_from=email_from, msg_id=msg_id)
510
511         group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
512         msg1 = group_pigs.message_post(body='My Body', subject='1')
513         msg2 = group_pigs.message_post(body='My Body', subject='2')
514         msg1, msg2 = self.mail_message.browse(cr, uid, [msg1, msg2])
515         self.assertTrue(msg1.message_id, "message_process: new message should have a proper message_id")
516
517         # Reply to msg1, make sure the reply is properly attached using the various reply identification mechanisms
518         # 0. Direct alias match
519         reply_msg1 = format(MAIL_TEMPLATE, to='Pretty Pigs <group+pigs@example.com>',
520                             extra='In-Reply-To: %s' % msg1.message_id,
521                             msg_id='<1198923581.41972151344608186760.JavaMail.2@agrolait.com>')
522         self.mail_group.message_process(cr, uid, None, reply_msg1)
523
524         # 1. In-Reply-To header
525         reply_msg2 = format(MAIL_TEMPLATE, to='erroneous@example.com',
526                             extra='In-Reply-To: %s' % msg1.message_id,
527                             msg_id='<1198923581.41972151344608186760.JavaMail.3@agrolait.com>')
528         self.mail_group.message_process(cr, uid, None, reply_msg2)
529
530         # 2. References header
531         reply_msg3 = format(MAIL_TEMPLATE, to='erroneous@example.com',
532                             extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id,
533                             msg_id='<1198923581.41972151344608186760.JavaMail.4@agrolait.com>')
534         self.mail_group.message_process(cr, uid, None, reply_msg3)
535
536         # 3. Subject contains [<ID>] + model passed to message+process -> only attached to group, but not to mail (not in msg1.child_ids)
537         reply_msg4 = format(MAIL_TEMPLATE, to='erroneous@example.com',
538                             extra='', subject='Re: [%s] 1' % self.group_pigs_id,
539                             msg_id='<1198923581.41972151344608186760.JavaMail.5@agrolait.com>')
540         self.mail_group.message_process(cr, uid, 'mail.group', reply_msg4)
541
542         group_pigs.refresh()
543         msg1.refresh()
544         self.assertEqual(6, len(group_pigs.message_ids), 'message_process: group should contain 6 messages')
545         self.assertEqual(3, len(msg1.child_ids), 'message_process: msg1 should have 3 children now')
546
547     def test_30_private_discussion(self):
548         """ Testing private discussion between partners. """
549         cr, uid = self.cr, self.uid
550
551         def format(template, to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: 1',
552                                 extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
553                                 msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>'):
554             return template.format(to=to, subject=subject, extra=extra, email_from=email_from, msg_id=msg_id)
555
556         # Do: Raoul writes to Bert and Administrator, with a thread_model in context that should not be taken into account
557         msg1_pids = [self.partner_admin_id, self.partner_bert_id]
558         msg1_id = self.mail_thread.message_post(
559             cr, self.user_raoul_id, False,
560             partner_ids=msg1_pids,
561             subtype='mail.mt_comment',
562             context={'thread_model': 'mail.group'}
563         )
564
565         # Test: message recipients
566         msg = self.mail_message.browse(cr, uid, msg1_id)
567         msg_pids = [p.id for p in msg.partner_ids]
568         msg_nids = [p.id for p in msg.notified_partner_ids]
569         test_pids = msg1_pids
570         test_nids = msg1_pids
571         self.assertEqual(set(msg_pids), set(test_pids),
572                          'message_post: private discussion: incorrect recipients')
573         self.assertEqual(set(msg_nids), set(test_nids),
574                          'message_post: private discussion: incorrect notified recipients')
575         self.assertEqual(msg.model, False,
576                          'message_post: private discussion: context key "thread_model" not correctly ignored when having no res_id')
577         # Test: message-id
578         self.assertIn('openerp-private', msg.message_id,
579                       'message_post: private discussion: message-id should contain the private keyword')
580
581         # Do: Bert replies through mailgateway (is a customer)
582         reply_message = format(MAIL_TEMPLATE, to='not_important@mydomain.com',
583                                email_from='bert@bert.fr',
584                                extra='In-Reply-To: %s' % msg.message_id,
585                                msg_id='<test30.JavaMail.0@agrolait.com>')
586         self.mail_thread.message_process(cr, uid, None, reply_message)
587
588         # Test: last mail_message created
589         msg2_id = self.mail_message.search(cr, uid, [], limit=1)[0]
590
591         # Test: message recipients
592         msg = self.mail_message.browse(cr, uid, msg2_id)
593         msg_pids = [p.id for p in msg.partner_ids]
594         msg_nids = [p.id for p in msg.notified_partner_ids]
595         test_pids = [self.partner_admin_id, self.partner_raoul_id]
596         test_nids = test_pids
597         self.assertEqual(msg.author_id.id, self.partner_bert_id,
598                          'message_post: private discussion: wrong author through mailgatewya based on email')
599         self.assertEqual(set(msg_pids), set(test_pids),
600                          'message_post: private discussion: incorrect recipients when replying')
601         self.assertEqual(set(msg_nids), set(test_nids),
602                          'message_post: private discussion: incorrect notified recipients when replying')
603
604         # Do: Bert replies through chatter (is a customer)
605         msg3_id = self.mail_thread.message_post(
606             cr, uid, False,
607             author_id=self.partner_bert_id,
608             parent_id=msg1_id, subtype='mail.mt_comment')
609
610         # Test: message recipients
611         msg = self.mail_message.browse(cr, uid, msg3_id)
612         msg_pids = [p.id for p in msg.partner_ids]
613         msg_nids = [p.id for p in msg.notified_partner_ids]
614         test_pids = [self.partner_admin_id, self.partner_raoul_id]
615         test_nids = test_pids
616         self.assertEqual(set(msg_pids), set(test_pids),
617                          'message_post: private discussion: incorrect recipients when replying')
618         self.assertEqual(set(msg_nids), set(test_nids),
619                          'message_post: private discussion: incorrect notified recipients when replying')
620
621         # Do: Administrator replies
622         msg3_id = self.mail_thread.message_post(cr, uid, False, parent_id=msg3_id, subtype='mail.mt_comment')
623
624         # Test: message recipients
625         msg = self.mail_message.browse(cr, uid, msg3_id)
626         msg_pids = [p.id for p in msg.partner_ids]
627         msg_nids = [p.id for p in msg.notified_partner_ids]
628         test_pids = [self.partner_bert_id, self.partner_raoul_id]
629         test_nids = test_pids
630         self.assertEqual(set(msg_pids), set(test_pids),
631                          'message_post: private discussion: incorrect recipients when replying')
632         self.assertEqual(set(msg_nids), set(test_nids),
633                          'message_post: private discussion: incorrect notified recipients when replying')