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