diff options
| author | Thibault Delavallée <tde@odoo.com> | 2019-04-15 15:33:23 +0000 |
|---|---|---|
| committer | Thibault Delavallée <tde@odoo.com> | 2019-04-26 10:28:09 +0000 |
| commit | e87189222dfcb1c2ca3285862c9d7a28d6ea1426 (patch) | |
| tree | f5943ce9a4458504f4bac0d5595db9324468413e /addons | |
| parent | 6aac6418fc1d0a37ae4e9165d73485f34638a3ef (diff) | |
[IMP] test_mail: add tests for mail gateway and perform light cleaning
This commit add some tests related to the mail gateway: more bounce management
tests and some additional thread formation tests. Some test asserts about
bounce / blacklist management are commented as they are not completely working
currently. This will be improved in master soon.
Some cleaning in also done in all mail gateway tests. Notably some call to
tool methods are cleaned / simplified, duplicate tests are removed. Some
low-level checks are removed.
A new test model is added for mail gateway: mail.test.gateway. It is a
chatter model with blacklist enabled on it. It allows to tests the
various blacklist-related overrides and features as well as all basic
mail gateway features.
Sub-part of task 1853147 (pre-cleaning before implementing mail gateway
improvements)
Linked to PR #32974
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/test_mail/data/test_mail_data.py | 126 | ||||
| -rw-r--r-- | addons/test_mail/models/test_mail_models.py | 13 | ||||
| -rw-r--r-- | addons/test_mail/security/ir.model.access.csv | 2 | ||||
| -rw-r--r-- | addons/test_mail/tests/common.py | 10 | ||||
| -rw-r--r-- | addons/test_mail/tests/test_mail_cc.py | 55 | ||||
| -rw-r--r-- | addons/test_mail/tests/test_mail_gateway.py | 492 | ||||
| -rw-r--r-- | addons/test_mail/tests/test_message_compose.py | 7 |
7 files changed, 455 insertions, 250 deletions
diff --git a/addons/test_mail/data/test_mail_data.py b/addons/test_mail/data/test_mail_data.py index adf963fcd7b..eb2b53dbf57 100644 --- a/addons/test_mail/data/test_mail_data.py +++ b/addons/test_mail/data/test_mail_data.py @@ -293,8 +293,8 @@ AAAAACwAAAAAAgACAAAEA3DJFQA7 """ MAIL_EML_ATTACHMENT = """Subject: Re: test attac -From: Anon <anon@odoo.com> -To: anon@gmail.com +From: {email_from} +To: {to} References: <f3b9f8f8-28fa-2543-cab2-7aa68f679ebb@odoo.com> Message-ID: <cb7eaf62-58dc-2017-148c-305d0c78892f@odoo.com> Date: Wed, 14 Mar 2018 14:26:58 +0100 @@ -508,8 +508,8 @@ X-ME-Sender: <xms:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa> Received: from [1.1.1.1] (unknown [1.1.1.1]) by mail.xxxx.com (Postfix) with ESMTPA id BF5E1111D for <catchall@xxxx.xxxx>; Fri, 13 Apr 2018 22:11:50 -0400 (EDT) -From: "xxxx xxxx" <xxxx@xxxx.com> -To: "xxxx" <catchall@xxxx.xxxx> +From: Sylvie Lelitre <test.sylvie.lelitre@agrolait.com> +To: generic@mydomain.com Subject: Re: xxxx (Ref PO1) Date: Sat, 14 Apr 2018 02:11:42 +0000 Message-Id: <em67f5c44a-xxxx-xxxx-xxxx-69f56d618a94@wswin7hg4n4l1ce> @@ -674,3 +674,121 @@ o</a>. </body></html> --------=_MB48E455BD-2850-42EC-B1CA-886CDF48905E--""" + +MAIL_BOUNCE = """Return-Path: <> +X-Original-To: {to} +Delivered-To: {to} +Received: by mail2.test.ironsky (Postfix) + id 93A83A5F0D; Mon, 15 Apr 2019 15:41:06 +0200 (CEST) +Date: Mon, 15 Apr 2019 15:41:06 +0200 (CEST) +From: MAILER-DAEMON@mail2.test.ironsky (Mail Delivery System) +Subject: {subject} +To: {to} +Auto-Submitted: auto-replied +MIME-Version: 1.0 +Content-Type: multipart/report; report-type=delivery-status; + boundary="92726A5F09.1555335666/mail2.test.ironsky" +Message-Id: <20190415134106.93A83A5F0D@mail2.test.ironsky> + +This is a MIME-encapsulated message. + +--92726A5F09.1555335666/mail2.test.ironsky +Content-Description: Notification +Content-Type: text/plain; charset=us-ascii + +This is the mail system at host mail2.test.ironsky. + +I'm sorry to have to inform you that your message could not +be delivered to one or more recipients. It's attached below. + +For further assistance, please send mail to postmaster. + +If you do so, please include this problem report. You can +delete your own text from the attached returned message. + + The mail system + +<{email_from}>: host tribulant.com[23.22.38.89] said: 550 No such + person at this address. (in reply to RCPT TO command) + +--92726A5F09.1555335666/mail2.test.ironsky +Content-Description: Delivery report +Content-Type: message/delivery-status + +Reporting-MTA: dns; mail2.test.ironsky +X-Postfix-Queue-ID: 92726A5F09 +X-Postfix-Sender: rfc822; {to} +Arrival-Date: Mon, 15 Apr 2019 15:40:24 +0200 (CEST) + +Final-Recipient: rfc822; {email_from} +Original-Recipient: rfc822;{email_from} +Action: failed +Status: 5.0.0 +Remote-MTA: dns; tribulant.com +Diagnostic-Code: smtp; 550 No such person at this address. + +--92726A5F09.1555335666/mail2.test.ironsky +Content-Description: Undelivered Message +Content-Type: message/rfc822 + +Return-Path: <{to}> +Received: from [127.0.0.1] (host-212-68-194-133.dynamic.voo.be [212.68.194.133]) + (Authenticated sender: aaa) + by mail2.test.ironsky (Postfix) with ESMTPSA id 92726A5F09 + for <{email_from}>; Mon, 15 Apr 2019 15:40:24 +0200 (CEST) +DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=test.ironsky; s=mail; + t=1555335624; bh=x6cSjphxNDiRDMmm24lMAUKtdCFfftM8w/fdUyfoeFs=; + h=references:Subject:From:Reply-To:To:Date:From; + b=Bo0BsXAHgKiBfBtMvvO/+KaS9PuuS0+AozL4SxU05jHZcJFc7qFIPEpqkJIdbzNcQ + wq0PJYclgX7QZDOMm3VHQwcwOxBDXAbdnpfkPM9/wa+FWKfr6ikowMTHHT3CA1qNbe + h+BQVyBKIvr/LDFPSN2hQmfXWwWupm1lgUhJ07T4= +Content-Type: multipart/mixed; boundary="===============7355787381227985247==" +MIME-Version: 1.0 +Message-Id: <288877393590487.1555335624.090619087219238-openerp-32-project.task@aaa> +references: <670034078674109.1555335454.587288856506348-openerp-32-project.task@aaa> +Subject: Re: Test +From: Mitchell Admin <admin@yourcompany.example.com> +Reply-To: YourCompany Research & Development <aaa+catchall@test.ironsky> +To: Raoul <{email_from}> +Date: Mon, 15 Apr 2019 13:40:24 -0000 +X-Odoo-Objects: project.project-3, ,project.task-32 +X-Spam-Status: No, score=-2.0 required=5.0 tests=ALL_TRUSTED,BAYES_00, + DKIM_ADSP_NXDOMAIN,HEADER_FROM_DIFFERENT_DOMAINS,HTML_MESSAGE + shortcircuit=no autolearn=no autolearn_force=no version=3.4.2 +X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail2.test.ironsky + +--===============7355787381227985247== +Content-Type: multipart/alternative; boundary="===============8588563873240298690==" +MIME-Version: 1.0 + +--===============8588563873240298690== +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: base64 + +CgpaYm91bGl1b2l1b2l6ZWYKCi0tCkFkbWluaXN0cmF0b3IKU2VudApieQpbMV0gWW91ckNvbXBh +bnkKCnVzaW5nCk9kb28gWzJdIC4KCgoKWzFdIGh0dHA6Ly93d3cuZXhhbXBsZS5jb20KWzJdIGh0 +dHBzOi8vd3d3Lm9kb28uY29tP3V0bV9zb3VyY2U9ZGImdXRtX21lZGl1bT1lbWFpbAo= + +--===============8588563873240298690== +Content-Type: text/html; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: base64 + +CjxkaXY+CgoKPGRpdj48cD5aYm91bGl1b2l1b2l6ZWY8L3A+PC9kaXY+Cgo8ZGl2IGNsYXNzPSJm +b250LXNpemU6IDEzcHg7Ij48c3BhbiBkYXRhLW8tbWFpbC1xdW90ZT0iMSI+LS0gPGJyIGRhdGEt +by1tYWlsLXF1b3RlPSIxIj4KQWRtaW5pc3RyYXRvcjwvc3Bhbj48L2Rpdj4KPHAgc3R5bGU9ImNv +bG9yOiAjNTU1NTU1OyBtYXJnaW4tdG9wOjMycHg7Ij4KICAgIFNlbnQKICAgIDxzcGFuPgogICAg +YnkKICAgIDxhIHN0eWxlPSJ0ZXh0LWRlY29yYXRpb246bm9uZTsgY29sb3I6ICM4NzVBN0I7IiBo +cmVmPSJodHRwOi8vd3d3LmV4YW1wbGUuY29tIj4KICAgICAgICA8c3Bhbj5Zb3VyQ29tcGFueTwv +c3Bhbj4KICAgIDwvYT4KICAgIAogICAgPC9zcGFuPgogICAgdXNpbmcKICAgIDxhIHRhcmdldD0i +X2JsYW5rIiBocmVmPSJodHRwczovL3d3dy5vZG9vLmNvbT91dG1fc291cmNlPWRiJmFtcDt1dG1f +bWVkaXVtPWVtYWlsIiBzdHlsZT0idGV4dC1kZWNvcmF0aW9uOm5vbmU7IGNvbG9yOiAjODc1QTdC +OyI+T2RvbzwvYT4uCjwvcD4KPC9kaXY+CiAgICAgICAg + +--===============8588563873240298690==-- + +--===============7355787381227985247==-- + +--92726A5F09.1555335666/mail2.test.ironsky-- +""" diff --git a/addons/test_mail/models/test_mail_models.py b/addons/test_mail/models/test_mail_models.py index 50de0aca07d..bdf9f164da8 100644 --- a/addons/test_mail/models/test_mail_models.py +++ b/addons/test_mail/models/test_mail_models.py @@ -15,6 +15,19 @@ class MailTestSimple(models.Model): email_from = fields.Char() +class MailTestGateway(models.Model): + """ A very simple model only inheriting from mail.thread to test pure mass + mailing features and base performances. """ + _description = 'Simple Chatter Model for Mail Gateway' + _name = 'mail.test.gateway' + _inherit = ['mail.blacklist.mixin', 'mail.thread'] + _primary_email = 'email_from' + + name = fields.Char() + email_from = fields.Char() + custom_field = fields.Char() + + class MailTestStandard(models.Model): """ This model can be used in tests when automatic subscription and simple tracking is necessary. Most features are present in a simple way. """ diff --git a/addons/test_mail/security/ir.model.access.csv b/addons/test_mail/security/ir.model.access.csv index a856d7a3bac..b0d62e69a4a 100644 --- a/addons/test_mail/security/ir.model.access.csv +++ b/addons/test_mail/security/ir.model.access.csv @@ -2,6 +2,8 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_test_performance_mail,access_test_performance_mail,model_test_performance_mail,,1,1,1,1 access_mail_test_simple_portal,mail.test.simple.portal,model_mail_test_simple,base.group_portal,1,0,0,0 access_mail_test_simple_user,mail.test.simple.user,model_mail_test_simple,base.group_user,1,1,1,1 +access_mail_test_gateway_portal,mail.test.gateway.portal,model_mail_test_gateway,base.group_portal,1,0,0,0 +access_mail_test_gateway_user,mail.test.gateway.user,model_mail_test_gateway,base.group_user,1,1,1,1 access_mail_test_track_portal,mail.test.track.portal,model_mail_test_track,base.group_portal,0,0,0,0 access_mail_test_track_user,mail.test.track.user.employee,model_mail_test_track,base.group_user,1,1,1,1 access_mail_test_activity_portal,mail.test.activity.portal,model_mail_test_activity,base.group_portal,1,0,0,0 diff --git a/addons/test_mail/tests/common.py b/addons/test_mail/tests/common.py index 531bf4ea225..fd6ec9ce627 100644 --- a/addons/test_mail/tests/common.py +++ b/addons/test_mail/tests/common.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import json +import time from contextlib import contextmanager from email.utils import formataddr @@ -254,11 +255,12 @@ class MockEmails(common.SingleTransactionCase): cc='', msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>'): return template.format(to=to, subject=subject, cc=cc, extra=extra, email_from=email_from, msg_id=msg_id) - def format_and_process(self, template, to='groups@example.com, other@gmail.com', subject='Frogs', - extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', - cc='', msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>', - model=None, target_model='mail.test.simple', target_field='name'): + def format_and_process(self, template, email_from, to, subject='Frogs', extra='', cc='', msg_id=False, + model=None, target_model='mail.test.gateway', target_field='name'): self.assertFalse(self.env[target_model].search([(target_field, '=', subject)])) + if not msg_id: + msg_id = "<%.7f-test@iron.sky>" % (time.time()) + mail = self.format(template, to=to, subject=subject, cc=cc, extra=extra, email_from=email_from, msg_id=msg_id) self.env['mail.thread'].with_context(mail_channel_noautofollow=True).message_process(model, mail) return self.env[target_model].search([(target_field, '=', subject)]) diff --git a/addons/test_mail/tests/test_mail_cc.py b/addons/test_mail/tests/test_mail_cc.py index 30c4613b02d..3e512c8798f 100644 --- a/addons/test_mail/tests/test_mail_cc.py +++ b/addons/test_mail/tests/test_mail_cc.py @@ -7,51 +7,48 @@ from odoo.tools import mute_logger, email_split_and_format from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE
-@tagged('cc_test')
-class TestMailResend(common.BaseFunctionalTest, common.MockEmails):
+@tagged('mail_thread_cc')
+class TestMailCc(common.BaseFunctionalTest, common.MockEmails):
- @mute_logger('odoo.addons.mail.models.mail_thread')
- def test_message_cc_new(self):
- alias = self.env['mail.alias'].create({
+ @classmethod
+ def setUpClass(cls):
+ super(TestMailCc, cls).setUpClass()
+
+ cls.email_from = 'Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>'
+ cls.alias = cls.env['mail.alias'].create({
'alias_name': 'cc_record',
'alias_user_id': False,
- 'alias_model_id': self.env['ir.model']._get('mail.test.cc').id,
+ 'alias_model_id': cls.env['ir.model']._get('mail.test.cc').id,
'alias_contact': 'everyone'})
- record = self.format_and_process(MAIL_TEMPLATE, target_model='mail.test.cc', to='cc_record@example.com',
- cc='cc1@example.com, cc2@example.com')
+
+ cls.env['ir.config_parameter'].set_param('mail.bounce.alias', 'bounce.test')
+ cls.env['ir.config_parameter'].set_param('mail.catchall.domain', 'test.com')
+ cls.env['ir.config_parameter'].set_param('mail.catchall.alias', 'catchall.test')
+
+ @mute_logger('odoo.addons.mail.models.mail_thread')
+ def test_message_cc_new(self):
+ record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
+ cc='cc1@example.com, cc2@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
- self.assertEqual(sorted(cc), ['cc1@example.com', 'cc2@example.com'], 'cc should contains exactly 2 cc')
+ self.assertEqual(sorted(cc), ['cc1@example.com', 'cc2@example.com'])
@mute_logger('odoo.addons.mail.models.mail_thread')
def test_message_cc_update_with_old(self):
record = self.env['mail.test.cc'].create({'email_cc': 'cc1 <cc1@example.com>, cc2@example.com'})
- alias = self.env['mail.alias'].create({
- 'alias_name': 'cc_record',
- 'alias_user_id': False,
- 'alias_model_id': self.env['ir.model']._get('mail.test.cc').id,
- 'alias_contact': 'everyone',
- 'alias_force_thread_id': record.id})
+ self.alias.write({'alias_force_thread_id': record.id})
- self.format_and_process(MAIL_TEMPLATE, subject='Re: Frogs', target_model='mail.test.cc',
- msg_id='1198923581.41972151344608186799.JavaMail.diff1@agrolait.com',
- to='cc_record@example.com',
- cc='cc2 <cc2@example.com>, cc3@example.com')
+ self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
+ cc='cc2 <cc2@example.com>, cc3@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
self.assertEqual(sorted(cc), ['cc1 <cc1@example.com>', 'cc2@example.com', 'cc3@example.com'], 'new cc should have been added on record (unique)')
@mute_logger('odoo.addons.mail.models.mail_thread')
def test_message_cc_update_no_old(self):
record = self.env['mail.test.cc'].create({})
- alias = self.env['mail.alias'].create({
- 'alias_name': 'cc_record',
- 'alias_user_id': False,
- 'alias_model_id': self.env['ir.model']._get('mail.test.cc').id,
- 'alias_contact': 'everyone',
- 'alias_force_thread_id': record.id})
- self.format_and_process(MAIL_TEMPLATE, subject='Re: Frogs', target_model='mail.test.cc',
- msg_id='1198923581.41972151344608186799.JavaMail.diff1@agrolait.com',
- to='cc_record@example.com',
- cc='cc2 <cc2@example.com>, cc3@example.com')
+ self.alias.write({'alias_force_thread_id': record.id})
+
+ self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
+ cc='cc2 <cc2@example.com>, cc3@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
self.assertEqual(sorted(cc), ['cc2 <cc2@example.com>', 'cc3@example.com'], 'new cc should have been added on record (unique)')
diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py index dea415d6765..d81dd38d66d 100644 --- a/addons/test_mail/tests/test_mail_gateway.py +++ b/addons/test_mail/tests/test_mail_gateway.py @@ -5,23 +5,22 @@ import socket from email.utils import formataddr -from odoo.addons.test_mail.data.test_mail_data import \ - MAIL_TEMPLATE, MAIL_TEMPLATE_PLAINTEXT, MAIL_MULTIPART_MIXED, MAIL_MULTIPART_MIXED_TWO, \ - MAIL_MULTIPART_IMAGE, MAIL_SINGLE_BINARY, MAIL_EML_ATTACHMENT, MAIL_XHTML +from odoo.addons.test_mail.data import test_mail_data +from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE from odoo.addons.test_mail.tests.common import BaseFunctionalTest, MockEmails from odoo.addons.test_mail.tests.common import mail_new_test_user from odoo.tools import mute_logger -class TestEmailParsing(BaseFunctionalTest, MockEmails): +from odoo.tests import tagged - @mute_logger('odoo.addons.mail.models.mail_thread') - def test_message_parse(self): - pass + +@tagged('mail_gateway') +class TestEmailParsing(BaseFunctionalTest, MockEmails): def test_message_parse_body(self): # test pure plaintext - plaintext = self.format(MAIL_TEMPLATE_PLAINTEXT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>') + plaintext = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>') res = self.env['mail.thread'].message_parse(plaintext) self.assertIn('Please call me as soon as possible this afternoon!', res['body']) @@ -31,7 +30,7 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails): self.assertIn('<p>Please call me as soon as possible this afternoon!</p>', res['body']) # test multipart / mixed - res = self.env['mail.thread'].message_parse(MAIL_MULTIPART_MIXED) + res = self.env['mail.thread'].message_parse(test_mail_data.MAIL_MULTIPART_MIXED) self.assertNotIn( 'Should create a multipart/mixed: from gmail, *bold*, with attachment', res['body'], 'message_parse: text version should not be in body after parsing multipart/mixed') @@ -39,7 +38,7 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails): '<div dir="ltr">Should create a multipart/mixed: from gmail, <b>bold</b>, with attachment.<br clear="all"><div><br></div>', res['body'], 'message_parse: html version should be in body after parsing multipart/mixed') - res = self.env['mail.thread'].message_parse(MAIL_MULTIPART_MIXED_TWO) + res = self.env['mail.thread'].message_parse(test_mail_data.MAIL_MULTIPART_MIXED_TWO) self.assertNotIn('First and second part', res['body'], 'message_parse: text version should not be in body after parsing multipart/mixed') self.assertIn('First part', res['body'], @@ -47,56 +46,95 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails): self.assertIn('Second part', res['body'], 'message_parse: second part of the html version should be in body after parsing multipart/mixed') - res = self.env['mail.thread'].message_parse(MAIL_SINGLE_BINARY) + res = self.env['mail.thread'].message_parse(test_mail_data.MAIL_SINGLE_BINARY) self.assertEqual(res['body'], '') self.assertEqual(res['attachments'][0][0], 'thetruth.pdf') + def test_message_parse_eml(self): + # Test that the parsing of mail with embedded emails as eml(msg) which generates empty attachments, can be processed. + self.env['mail.thread'].message_parse(self.format(test_mail_data.MAIL_EML_ATTACHMENT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')) + + def test_message_parse_plaintext(self): + """ Incoming email in plaintext should be stored as html """ + res = self.env['mail.thread'].message_parse(self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')) + self.assertIn('<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>', res['body']) + + def test_message_parse_xhtml(self): + # Test that the parsing of XHTML mails does not fail + self.env['mail.thread'].message_parse(test_mail_data.MAIL_XHTML) + + +@tagged('mail_gateway') class TestMailgateway(BaseFunctionalTest, MockEmails): @classmethod def setUpClass(cls): super(TestMailgateway, cls).setUpClass() - mail_test_simple_model = cls.env['ir.model']._get('mail.test.simple') + cls.test_model = cls.env['ir.model']._get('mail.test.gateway') + cls.email_from = 'Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>' + + cls.test_record = cls.env['mail.test.gateway'].with_context(cls._test_context).create({ + 'name': 'Test', + 'email_from': 'ignasse@example.com', + }).with_context({}) - cls.partner_1 = cls.env['res.partner'].with_context(BaseFunctionalTest._test_context).create({ + cls.partner_1 = cls.env['res.partner'].with_context(cls._test_context).create({ 'name': 'Valid Lelitre', 'email': 'valid.lelitre@agrolait.com', }) - # groups@.. will cause the creation of new mail.test.simple + # groups@.. will cause the creation of new mail.test.gateway cls.alias = cls.env['mail.alias'].create({ 'alias_name': 'groups', 'alias_user_id': False, - 'alias_model_id': mail_test_simple_model.id, + 'alias_model_id': cls.test_model.id, 'alias_contact': 'everyone'}) # Set a first message on public group to test update and hierarchy cls.fake_email = cls.env['mail.message'].create({ - 'model': 'mail.test.simple', + 'model': 'mail.test.gateway', 'res_id': cls.test_record.id, 'subject': 'Public Discussion', 'message_type': 'email', + 'subtype_id': cls.env.ref('mail.mt_comment').id, 'author_id': cls.partner_1.id, - 'message_id': '<123456-openerp-%s-mail.test.simple@%s>' % (cls.test_record.id, socket.gethostname()), + 'message_id': '<123456-openerp-%s-mail.test.gateway@%s>' % (cls.test_record.id, socket.gethostname()), }) - @mute_logger('odoo.addons.mail.models.mail_thread') - def test_message_parse_eml(self): - """ Test that the parsing of mail with embedded emails as eml(msg) which generates empty attachments, can be processed. - """ - self.env['mail.thread'].message_process('mail.channel', MAIL_EML_ATTACHMENT) + cls.env['ir.config_parameter'].set_param('mail.bounce.alias', 'bounce.test') + cls.env['ir.config_parameter'].set_param('mail.catchall.domain', 'test.com') + cls.env['ir.config_parameter'].set_param('mail.catchall.alias', 'catchall.test') - @mute_logger('odoo.addons.mail.models.mail_thread') - def test_message_parse_xhtml(self): - """ Test that the parsing of mail with embedded emails as eml(msg) which generates empty attachments, can be processed. - """ - self.env['mail.thread'].message_process('mail.channel', MAIL_XHTML) + # -------------------------------------------------- + # Base low-level tests + # -------------------------------------------------- + + @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') + def test_message_process_alias_basic(self): + """ Test details of created message going through mailgateway """ + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Specific') + + # Test: one group created by mailgateway administrator as user_id is not set + self.assertEqual(len(record), 1, 'message_process: a new mail.test should have been created') + res = record.get_metadata()[0].get('create_uid') or [None] + self.assertEqual(res[0], self.env.uid) + + # Test: one message that is the incoming email + self.assertEqual(len(record.message_ids), 1) + msg = record.message_ids[0] + self.assertEqual(msg.subject, 'Specific') + self.assertIn('Please call me as soon as possible this afternoon!', msg.body) + self.assertEqual(msg.message_type, 'email') + self.assertEqual(msg.subtype_id, self.env.ref('mail.mt_comment')) @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_process_cid(self): - record = self.format_and_process(MAIL_MULTIPART_IMAGE, to='groups@example.com') + record = self.format_and_process(test_mail_data.MAIL_MULTIPART_IMAGE, self.email_from, 'groups@test.com') message = record.message_ids[0] for attachment in message.attachment_ids: self.assertIn('/web/image/%s' % attachment.id, message.body) + self.assertEqual( + set(message.attachment_ids.mapped('name')), + set(['rosaçée.gif', 'verte!µ.gif', 'orangée.gif'])) def test_message_process_followers(self): pass @@ -113,24 +151,6 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): # self.assertEqual(self.test_public.message_channel_ids, self.env['mail.test'], # 'message_process: after reply, group should have 2 followers (0 channels)') - @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') - def test_message_process_alias_basic(self): - """ Test details of created message going through mailgateway """ - record = self.format_and_process(MAIL_TEMPLATE, subject='Specific') - - # Test: one group created by mailgateway administrator as user_id is not set - self.assertEqual(len(record), 1, 'message_process: a new mail.test should have been created') - res = record.get_metadata()[0].get('create_uid') or [None] - self.assertEqual(res[0], self.env.uid) - - # Test: one message that is the incoming email - self.assertEqual(len(record.message_ids), 1) - msg = record.message_ids[0] - self.assertEqual(msg.subject, 'Specific') - self.assertIn('Please call me as soon as possible this afternoon!', msg.body) - self.assertEqual(msg.message_type, 'email') - self.assertEqual(msg.subtype_id, self.env.ref('mail.mt_comment')) - # -------------------------------------------------- # Author recognition # -------------------------------------------------- @@ -138,24 +158,53 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_email_email_from(self): """ Incoming email: not recognized author: email_from, no author_id, no followers """ - email_from = 'Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>' - - record = self.format_and_process(MAIL_TEMPLATE, email_from=email_from) + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com') self.assertFalse(record.message_ids[0].author_id, 'message_process: unrecognized email -> no author_id') - self.assertEqual(record.message_ids[0].email_from, email_from) + self.assertEqual(record.message_ids[0].email_from, self.email_from) self.assertEqual(len(record.message_partner_ids), 0, 'message_process: newly create group should not have any follower') @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_email_author(self): """ Incoming email: recognized author: email_from, author_id, added as follower """ - record = self.format_and_process(MAIL_TEMPLATE, email_from=formataddr((self.partner_1.name, self.partner_1.email))) + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com', subject='Test1') + + self.assertEqual(record.message_ids[0].author_id, self.partner_1, + 'message_process: recognized email -> author_id') + self.assertEqual(record.message_ids[0].email_from, self.partner_1.email_formatted) + self.assertEqual(len(self._mails), 0, 'No notification / bounce should be sent') + + # Email recognized if partner has a formatted email + self.partner_1.write({'email': {'Valid Lelitre <%s>' % self.partner_1.email}}) + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email, 'groups@test.com', subject='Test2') self.assertEqual(record.message_ids[0].author_id, self.partner_1, 'message_process: recognized email -> author_id') - self.assertEqual(record.message_ids[0].email_from, formataddr((self.partner_1.name, self.partner_1.email))) + self.assertEqual(record.message_ids[0].email_from, self.partner_1.email) self.assertEqual(len(self._mails), 0, 'No notification / bounce should be sent') + @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') + def test_message_process_partner_find(self): + """ Finding the partner based on email, based on partner / user / follower """ + self.alias.write({'alias_force_thread_id': self.test_record.id}) + from_1 = self.env['res.partner'].create({'name': 'Brice Denisse', 'email': 'from.test@example.com'}) + + self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, 'groups@test.com') + self.assertEqual(self.test_record.message_ids[0].author_id, from_1) + self.test_record.message_unsubscribe([from_1.id]) + + from_2 = mail_new_test_user(self.env, login='B', groups='base.group_user', name='User Denisse', email='from.test@example.com') + + self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, 'groups@test.com') + self.assertEqual(self.test_record.message_ids[0].author_id, from_2.partner_id) + self.test_record.message_unsubscribe([from_2.partner_id.id]) + + from_3 = self.env['res.partner'].create({'name': 'FOllower Denisse', 'email': 'from.test@example.com'}) + self.test_record.message_subscribe([from_3.id]) + + self.format_and_process(MAIL_TEMPLATE, from_1.email_formatted, 'groups@test.com') + self.assertEqual(self.test_record.message_ids[0].author_id, from_3) + # -------------------------------------------------- # Alias configuration # -------------------------------------------------- @@ -165,7 +214,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): """ Test alias ownership """ self.alias.write({'alias_user_id': self.user_employee.id}) - record = self.format_and_process(MAIL_TEMPLATE) + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com') self.assertEqual(len(record), 1) res = record.get_metadata()[0].get('create_uid') or [None] self.assertEqual(res[0], self.user_employee.id) @@ -175,20 +224,18 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): """ Incoming email: everyone: new record + message_new """ self.alias.write({'alias_contact': 'everyone'}) - record = self.format_and_process(MAIL_TEMPLATE, subject='Specific') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Specific') self.assertEqual(len(record), 1) self.assertEqual(len(record.message_ids), 1) @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') def test_message_process_alias_partners_bounce(self): """ Incoming email from an unknown partner on a Partners only alias -> bounce + test bounce email """ - self.env['ir.config_parameter'].set_param('mail.bounce.alias', 'bounce.test') - self.env['ir.config_parameter'].set_param('mail.catchall.domain', 'test.com') self.alias.write({'alias_contact': 'partners'}) # Test: no group created, email bounced - record = self.format_and_process(MAIL_TEMPLATE, subject='Should Bounce') - self.assertTrue(len(record) == 0) + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Should Bounce') + self.assertFalse(record) self.assertEqual(len(self._mails), 1, 'message_process: incoming email on Partners alias should send a bounce email') # Test bounce email @@ -201,28 +248,34 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): """ Incoming email from unknown partner / not follower partner on a Followers only alias -> bounce """ self.alias.write({ 'alias_contact': 'followers', - 'alias_parent_model_id': self.env['ir.model']._get('mail.test.simple').id, + 'alias_parent_model_id': self.env['ir.model']._get('mail.test.gateway').id, 'alias_parent_thread_id': self.test_record.id, }) # Test: unknown on followers alias -> bounce - record = self.format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com') - self.assertEqual(len(record), 0, 'message_process: should have bounced') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Should Bounce') + self.assertFalse(record, 'message_process: should have bounced') self.assertEqual(len(self._mails), 1, 'message_process: incoming email on Followers alias should send a bounce email') + self.assertEqual(self._mails[0].get('subject'), 'Re: Should Bounce') + self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com') + self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com'))) # Test: partner on followers alias -> bounce self._init_mock_build_email() - record = self.format_and_process(MAIL_TEMPLATE, email_from=formataddr((self.partner_1.name, self.partner_1.email))) - self.assertTrue(len(record) == 0, 'message_process: should have bounced') + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com', subject='Should Bounce') + self.assertFalse(record, 'message_process: should have bounced') self.assertEqual(len(self._mails), 1, 'message_process: incoming email on Followers alias should send a bounce email') + self.assertEqual(self._mails[0].get('subject'), 'Re: Should Bounce') + self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com') + self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com'))) @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_alias_partner(self): """ Incoming email from a known partner on a Partners alias -> ok (+ test on alias.user_id) """ self.alias.write({'alias_contact': 'partners'}) - record = self.format_and_process(MAIL_TEMPLATE, email_from=formataddr((self.partner_1.name, self.partner_1.email))) + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com') # Test: one group created by alias user self.assertEqual(len(record), 1) @@ -233,11 +286,11 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): """ Incoming email from a parent document follower on a Followers only alias -> ok """ self.alias.write({ 'alias_contact': 'followers', - 'alias_parent_model_id': self.env['ir.model']._get('mail.test.simple').id, + 'alias_parent_model_id': self.env['ir.model']._get('mail.test.gateway').id, 'alias_parent_thread_id': self.test_record.id, }) self.test_record.message_subscribe(partner_ids=[self.partner_1.id]) - record = self.format_and_process(MAIL_TEMPLATE, email_from=formataddr((self.partner_1.name, self.partner_1.email))) + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, 'groups@test.com') # Test: one group created by Raoul (or Sylvie maybe, if we implement it) self.assertEqual(len(record), 1) @@ -249,32 +302,26 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): self.test_record.message_subscribe(partner_ids=[self.partner_1.id]) record = self.format_and_process( - MAIL_TEMPLATE, email_from='valid.other@gmail.com', - msg_id='<1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>', - to='groups@example.com>', subject='Re: cats') + MAIL_TEMPLATE, self.email_from, 'groups@test.com>', + msg_id='<1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>', subject='Re: cats') # Test: no new group + new message - self.assertEqual(len(record), 0, 'message_process: alias update should not create new records') + self.assertFalse(record, 'message_process: alias update should not create new records') self.assertEqual(len(self.test_record.message_ids), 2) # Test: sent emails: 1 (Sylvie copy of the incoming email) - self.assertEqual(len(self._mails), 1, - 'message_process: one email should have been generated') - self.assertEqual(formataddr((self.partner_1.name, self.partner_1.email)), self._mails[0].get('email_to')[0], - 'message_process: email should be sent to Sylvie') + self.assertEmails(False, self.partner_1, email_from=self.email_from) # -------------------------------------------------- # Email Management # -------------------------------------------------- + @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_process_write_to_catchall(self): """ Writing directly to catchall should bounce """ - self.env['ir.config_parameter'].set_param('mail.bounce.alias', 'bounce.test') - self.env['ir.config_parameter'].set_param('mail.catchall.alias', 'catchall.test') - self.env['ir.config_parameter'].set_param('mail.catchall.domain', 'test.com') # Test: no group created, email bounced - record = self.format_and_process(MAIL_TEMPLATE, to='catchall.test@test.com', subject='Should Bounce') - self.assertTrue(len(record) == 0) + record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email_formatted, '"My Super Catchall" <catchall.test@test.com>', subject='Should Bounce') + self.assertFalse(record) self.assertEqual(len(self._mails), 1, 'message_process: writing directly to catchall should bounce') # Test bounce email @@ -282,6 +329,44 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com') self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com'))) + @mute_logger('odoo.addons.mail.models.mail_thread') + def test_message_process_bounce(self): + """ Writing to bounce alias is considered as a bounce """ + self.assertEqual(self.partner_1.message_bounce, 0) + + bounced_mail_id = 4442 + bounce_email_to = '%s+%s-%s-%s@%s' % ('bounce.test', bounced_mail_id, self.test_record._name, self.test_record.id, 'test.com') + record = self.format_and_process(test_mail_data.MAIL_BOUNCE, self.partner_1.email_formatted, bounce_email_to, subject='Undelivered Mail Returned to Sender') + self.assertFalse(record) + # self.assertEqual(self.partner_1.message_bounce, 1) # TDE FIXME: smart test crashing in current master + + @mute_logger('odoo.addons.mail.models.mail_thread') + def test_message_process_bounce_whatever_from(self): + """ Writing to bounce alias is considered as a bounce """ + self.assertEqual(self.partner_1.message_bounce, 0) + + bounced_mail_id = 4442 + bounce_email_to = '%s+%s-%s-%s@%s' % ('bounce.test', bounced_mail_id, self.test_record._name, self.test_record.id, 'test.com') + record = self.format_and_process(test_mail_data.MAIL_BOUNCE, 'Whatever <what@ever.com>', bounce_email_to, subject='Undelivered Mail Returned to Sender') + self.assertFalse(record) + # self.assertEqual(self.partner_1.message_bounce, 1) # TDE FIXME: smart test crashing in current master + + @mute_logger('odoo.addons.mail.models.mail_thread') + def test_message_process_bounce_multipart_report(self): + """ Multipart/report emails should be considered as bounce """ + self.assertEqual(self.partner_1.message_bounce, 0) + record = self.format_and_process(test_mail_data.MAIL_BOUNCE, 'Whatever <what@ever.com>', 'groups@test.com', subject='Undelivered Mail Returned to Sender') + self.assertFalse(record) + # self.assertEqual(self.partner_1.message_bounce, 1) # TDE FIXME: smart test crashing in current master + + @mute_logger('odoo.addons.mail.models.mail_thread') + def test_message_process_bounce_mailer_demon(self): + """ Multipart/report emails should be considered as bounce """ + self.assertEqual(self.partner_1.message_bounce, 0) + record = self.format_and_process(MAIL_TEMPLATE, 'MAILER-DAEMON@example.com', 'groups@test.com', subject='Undelivered Mail Returned to Sender') + self.assertFalse(record) + # self.assertEqual(self.partner_1.message_bounce, 1) # TDE FIXME: smart test crashing in current master + # -------------------------------------------------- # Thread formation # -------------------------------------------------- @@ -289,26 +374,24 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_in_reply_to(self): """ Incoming email using in-rely-to should go into the right destination even with a wrong destination """ + init_msg_count = len(self.test_record.message_ids) self.format_and_process( - MAIL_TEMPLATE, email_from='valid.other@gmail.com', - msg_id='<1198923581.41972151344608186800.JavaMail.diff1@agrolait.com>', - to='erroneous@example.com>', subject='Re: news', - extra='In-Reply-To:\r\n\t%s\n' % self.fake_email.message_id) + MAIL_TEMPLATE, 'valid.other@gmail.com', 'erroneous@test.com>', + subject='Re: news', extra='In-Reply-To:\r\n\t%s\n' % self.fake_email.message_id) - self.assertEqual(len(self.test_record.message_ids), 2, 'message_process: group should contain one new message') - self.assertEqual(len(self.fake_email.child_ids), 1, 'message_process: new message should be children of the existing one') + self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) + self.assertEqual(self.fake_email.child_ids, self.test_record.message_ids[0]) @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_references(self): """ Incoming email using references should go into the right destination even with a wrong destination """ init_msg_count = len(self.test_record.message_ids) self.format_and_process( - MAIL_TEMPLATE, to='erroneous@example.com', - extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, - msg_id='<1198923581.41972151344608186800.JavaMail.4@agrolait.com>') + MAIL_TEMPLATE, self.email_from, 'erroneous@test.com', + extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id) self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) - self.assertEqual(len(self.fake_email.child_ids), 1) + self.assertEqual(self.fake_email.child_ids, self.test_record.message_ids[0]) @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_process_references_external(self): @@ -319,28 +402,25 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): }) init_msg_count = len(self.test_record.message_ids) self.format_and_process( - MAIL_TEMPLATE, to='erroneous@example.com', - extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, - msg_id='<1198923581.41972151344608186800.JavaMail.4@agrolait.com>') + MAIL_TEMPLATE, self.email_from, 'erroneous@test.com', + extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id) self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) - self.assertEqual(len(self.fake_email.child_ids), 1) + self.assertEqual(self.fake_email.child_ids, self.test_record.message_ids[0]) @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_process_references_forward(self): """ Incoming email using references but with alias forward should not go into references destination """ - new_alias_2 = self.env['mail.alias'].create({ - 'alias_name': 'test', + self.env['mail.alias'].create({ + 'alias_name': 'test.alias', 'alias_user_id': False, 'alias_model_id': self.env['ir.model']._get('mail.test').id, 'alias_contact': 'everyone', }) init_msg_count = len(self.test_record.message_ids) res_test = self.format_and_process( - MAIL_TEMPLATE, to='test@example.com', - subject='My Dear Forward', - extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, - msg_id='<1198923581.41972151344608186800.JavaMail.4@agrolait.com>', + MAIL_TEMPLATE, self.email_from, 'test.alias@test.com', + subject='My Dear Forward', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, target_model='mail.test') self.assertEqual(len(self.test_record.message_ids), init_msg_count) @@ -349,27 +429,97 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): self.assertEqual(len(res_test.message_ids), 1) @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') + def test_message_process_references_forward_same_model(self): + """ Incoming email using references but with alias forward on same model should be considered as a reply """ + self.env['mail.alias'].create({ + 'alias_name': 'test.alias', + 'alias_user_id': False, + 'alias_model_id': self.env['ir.model']._get('mail.test.gateway').id, + 'alias_contact': 'everyone', + }) + init_msg_count = len(self.test_record.message_ids) + res_test = self.format_and_process( + MAIL_TEMPLATE, self.email_from, 'test.alias@test.com', + subject='My Dear Forward', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, + target_model='mail.test') + + self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) + self.assertEqual(len(self.fake_email.child_ids), 1) + self.assertFalse(res_test) + + @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_references_forward_cc(self): - """ Incoming email using references but with alias forward should not go into references destination """ - self.format_and_process( - MAIL_TEMPLATE, to='erroneous@example.com', cc='test@example.com', - subject='My Dear Forward', - extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, - msg_id='<1198923581.41972151344608186800.JavaMail.4@agrolait.com>', - target_model='mail.test.simple') + """ Incoming email using references but with alias forward in CC should be considered as a repy (To > Cc) """ + self.env['mail.alias'].create({ + 'alias_name': 'test.alias', + 'alias_user_id': False, + 'alias_model_id': self.env['ir.model']._get('mail.test').id, + 'alias_contact': 'everyone', + }) + init_msg_count = len(self.test_record.message_ids) + res_test = self.format_and_process( + MAIL_TEMPLATE, self.email_from, 'catchall.test@test.com', cc='test.alias@test.com', + subject='My Dear Forward', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, + target_model='mail.test') - self.assertEqual(len(self.test_record.message_ids), 2, 'message_process: group should contain one new message') - self.assertEqual(len(self.fake_email.child_ids), 1, 'message_process: new message should be children of the existing one') + self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) + self.assertEqual(len(self.fake_email.child_ids), 1) + self.assertFalse(res_test) + + @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models', 'odoo.addons.mail.models.mail_mail') + def test_message_process_reply_to_new_thread(self): + """ Test replies not being considered as replies but use destination information instead (aka, mass post + specific reply to using aliases) """ + first_record = self.env['mail.test.simple'].sudo(self.user_employee).create({'name': 'Replies to Record'}) + record_msg = first_record.message_post( + subject='Discussion', + no_auto_thread=False, + subtype='mail.mt_comment', + ) + self.assertEqual(record_msg.reply_to, formataddr(('%s %s' % (self.user_employee.company_id.name, first_record.name), '%s@%s' % ('catchall.test', 'test.com')))) + mail_msg = first_record.message_post( + subject='Replies to Record', + reply_to='groups@test.com', + no_auto_thread=True, + subtype='mail.mt_comment', + ) + self.assertEqual(mail_msg.reply_to, 'groups@test.com') + + # reply to mail but should be considered as a new mail for alias + msgID = '<this.is.duplicate.test@iron.sky>' + res_test = self.format_and_process( + MAIL_TEMPLATE, self.email_from, record_msg.reply_to, cc='', + subject='Re: Replies to Record', extra='In-Reply-To: %s' % record_msg.message_id, + msg_id=msgID, target_model='mail.test.simple') + incoming_msg = self.env['mail.message'].search([('message_id', '=', msgID)]) + self.assertFalse(res_test) + self.assertEqual(incoming_msg.model, 'mail.test.simple') + self.assertEqual(incoming_msg.parent_id, first_record.message_ids[-1]) + self.assertTrue(incoming_msg.res_id == first_record.id) + + # reply to mail but should be considered as a new mail for alias + msgID = '<this.is.for.testing@iron.sky>' + res_test = self.format_and_process( + MAIL_TEMPLATE, self.email_from, mail_msg.reply_to, cc='', + subject='Re: Replies to Record', extra='In-Reply-To: %s' % mail_msg.message_id, + msg_id=msgID, target_model='mail.test.gateway') + incoming_msg = self.env['mail.message'].search([('message_id', '=', msgID)]) + self.assertEqual(len(res_test), 1) + self.assertEqual(res_test.name, 'Re: Replies to Record') + self.assertEqual(incoming_msg.model, 'mail.test.gateway') + self.assertFalse(incoming_msg.parent_id) + self.assertTrue(incoming_msg.res_id == res_test.id) + + # -------------------------------------------------- + # Thread formation: mail gateway corner cases + # -------------------------------------------------- @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') - def test_message_process_model_res_id(self): + def test_message_process_extra_model_res_id(self): """ Incoming email with ref holding model / res_id but that does not match any message in the thread: must raise since OpenERP saas-3 """ self.assertRaises(ValueError, self.format_and_process, MAIL_TEMPLATE, - email_from=formataddr((self.partner_1.name, self.partner_1.email)), - to='noone@example.com', subject='spam', - extra='In-Reply-To: <12321321-openerp-%d-mail.test.simple@%s>' % (self.test_record.id, socket.gethostname()), - msg_id='<1198923581.41972151344608186802.JavaMail.diff1@agrolait.com>') + self.partner_1.email_formatted, 'noone@test.com', subject='spam', + extra='In-Reply-To: <12321321-openerp-%d-mail.test.gateway@%s>' % (self.test_record.id, socket.gethostname())) # when 6.1 messages are present, compat mode is available # Odoo 10 update: compat mode has been removed and should not work anymore @@ -377,20 +527,9 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): # Do: compat mode accepts partial-matching emails self.assertRaises( ValueError, - self.format_and_process, - MAIL_TEMPLATE, email_from=formataddr((self.partner_1.name, self.partner_1.email)), - msg_id='<1.2.JavaMail.new@agrolait.com>', - to='noone@example.com>', subject='spam', - extra='In-Reply-To: <12321321-openerp-%d-mail.test.simple@%s>' % (self.test_record.id, socket.gethostname())) - - # 3''. 6.1 compat mode should not work if hostname does not match! - # Odoo 10 update: compat mode has been removed and should not work anymore and does not depend from hostname - self.assertRaises(ValueError, - self.format_and_process, - MAIL_TEMPLATE, email_from=formataddr((self.partner_1.name, self.partner_1.email)), - msg_id='<1.3.JavaMail.new@agrolait.com>', - to='noone@example.com>', subject='spam', - extra='In-Reply-To: <12321321-openerp-%d-mail.test@neighbor.com>' % self.test_record.id) + self.format_and_process, MAIL_TEMPLATE, + self.partner_1.email_formatted, 'noone@test.com>', subject='spam', + extra='In-Reply-To: <12321321-openerp-%d-mail.test.gateway@%s>' % (self.test_record.id, socket.gethostname())) # Test created messages self.assertEqual(len(self.test_record.message_ids), 1) @@ -402,86 +541,47 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): self.alias.write({'alias_force_thread_id': self.test_record.id,}) # Post a base message - record = self.format_and_process( - MAIL_TEMPLATE, email_from='valid.other@gmail.com', subject='Re: super cats', - msg_id='<1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>') + record = self.format_and_process(MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Re: super cats', msg_id='<123?456.diff1@agrolait.com>') + self.assertFalse(record) + self.assertEqual(len(self.test_record.message_ids), 2) # Do: due to some issue, same email goes back into the mailgateway record = self.format_and_process( - MAIL_TEMPLATE, email_from='other4@gmail.com', subject='Re: news', - msg_id='<1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>', - extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>\n') - - # Test: no group 'Re: news' created, still only 1 Frogs group - self.assertEqual(len(record), 0) + MAIL_TEMPLATE, self.email_from, 'groups@test.com', subject='Re: news', + msg_id='<123?456.diff1@agrolait.com>', extra='In-Reply-To: <1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>\n') + self.assertFalse(record) + self.assertEqual(len(self.test_record.message_ids), 2) - # Test: no new message - self.assertEqual(len(self.test_record.message_ids), 2, 'message_process: message with already existing message_id should not have been duplicated') # Test: message_id is still unique - no_of_msg = self.env['mail.message'].search_count([('message_id', 'ilike', '<1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>')]) + no_of_msg = self.env['mail.message'].search_count([('message_id', 'ilike', '<123?456.diff1@agrolait.com>')]) self.assertEqual(no_of_msg, 1, 'message_process: message with already existing message_id should not have been duplicated') @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') - def test_message_process_partner_find(self): - """ Finding the partner based on email, based on partner / user / follower """ - self.alias.write({'alias_force_thread_id': self.test_record.id}) - from_1 = self.env['res.partner'].create({'name': 'Brice Denisse', 'email': 'from.test@example.com'}) - - self.format_and_process(MAIL_TEMPLATE, msg_id='<1>', email_from='Brice Denisse <from.test@example.com>') - self.assertEqual(self.test_record.message_ids[0].author_id, from_1) - self.test_record.message_unsubscribe([from_1.id]) - - from_2 = mail_new_test_user(self.env, login='B', groups='base.group_user', name='Brice Denisse', email='from.test@example.com') - - self.format_and_process(MAIL_TEMPLATE, msg_id='<2>', email_from='Brice Denisse <from.test@example.com>') - self.assertEqual(self.test_record.message_ids[0].author_id, from_2.partner_id) - self.test_record.message_unsubscribe([from_2.partner_id.id]) - - from_3 = self.env['res.partner'].create({'name': 'Brice Denisse', 'email': 'from.test@example.com'}) - self.test_record.message_subscribe([from_3.id]) - - self.format_and_process(MAIL_TEMPLATE, msg_id='<3>', email_from='Brice Denisse <from.test@example.com>') - self.assertEqual(self.test_record.message_ids[0].author_id, from_3) - - @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_crash_wrong_model(self): """ Incoming email with model that does not accepts incoming emails must raise """ self.assertRaises(ValueError, self.format_and_process, - MAIL_TEMPLATE, - to='noone@example.com', subject='spam', extra='', model='res.country', - msg_id='<1198923581.41972151344608186760.JavaMail.new4@agrolait.com>') + MAIL_TEMPLATE, self.email_from, 'noone@test.com', + subject='spam', extra='', model='res.country') @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_crash_no_data(self): """ Incoming email without model and without alias must raise """ self.assertRaises(ValueError, self.format_and_process, - MAIL_TEMPLATE, - to='noone@example.com', subject='spam', extra='', - msg_id='<1198923581.41972151344608186760.JavaMail.new5@agrolait.com>') + MAIL_TEMPLATE, self.email_from, 'noone@test.com', + subject='spam', extra='') @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_process_fallback(self): """ Incoming email with model that accepting incoming emails as fallback """ record = self.format_and_process( - MAIL_TEMPLATE, to='noone@example.com', subject='Spammy', extra='', model='mail.test.simple', - msg_id='<1198923581.41972151344608186760.JavaMail.new6@agrolait.com>') + MAIL_TEMPLATE, self.email_from, 'noone@test.com', + subject='Spammy', extra='', model='mail.test.gateway') self.assertEqual(len(record), 1) - self.assertEqual(record._name, 'mail.test.simple') - - @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') - def test_message_process_plain_text(self): - """ Incoming email in plaintext should be stored as html """ - record = self.format_and_process( - MAIL_TEMPLATE_PLAINTEXT, to='groups@example.com', subject='Frogs Return', extra='', - msg_id='<deadcafe.1337@smtp.agrolait.com>') - self.assertEqual(len(record), 1, 'message_process: a new mail.test should have been created') - msg = record.message_ids[0] - # signature recognition -> Sylvie should be in a span - self.assertIn('<pre>\nPlease call me as soon as possible this afternoon!\n<span data-o-mail-quote="1">\n--\nSylvie\n</span></pre>', msg.body, - 'message_process: plaintext incoming email incorrectly parsed') + self.assertEqual(record.name, 'Spammy') + self.assertEqual(record._name, 'mail.test.gateway') @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models', 'odoo.addons.mail.models.mail_mail') def test_private_discussion(self): @@ -505,9 +605,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): self.assertIn('openerp-private', msg.message_id.split('@')[0], 'message_post: private discussion: message-id should contain the private keyword') # Do: Bert replies through mailgateway (is a customer) - self.format_and_process( - MAIL_TEMPLATE, to='not_important@mydomain.com', email_from='valid.lelitre@agrolait.com', - extra='In-Reply-To: %s' % msg.message_id, msg_id='<test30.JavaMail.0@agrolait.com>') + self.format_and_process(MAIL_TEMPLATE, 'valid.lelitre@agrolait.com', 'not_important@mydomain.com', extra='In-Reply-To: %s' % msg.message_id) # Test: last mail_message created msg2 = self.env['mail.message'].search([], limit=1) @@ -526,27 +624,3 @@ class TestMailgateway(BaseFunctionalTest, MockEmails): 'message_post: private discussion: incorrect recipients when replying') self.assertEqual(msg.needaction_partner_ids, self.user_employee.partner_id | self.partner_2, 'message_post: private discussion: incorrect notified recipients when replying') - - @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models', 'odoo.addons.mail.models.mail_mail') - def test_forward_parent_id(self): - msg = self.test_record.sudo(self.user_employee).message_post(no_auto_thread=True, subtype='mail.mt_comment') - self.assertNotIn(msg.model, msg.message_id.split('@')[0]) - self.assertNotIn('-%d-' % msg.res_id, msg.message_id.split('@')[0]) - self.assertIn('reply_to', msg.message_id.split('@')[0]) - - # forward it to a new thread AND an existing thread - fw_msg_id = '<THIS.IS.A.FW.MESSAGE.1@bert.fr>' - fw_message = MAIL_TEMPLATE.format(to='groups@example.com', - cc='', - subject='FW: Re: 1', - email_from='b.t@example.com', - extra='In-Reply-To: %s' % msg.message_id, - msg_id=fw_msg_id) - self.env['mail.thread'].message_process(None, fw_message) - msg_fw = self.env['mail.message'].search([('message_id', '=', fw_msg_id)]) - self.assertEqual(len(msg_fw), 1) - new_record = self.env['mail.test.simple'].search([('name', "=", msg_fw.subject)]) - self.assertEqual(len(new_record), 1) - self.assertEqual(msg_fw.model, 'mail.test.simple') - self.assertFalse(msg_fw.parent_id) - self.assertTrue(msg_fw.res_id == new_record.id) diff --git a/addons/test_mail/tests/test_message_compose.py b/addons/test_mail/tests/test_message_compose.py index 859dfa2a715..2397023db24 100644 --- a/addons/test_mail/tests/test_message_compose.py +++ b/addons/test_mail/tests/test_message_compose.py @@ -182,11 +182,10 @@ class TestMessagePost(BaseFunctionalTest, MockEmails, TestRecipients): self.assertEqual(msg.needaction_partner_ids, self.env['res.partner']) self.format_and_process( - MAIL_TEMPLATE_PLAINTEXT, - email_from=self.user_admin.email, + MAIL_TEMPLATE_PLAINTEXT, self.user_admin.email, 'not_my_businesss@example.com', msg_id='<1198923581.41972151344608186800.JavaMail.diff1@agrolait.com>', - to='not_my_businesss@example.com', - extra='In-Reply-To:\r\n\t%s\n' % msg.message_id) + extra='In-Reply-To:\r\n\t%s\n' % msg.message_id, + target_model='mail.test.simple') reply = self.test_record.message_ids - msg self.assertTrue(reply) self.assertEqual(reply.subtype_id, self.env.ref('mail.mt_note')) |
