[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
This commit is contained in:
Thibault Delavallée
2019-04-26 10:28:09 +00:00
parent 6aac6418fc
commit e87189222d
7 changed files with 457 additions and 252 deletions
+122 -4
View File
@@ -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--
"""
@@ -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. """
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_test_performance_mail access_test_performance_mail model_test_performance_mail 1 1 1 1
3 access_mail_test_simple_portal mail.test.simple.portal model_mail_test_simple base.group_portal 1 0 0 0
4 access_mail_test_simple_user mail.test.simple.user model_mail_test_simple base.group_user 1 1 1 1
5 access_mail_test_gateway_portal mail.test.gateway.portal model_mail_test_gateway base.group_portal 1 0 0 0
6 access_mail_test_gateway_user mail.test.gateway.user model_mail_test_gateway base.group_user 1 1 1 1
7 access_mail_test_track_portal mail.test.track.portal model_mail_test_track base.group_portal 0 0 0 0
8 access_mail_test_track_user mail.test.track.user.employee model_mail_test_track base.group_user 1 1 1 1
9 access_mail_test_activity_portal mail.test.activity.portal model_mail_test_activity base.group_portal 1 0 0 0
+6 -4
View File
@@ -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)])
+27 -30
View File
@@ -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):
@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': cls.env['ir.model']._get('mail.test.cc').id,
'alias_contact': 'everyone'})
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):
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'})
record = self.format_and_process(MAIL_TEMPLATE, target_model='mail.test.cc', to='cc_record@example.com',
cc='cc1@example.com, cc2@example.com')
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)')
+284 -210
View File
@@ -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.partner_1 = cls.env['res.partner'].with_context(BaseFunctionalTest._test_context).create({
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(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, formataddr((self.partner_1.name, self.partner_1.email)))
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, 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_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')
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), 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')
def test_message_process_model_res_id(self):
def test_message_process_references_forward_cc(self):
""" 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), 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_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')
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 group 'Re: news' created, still only 1 Frogs group
self.assertEqual(len(record), 0)
# 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)
@@ -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'))