mirror of
https://github.com/bringout/oca-ocb-mail.git
synced 2026-04-20 09:02:00 +02:00
19.0 vanilla
This commit is contained in:
parent
5df8c07b59
commit
daa394e8b0
2114 changed files with 564841 additions and 299642 deletions
|
|
@ -6,6 +6,7 @@ from . import test_blacklist_behavior
|
|||
from . import test_blacklist_mixin
|
||||
from . import test_link_tracker
|
||||
from . import test_link_tracker_sms
|
||||
from . import test_mail_composer
|
||||
from . import test_mailing
|
||||
from . import test_mailing_server
|
||||
from . import test_mailing_sms
|
||||
|
|
@ -13,4 +14,5 @@ from . import test_mailing_statistics
|
|||
from . import test_mailing_statistics_sms
|
||||
from . import test_mailing_test
|
||||
from . import test_performance
|
||||
from . import test_sms_controller
|
||||
from . import test_utm
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@
|
|||
from odoo.addons.phone_validation.tools import phone_validation
|
||||
|
||||
from odoo.addons.mass_mailing_sms.tests.common import MassSMSCommon
|
||||
from odoo.addons.test_mail_sms.tests.common import TestSMSCommon
|
||||
|
||||
|
||||
class TestMassMailCommon(MassSMSCommon, TestSMSCommon):
|
||||
class TestMassMailCommon(MassSMSCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
|
@ -15,7 +14,6 @@ class TestMassMailCommon(MassSMSCommon, TestSMSCommon):
|
|||
|
||||
cls.test_alias = cls.env['mail.alias'].create({
|
||||
'alias_name': 'test.alias',
|
||||
'alias_user_id': False,
|
||||
'alias_model_id': cls.env['ir.model']._get('mailing.test.simple').id,
|
||||
'alias_contact': 'everyone'
|
||||
})
|
||||
|
|
@ -119,7 +117,7 @@ class TestMassSMSCommon(TestMassMailCommon):
|
|||
'name': 'Partner_%s' % (x),
|
||||
'email': '_test_partner_%s@example.com' % (x),
|
||||
'country_id': country_be_id,
|
||||
'mobile': '045600%s%s99' % (x, x)
|
||||
'phone': '045600%s%s99' % (x, x)
|
||||
})
|
||||
records += cls.env['mail.test.sms'].with_context(**cls._test_context).create({
|
||||
'name': 'MassSMSTest_%s' % (x),
|
||||
|
|
@ -137,6 +135,24 @@ class TestMassSMSCommon(TestMassMailCommon):
|
|||
})
|
||||
|
||||
cls.partner_numbers = [
|
||||
phone_validation.phone_format(partner.mobile, partner.country_id.code, partner.country_id.phone_code, force_format='E164')
|
||||
phone_validation.phone_format(partner.phone, partner.country_id.code, partner.country_id.phone_code, force_format='E164')
|
||||
for partner in partners
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _get_sms_test_records(cls, mobile_numbers):
|
||||
""" Helper to create data. Currently simple, to be improved. """
|
||||
country_be_id = cls.env.ref('base.be').id
|
||||
partners = cls.env['res.partner'].with_context(**cls._test_context).create([{
|
||||
'name': f'Partner_{x}',
|
||||
'email': f'_test_partner_{x}@example.com',
|
||||
'country_id': country_be_id,
|
||||
'phone': mobile_numbers[x]
|
||||
} for x, mobile_number in enumerate(mobile_numbers)])
|
||||
records = cls.env['mail.test.sms'].with_context(**cls._test_context).create([{
|
||||
'name': f'MassSMSTest_{x}',
|
||||
'customer_id': partner.id,
|
||||
'phone_nbr': mobile_number
|
||||
} for x, (mobile_number, partner) in enumerate(zip(mobile_numbers, partners))])
|
||||
|
||||
return records
|
||||
|
|
|
|||
|
|
@ -63,20 +63,23 @@ class TestAutoBlacklist(common.TestMassMailCommon):
|
|||
# is not sufficient
|
||||
with freeze_time(new_dt), patch.object(Cursor, 'now', lambda *args, **kwargs: new_dt):
|
||||
traces += self._create_bounce_trace(new_mailing, target, dt=datetime.datetime.now() - datetime.timedelta(weeks=idx+2))
|
||||
self.gateway_mail_bounce(new_mailing, target, bounce_base_values)
|
||||
self.gateway_mail_trace_bounce(new_mailing, target, bounce_base_values)
|
||||
|
||||
# mass mail record: ok, not blacklisted yet
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False):
|
||||
mailing.action_send_mail()
|
||||
|
||||
self.assertMailTraces(
|
||||
[{'email': 'test.record.00@test.example.com'}],
|
||||
[{
|
||||
'email': 'test.record.00@test.example.com',
|
||||
'email_to_mail': '"TestCustomer 00" <test.record.00@test.example.com>',
|
||||
}],
|
||||
mailing, target,
|
||||
check_mail=True
|
||||
)
|
||||
|
||||
# call bounced
|
||||
self.gateway_mail_bounce(mailing, target, bounce_base_values)
|
||||
self.gateway_mail_trace_bounce(mailing, target, bounce_base_values)
|
||||
|
||||
# check blacklist
|
||||
blacklist_record = self.env['mail.blacklist'].sudo().search([('email', '=', target.email_normalized)])
|
||||
|
|
@ -88,6 +91,11 @@ class TestAutoBlacklist(common.TestMassMailCommon):
|
|||
with self.mock_mail_gateway(mail_unlink_sent=False):
|
||||
new_mailing.action_send_mail()
|
||||
self.assertMailTraces(
|
||||
[{'email': 'test.record.00@test.example.com', 'trace_status': 'cancel', 'failure_type': 'mail_bl'}],
|
||||
[{
|
||||
'email': 'test.record.00@test.example.com',
|
||||
'email_to_mail': '"TestCustomer 00" <test.record.00@test.example.com>',
|
||||
'failure_type': 'mail_bl',
|
||||
'trace_status': 'cancel',
|
||||
}],
|
||||
new_mailing, target, check_mail=True
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.test_mass_mailing.models.mailing_models import MailingBLacklist
|
||||
from odoo.addons.test_mass_mailing.models.mailing_models import MailingTestBlacklist
|
||||
from odoo.addons.test_mass_mailing.tests import common
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests.common import users
|
||||
|
|
@ -23,15 +23,15 @@ class TestBLMixin(common.TestMassMailCommon):
|
|||
|
||||
@users('employee')
|
||||
def test_bl_mixin_primary_field_consistency(self):
|
||||
MailingBLacklist._primary_email = 'not_a_field'
|
||||
MailingTestBlacklist._primary_email = 'not_a_field'
|
||||
with self.assertRaises(UserError):
|
||||
self.env['mailing.test.blacklist'].search([('is_blacklisted', '=', False)])
|
||||
|
||||
MailingBLacklist._primary_email = ['not_a_str']
|
||||
MailingTestBlacklist._primary_email = ['not_a_str']
|
||||
with self.assertRaises(UserError):
|
||||
self.env['mailing.test.blacklist'].search([('is_blacklisted', '=', False)])
|
||||
|
||||
MailingBLacklist._primary_email = 'email_from'
|
||||
MailingTestBlacklist._primary_email = 'email_from'
|
||||
self.env['mailing.test.blacklist'].search([('is_blacklisted', '=', False)])
|
||||
|
||||
@users('employee')
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ class TestLinkTracker(common.TestMassMailCommon):
|
|||
def setUp(self):
|
||||
super(TestLinkTracker, self).setUp()
|
||||
|
||||
self.link = self.env['link.tracker'].search_or_create({
|
||||
self.link = self.env['link.tracker'].search_or_create([{
|
||||
'url': 'https://www.example.com'
|
||||
})
|
||||
}])
|
||||
|
||||
self.click = self.env['link.tracker.click'].create({
|
||||
'link_id': self.link.id,
|
||||
|
|
@ -34,15 +34,6 @@ class TestLinkTracker(common.TestMassMailCommon):
|
|||
self.assertEqual(click.country_id, self.env.ref('base.be'))
|
||||
self.assertEqual(self.link.count, 2)
|
||||
|
||||
# click from same IP (even another country) does not create a new entry
|
||||
click = self.env['link.tracker.click'].sudo().add_click(
|
||||
code,
|
||||
ip='100.00.00.01',
|
||||
country_code='FRA'
|
||||
)
|
||||
self.assertEqual(click, None)
|
||||
self.assertEqual(self.link.count, 2)
|
||||
|
||||
@users('user_marketing')
|
||||
def test_add_link_mail_stat(self):
|
||||
record = self.env['mailing.test.blacklist'].create({})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields
|
||||
from odoo.tests import freeze_time, tagged, users
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
from odoo.addons.test_mail.tests.test_mail_composer import TestMailComposer
|
||||
from odoo.addons.test_mass_mailing.tests import common
|
||||
|
||||
|
||||
@tagged('mail_composer')
|
||||
class TestMailComposerMassMailing(TestMailComposer, common.TestMassMailCommon):
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mass_mailing.models.mailing')
|
||||
@freeze_time('2025-08-06 15:02:00')
|
||||
def test_mail_composer_mailing_creation(self):
|
||||
"""Check mailing configuration created through the mail composer."""
|
||||
for use_exclusion_list in (True, False):
|
||||
mass_mailing_name = f'Test Create Mass Mailing From Composer (use_exclusion_list: {use_exclusion_list})'
|
||||
composer = self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records)
|
||||
).create({
|
||||
'body': '<p>Body</p>',
|
||||
'mass_mailing_name': mass_mailing_name,
|
||||
'subject': 'Test',
|
||||
'use_exclusion_list': use_exclusion_list,
|
||||
})
|
||||
composer._action_send_mail()
|
||||
mailing = self.env['mailing.mailing'].search([('name', '=', mass_mailing_name)])
|
||||
self.assertTrue(mailing)
|
||||
self.assertEqual(mailing.body_html, '<p>Body</p>')
|
||||
self.assertEqual(mailing.mailing_domain, f"[('id', 'in', {self.test_records.ids})]")
|
||||
self.assertEqual(mailing.mailing_model_name, self.test_record._name)
|
||||
self.assertEqual(mailing.sent_date, fields.Datetime.now())
|
||||
self.assertEqual(mailing.state, 'done')
|
||||
self.assertEqual(mailing.subject, 'Test')
|
||||
self.assertEqual(mailing.use_exclusion_list, use_exclusion_list)
|
||||
|
|
@ -11,9 +11,33 @@ from odoo.tools import mute_logger, email_normalize
|
|||
@tagged('mass_mailing')
|
||||
class TestMassMailing(TestMassMailCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestMassMailing, cls).setUpClass()
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_mailing_author(self):
|
||||
""" Check the author of the mails sent by a mailing. """
|
||||
mailing = self.env['mailing.mailing'].with_user(self.user_marketing).create({
|
||||
'body_html': '<p>Test</p>',
|
||||
'mailing_domain': [('id', 'in', self.user_employee.partner_id.ids)],
|
||||
'mailing_model_id': self.env['ir.model']._get_id('res.partner'),
|
||||
'name': 'test',
|
||||
'subject': 'Test author',
|
||||
})
|
||||
mailing_2 = mailing.copy({'user_id': self.user_marketing_1.id})
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False):
|
||||
(mailing | mailing_2).with_user(self.env.ref('base.user_root')).action_send_mail()
|
||||
self.assertEqual(
|
||||
self._new_mails.filtered(lambda m: m.model == 'res.partner').mapped('author_id'),
|
||||
self.user_marketing.partner_id | self.user_marketing_1.partner_id,
|
||||
"When a mailing is sent by OdooBot, the author of the mails must be the author of the mailing."
|
||||
)
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False):
|
||||
(mailing | mailing_2).copy().with_user(self.user_marketing_1).action_send_mail()
|
||||
self.assertEqual(
|
||||
self._new_mails.filtered(lambda m: m.model == 'res.partner').mapped('author_id'),
|
||||
self.user_marketing_1.partner_id,
|
||||
"When a mailing is sent by a user, the author of the mails must be the author who has sent the mailing."
|
||||
)
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_thread')
|
||||
|
|
@ -86,8 +110,10 @@ class TestMassMailing(TestMassMailCommon):
|
|||
mailing.action_send_mail()
|
||||
|
||||
self.assertMailTraces(
|
||||
[{'email': record.email_normalized}
|
||||
for record in recipients],
|
||||
[{
|
||||
'email': record.email_normalized,
|
||||
'email_to_mail': record.email_from,
|
||||
} for record in recipients],
|
||||
mailing, recipients,
|
||||
mail_links_info=[[
|
||||
('url0', 'https://www.odoo.tz/my/%s' % record.name, True, {}),
|
||||
|
|
@ -107,16 +133,23 @@ class TestMassMailing(TestMassMailCommon):
|
|||
self.assertMailingStatistics(mailing, expected=5, delivered=5, sent=5)
|
||||
|
||||
# simulate a click
|
||||
self.gateway_mail_click(mailing, recipients[0], 'https://www.odoo.be')
|
||||
self.gateway_mail_trace_click(mailing, recipients[0], 'https://www.odoo.be')
|
||||
mailing.invalidate_recordset()
|
||||
self.assertMailingStatistics(mailing, expected=5, delivered=5, sent=5, opened=1, clicked=1)
|
||||
|
||||
# simulate a bounce
|
||||
self.assertEqual(recipients[1].message_bounce, 0)
|
||||
self.gateway_mail_bounce(mailing, recipients[1])
|
||||
self.gateway_mail_trace_bounce(mailing, recipients[1])
|
||||
mailing.invalidate_recordset()
|
||||
self.assertMailingStatistics(mailing, expected=5, delivered=4, sent=5, opened=1, clicked=1, bounced=1)
|
||||
self.assertEqual(recipients[1].message_bounce, 1)
|
||||
self.assertMailTraces([{
|
||||
'email': 'test.record.01@test.example.com',
|
||||
'email_to_mail': recipients[1].email_from,
|
||||
'failure_reason': 'This is the bounce email',
|
||||
'failure_type': 'mail_bounce',
|
||||
'trace_status': 'bounce',
|
||||
}], mailing, recipients[1], check_mail=False)
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -209,51 +242,57 @@ class TestMassMailing(TestMassMailCommon):
|
|||
# -> email_to_recipients: email_to for outgoing emails, list means several recipients
|
||||
self.assertMailTraces(
|
||||
[
|
||||
{'email': 'customer.multi.1@example.com, "Test Multi 2" <customer.multi.2@example.com>',
|
||||
{'email': 'customer.multi.1@example.com',
|
||||
'email_to_mail': '', # using recipient_ids, not email_to
|
||||
'email_to_recipients': [[f'"{customer_mult.name}" <customer.multi.1@example.com>', f'"{customer_mult.name}" <customer.multi.2@example.com>']],
|
||||
'failure_type': False,
|
||||
'partner': customer_mult,
|
||||
'trace_status': 'sent'},
|
||||
{'email': '"Formatted Customer" <test.customer.format@example.com>',
|
||||
{'email': 'test.customer.format@example.com',
|
||||
'email_to_mail': '', # using recipient_ids, not email_to
|
||||
# mail to avoids double encapsulation
|
||||
'email_to_recipients': [[f'"{customer_fmt.name}" <test.customer.format@example.com>']],
|
||||
'failure_type': False,
|
||||
'partner': customer_fmt,
|
||||
'trace_status': 'sent'},
|
||||
{'email': '"Unicode Customer" <test.customer.😊@example.com>',
|
||||
{'email': 'test.customer.😊@example.com',
|
||||
'email_to_mail': '', # using recipient_ids, not email_to
|
||||
# mail to avoids double encapsulation
|
||||
'email_to_recipients': [[f'"{customer_unic.name}" <test.customer.😊@example.com>']],
|
||||
'failure_type': False,
|
||||
'partner': customer_unic,
|
||||
'trace_status': 'sent'},
|
||||
{'email': 'TEST.CUSTOMER.CASE@EXAMPLE.COM',
|
||||
{'email': 'test.customer.case@example.com',
|
||||
'email_to_mail': '', # using recipient_ids, not email_to
|
||||
'email_to_recipients': [[f'"{customer_case.name}" <test.customer.case@example.com>']],
|
||||
'failure_type': False,
|
||||
'partner': customer_case,
|
||||
'trace_status': 'sent'}, # lower cased
|
||||
{'email': 'test.customer.weird@example.com Weird Format',
|
||||
{'email': 'test.customer.weird@example.comweirdformat',
|
||||
'email_to_mail': '', # using recipient_ids, not email_to
|
||||
'email_to_recipients': [[f'"{customer_weird.name}" <test.customer.weird@example.comweirdformat>']],
|
||||
'failure_type': False,
|
||||
'partner': customer_weird,
|
||||
'trace_status': 'sent'}, # concatenates everything after domain
|
||||
{'email': 'Weird Format2 test.customer.weird.2@example.com',
|
||||
{'email': 'test.customer.weird.2@example.com',
|
||||
'email_to_mail': '', # using recipient_ids, not email_to
|
||||
'email_to_recipients': [[f'"{customer_weird_2.name}" <test.customer.weird.2@example.com>']],
|
||||
'failure_type': False,
|
||||
'partner': customer_weird_2,
|
||||
'trace_status': 'sent'},
|
||||
{'email': 'record.multi.1@example.com',
|
||||
'email_to_mail': 'record.multi.1@example.com,record.multi.2@example.com',
|
||||
'email_to_recipients': [['record.multi.1@example.com', 'record.multi.2@example.com']],
|
||||
'email_to_mail': 'record.multi.1@example.com,"Record Multi 2" <record.multi.2@example.com>',
|
||||
'email_to_recipients': [['record.multi.1@example.com', '"Record Multi 2" <record.multi.2@example.com>']],
|
||||
'failure_type': False,
|
||||
'trace_status': 'sent'},
|
||||
{'email': 'record.format@example.com',
|
||||
'email_to_mail': 'record.format@example.com',
|
||||
'email_to_recipients': [['record.format@example.com']],
|
||||
'email_to_mail': '"Formatted Record" <record.format@example.com>',
|
||||
'email_to_recipients': [['"Formatted Record" <record.format@example.com>']],
|
||||
'failure_type': False,
|
||||
'trace_status': 'sent'},
|
||||
{'email': 'record.😊@example.com',
|
||||
'email_to_mail': 'record.😊@example.com',
|
||||
'email_to_recipients': [['record.😊@example.com']],
|
||||
'email_to_mail': '"Unicode Record" <record.😊@example.com>',
|
||||
'email_to_recipients': [['"Unicode Record" <record.😊@example.com>']],
|
||||
'failure_type': False,
|
||||
'trace_status': 'sent'},
|
||||
{'email': 'test.record.case@example.com',
|
||||
|
|
@ -267,8 +306,8 @@ class TestMassMailing(TestMassMailCommon):
|
|||
'failure_type': False,
|
||||
'trace_status': 'sent'},
|
||||
{'email': 'test.record.weird.2@example.com',
|
||||
'email_to_mail': 'test.record.weird.2@example.com',
|
||||
'email_to_recipients': [['test.record.weird.2@example.com']],
|
||||
'email_to_mail': '"Weird Format2" <test.record.weird.2@example.com>',
|
||||
'email_to_recipients': [['"Weird Format2" <test.record.weird.2@example.com>']],
|
||||
'failure_type': False,
|
||||
'trace_status': 'sent'},
|
||||
],
|
||||
|
|
@ -294,7 +333,11 @@ class TestMassMailing(TestMassMailCommon):
|
|||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
mailing.action_send_mail()
|
||||
|
||||
answer_rec = self.gateway_mail_reply_wemail(MAIL_TEMPLATE, recipients[0].email_normalized, target_model=self.test_alias.alias_model_id.model)
|
||||
answer_rec = self.gateway_mail_reply_wemail(
|
||||
MAIL_TEMPLATE,
|
||||
recipients[0].email_from,
|
||||
target_model=self.test_alias.alias_model_id.model,
|
||||
)
|
||||
self.assertTrue(bool(answer_rec))
|
||||
self.assertEqual(answer_rec.name, 'Re: %s' % mailing.subject)
|
||||
self.assertEqual(
|
||||
|
|
@ -320,7 +363,11 @@ class TestMassMailing(TestMassMailCommon):
|
|||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
mailing.action_send_mail()
|
||||
|
||||
answer_rec = self.gateway_mail_reply_wemail(MAIL_TEMPLATE, recipients[0].email_normalized, target_model=self.test_alias.alias_model_id.model)
|
||||
answer_rec = self.gateway_mail_reply_wemail(
|
||||
MAIL_TEMPLATE,
|
||||
recipients[0].email_from,
|
||||
target_model=self.test_alias.alias_model_id.model,
|
||||
)
|
||||
self.assertFalse(bool(answer_rec))
|
||||
self.assertEqual(
|
||||
recipients[0].message_ids[1].subject, mailing.subject,
|
||||
|
|
@ -388,8 +435,8 @@ class TestMassMailing(TestMassMailCommon):
|
|||
self.env['mail.blacklist'].create({'email': recipients[4].email_normalized})
|
||||
|
||||
# unblacklist record 2
|
||||
self.env['mail.blacklist'].action_remove_with_reason(
|
||||
recipients[2].email_normalized, "human error"
|
||||
self.env['mail.blacklist']._remove(
|
||||
recipients[2].email_normalized, message="human error"
|
||||
)
|
||||
self.env['mail.blacklist'].flush_model(['active'])
|
||||
|
||||
|
|
@ -398,14 +445,36 @@ class TestMassMailing(TestMassMailCommon):
|
|||
mailing.action_send_mail()
|
||||
|
||||
self.assertMailTraces(
|
||||
[{'email': 'test.record.00@test.example.com'},
|
||||
{'email': 'test.record.01@test.example.com'},
|
||||
{'email': 'test.record.02@test.example.com'},
|
||||
{'email': 'test.record.03@test.example.com', 'trace_status': 'cancel', 'failure_type': 'mail_bl'},
|
||||
{'email': 'test.record.04@test.example.com', 'trace_status': 'cancel', 'failure_type': 'mail_bl'}],
|
||||
mailing, recipients, check_mail=True
|
||||
[{
|
||||
'email': record.email_normalized,
|
||||
'email_to_mail': record.email_from,
|
||||
} for record in recipients[:3]] + [{
|
||||
'email': record.email_normalized,
|
||||
'email_to_mail': record.email_from,
|
||||
'failure_type': 'mail_bl',
|
||||
'trace_status': 'cancel',
|
||||
} for record in recipients[3:]],
|
||||
mailing, recipients
|
||||
)
|
||||
self.assertEqual(mailing.canceled, 2)
|
||||
self.assertEqual(len(self.env['mail.mail'].sudo().search([('mailing_id', '=', mailing.id)])), 3,
|
||||
"Only the 3 sent mails have been created, the canceled ones have not been created")
|
||||
|
||||
# Same test but with the option use_exclusion_list set to False
|
||||
mailing = mailing.copy()
|
||||
mailing.use_exclusion_list = False
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False):
|
||||
mailing.action_send_mail()
|
||||
|
||||
self.assertMailTraces(
|
||||
[{
|
||||
'email': record.email_normalized,
|
||||
'email_to_mail': record.email_from,
|
||||
} for record in recipients],
|
||||
mailing, recipients, check_mail=True
|
||||
)
|
||||
self.assertEqual(mailing.canceled, 0)
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -413,6 +482,9 @@ class TestMassMailing(TestMassMailCommon):
|
|||
"""Test that blacklist is applied even if the target model doesn't inherit
|
||||
from mail.thread.blacklist."""
|
||||
test_records = self._create_mailing_test_records(model='mailing.test.simple', count=2)
|
||||
# Normalize email_from for assertMailTraces
|
||||
for test_record in test_records:
|
||||
test_record.email_from = email_normalize(test_record.email_from)
|
||||
self.mailing_bl.write({
|
||||
'mailing_domain': [('id', 'in', test_records.ids)],
|
||||
'mailing_model_id': self.env['ir.model']._get('mailing.test.simple').id,
|
||||
|
|
@ -425,9 +497,26 @@ class TestMassMailing(TestMassMailCommon):
|
|||
with self.mock_mail_gateway(mail_unlink_sent=False):
|
||||
self.mailing_bl.action_send_mail()
|
||||
self.assertMailTraces([
|
||||
{'email': email_normalize(test_records[0].email_from), 'trace_status': 'cancel', 'failure_type': 'mail_bl'},
|
||||
{'email': email_normalize(test_records[1].email_from), 'trace_status': 'sent'},
|
||||
], self.mailing_bl, test_records, check_mail=False)
|
||||
{'email': test_records[0].email_from, 'trace_status': 'cancel', 'failure_type': 'mail_bl'},
|
||||
{'email': test_records[1].email_from, 'trace_status': 'sent'},
|
||||
], self.mailing_bl, test_records, check_mail=True)
|
||||
|
||||
self.assertEqual(self.mailing_bl.canceled, 1)
|
||||
self.assertEqual(len(self.env['mail.mail'].sudo().search([('mailing_id', '=', self.mailing_bl.id)])), 1,
|
||||
"Only the sent mail has been created, the canceled one has not been created")
|
||||
|
||||
# Same test but with the option use_exclusion_list set to False
|
||||
mailing_no_blacklist = self.mailing_bl.copy()
|
||||
mailing_no_blacklist.use_exclusion_list = False
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False):
|
||||
mailing_no_blacklist.action_send_mail()
|
||||
|
||||
self.assertMailTraces([
|
||||
{'email': test_records[0].email_from, 'trace_status': 'sent'},
|
||||
{'email': test_records[1].email_from, 'trace_status': 'sent'},
|
||||
], mailing_no_blacklist, test_records, check_mail=True)
|
||||
self.assertEqual(mailing_no_blacklist.canceled, 0)
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -448,14 +537,23 @@ class TestMassMailing(TestMassMailCommon):
|
|||
mailing.action_send_mail()
|
||||
|
||||
self.assertMailTraces(
|
||||
[{'email': 'test.record.00@test.example.com', 'trace_status': 'cancel', 'failure_type': 'mail_optout'},
|
||||
{'email': 'test.record.01@test.example.com', 'trace_status': 'cancel', 'failure_type': 'mail_optout'},
|
||||
{'email': 'test.record.02@test.example.com'},
|
||||
{'email': 'test.record.03@test.example.com'},
|
||||
{'email': 'test.record.04@test.example.com', 'trace_status': 'cancel', 'failure_type': 'mail_bl'}],
|
||||
mailing, recipients, check_mail=True
|
||||
)
|
||||
[{
|
||||
'email': record.email_normalized,
|
||||
'email_to_mail': record.email_from,
|
||||
'failure_type': 'mail_optout',
|
||||
'trace_status': 'cancel',
|
||||
} for record in recipients[:2]] + [{
|
||||
'email': record.email_normalized,
|
||||
'email_to_mail': record.email_from,
|
||||
} for record in recipients[2:4]] + [{
|
||||
'email': record.email_normalized,
|
||||
'email_to_mail': record.email_from,
|
||||
'failure_type': 'mail_bl',
|
||||
'trace_status': 'cancel'
|
||||
} for record in recipients[4:]], mailing, recipients)
|
||||
self.assertEqual(mailing.canceled, 3)
|
||||
self.assertEqual(len(self.env['mail.mail'].sudo().search([('mailing_id', '=', self.mailing_bl.id)])), 2,
|
||||
"Only the 2 sent mails have been created, the canceled ones have not been created")
|
||||
|
||||
@users('user_marketing')
|
||||
def test_mailing_w_seenlist(self):
|
||||
|
|
@ -481,7 +579,7 @@ class TestMassMailing(TestMassMailCommon):
|
|||
|
||||
with self.mock_mail_gateway():
|
||||
for i in range(0, 20, BATCH_SIZE):
|
||||
mailing.action_send_mail(records[i:i + BATCH_SIZE].mapped('id'))
|
||||
mailing.action_send_mail(records[i:i + BATCH_SIZE].ids)
|
||||
self.assertEqual(len(self._mails), BATCH_SIZE)
|
||||
self.assertEqual(mailing.canceled, 15)
|
||||
mails_sent = [email_normalize(mail['email_to'][0]) for mail in self._mails]
|
||||
|
|
@ -532,11 +630,13 @@ class TestMassMailing(TestMassMailCommon):
|
|||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_mailing_mailing_list_optout(self):
|
||||
""" Test mailing list model specific optout behavior """
|
||||
mailing_contact_1 = self.env['mailing.contact'].create({'name': 'test 1A', 'email': 'test@test.example.com'})
|
||||
mailing_contact_2 = self.env['mailing.contact'].create({'name': 'test 1B', 'email': 'test@test.example.com'})
|
||||
# as duplication checks body and subject, we create 2 exact copies to make sure only 1 is sent
|
||||
mailing_contact_1 = self.env['mailing.contact'].create({'name': 'test 1', 'email': 'test@test.example.com'})
|
||||
mailing_contact_2 = self.env['mailing.contact'].create({'name': 'test 1', 'email': 'test@test.example.com'})
|
||||
mailing_contact_3 = self.env['mailing.contact'].create({'name': 'test 3', 'email': 'test3@test.example.com'})
|
||||
mailing_contact_4 = self.env['mailing.contact'].create({'name': 'test 4', 'email': 'test4@test.example.com'})
|
||||
mailing_contact_5 = self.env['mailing.contact'].create({'name': 'test 5', 'email': 'test5@test.example.com'})
|
||||
records = mailing_contact_1 + mailing_contact_2 + mailing_contact_3 + mailing_contact_4 + mailing_contact_5
|
||||
|
||||
# create mailing list record
|
||||
mailing_list_1 = self.env['mailing.list'].create({
|
||||
|
|
@ -558,7 +658,7 @@ class TestMassMailing(TestMassMailCommon):
|
|||
# contact_1 is optout but same email is not optout from the same list
|
||||
# contact 3 is optout in list 1 but not in list 2
|
||||
# contact 5 is optout
|
||||
subs = self.env['mailing.contact.subscription'].search([
|
||||
subs = self.env['mailing.subscription'].search([
|
||||
'|', '|',
|
||||
'&', ('contact_id', '=', mailing_contact_1.id), ('list_id', '=', mailing_list_1.id),
|
||||
'&', ('contact_id', '=', mailing_contact_3.id), ('list_id', '=', mailing_list_1.id),
|
||||
|
|
@ -578,13 +678,12 @@ class TestMassMailing(TestMassMailCommon):
|
|||
mailing.action_send_mail()
|
||||
|
||||
self.assertMailTraces(
|
||||
[{'email': 'test@test.example.com', 'trace_status': 'sent'},
|
||||
{'email': 'test@test.example.com', 'trace_status': 'cancel', 'failure_type': 'mail_dup'},
|
||||
[{'email': 'test@test.example.com', 'trace_status': 'cancel', 'failure_type': 'mail_dup'},
|
||||
{'email': 'test@test.example.com', 'trace_status': 'sent'},
|
||||
{'email': 'test3@test.example.com'},
|
||||
{'email': 'test4@test.example.com'},
|
||||
{'email': 'test5@test.example.com', 'trace_status': 'cancel', 'failure_type': 'mail_optout'}],
|
||||
mailing,
|
||||
mailing_contact_1 + mailing_contact_2 + mailing_contact_3 + mailing_contact_4 + mailing_contact_5,
|
||||
check_mail=True
|
||||
)
|
||||
self.assertEqual(mailing.canceled, 2)
|
||||
|
|
|
|||
|
|
@ -8,13 +8,12 @@ from odoo.tests.common import users
|
|||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
@tagged('mass_mailing')
|
||||
@tagged('mass_mailing', 'mail_server')
|
||||
class TestMassMailingServer(TestMassMailCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestMassMailingServer, cls).setUpClass()
|
||||
cls._init_mail_servers()
|
||||
cls.recipients = cls._create_mailing_test_records(model='mailing.test.optout', count=8)
|
||||
|
||||
def test_mass_mailing_server_archived_usage_protection(self):
|
||||
|
|
@ -73,98 +72,48 @@ class TestMassMailingServer(TestMassMailCommon):
|
|||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.addons.mass_mailing.models.mailing')
|
||||
def test_mass_mailing_server_batch(self):
|
||||
def test_mass_mailing_server_find(self):
|
||||
"""Test that the right mail server is chosen to send the mailing.
|
||||
|
||||
Test also the envelop and the SMTP headers.
|
||||
"""
|
||||
# Test sending mailing in batch
|
||||
mailings = self.env['mailing.mailing'].create([{
|
||||
'subject': 'Mailing',
|
||||
'body_html': 'Body for <t t-out="object.name" />',
|
||||
'email_from': 'specific_user@test.com',
|
||||
'mailing_model_id': self.env['ir.model']._get('mailing.test.optout').id,
|
||||
'email_from': 'specific_user@test.mycompany.com',
|
||||
'mailing_model_id': self.env['ir.model']._get_id('mailing.test.optout'),
|
||||
}, {
|
||||
'subject': 'Mailing',
|
||||
'body_html': 'Body for <t t-out="object.name" />',
|
||||
'email_from': 'unknown_name@test.com',
|
||||
'mailing_model_id': self.env['ir.model']._get('mailing.test.optout').id,
|
||||
}])
|
||||
with self.mock_smtplib_connection():
|
||||
mailings.action_send_mail()
|
||||
self.assertEqual(self.find_mail_server_mocked.call_count, 2, 'Must be called only once per mail from')
|
||||
|
||||
self.assert_email_sent_smtp(
|
||||
smtp_from='specific_user@test.com',
|
||||
message_from='specific_user@test.com',
|
||||
from_filter=self.server_user.from_filter,
|
||||
emails_count=8,
|
||||
)
|
||||
|
||||
self.assert_email_sent_smtp(
|
||||
# Must use the bounce address here because the mail server
|
||||
# is configured for the entire domain "test.com"
|
||||
smtp_from=lambda x: 'bounce' in x,
|
||||
message_from='unknown_name@test.com',
|
||||
from_filter=self.server_domain.from_filter,
|
||||
emails_count=8,
|
||||
)
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.addons.mass_mailing.models.mailing')
|
||||
def test_mass_mailing_server_default(self):
|
||||
# We do not have a mail server for this address email, so fall back to the
|
||||
# "notifications@domain" email.
|
||||
mailings = self.env['mailing.mailing'].create([{
|
||||
'email_from': 'unknown_name@test.mycompany.com',
|
||||
'mailing_model_id': self.env['ir.model']._get_id('mailing.test.optout'),
|
||||
}, {
|
||||
'subject': 'Mailing',
|
||||
'body_html': 'Body for <t t-out="object.name" />',
|
||||
'email_from': '"Testing" <unknow_email@unknow_domain.com>',
|
||||
'mailing_model_id': self.env['ir.model']._get('mailing.test.optout').id,
|
||||
}])
|
||||
|
||||
with self.mock_smtplib_connection():
|
||||
mailings.action_send_mail()
|
||||
|
||||
self.assertEqual(self.find_mail_server_mocked.call_count, 1)
|
||||
self.assert_email_sent_smtp(
|
||||
smtp_from='notifications@test.com',
|
||||
message_from='"Testing" <notifications@test.com>',
|
||||
from_filter=self.server_notification.from_filter,
|
||||
emails_count=8,
|
||||
)
|
||||
|
||||
self.assertEqual(self.find_mail_server_mocked.call_count, 1, 'Must be called only once')
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink', 'odoo.addons.mass_mailing.models.mailing')
|
||||
def test_mass_mailing_server_forced(self):
|
||||
# We force a mail server on one mailing
|
||||
mailings = self.env['mailing.mailing'].create([{
|
||||
'subject': 'Mailing',
|
||||
'body_html': 'Body for <t t-out="object.name" />',
|
||||
'email_from': self.server_user.from_filter,
|
||||
'mailing_model_id': self.env['ir.model']._get('mailing.test.optout').id,
|
||||
'mailing_model_id': self.env['ir.model']._get_id('mailing.test.optout'),
|
||||
}, {
|
||||
'subject': 'Mailing',
|
||||
'subject': 'Mailing Forced',
|
||||
'body_html': 'Body for <t t-out="object.name" />',
|
||||
'email_from': 'unknow_email@unknow_domain.com',
|
||||
'mailing_model_id': self.env['ir.model']._get('mailing.test.optout').id,
|
||||
'mail_server_id': self.server_notification.id,
|
||||
'mailing_model_id': self.env['ir.model']._get_id('mailing.test.optout'),
|
||||
'mail_server_id': self.mail_server_notification.id,
|
||||
}])
|
||||
with self.mock_smtplib_connection():
|
||||
mailings.action_send_mail()
|
||||
self.assertEqual(self.find_mail_server_mocked.call_count, 1, 'Must not be called when mail server is forced')
|
||||
self.assertEqual(self.find_mail_server_mocked.call_count, 3, 'Must be called only once per mail from except when forced')
|
||||
|
||||
self.assert_email_sent_smtp(
|
||||
smtp_from='specific_user@test.com',
|
||||
message_from='specific_user@test.com',
|
||||
from_filter=self.server_user.from_filter,
|
||||
emails_count=8,
|
||||
)
|
||||
|
||||
self.assert_email_sent_smtp(
|
||||
smtp_from='unknow_email@unknow_domain.com',
|
||||
message_from='unknow_email@unknow_domain.com',
|
||||
from_filter=self.server_notification.from_filter,
|
||||
emails_count=8,
|
||||
)
|
||||
for (expected_smtp_from, expected_msg_from, expected_mail_server) in [
|
||||
('specific_user@test.mycompany.com', 'specific_user@test.mycompany.com', self.mail_server_user),
|
||||
(f'{self.alias_bounce}@{self.alias_domain}', 'unknown_name@test.mycompany.com', self.mail_server_domain),
|
||||
# We do not have a mail server for this address email, so fall back to the "notifications@domain" email.
|
||||
(f'{self.default_from}@{self.alias_domain}', f'"Testing" <{self.default_from}@{self.alias_domain}>', self.mail_server_notification),
|
||||
# forced sever
|
||||
('unknow_email@unknow_domain.com', 'unknow_email@unknow_domain.com', self.mail_server_notification),
|
||||
]:
|
||||
self.assertSMTPEmailsSent(
|
||||
smtp_from=expected_smtp_from,
|
||||
message_from=expected_msg_from,
|
||||
mail_server=expected_mail_server,
|
||||
emails_count=8,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
from ast import literal_eval
|
||||
|
||||
from odoo.addons.phone_validation.tools import phone_validation
|
||||
from odoo.addons.sms_twilio.tests.common import MockSmsTwilioApi
|
||||
from odoo.addons.test_mass_mailing.tests.common import TestMassSMSCommon
|
||||
from odoo import exceptions
|
||||
from odoo.tests import tagged
|
||||
|
|
@ -30,7 +31,7 @@ class TestMassSMSInternals(TestMassSMSCommon):
|
|||
'mailing_model_id': self.env['ir.model']._get('mail.test.sms.bl').id,
|
||||
'mailing_type': 'sms',
|
||||
})
|
||||
self.assertEqual(literal_eval(mailing.mailing_domain), [('phone_sanitized_blacklisted', '=', False)])
|
||||
self.assertEqual(literal_eval(mailing.mailing_domain), [])
|
||||
|
||||
@users('user_marketing')
|
||||
def test_mass_sms_internals(self):
|
||||
|
|
@ -97,7 +98,7 @@ class TestMassSMSInternals(TestMassSMSCommon):
|
|||
nr2_partner = self.env['res.partner'].create({
|
||||
'name': 'Partner_nr2',
|
||||
'country_id': country_be_id,
|
||||
'mobile': '0456449999',
|
||||
'phone': '0456449999',
|
||||
})
|
||||
new_record_2 = self.env['mail.test.sms'].create({
|
||||
'name': 'MassSMSTest_nr2',
|
||||
|
|
@ -107,17 +108,27 @@ class TestMassSMSInternals(TestMassSMSCommon):
|
|||
records_numbers = self.records_numbers + ['+32456999999']
|
||||
|
||||
mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids)
|
||||
mailing.write({'sms_force_send': False}) # force outgoing sms, not sent
|
||||
mailing.write({
|
||||
'sms_force_send': False, # force outgoing sms, not sent
|
||||
'keep_archives': True, # keep a note on document (mass_keep_log)
|
||||
})
|
||||
with self.with_user('user_marketing'):
|
||||
with self.mockSMSGateway():
|
||||
mailing.action_send_sms()
|
||||
|
||||
valid_records = self.records | new_record_1
|
||||
self.assertSMSTraces(
|
||||
[{'partner': record.customer_id, 'number': records_numbers[i],
|
||||
'content': 'Dear %s this is a mass SMS' % record.display_name}
|
||||
for i, record in enumerate(self.records | new_record_1)],
|
||||
mailing, self.records | new_record_1,
|
||||
mailing, valid_records,
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.env['mail.message'].search([
|
||||
('res_id', 'in', valid_records.ids), ('model', '=', 'mail.test.sms'),
|
||||
('id', 'in', self._new_sms.mail_message_id.ids)])),
|
||||
len(valid_records),
|
||||
"Only not canceled message must be logged in the chatter")
|
||||
# duplicates
|
||||
self.assertSMSTraces(
|
||||
[{'partner': new_record_2.customer_id, 'number': self.records_numbers[0],
|
||||
|
|
@ -147,11 +158,30 @@ class TestMassSMSInternals(TestMassSMSCommon):
|
|||
for record in falsy_record_1 + falsy_record_2],
|
||||
mailing, falsy_record_1 + falsy_record_2,
|
||||
)
|
||||
self.assertEqual(mailing.canceled, 5)
|
||||
|
||||
# Same test using use_exclusion_list = False
|
||||
mailing_no_blacklist = mailing.copy()
|
||||
mailing_no_blacklist.use_exclusion_list = False
|
||||
with self.with_user('user_marketing'):
|
||||
with self.mockSMSGateway():
|
||||
mailing_no_blacklist.action_send_sms()
|
||||
|
||||
self.assertSMSTraces(
|
||||
[{'partner': bl_record_1.customer_id,
|
||||
'number': phone_validation.phone_format(bl_record_1.phone_nbr, 'BE', '32', force_format='E164'),
|
||||
'content': 'Dear %s this is a mass SMS' % bl_record_1.display_name}],
|
||||
mailing_no_blacklist, bl_record_1,
|
||||
)
|
||||
self.assertEqual(mailing_no_blacklist.canceled, 4)
|
||||
|
||||
@users('user_marketing')
|
||||
def test_mass_sms_internals_done_ids(self):
|
||||
mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids)
|
||||
mailing.write({'sms_force_send': False}) # check with outgoing traces, not already sent
|
||||
mailing.write({
|
||||
'sms_force_send': False, # check with outgoing traces, not already pending
|
||||
'keep_archives': True, # keep a note on document (mass_keep_log)
|
||||
})
|
||||
|
||||
with self.with_user('user_marketing'):
|
||||
with self.mockSMSGateway():
|
||||
|
|
@ -189,6 +219,75 @@ class TestMassSMSInternals(TestMassSMSCommon):
|
|||
mailing, self.records[5:],
|
||||
)
|
||||
|
||||
@users('user_marketing')
|
||||
def test_mass_sms_processing_with_force_send(self):
|
||||
"""Test that a `processing` status returned by IAP is immediately applied to traces and mailing stays sending.
|
||||
|
||||
The status update case where the sms are sent via the cron is done in TestSmsController.
|
||||
"""
|
||||
with self.with_user('user_marketing'):
|
||||
mailing = self.env['mailing.mailing'].create({
|
||||
'name': 'Xmas Spam',
|
||||
'subject': 'Xmas Spam',
|
||||
'mailing_model_id': self.env['ir.model']._get('mail.test.sms').id,
|
||||
'mailing_type': 'sms',
|
||||
'mailing_domain': '%s' % repr([('name', 'ilike', 'MassSMSTest')]),
|
||||
'sms_template_id': self.sms_template.id,
|
||||
'sms_allow_unsubscribe': False,
|
||||
})
|
||||
|
||||
remaining_res_ids = mailing._get_remaining_recipients()
|
||||
self.assertEqual(set(remaining_res_ids), set(self.records.ids))
|
||||
|
||||
mailing.sms_force_send = True
|
||||
with self.mockSMSGateway(moderated=True):
|
||||
mailing.action_send_sms()
|
||||
|
||||
self.assertEqual(mailing.state, 'sending')
|
||||
self.assertSMSTraces(
|
||||
[{'partner': record.customer_id,
|
||||
'number': self.records_numbers[i],
|
||||
'content': 'Dear %s this is a mass SMS.' % record.display_name,
|
||||
'trace_status': 'process',
|
||||
} for i, record in enumerate(self.records)],
|
||||
mailing, self.records,
|
||||
)
|
||||
|
||||
@users('user_marketing')
|
||||
def test_mass_sms_error_with_force_send(self):
|
||||
"""Test that a failed status returned by IAP is immediately applied to traces."""
|
||||
with self.with_user('user_marketing'):
|
||||
mailing = self.env['mailing.mailing'].create({
|
||||
'name': 'Xmas Spam',
|
||||
'subject': 'Xmas Spam',
|
||||
'mailing_model_id': self.env['ir.model']._get('mail.test.sms').id,
|
||||
'mailing_type': 'sms',
|
||||
'mailing_domain': '%s' % repr([('name', 'ilike', 'MassSMSTest')]),
|
||||
'sms_template_id': self.sms_template.id,
|
||||
'sms_allow_unsubscribe': False,
|
||||
})
|
||||
|
||||
remaining_res_ids = mailing._get_remaining_recipients()
|
||||
self.assertEqual(set(remaining_res_ids), set(self.records.ids))
|
||||
|
||||
mailing.sms_force_send = True
|
||||
with self.mockSMSGateway(sim_error='server_error'):
|
||||
mailing.action_send_sms()
|
||||
|
||||
self.assertSMSTraces(
|
||||
[{'partner': record.customer_id,
|
||||
'number': self.records_numbers[i],
|
||||
'content': 'Dear %s this is a mass SMS.' % record.display_name,
|
||||
'trace_status': 'error',
|
||||
'failure_type': 'sms_server'
|
||||
} for i, record in enumerate(self.records)],
|
||||
mailing, self.records,
|
||||
)
|
||||
|
||||
|
||||
@tagged('mass_mailing', 'mass_mailing_sms', 'mailing_test')
|
||||
class TestMassSMSTest(TestMassSMSCommon):
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_render_mixin')
|
||||
def test_mass_sms_test_button(self):
|
||||
mailing = self.env['mailing.mailing'].create({
|
||||
|
|
@ -199,15 +298,23 @@ class TestMassSMSInternals(TestMassSMSCommon):
|
|||
'mailing_type': 'sms',
|
||||
'body_plaintext': 'Hello {{ object.name }}',
|
||||
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
||||
'sms_allow_unsubscribe': True,
|
||||
})
|
||||
mailing_test = self.env['mailing.sms.test'].with_user(self.user_marketing).create({
|
||||
'numbers': '+32456001122',
|
||||
'numbers': '+32456001122\n+32455334455\nwrong\n\n',
|
||||
'mailing_id': mailing.id,
|
||||
})
|
||||
|
||||
with self.with_user('user_marketing'):
|
||||
with self.mockSMSGateway():
|
||||
mailing_test.action_send_sms()
|
||||
new_traces = self.env['mailing.trace'].search([('mass_mailing_id', '=', mailing.id)])
|
||||
self.assertEqual(len(new_traces), 2, 'Should have create 1 trace / valid number')
|
||||
self.assertEqual(new_traces.mapped('is_test_trace'), [True, True], 'Traces should be flagged as test')
|
||||
self.assertEqual(
|
||||
sorted(new_traces.mapped('sms_number')),
|
||||
['+32455334455', '+32456001122']
|
||||
)
|
||||
|
||||
# Test if bad inline_template in the body raises an error
|
||||
mailing.write({
|
||||
|
|
@ -238,7 +345,7 @@ class TestMassSMS(TestMassSMSCommon):
|
|||
self.assertSMSTraces(
|
||||
[{'partner': record.customer_id,
|
||||
'number': self.records_numbers[i],
|
||||
'trace_status': 'sent',
|
||||
'trace_status': 'pending',
|
||||
'content': 'Dear %s this is a mass SMS with two links' % record.display_name
|
||||
} for i, record in enumerate(self.records)],
|
||||
mailing, self.records,
|
||||
|
|
@ -273,7 +380,7 @@ class TestMassSMS(TestMassSMSCommon):
|
|||
self.assertSMSTraces(
|
||||
[{'partner': record.customer_id,
|
||||
'number': record.customer_id.phone_sanitized,
|
||||
'trace_status': 'sent',
|
||||
'trace_status': 'pending',
|
||||
'content': 'Dear %s this is a mass SMS with two links' % record.display_name
|
||||
} for record in records],
|
||||
mailing, records,
|
||||
|
|
@ -298,7 +405,7 @@ class TestMassSMS(TestMassSMSCommon):
|
|||
self.assertSMSTraces(
|
||||
[{'partner': new_record.customer_id,
|
||||
'number': new_record.customer_id.phone_sanitized,
|
||||
'trace_status': 'sent',
|
||||
'trace_status': 'pending',
|
||||
'content': 'Dear %s this is a mass SMS with two links' % new_record.display_name
|
||||
}],
|
||||
mailing, new_record,
|
||||
|
|
@ -345,6 +452,7 @@ class TestMassSMS(TestMassSMSCommon):
|
|||
mailing.write({
|
||||
'mailing_model_id': self.env['ir.model']._get('mail.test.sms.bl.optout'),
|
||||
'mailing_domain': [('id', 'in', recipients.ids)],
|
||||
'keep_archives': True, # keep a note on document (mass_keep_log)
|
||||
})
|
||||
|
||||
with self.mockSMSGateway():
|
||||
|
|
@ -353,9 +461,84 @@ class TestMassSMS(TestMassSMSCommon):
|
|||
self.assertSMSTraces(
|
||||
[{'number': '+32456000000', 'trace_status': 'cancel', 'failure_type': 'sms_optout'},
|
||||
{'number': '+32456000101', 'trace_status': 'cancel', 'failure_type': 'sms_optout'},
|
||||
{'number': '+32456000202', 'trace_status': 'sent'},
|
||||
{'number': '+32456000303', 'trace_status': 'sent'},
|
||||
{'number': '+32456000202', 'trace_status': 'pending'},
|
||||
{'number': '+32456000303', 'trace_status': 'pending'},
|
||||
{'number': '+32456000404', 'trace_status': 'cancel', 'failure_type': 'sms_blacklist'}],
|
||||
mailing, recipients
|
||||
)
|
||||
self.assertEqual(mailing.canceled, 3)
|
||||
|
||||
|
||||
@tagged('mass_mailing', 'mass_mailing_sms', 'twilio')
|
||||
class TestMassSMSTwilio(TestMassSMSCommon, MockSmsTwilioApi):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls._setup_sms_twilio(cls.user_admin.company_id)
|
||||
|
||||
cls.mailing_sms.write({
|
||||
'body_plaintext': 'This is a mass SMS',
|
||||
'sms_template_id': False,
|
||||
'sms_force_send': True,
|
||||
'sms_allow_unsubscribe': False,
|
||||
})
|
||||
cls.records_fail = cls.env['mail.test.sms'].create([
|
||||
{
|
||||
'name': 'MassSMSTest- No Number',
|
||||
'phone_nbr': False,
|
||||
}, {
|
||||
'name': 'MassSMSTest- Invalid Number',
|
||||
'phone_nbr': '1234',
|
||||
},
|
||||
])
|
||||
cls.records += cls.records_fail
|
||||
cls.records_numbers += [False, '1234']
|
||||
|
||||
@users('user_marketing')
|
||||
def test_mass_sms(self):
|
||||
""" Test SMS marketing using twilio """
|
||||
mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids)
|
||||
|
||||
with self.mock_sms_twilio_gateway():
|
||||
mailing.action_send_sms()
|
||||
|
||||
exp_failure_types = [False] * 10 + ['sms_number_missing', 'sms_number_format']
|
||||
self.assertSMSTraces(
|
||||
[{
|
||||
'failure_type': exp_failure_types[i],
|
||||
'partner': record.customer_id,
|
||||
'number': self.records_numbers[i],
|
||||
'trace_status': 'pending' if record not in self.records_fail else 'cancel',
|
||||
'content': 'This is a mass SMS',
|
||||
} for i, record in enumerate(self.records)],
|
||||
mailing,
|
||||
self.records,
|
||||
)
|
||||
|
||||
@users('user_marketing')
|
||||
def test_mass_sms_twilio_issue(self):
|
||||
""" Test specific propagation / update to trace failure_type from twilio
|
||||
errors """
|
||||
for error_type, exp_failure_type in [
|
||||
("twilio_acc_unverified", "sms_acc"),
|
||||
("twilio_callback", "twilio_callback"),
|
||||
]:
|
||||
with self.subTest(error_type=error_type):
|
||||
mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids).copy()
|
||||
|
||||
with self.mock_sms_twilio_gateway(error_type=error_type):
|
||||
mailing.action_send_sms()
|
||||
|
||||
exp_failure_types = [exp_failure_type] * 10 + ['sms_number_missing', 'sms_number_format']
|
||||
self.assertSMSTraces(
|
||||
[{
|
||||
'failure_type': exp_failure_types[i],
|
||||
'partner': record.customer_id,
|
||||
'number': self.records_numbers[i],
|
||||
'trace_status': 'error' if record not in self.records_fail else 'cancel',
|
||||
'content': 'This is a mass SMS',
|
||||
} for i, record in enumerate(self.records)],
|
||||
mailing,
|
||||
self.records,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -29,8 +29,9 @@ class TestMailingStatistics(TestMassMailCommon):
|
|||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mass_mailing.models.mailing', 'odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_thread')
|
||||
def test_mailing_statistics(self):
|
||||
target_records = self._create_mailing_test_records(model='mailing.test.blacklist', count=11)
|
||||
target_records = self._create_mailing_test_records(model='mailing.test.blacklist', count=13)
|
||||
target_records[10]['email_from'] = False # void email should lead to a 'cancel' trace_status
|
||||
target_records[11]['email_from'] = 'raoul@example¢¡.com' # wrong email should lead to a 'exception' trace_status
|
||||
mailing = self.env['mailing.mailing'].browse(self.mailing_bl.ids)
|
||||
mailing.write({'mailing_domain': [('id', 'in', target_records.ids)], 'user_id': self.user_marketing_2.id})
|
||||
mailing.action_put_in_queue()
|
||||
|
|
@ -41,20 +42,27 @@ class TestMailingStatistics(TestMassMailCommon):
|
|||
self.gateway_mail_reply_wrecord(MAIL_TEMPLATE, target_records[0], use_in_reply_to=True)
|
||||
self.gateway_mail_reply_wrecord(MAIL_TEMPLATE, target_records[1], use_in_reply_to=True)
|
||||
self.gateway_mail_reply_wrecord(MAIL_TEMPLATE, target_records[2], use_in_reply_to=True)
|
||||
self.gateway_mail_click(mailing, target_records[0], 'https://www.odoo.be')
|
||||
self.gateway_mail_click(mailing, target_records[2], 'https://www.odoo.be')
|
||||
self.gateway_mail_click(mailing, target_records[3], 'https://www.odoo.be')
|
||||
self.gateway_mail_trace_click(mailing, target_records[0], 'https://www.odoo.be')
|
||||
self.gateway_mail_trace_click(mailing, target_records[2], 'https://www.odoo.be')
|
||||
self.gateway_mail_trace_click(mailing, target_records[3], 'https://www.odoo.be')
|
||||
self.assertEqual(target_records[12].message_bounce, 0)
|
||||
self.gateway_mail_trace_bounce(mailing, target_records[12])
|
||||
self.assertEqual(target_records[12].message_bounce, 1)
|
||||
|
||||
# check mailing statistics
|
||||
self.assertEqual(mailing.bounced, 1)
|
||||
self.assertEqual(mailing.bounced_ratio, 9.09)
|
||||
self.assertEqual(mailing.canceled, 1)
|
||||
self.assertEqual(mailing.expected, 13)
|
||||
self.assertEqual(mailing.clicked, 3)
|
||||
self.assertEqual(mailing.clicks_ratio, 30)
|
||||
self.assertEqual(mailing.delivered, 10)
|
||||
self.assertEqual(mailing.failed, 1)
|
||||
self.assertEqual(mailing.opened, 4)
|
||||
self.assertEqual(mailing.opened_ratio, 40)
|
||||
self.assertEqual(mailing.replied, 3)
|
||||
self.assertEqual(mailing.replied_ratio, 30)
|
||||
self.assertEqual(mailing.sent, 10)
|
||||
self.assertEqual(mailing.sent, 11)
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
mailing._action_send_statistics()
|
||||
|
|
@ -69,13 +77,13 @@ class TestMailingStatistics(TestMassMailCommon):
|
|||
self.assertEqual(mail.state, 'outgoing')
|
||||
# test body content: KPIs
|
||||
body_html = html.fromstring(mail.body_html)
|
||||
kpi_values = body_html.xpath('//div[@data-field="mail"]//*[hasclass("kpi_value")]/text()')
|
||||
kpi_values = body_html.xpath('//table[@data-field="mail"]//*[hasclass("kpi_value")]/text()')
|
||||
self.assertEqual(
|
||||
[t.strip().strip('%') for t in kpi_values],
|
||||
['100', str(mailing.opened_ratio), str(mailing.replied_ratio)]
|
||||
['83.33', str(mailing.opened_ratio), str(mailing.replied_ratio)] # first value is received_ratio
|
||||
)
|
||||
# test body content: clicks (a bit hackish but hey we are in stable)
|
||||
kpi_click_values = body_html.xpath('//div[hasclass("global_layout")]/table//tr[contains(@style,"color: #888888")]/td[contains(@style,"width: 30%")]/text()')
|
||||
kpi_click_values = body_html.xpath('//table//tr[contains(@style,"color: #888888")]/td[contains(@style,"width: 30%")]/text()')
|
||||
first_link_value = int(kpi_click_values[0].strip().split()[1].strip('()'))
|
||||
self.assertEqual(first_link_value, mailing.clicked)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ from odoo.tests.common import users
|
|||
from odoo.tests import tagged
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
@tagged('digest', 'mass_mailing', 'mass_mailing_sms')
|
||||
class TestMailingStatistics(TestMassSMSCommon):
|
||||
|
||||
|
|
@ -24,6 +23,9 @@ class TestMailingStatistics(TestMassSMSCommon):
|
|||
name='Marie Marketing',
|
||||
signature='--\nMarie'
|
||||
)
|
||||
mobile_numbers = [f'045300{x}{x}99' for x in range(6)] + ['0453000099'] * 4
|
||||
cls.records += cls._get_sms_test_records(mobile_numbers=mobile_numbers)
|
||||
cls.records = cls._reset_mail_context(cls.records)
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mass_mailing_sms.models.mailing_mailing', 'odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_thread')
|
||||
|
|
@ -35,17 +37,31 @@ class TestMailingStatistics(TestMassSMSCommon):
|
|||
with self.mockSMSGateway():
|
||||
mailing.action_send_sms()
|
||||
|
||||
# simulate some replies and clicks
|
||||
self.gateway_sms_click(mailing, target_records[0])
|
||||
self.gateway_sms_click(mailing, target_records[2])
|
||||
self.gateway_sms_click(mailing, target_records[3])
|
||||
# simulate some delivery reports
|
||||
for record_idx in range(4):
|
||||
self.gateway_sms_delivered(mailing, target_records[record_idx])
|
||||
|
||||
# simulate some replies and clicks
|
||||
for record_idx in (0, 2, 3):
|
||||
self.gateway_sms_click(mailing, target_records[record_idx])
|
||||
|
||||
for record_idx in (7, 8):
|
||||
record = target_records[record_idx]
|
||||
trace = mailing.mailing_trace_ids.filtered(lambda t: t.model == record._name and t.res_id == record.id)
|
||||
trace.set_bounced()
|
||||
|
||||
# check mailing statistics
|
||||
self.assertEqual(mailing.clicked, 3)
|
||||
self.assertEqual(mailing.delivered, 10)
|
||||
self.assertEqual(mailing.delivered, 4)
|
||||
self.assertEqual(mailing.opened, 3)
|
||||
self.assertEqual(mailing.opened_ratio, 30)
|
||||
self.assertEqual(mailing.sent, 10)
|
||||
self.assertEqual(mailing.sent, 16)
|
||||
self.assertEqual(mailing.scheduled, 0)
|
||||
self.assertEqual(mailing.canceled, 4)
|
||||
self.assertEqual(mailing.process, 0)
|
||||
self.assertEqual(mailing.pending, 10)
|
||||
self.assertEqual(mailing.bounced, 2)
|
||||
self.assertEqual(mailing.received_ratio, 25)
|
||||
self.assertEqual(mailing.opened_ratio, 21.43)
|
||||
self.assertEqual(mailing.bounced_ratio, 12.5)
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
mailing._action_send_statistics()
|
||||
|
|
@ -60,12 +76,27 @@ class TestMailingStatistics(TestMassSMSCommon):
|
|||
self.assertEqual(mail.state, 'outgoing')
|
||||
# test body content: KPIs
|
||||
body_html = html.fromstring(mail.body_html)
|
||||
kpi_values = body_html.xpath('//div[@data-field="sms"]//*[hasclass("kpi_value")]/text()')
|
||||
kpi_values = body_html.xpath('//table[@data-field="sms"]//*[hasclass("kpi_value")]/text()')
|
||||
self.assertEqual(
|
||||
[t.strip().strip('%') for t in kpi_values],
|
||||
['100', str(mailing.clicks_ratio), str(mailing.bounced_ratio)]
|
||||
['25.0', str(float(mailing.clicks_ratio)), str(float(mailing.bounced_ratio))]
|
||||
)
|
||||
# test body content: clicks (a bit hackish but hey we are in stable)
|
||||
kpi_click_values = body_html.xpath('//div[hasclass("global_layout")]/table//tr[contains(@style,"color: #888888")]/td[contains(@style,"width: 30%")]/text()')
|
||||
kpi_click_values = body_html.xpath('//table//tr[contains(@style,"color: #888888")]/td[contains(@style,"width: 30%")]/text()')
|
||||
first_link_value = int(kpi_click_values[0].strip().split()[1].strip('()'))
|
||||
self.assertEqual(first_link_value, mailing.clicked)
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mass_mailing_sms.models.mailing_mailing', 'odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_thread')
|
||||
def test_sent_delivered_sms(self):
|
||||
""" Test that if we get delivered trace status first instead of sent from
|
||||
providers for some reasons, the statistics for sent SMS will be correct. """
|
||||
mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids)
|
||||
target_records = self.env['mail.test.sms'].browse(self.records.ids)
|
||||
mailing.write({'mailing_domain': [('id', 'in', target_records.ids)], 'user_id': self.user_marketing_2.id})
|
||||
mailing.action_put_in_queue()
|
||||
with self.mockSMSGateway(force_delivered=True):
|
||||
mailing.action_send_sms()
|
||||
|
||||
self.assertEqual(mailing.delivered, 16)
|
||||
self.assertEqual(mailing.sent, 16)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import lxml.html
|
||||
|
||||
from odoo.addons.sms_twilio.tests.common import MockSmsTwilioApi
|
||||
from odoo.addons.test_mass_mailing.tests.common import TestMassMailCommon
|
||||
from odoo.fields import Command
|
||||
from odoo.addons.test_mass_mailing.tests.common import TestMassSMSCommon
|
||||
from odoo.tests.common import users, tagged
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
|
@ -11,18 +12,31 @@ from odoo.tools import mute_logger
|
|||
@tagged('mailing_manage')
|
||||
class TestMailingTest(TestMassMailCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.test_records = cls.env['mailing.test.blacklist'].create([
|
||||
{
|
||||
'email_from': f'test.mailing.{idx}@test.example.com',
|
||||
'name': f'Test Mailing {idx}',
|
||||
'user_id': cls.user_marketing.id,
|
||||
}
|
||||
for idx in range(5)
|
||||
])
|
||||
cls.test_mailing_bl = cls.env['mailing.mailing'].create({
|
||||
'body_html': '<p>Hello <t t-out="object.name"/></p>',
|
||||
'mailing_domain': [('id', 'in', cls.test_records.ids)],
|
||||
'mailing_model_id': cls.env['ir.model']._get_id('mailing.test.blacklist'),
|
||||
'mailing_type': 'mail',
|
||||
'name': 'TestButton',
|
||||
'preview': 'Preview {{ object.name }}',
|
||||
'subject': 'Subject {{ object.name }}',
|
||||
})
|
||||
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_render_mixin')
|
||||
def test_mailing_test_button(self):
|
||||
mailing = self.env['mailing.mailing'].create({
|
||||
'name': 'TestButton',
|
||||
'subject': 'Subject {{ object.name }}',
|
||||
'preview': 'Preview {{ object.name }}',
|
||||
'state': 'draft',
|
||||
'mailing_type': 'mail',
|
||||
'body_html': '<p>Hello <t t-out="object.name"/></p>',
|
||||
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
||||
})
|
||||
mailing = self.test_mailing_bl.with_env(self.env)
|
||||
mailing_test = self.env['mailing.mailing.test'].create({
|
||||
'email_to': 'test@test.com',
|
||||
'mass_mailing_id': mailing.id,
|
||||
|
|
@ -62,27 +76,52 @@ class TestMailingTest(TestMassMailCommon):
|
|||
with self.mock_mail_gateway(), self.assertRaises(Exception):
|
||||
mailing_test.send_mail_test()
|
||||
|
||||
def test_mailing_test_equals_reality(self):
|
||||
@users('user_marketing')
|
||||
@mute_logger('odoo.addons.mail.models.mail_render_mixin')
|
||||
def test_mailing_test_button_links(self):
|
||||
"""This tests that the link provided by the View in Browser snippet is correctly replaced
|
||||
when sending a test mailing while the Unsubscribe button's link isn't, to preserve the testing route
|
||||
/unsubscribe_from_list.
|
||||
This also checks that other links containing the /view route aren't replaced along the way.
|
||||
"""
|
||||
Check that both test and real emails will format the qweb and inline placeholders correctly in body and subject.
|
||||
"""
|
||||
self.env['mailing.contact'].search([]).unlink()
|
||||
contact_list = self.env['mailing.list'].create({
|
||||
'name': 'Testers',
|
||||
'contact_ids': [Command.create({
|
||||
'name': 'Mitchell Admin',
|
||||
'email': 'real@real.com',
|
||||
})],
|
||||
mailing = self.test_mailing_bl.with_env(self.env)
|
||||
mailing_test = self.env['mailing.mailing.test'].create({
|
||||
'email_to': 'test@test.com',
|
||||
'mass_mailing_id': mailing.id,
|
||||
})
|
||||
# Test if link snippets are correctly converted
|
||||
mailing.write({
|
||||
'body_html':
|
||||
'''<p>
|
||||
Hello <a href="http://www.example.com/view">World<a/>
|
||||
<div class="o_snippet_view_in_browser o_mail_snippet_general pt16 pb16" style="text-align: center; padding-left: 15px; padding-right: 15px;">
|
||||
<a href="/view">
|
||||
View Online
|
||||
</a>
|
||||
</div>
|
||||
<div class="o_mail_footer_links">
|
||||
<a role="button" href="/unsubscribe_from_list" class="btn btn-link">Unsubscribe</a>
|
||||
</div>
|
||||
</p>''',
|
||||
'preview': 'Preview {{ object.name }}',
|
||||
'subject': 'Subject {{ object.name }}',
|
||||
})
|
||||
|
||||
mailing = self.env['mailing.mailing'].create({
|
||||
'name': 'TestButton',
|
||||
'subject': 'Subject {{ object.name }} <t t-out="object.name"/>',
|
||||
'state': 'draft',
|
||||
'mailing_type': 'mail',
|
||||
with self.mock_mail_gateway():
|
||||
mailing_test.send_mail_test()
|
||||
|
||||
body_html = self._mails.pop()['body']
|
||||
self.assertIn(f'/mailing/{mailing.id}/view', body_html) # Is replaced
|
||||
self.assertIn('/unsubscribe_from_list', body_html) # Isn't replaced
|
||||
self.assertIn('http://www.example.com/view', body_html) # Isn't replaced
|
||||
|
||||
def test_mailing_test_equals_reality(self):
|
||||
""" Check that both test and real emails will format the qweb and inline
|
||||
placeholders correctly in body and subject. """
|
||||
mailing = self.test_mailing_bl.with_env(self.env)
|
||||
mailing.write({
|
||||
'body_html': '<p>Hello {{ object.name }} <t t-out="object.name"/></p>',
|
||||
'mailing_model_id': self.env['ir.model']._get('mailing.list').id,
|
||||
'contact_list_ids': [contact_list.id],
|
||||
'subject': 'Subject {{ object.name }} <t t-out="object.name"/>',
|
||||
})
|
||||
mailing_test = self.env['mailing.mailing.test'].create({
|
||||
'email_to': 'test@test.com',
|
||||
|
|
@ -92,11 +131,13 @@ class TestMailingTest(TestMassMailCommon):
|
|||
with self.mock_mail_gateway():
|
||||
mailing_test.send_mail_test()
|
||||
|
||||
expected_subject = 'Subject Mitchell Admin <t t-out="object.name"/>'
|
||||
expected_body = 'Hello {{ object.name }} Mitchell Admin'
|
||||
expected_test_record = self.env[mailing.mailing_model_real].search([], limit=1)
|
||||
self.assertEqual(expected_test_record, self.test_records[0], 'Should take first found one')
|
||||
expected_subject = f'Subject {expected_test_record.name} <t t-out="object.name"/>'
|
||||
expected_body = 'Hello {{ object.name }}' + f' {expected_test_record.name}'
|
||||
|
||||
self.assertSentEmail(self.env.user.partner_id, ['test@test.com'],
|
||||
subject=expected_subject,
|
||||
subject='[TEST] %s' % expected_subject,
|
||||
body_content=expected_body)
|
||||
|
||||
with self.mock_mail_gateway():
|
||||
|
|
@ -104,6 +145,77 @@ class TestMailingTest(TestMassMailCommon):
|
|||
mailing.action_launch()
|
||||
self.env.ref('mass_mailing.ir_cron_mass_mailing_queue').method_direct_trigger()
|
||||
|
||||
self.assertSentEmail(self.env.user.partner_id, ['real@real.com'],
|
||||
self.assertSentEmail(
|
||||
self.env.user.partner_id,
|
||||
[expected_test_record.email_from],
|
||||
subject=expected_subject,
|
||||
body_content=expected_body)
|
||||
body_content=expected_body,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
self.env['mailing.mailing.test'].create({
|
||||
'mass_mailing_id': mailing.id,
|
||||
}).email_to,
|
||||
'test@test.com',
|
||||
"Should use the value of the previous record's email_to as default",
|
||||
)
|
||||
|
||||
# Also test that related messages were properly deleted
|
||||
mailing.subject = 'Dummy Subject'
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
mailing_test.send_mail_test()
|
||||
|
||||
test_subject = '[TEST] %s' % mailing.subject
|
||||
self.assertSentEmail(
|
||||
self.env.user.partner_id,
|
||||
['test@test.com'],
|
||||
subject=test_subject,
|
||||
)
|
||||
self.assertFalse(self.env['mail.mail'].search([('subject', '=', test_subject)]))
|
||||
self.assertFalse(self.env['mail.message'].search([('subject', '=', test_subject)]))
|
||||
|
||||
|
||||
@tagged('mailing_manage', 'twilio')
|
||||
class TestMailingSMSTest(TestMassSMSCommon, MockSmsTwilioApi):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls._setup_sms_twilio(cls.user_marketing.company_id)
|
||||
|
||||
def test_mass_sms_test_button_twilio(self):
|
||||
""" Test the testing tool when twilio is activated on company """
|
||||
self._setup_sms_twilio(self.user_marketing.company_id)
|
||||
|
||||
mailing = self.env['mailing.mailing'].create({
|
||||
'name': 'TestButton',
|
||||
'subject': 'Subject {{ object.name }}',
|
||||
'preview': 'Preview {{ object.name }}',
|
||||
'state': 'draft',
|
||||
'mailing_type': 'sms',
|
||||
'body_plaintext': 'Hello {{ object.name }}',
|
||||
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
||||
})
|
||||
mailing_test = self.env['mailing.sms.test'].with_user(self.user_marketing).create({
|
||||
'numbers': '+32456001122',
|
||||
'mailing_id': mailing.id,
|
||||
})
|
||||
|
||||
for error_type, exp_state, exp_msg in [
|
||||
(False, 'outgoing', '<ul><li>Test SMS successfully sent to +32456001122</li></ul>'),
|
||||
(
|
||||
'wrong_number_format', 'outgoing', # not sure why outgoing but hey
|
||||
"<ul><li>Test SMS could not be sent to +32456001122: The number you're trying to reach is not correctly formatted</li></ul>"
|
||||
),
|
||||
]:
|
||||
with self.subTest(error_type=error_type):
|
||||
with self.with_user('user_marketing'):
|
||||
with self.mock_sms_twilio_gateway(error_type=error_type):
|
||||
mailing_test.action_send_sms()
|
||||
|
||||
notification = mailing.message_ids[0]
|
||||
self.assertEqual(notification.body, exp_msg)
|
||||
self.assertSMS(
|
||||
self.env["res.partner"], '+32456001122', exp_state,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -46,8 +46,11 @@ class TestMassMailPerformance(TestMassMailPerformanceBase):
|
|||
'mailing_domain': [('id', 'in', self.mm_recs.ids)],
|
||||
})
|
||||
|
||||
# runbot needs +51 compared to local
|
||||
with self.assertQueryCount(__system__=1473, marketing=1474):
|
||||
# runbot needs +101 compared to local
|
||||
with (
|
||||
self.mock_mail_gateway(mail_unlink_sent=True),
|
||||
self.assertQueryCount(__system__=1379, marketing=1383), # 1227, 1229
|
||||
):
|
||||
mailing.action_send_mail()
|
||||
|
||||
self.assertEqual(mailing.sent, 50)
|
||||
|
|
@ -89,12 +92,14 @@ class TestMassMailBlPerformance(TestMassMailPerformanceBase):
|
|||
'mailing_domain': [('id', 'in', self.mm_recs.ids)],
|
||||
})
|
||||
|
||||
# runbot needs +51 compared to local
|
||||
with self.assertQueryCount(__system__=1546, marketing=1547):
|
||||
# runbot needs +153 compared to local
|
||||
with self.assertQueryCount(__system__=1410, marketing=1417): # 1257, 1260
|
||||
mailing.action_send_mail()
|
||||
|
||||
self.assertEqual(mailing.sent, 50)
|
||||
self.assertEqual(mailing.delivered, 50)
|
||||
self.assertEqual(mailing.canceled, 12)
|
||||
|
||||
cancelled_mail_count = self.env['mail.mail'].sudo().search([('mailing_id', '=', mailing.id)])
|
||||
self.assertEqual(len(cancelled_mail_count), 12, 'Should not have auto deleted the blacklisted emails')
|
||||
mail_mail_count = len(self.env['mail.mail'].sudo().search([('mailing_id', '=', mailing.id)]))
|
||||
self.assertEqual(mail_mail_count, 0,
|
||||
"Mail_mail for blacklisted emails mustn't have been created and others must have been deleted")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
from odoo.addons.test_mass_mailing.tests.common import TestMassSMSCommon
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
class TestSmsController(TestMassSMSCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.recipients = cls._create_mailing_sms_test_records(model='mail.test.sms', count=5)
|
||||
cls.mailing_sms.mailing_domain = [('id', 'in', cls.recipients.ids)]
|
||||
|
||||
def _send_sms_immediately_and_assert_traces(self, moderated=False):
|
||||
self.mailing_sms.sms_force_send = True
|
||||
with self.mockSMSGateway(moderated=moderated):
|
||||
self.mailing_sms.action_send_sms()
|
||||
|
||||
all_traces = self.assertSMSTraces(
|
||||
[{'partner': record.customer_id,
|
||||
'number': '+32' + record.phone_nbr[1:],
|
||||
'trace_status': 'process' if moderated else 'pending',
|
||||
} for i, record in enumerate(self.recipients)],
|
||||
self.mailing_sms, self.recipients,
|
||||
)
|
||||
return all_traces
|
||||
|
||||
@mute_logger("odoo.addons.base.models.ir_http")
|
||||
def test_webhook_update_traces_pending_to_sent(self):
|
||||
all_traces = self._send_sms_immediately_and_assert_traces()
|
||||
first_two_traces = all_traces[:2]
|
||||
other_traces = all_traces[2:]
|
||||
statuses = [{'sms_status': 'delivered', 'uuids': first_two_traces.sms_tracker_ids.mapped('sms_uuid')}]
|
||||
self.assertEqual(self._make_webhook_jsonrpc_request(statuses), 'OK')
|
||||
self.assertEqual(set(first_two_traces.mapped('trace_status')), {'sent'})
|
||||
self.assertEqual(set(other_traces.mapped('trace_status')), {'pending'})
|
||||
|
||||
@mute_logger("odoo.addons.base.models.ir_http")
|
||||
def test_webhook_update_traces_process_to_pending(self):
|
||||
self.assertEqual(self.mailing_sms.state, 'draft')
|
||||
all_traces = self._send_sms_immediately_and_assert_traces(moderated=True)
|
||||
self.assertEqual(self.mailing_sms.state, 'sending')
|
||||
statuses = [{'sms_status': 'sent', 'uuids': all_traces.sms_tracker_ids.mapped('sms_uuid')}]
|
||||
self.assertEqual(self._make_webhook_jsonrpc_request(statuses), 'OK')
|
||||
self.assertEqual(set(all_traces.mapped('trace_status')), {'pending'})
|
||||
self.assertEqual(self.mailing_sms.state, 'done')
|
||||
|
||||
@mute_logger("odoo.addons.base.models.ir_http")
|
||||
def test_webhook_update_traces_sent_to_bounce_and_failed(self):
|
||||
all_traces = self._send_sms_immediately_and_assert_traces()
|
||||
trace_1, trace_2 = all_traces[:2]
|
||||
other_traces = all_traces[2:]
|
||||
statuses = [
|
||||
{'sms_status': 'invalid_destination', 'uuids': [trace_1.sms_tracker_ids.sms_uuid]},
|
||||
{'sms_status': 'sms_not_delivered', 'uuids': [trace_2.sms_tracker_ids.sms_uuid]},
|
||||
{'sms_status': 'delivered', 'uuids': other_traces.sms_tracker_ids.mapped('sms_uuid')}
|
||||
]
|
||||
self.assertEqual(self._make_webhook_jsonrpc_request(statuses), 'OK')
|
||||
self.assertEqual(trace_1.trace_status, 'bounce')
|
||||
self.assertEqual(trace_2.trace_status, 'error')
|
||||
self.assertTrue(set(other_traces.mapped('trace_status')), {'sent'})
|
||||
Loading…
Add table
Add a link
Reference in a new issue