summaryrefslogtreecommitdiff
path: root/addons/mass_mailing_sms
diff options
context:
space:
mode:
authorThibault Delavallée <tde@odoo.com>2021-04-16 11:52:45 +0000
committerThibault Delavallée <tde@odoo.com>2021-04-19 14:11:13 +0000
commite77b9e575e398f42df524bdaf5d692ea882a4521 (patch)
treedd846e1a25aea75549126c77fd7504caa79e4c25 /addons/mass_mailing_sms
parent509d761921167a041bfe5dd403181d399c20dacc (diff)
[IMP] event_sms, link_tracker, sms, test_mail_full: improve SMS-based mailing tests
PURPOSE Purpose is to have more tests when sending sms mailings, notably about canceled or failed mails or sms as well as jinja and links rendering. SPECIFICATIONS In this commit we improve SMS Marketing mailing tests. We notably * add tests for void and invalid email and numbers. It allows to check they correctly update their trace status; * add tests for unsubscribe and view links embedded in sms marketing; * add tests to simulate a click on links sent through sms marketing and ensure click statistics are effectively updated; * ensure content of sent sms are checked; Default content of mailings used in test is updated to ensure jinja is correctly rendered, including links and some corner cases. This will also helps ensuring behavior is kept when converting to QWeb. In event we now correctly use sms gateway mock to ensure SMS finding. In link_tracker a helper is added to check url tracking in plaintext content. LINKS Task ID-2508643 Followup of odoo/odoo#68874 (improve mail tests) Prepares Task ID-27033 (support QWeb in templates) Prepares Task ID-2377974 (clean trace and status management in mass mailing) COM PR odoo/odoo#69461 ENT PR odoo/enterprise#17780
Diffstat (limited to 'addons/mass_mailing_sms')
-rw-r--r--addons/mass_mailing_sms/tests/common.py162
1 files changed, 107 insertions, 55 deletions
diff --git a/addons/mass_mailing_sms/tests/common.py b/addons/mass_mailing_sms/tests/common.py
index 0f015865256..eb48b4b669f 100644
--- a/addons/mass_mailing_sms/tests/common.py
+++ b/addons/mass_mailing_sms/tests/common.py
@@ -11,63 +11,54 @@ from odoo.addons.sms.tests.common import SMSCase, SMSCommon
class MassSMSCase(SMSCase):
# ------------------------------------------------------------
- # GATEWAY TOOLS
- # ------------------------------------------------------------
-
- def gateway_sms_click(self, sent_sms):
- """ Simulate a click on a sent SMS. Usage: giving a partner and/or
- a number, find an SMS sent to him, find shortened links in its body
- and call add_click to simulate a click. """
- for url in re.findall(tools.TEXT_URL_REGEX, sent_sms['body']):
- if '/r/' in url: # shortened link, like 'http://localhost:8069/r/LBG/s/53'
- parsed_url = werkzeug.urls.url_parse(url)
- path_items = parsed_url.path.split('/')
- code, sms_sms_id = path_items[2], int(path_items[4])
- trace_id = self.env['mailing.trace'].sudo().search([('sms_sms_id_int', '=', sms_sms_id)]).id
-
- self.env['link.tracker.click'].sudo().add_click(
- code,
- ip='100.200.300.400',
- country_code='BE',
- mailing_trace_id=trace_id
- )
-
-
- # ------------------------------------------------------------
# ASSERTS
# ------------------------------------------------------------
- def assertSMSOutgoingStatistics(self, partners, numbers, records, mailing):
- found_sms = self.env['sms.sms'].sudo().search([
- '|', ('partner_id', 'in', partners.ids), ('number', 'in', numbers),
- ('state', '=', 'outgoing')
- ])
- self.assertEqual(found_sms.filtered(lambda s: s.partner_id).mapped('partner_id'), partners)
- self.assertEqual(set(found_sms.filtered(lambda s: not s.partner_id).mapped('number')), set(numbers))
-
- found_traces = self.env['mailing.trace'].sudo().search([
- ('sms_sms_id_int', 'in', found_sms.ids),
- ])
- self.assertEqual(len(found_sms), len(found_traces))
- self.assertTrue(all(s.state == 'outgoing' for s in found_traces))
- self.assertTrue(all(s.res_model == records._name for s in found_traces))
- self.assertEqual(set(found_traces.mapped('res_id')), set(records.ids))
- self.assertTrue(all(s.mass_mailing_id == mailing for s in found_traces))
-
def assertSMSStatistics(self, recipients_info, mailing, records, check_sms=True):
- """ Check content of notifications.
+ """ Deprecated, remove in 14.4 """
+ return self.assertSMSTraces(recipients_info, mailing, records, check_sms=check_sms)
+
+ def assertSMSTraces(self, recipients_info, mailing, records,
+ check_sms=True, sent_unlink=False,
+ sms_links_info=None):
+ """ Check content of traces. Traces are fetched based on a given mailing
+ and records. Their content is compared to recipients_info structure that
+ holds expected information. Links content may be checked, notably to
+ assert shortening or unsubscribe links. Sms.sms records may optionally
+ be checked.
:param recipients_info: list[{
+ # TRACE
'partner': res.partner record (may be empty),
'number': number used for notification (may be empty, computed based on partner),
- 'state': outgoing / sent / ignored / exception / opened (sent by default),
+ 'state': outgoing / sent / ignored / bounced / exception / opened (outgoing by default),
'record: linked record,
- 'content': optional: if set, check content of sent SMS
- 'failure_type': optional: sms_number_missing / sms_number_format / sms_credit / sms_server
+ # SMS.SMS
+ 'content': optional: if set, check content of sent SMS;
+ 'failure_type': error code linked to sms failure (see ``error_code``
+ field on ``sms.sms`` model);
},
- { ... }
+ { ... }];
+ :param mailing: a mailing.mailing record from which traces have been
+ generated;
+ :param records: records given to mailing that generated traces. It is
+ used notably to find traces using their IDs;
+ :param check_sms: if set, check sms.sms records that should be linked to traces;
+ :param sent_unlink: it True, sent sms.sms are deleted and we check gateway
+ output result instead of actual sms.sms records;
+ :param sms_links_info: if given, should follow order of ``recipients_info``
+ and give details about links. See ``assertLinkShortenedHtml`` helper for
+ more details about content to give;
]
"""
+ # map trace state to sms state
+ state_mapping = {
+ 'sent': 'sent',
+ 'outgoing': 'outgoing',
+ 'exception': 'error',
+ 'ignored': 'canceled',
+ 'bounced': 'error',
+ }
traces = self.env['mailing.trace'].search([
('mass_mailing_id', 'in', mailing.ids),
('res_id', 'in', records.ids)
@@ -77,7 +68,10 @@ class MassSMSCase(SMSCase):
# self.assertTrue(all(s.utm_campaign_id == mailing.campaign_id for s in traces))
self.assertEqual(set(s.res_id for s in traces), set(records.ids))
- for recipient_info in recipients_info:
+ # check each trace
+ if not sms_links_info:
+ sms_links_info = [None] * len(recipients_info)
+ for recipient_info, link_info, record in zip(recipients_info, sms_links_info, records):
partner = recipient_info.get('partner', self.env['res.partner'])
number = recipient_info.get('number')
state = recipient_info.get('state', 'outgoing')
@@ -85,21 +79,79 @@ class MassSMSCase(SMSCase):
if number is None and partner:
number = partner._sms_get_recipients_info()[partner.id]['sanitized']
- notif = traces.filtered(lambda s: s.sms_number == number and s.state == state)
- self.assertTrue(notif, 'SMS: not found notification for number %s, (state: %s)' % (number, state))
+ trace = traces.filtered(
+ lambda t: t.sms_number == number and t.state == state and (t.res_id == record.id if record else True)
+ )
+ self.assertTrue(len(trace) == 1,
+ 'SMS: found %s notification for number %s, (state: %s) (1 expected)' % (len(trace), number, state))
+ self.assertTrue(bool(trace.sms_sms_id_int))
if check_sms:
if state == 'sent':
- self.assertSMSSent([number], content)
- elif state == 'outgoing':
- self.assertSMSOutgoing(partner, number, content)
- elif state == 'exception':
- self.assertSMSFailed(partner, number, recipient_info.get('failure_type'), content)
- elif state == 'ignored':
- self.assertSMSCanceled(partner, number, recipient_info.get('failure_type', False), content)
+ if sent_unlink:
+ self.assertSMSIapSent([number], content=content)
+ else:
+ self.assertSMS(partner, number, 'sent', content=content)
+ elif state in state_mapping:
+ sms_state = state_mapping[state]
+ error_code = recipient_info['failure_type'] if state in ('exception', 'ignored', 'bounced') else None
+ self.assertSMS(partner, number, sms_state, error_code=error_code, content=content)
else:
raise NotImplementedError()
+ if link_info:
+ # shortened links are directly included in sms.sms record as well as
+ # in sent sms (not like mails who are post-processed)
+ sms_sent = self._find_sms_sent(partner, number)
+ sms_sms = self._find_sms_sms(partner, number, state_mapping[state])
+ for (url, is_shortened, add_link_params) in link_info:
+ if url == 'unsubscribe':
+ url = '%s/sms/%d/%s' % (mailing.get_base_url(), mailing.id, trace.sms_code)
+ link_params = {'utm_medium': 'SMS', 'utm_source': mailing.name}
+ if add_link_params:
+ link_params.update(**add_link_params)
+ self.assertLinkShortenedText(
+ sms_sms.body,
+ (url, is_shortened),
+ link_params=link_params,
+ )
+ self.assertLinkShortenedText(
+ sms_sent['body'],
+ (url, is_shortened),
+ link_params=link_params,
+ )
+
+ # ------------------------------------------------------------
+ # GATEWAY TOOLS
+ # ------------------------------------------------------------
+
+ def gateway_sms_click(self, mailing, record):
+ """ Simulate a click on a sent SMS. Usage: giving a partner and/or
+ a number, find an SMS sent to him, find shortened links in its body
+ and call add_click to simulate a click. """
+ trace = mailing.mailing_trace_ids.filtered(lambda t: t.model == record._name and t.res_id == record.id)
+ sms_sent = self._find_sms_sent(self.env['res.partner'], trace.sms_number)
+ self.assertTrue(bool(sms_sent))
+ return self.gateway_sms_sent_click(sms_sent)
+
+ def gateway_sms_sent_click(self, sms_sent):
+ """ When clicking on a link in a SMS we actually don't have any
+ easy information in body, only body. We currently click on all found
+ shortened links. """
+ for url in re.findall(tools.TEXT_URL_REGEX, sms_sent['body']):
+ if '/r/' in url: # shortened link, like 'http://localhost:8069/r/LBG/s/53'
+ parsed_url = werkzeug.urls.url_parse(url)
+ path_items = parsed_url.path.split('/')
+ code, sms_sms_id = path_items[2], int(path_items[4])
+ trace_id = self.env['mailing.trace'].sudo().search([('sms_sms_id_int', '=', sms_sms_id)]).id
+
+ self.env['link.tracker.click'].sudo().add_click(
+ code,
+ ip='100.200.300.400',
+ country_code='BE',
+ mailing_trace_id=trace_id
+ )
+
class MassSMSCommon(MassMailCommon, SMSCommon, MassSMSCase):