summaryrefslogtreecommitdiff
path: root/addons/test_mail_full
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/test_mail_full
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/test_mail_full')
-rw-r--r--addons/test_mail_full/__init__.py4
-rw-r--r--addons/test_mail_full/__manifest__.py32
-rw-r--r--addons/test_mail_full/i18n/test_mail_full.pot291
-rw-r--r--addons/test_mail_full/models/__init__.py4
-rw-r--r--addons/test_mail_full/models/mailing_mailing.py29
-rw-r--r--addons/test_mail_full/models/test_mail_models.py109
-rw-r--r--addons/test_mail_full/security/ir.model.access.csv11
-rw-r--r--addons/test_mail_full/tests/__init__.py14
-rw-r--r--addons/test_mail_full/tests/common.py56
-rw-r--r--addons/test_mail_full/tests/test_mass_mailing.py119
-rw-r--r--addons/test_mail_full/tests/test_mass_sms.py405
-rw-r--r--addons/test_mail_full/tests/test_odoobot.py143
-rw-r--r--addons/test_mail_full/tests/test_phone_blacklist.py82
-rw-r--r--addons/test_mail_full/tests/test_sms_composer.py567
-rw-r--r--addons/test_mail_full/tests/test_sms_management.py116
-rw-r--r--addons/test_mail_full/tests/test_sms_performance.py163
-rw-r--r--addons/test_mail_full/tests/test_sms_post.py437
-rw-r--r--addons/test_mail_full/tests/test_sms_server_actions.py67
-rw-r--r--addons/test_mail_full/tests/test_sms_sms.py162
-rw-r--r--addons/test_mail_full/tests/test_sms_template.py91
20 files changed, 2902 insertions, 0 deletions
diff --git a/addons/test_mail_full/__init__.py b/addons/test_mail_full/__init__.py
new file mode 100644
index 00000000..567548c7
--- /dev/null
+++ b/addons/test_mail_full/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+
+from . import models
+from . import tests
diff --git a/addons/test_mail_full/__manifest__.py b/addons/test_mail_full/__manifest__.py
new file mode 100644
index 00000000..586ee908
--- /dev/null
+++ b/addons/test_mail_full/__manifest__.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+
+{
+ 'name': 'Mail Tests (Full)',
+ 'version': '1.0',
+ 'category': 'Hidden',
+ 'sequence': 9876,
+ 'summary': 'Mail Tests: performances and tests specific to mail with all sub-modules',
+ 'description': """This module contains tests related to various mail features
+and mail-related sub modules. Those tests are present in a separate module as it
+contains models used only to perform tests independently to functional aspects of
+real applications. """,
+ 'depends': [
+ 'test_mail',
+ 'test_mass_mailing',
+ 'mail',
+ 'mail_bot',
+ # 'snailmail',
+ 'mass_mailing',
+ 'mass_mailing_sms',
+ 'phone_validation',
+ 'sms',
+ ],
+ 'data': [
+ 'security/ir.model.access.csv',
+ ],
+ 'demo': [
+ ],
+ 'installable': True,
+ 'application': False,
+ 'license': 'LGPL-3',
+}
diff --git a/addons/test_mail_full/i18n/test_mail_full.pot b/addons/test_mail_full/i18n/test_mail_full.pot
new file mode 100644
index 00000000..75ac8eee
--- /dev/null
+++ b/addons/test_mail_full/i18n/test_mail_full.pot
@@ -0,0 +1,291 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * test_mail_full
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server saas~12.5\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-09-09 10:49+0000\n"
+"PO-Revision-Date: 2019-09-09 10:49+0000\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_needaction
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_needaction
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_needaction
+msgid "Action Needed"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_attachment_count
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_attachment_count
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_attachment_count
+msgid "Attachment Count"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model,name:test_mail_full.model_mail_test_sms
+#: model:ir.model,name:test_mail_full.model_mail_test_sms_bl
+msgid "Chatter Model for SMS Gateway"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model,name:test_mail_full.model_mail_test_sms_partner
+msgid "Chatter Model for SMS Gateway (Partner only)"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__create_uid
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__create_uid
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__create_uid
+msgid "Created by"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__create_date
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__create_date
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__create_date
+msgid "Created on"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__customer_id
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__customer_id
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__partner_id
+msgid "Customer"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__display_name
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__display_name
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__email_from
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__email_from
+msgid "Email From"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__phone_sanitized
+msgid ""
+"Field used to store sanitized phone number. Helps speeding up searches and "
+"comparisons."
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_follower_ids
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_follower_ids
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_follower_ids
+msgid "Followers"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_channel_ids
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_channel_ids
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_channel_ids
+msgid "Followers (Channels)"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_partner_ids
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_partner_ids
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_partner_ids
+msgid "Followers (Partners)"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__id
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__id
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__id
+msgid "ID"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_needaction
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_unread
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_needaction
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_unread
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_needaction
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_unread
+msgid "If checked, new messages require your attention."
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_has_error
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_has_sms_error
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_has_error
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_has_sms_error
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_has_error
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_has_sms_error
+msgid "If checked, some messages have a delivery error."
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__phone_blacklisted
+msgid ""
+"If the email address is on the blacklist, the contact won't receive mass "
+"mailing anymore, from any list"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_is_follower
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_is_follower
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_is_follower
+msgid "Is Follower"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms____last_update
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl____last_update
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner____last_update
+msgid "Last Modified on"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__write_uid
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__write_uid
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__write_uid
+msgid "Last Updated by"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__write_date
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__write_date
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__write_date
+msgid "Last Updated on"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_main_attachment_id
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_main_attachment_id
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_main_attachment_id
+msgid "Main Attachment"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_has_error
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_has_error
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_has_error
+msgid "Message Delivery error"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_ids
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_ids
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_ids
+msgid "Messages"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__mobile_nbr
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__mobile_nbr
+msgid "Mobile Nbr"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__name
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__name
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__name
+msgid "Name"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_needaction_counter
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_needaction_counter
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_needaction_counter
+msgid "Number of Actions"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_has_error_counter
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_has_error_counter
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_has_error_counter
+msgid "Number of errors"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_needaction_counter
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_needaction_counter
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_needaction_counter
+msgid "Number of messages which requires an action"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_has_error_counter
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_has_error_counter
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_has_error_counter
+msgid "Number of messages with delivery error"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__message_unread_counter
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__message_unread_counter
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__message_unread_counter
+msgid "Number of unread messages"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__phone_blacklisted
+msgid "Phone Blacklisted"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__phone_nbr
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__phone_nbr
+msgid "Phone Nbr"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_has_sms_error
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_has_sms_error
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_has_sms_error
+msgid "SMS Delivery error"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__phone_sanitized
+msgid "Sanitized Number"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__subject
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__subject
+msgid "Subject"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_unread
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_unread
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_unread
+msgid "Unread Messages"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__message_unread_counter
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__message_unread_counter
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__message_unread_counter
+msgid "Unread Messages Counter"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms__website_message_ids
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_bl__website_message_ids
+#: model:ir.model.fields,field_description:test_mail_full.field_mail_test_sms_partner__website_message_ids
+msgid "Website Messages"
+msgstr ""
+
+#. module: test_mail_full
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms__website_message_ids
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_bl__website_message_ids
+#: model:ir.model.fields,help:test_mail_full.field_mail_test_sms_partner__website_message_ids
+msgid "Website communication history"
+msgstr ""
diff --git a/addons/test_mail_full/models/__init__.py b/addons/test_mail_full/models/__init__.py
new file mode 100644
index 00000000..0fe839f2
--- /dev/null
+++ b/addons/test_mail_full/models/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+
+from . import test_mail_models
+from . import mailing_mailing
diff --git a/addons/test_mail_full/models/mailing_mailing.py b/addons/test_mail_full/models/mailing_mailing.py
new file mode 100644
index 00000000..8d8a49a1
--- /dev/null
+++ b/addons/test_mail_full/models/mailing_mailing.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import logging
+
+from odoo import models
+
+_logger = logging.getLogger(__name__)
+
+
+class Mailing(models.Model):
+ _inherit = 'mailing.mailing'
+
+ def _get_opt_out_list_sms(self):
+ """Returns a set of emails opted-out in target model"""
+ self.ensure_one()
+ if self.mailing_model_real in ('mail.test.sms.bl.optout',
+ 'mail.test.sms.partner',
+ 'mail.test.sms.partner.2many'):
+ res_ids = self._get_recipients()
+ opt_out_contacts = set(self.env[self.mailing_model_real].search([
+ ('id', 'in', res_ids),
+ ('opt_out', '=', True)
+ ]).ids)
+ _logger.info(
+ "Mass-mailing %s targets %s, optout: %s emails",
+ self, self.mailing_model_real, len(opt_out_contacts))
+ return opt_out_contacts
+ return super(Mailing, self)._get_opt_out_list_sms()
diff --git a/addons/test_mail_full/models/test_mail_models.py b/addons/test_mail_full/models/test_mail_models.py
new file mode 100644
index 00000000..a3b5eb8d
--- /dev/null
+++ b/addons/test_mail_full/models/test_mail_models.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import fields, models
+
+
+class MailTestSMS(models.Model):
+ """ A model inheriting from mail.thread with some fields used for SMS
+ gateway, like a partner, a specific mobile phone, ... """
+ _description = 'Chatter Model for SMS Gateway'
+ _name = 'mail.test.sms'
+ _inherit = ['mail.thread']
+ _order = 'name asc, id asc'
+
+ name = fields.Char()
+ subject = fields.Char()
+ email_from = fields.Char()
+ phone_nbr = fields.Char()
+ mobile_nbr = fields.Char()
+ customer_id = fields.Many2one('res.partner', 'Customer')
+
+ def _sms_get_partner_fields(self):
+ return ['customer_id']
+
+ def _sms_get_number_fields(self):
+ return ['phone_nbr', 'mobile_nbr']
+
+
+class MailTestSMSBL(models.Model):
+ """ A model inheriting from mail.thread.phone allowing to test auto formatting
+ of phone numbers, blacklist, ... """
+ _description = 'SMS Mailing Blacklist Enabled'
+ _name = 'mail.test.sms.bl'
+ _inherit = ['mail.thread.phone']
+ _order = 'name asc, id asc'
+
+ name = fields.Char()
+ subject = fields.Char()
+ email_from = fields.Char()
+ phone_nbr = fields.Char()
+ mobile_nbr = fields.Char()
+ customer_id = fields.Many2one('res.partner', 'Customer')
+
+ def _sms_get_partner_fields(self):
+ return ['customer_id']
+
+ def _sms_get_number_fields(self):
+ # TDE note: should override _phone_get_number_fields but ok as sms in dependencies
+ return ['phone_nbr', 'mobile_nbr']
+
+
+class MailTestSMSOptout(models.Model):
+ """ Model using blacklist mechanism and a hijacked opt-out mechanism for
+ mass mailing features. """
+ _description = 'SMS Mailing Blacklist / Optout Enabled'
+ _name = 'mail.test.sms.bl.optout'
+ _inherit = ['mail.thread.phone']
+ _order = 'name asc, id asc'
+
+ name = fields.Char()
+ subject = fields.Char()
+ email_from = fields.Char()
+ phone_nbr = fields.Char()
+ mobile_nbr = fields.Char()
+ customer_id = fields.Many2one('res.partner', 'Customer')
+ opt_out = fields.Boolean()
+
+ def _sms_get_partner_fields(self):
+ return ['customer_id']
+
+ def _sms_get_number_fields(self):
+ # TDE note: should override _phone_get_number_fields but ok as sms in dependencies
+ return ['phone_nbr', 'mobile_nbr']
+
+
+class MailTestSMSPartner(models.Model):
+ """ A model like sale order having only a customer, not specific phone
+ or mobile fields. """
+ _description = 'Chatter Model for SMS Gateway (Partner only)'
+ _name = 'mail.test.sms.partner'
+ _inherit = ['mail.thread']
+
+ name = fields.Char()
+ customer_id = fields.Many2one('res.partner', 'Customer')
+ opt_out = fields.Boolean()
+
+ def _sms_get_partner_fields(self):
+ return ['customer_id']
+
+ def _sms_get_number_fields(self):
+ return []
+
+
+class MailTestSMSPartner2Many(models.Model):
+ """ A model like sale order having only a customer, not specific phone
+ or mobile fields. """
+ _description = 'Chatter Model for SMS Gateway (M2M Partners only)'
+ _name = 'mail.test.sms.partner.2many'
+ _inherit = ['mail.thread']
+
+ name = fields.Char()
+ customer_ids = fields.Many2many('res.partner', string='Customers')
+ opt_out = fields.Boolean()
+
+ def _sms_get_partner_fields(self):
+ return ['customer_ids']
+
+ def _sms_get_number_fields(self):
+ return []
diff --git a/addons/test_mail_full/security/ir.model.access.csv b/addons/test_mail_full/security/ir.model.access.csv
new file mode 100644
index 00000000..5a099a7d
--- /dev/null
+++ b/addons/test_mail_full/security/ir.model.access.csv
@@ -0,0 +1,11 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_mail_test_sms_all,mail.test.sms.all,model_mail_test_sms,,0,0,0,0
+access_mail_test_sms_user,mail.test.sms.user,model_mail_test_sms,base.group_user,1,1,1,1
+access_mail_test_sms_bl_all,mail.test.sms.bl.all,model_mail_test_sms_bl,,0,0,0,0
+access_mail_test_sms_bl_user,mail.test.sms.bl.user,model_mail_test_sms_bl,base.group_user,1,1,1,1
+access_mail_test_sms_bl_optout_all,mail.test.sms.bl.optout.all,model_mail_test_sms_bl_optout,,0,0,0,0
+access_mail_test_sms_bl_optout_user,mail.test.sms.bl.optout.user,model_mail_test_sms_bl_optout,base.group_user,1,1,1,1
+access_mail_test_sms_partner_all,mail.test.sms.partner.all,model_mail_test_sms_partner,,0,0,0,0
+access_mail_test_sms_partner_user,mail.test.sms.partner.user,model_mail_test_sms_partner,base.group_user,1,1,1,1
+access_mail_test_sms_partner_2many_all,mail.test.sms.partner.2many.all,model_mail_test_sms_partner_2many,,0,0,0,0
+access_mail_test_sms_partner_2many_user,mail.test.sms.partner.2many.user,model_mail_test_sms_partner_2many,base.group_user,1,1,1,1
diff --git a/addons/test_mail_full/tests/__init__.py b/addons/test_mail_full/tests/__init__.py
new file mode 100644
index 00000000..9c4bf096
--- /dev/null
+++ b/addons/test_mail_full/tests/__init__.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+from . import common
+from . import test_odoobot
+from . import test_phone_blacklist
+from . import test_mass_mailing
+from . import test_mass_sms
+from . import test_sms_composer
+from . import test_sms_management
+from . import test_sms_performance
+from . import test_sms_post
+from . import test_sms_server_actions
+from . import test_sms_sms
+from . import test_sms_template
diff --git a/addons/test_mail_full/tests/common.py b/addons/test_mail_full/tests/common.py
new file mode 100644
index 00000000..e41d4880
--- /dev/null
+++ b/addons/test_mail_full/tests/common.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.phone_validation.tools import phone_validation
+from odoo.addons.mass_mailing_sms.tests.common import MassSMSCommon
+from odoo.addons.test_mail.tests.common import TestRecipients
+from odoo.addons.test_mass_mailing.tests.common import TestMassMailCommon
+
+
+class TestMailFullCommon(TestMassMailCommon, MassSMSCommon):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestMailFullCommon, cls).setUpClass()
+
+ cls.mailing_sms = cls.env['mailing.mailing'].with_user(cls.user_marketing).create({
+ 'name': 'XMas SMS',
+ 'subject': 'Xmas SMS for {object.name}',
+ 'mailing_model_id': cls.env['ir.model']._get('mail.test.sms').id,
+ 'mailing_type': 'sms',
+ 'mailing_domain': '%s' % repr([('name', 'ilike', 'SMSTest')]),
+ 'body_plaintext': 'Dear ${object.display_name} this is a mass SMS with two links http://www.odoo.com/smstest and http://www.odoo.com/smstest/${object.id}',
+ 'sms_force_send': True,
+ 'sms_allow_unsubscribe': True,
+ })
+
+ @classmethod
+ def _create_mailing_sms_test_records(cls, model='mail.test.sms', partners=None, count=1):
+ """ Helper to create data. Currently simple, to be improved. """
+ Model = cls.env[model]
+ phone_field = 'phone_nbr' if 'phone_nbr' in Model else 'phone'
+ partner_field = 'customer_id' if 'customer_id' in Model else 'partner_id'
+
+ vals_list = []
+ for idx in range(count):
+ vals = {
+ 'name': 'SMSTestRecord_%02d' % idx,
+ phone_field: '045600%02d%02d' % (idx, idx)
+ }
+ if partners:
+ vals[partner_field] = partners[idx % len(partners)]
+
+ vals_list.append(vals)
+
+ return cls.env[model].create(vals_list)
+
+
+class TestRecipients(TestRecipients):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestRecipients, cls).setUpClass()
+ cls.partner_numbers = [
+ phone_validation.phone_format(partner.mobile, partner.country_id.code, partner.country_id.phone_code, force_format='E164')
+ for partner in (cls.partner_1 | cls.partner_2)
+ ]
diff --git a/addons/test_mail_full/tests/test_mass_mailing.py b/addons/test_mail_full/tests/test_mass_mailing.py
new file mode 100644
index 00000000..a01804df
--- /dev/null
+++ b/addons/test_mail_full/tests/test_mass_mailing.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import werkzeug
+
+from odoo.addons.test_mail_full.tests.common import TestMailFullCommon
+from odoo.tests.common import users
+from odoo.tools import mute_logger
+from odoo.tests import tagged
+
+
+@tagged('mass_mailing')
+class TestMassMailing(TestMailFullCommon):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestMassMailing, cls).setUpClass()
+
+ @users('user_marketing')
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_mailing_w_blacklist_opt_out(self):
+ mailing = self.env['mailing.mailing'].browse(self.mailing_bl.ids)
+
+ mailing.write({'mailing_model_id': self.env['ir.model']._get('mailing.test.optout').id})
+ recipients = self._create_mailing_test_records(model='mailing.test.optout', count=10)
+
+ # optout records 1 and 2
+ (recipients[1] | recipients[2]).write({'opt_out': True})
+ # blacklist records 3 and 4
+ self.env['mail.blacklist'].create({'email': recipients[3].email_normalized})
+ self.env['mail.blacklist'].create({'email': recipients[4].email_normalized})
+ # have a duplicate email for 9
+ recipient_dup_1 = recipients[9].copy()
+ # have a void mail
+ recipient_void_1 = self.env['mailing.test.optout'].create({'name': 'TestRecord_void_1'})
+ # have a falsy mail
+ recipient_falsy_1 = self.env['mailing.test.optout'].create({
+ 'name': 'TestRecord_falsy_1',
+ 'email_from': 'falsymail'
+ })
+ recipients_all = recipients + recipient_dup_1 + recipient_void_1 + recipient_falsy_1
+
+ mailing.write({'mailing_domain': [('id', 'in', recipients_all.ids)]})
+ mailing.action_put_in_queue()
+ with self.mock_mail_gateway(mail_unlink_sent=False):
+ mailing.action_send_mail()
+
+ for recipient in recipients_all:
+ recipient_info = {
+ 'email': recipient.email_normalized,
+ 'content': 'Hello %s' % recipient.name}
+ # opt-out: ignored (cancel mail)
+ if recipient in recipients[1] | recipients[2]:
+ recipient_info['state'] = 'ignored'
+ # blacklisted: ignored (cancel mail)
+ elif recipient in recipients[3] | recipients[4]:
+ recipient_info['state'] = 'ignored'
+ # duplicates: ignored (cancel mail)
+ elif recipient == recipient_dup_1:
+ recipient_info['state'] = 'ignored'
+ # void: ignored (cancel mail)
+ elif recipient == recipient_void_1:
+ recipient_info['state'] = 'ignored'
+ # falsy: ignored (cancel mail)
+ elif recipient == recipient_falsy_1:
+ recipient_info['state'] = 'ignored'
+ recipient_info['email'] = recipient.email_from # normalized is False but email should be falsymail
+ else:
+ email = self._find_sent_mail_wemail(recipient.email_normalized)
+ # preview correctly integrated rendered jinja
+ self.assertIn(
+ 'Hi %s :)' % recipient.name,
+ email['body'])
+ # rendered unsubscribe
+ self.assertIn(
+ '%s/mail/mailing/%s/unsubscribe' % (mailing.get_base_url(), mailing.id),
+ email['body'])
+ unsubscribe_href = self._get_href_from_anchor_id(email['body'], "url6")
+ unsubscribe_url = werkzeug.urls.url_parse(unsubscribe_href)
+ unsubscribe_params = unsubscribe_url.decode_query().to_dict(flat=True)
+ self.assertEqual(int(unsubscribe_params['res_id']), recipient.id)
+ self.assertEqual(unsubscribe_params['email'], recipient.email_normalized)
+ self.assertEqual(
+ mailing._unsubscribe_token(unsubscribe_params['res_id'], (unsubscribe_params['email'])),
+ unsubscribe_params['token']
+ )
+ # rendered view
+ self.assertIn(
+ '%s/mailing/%s/view' % (mailing.get_base_url(), mailing.id),
+ email['body'])
+ view_href = self._get_href_from_anchor_id(email['body'], "url6")
+ view_url = werkzeug.urls.url_parse(view_href)
+ view_params = view_url.decode_query().to_dict(flat=True)
+ self.assertEqual(int(view_params['res_id']), recipient.id)
+ self.assertEqual(view_params['email'], recipient.email_normalized)
+ self.assertEqual(
+ mailing._unsubscribe_token(view_params['res_id'], (view_params['email'])),
+ view_params['token']
+ )
+
+ self.assertMailTraces(
+ [recipient_info], mailing, recipient,
+ mail_links_info=[[
+ ('url0', 'https://www.odoo.tz/my/%s' % recipient.name, True, {}),
+ ('url1', 'https://www.odoo.be', True, {}),
+ ('url2', 'https://www.odoo.com', True, {}),
+ ('url3', 'https://www.odoo.eu', True, {}),
+ ('url4', 'https://www.example.com/foo/bar?baz=qux', True, {'baz': 'qux'}),
+ ('url5', '%s/event/dummy-event-0' % mailing.get_base_url(), True, {}),
+ # view is not shortened and parsed at sending
+ ('url6', '%s/view' % mailing.get_base_url(), False, {}),
+ ('url7', 'mailto:test@odoo.com', False, {}),
+ # unsubscribe is not shortened and parsed at sending
+ ('url8', '%s/unsubscribe_from_list' % mailing.get_base_url(), False, {}),
+ ]],
+ check_mail=True,)
+
+ # sent: 13, 2 bl, 2 opt-out, 3 invalid -> 6 remaining
+ self.assertMailingStatistics(mailing, expected=13, delivered=6, sent=6, ignored=7)
diff --git a/addons/test_mail_full/tests/test_mass_sms.py b/addons/test_mail_full/tests/test_mass_sms.py
new file mode 100644
index 00000000..dbc91b95
--- /dev/null
+++ b/addons/test_mail_full/tests/test_mass_sms.py
@@ -0,0 +1,405 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from ast import literal_eval
+
+from odoo.addons.phone_validation.tools import phone_validation
+from odoo.addons.test_mail_full.tests.common import TestMailFullCommon
+from odoo import exceptions
+from odoo.tests import tagged
+from odoo.tests.common import users
+from odoo.tools import mute_logger
+
+
+@tagged('mass_mailing')
+class TestMassSMSCommon(TestMailFullCommon):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestMassSMSCommon, cls).setUpClass()
+ cls._test_body = 'Mass SMS in your face'
+
+ records = cls.env['mail.test.sms']
+ partners = cls.env['res.partner']
+ country_be_id = cls.env.ref('base.be').id,
+ country_us_id = cls.env.ref('base.us').id,
+
+ for x in range(10):
+ partners += cls.env['res.partner'].with_context(**cls._test_context).create({
+ 'name': 'Partner_%s' % (x),
+ 'email': '_test_partner_%s@example.com' % (x),
+ 'country_id': country_be_id,
+ 'mobile': '045600%s%s99' % (x, x)
+ })
+ records += cls.env['mail.test.sms'].with_context(**cls._test_context).create({
+ 'name': 'MassSMSTest_%s' % (x),
+ 'customer_id': partners[x].id,
+ 'phone_nbr': '045600%s%s44' % (x, x)
+ })
+ cls.records = cls._reset_mail_context(records)
+ cls.records_numbers = [phone_validation.phone_format(r.phone_nbr, 'BE', '32', force_format='E164') for r in cls.records]
+ cls.partners = partners
+
+ cls.sms_template = cls.env['sms.template'].create({
+ 'name': 'Test Template',
+ 'model_id': cls.env['ir.model']._get('mail.test.sms').id,
+ 'body': 'Dear ${object.display_name} this is a mass SMS.',
+ })
+
+ cls.partner_numbers = [
+ phone_validation.phone_format(partner.mobile, partner.country_id.code, partner.country_id.phone_code, force_format='E164')
+ for partner in partners
+ ]
+
+ cls.mailing = cls.env['mailing.mailing'].with_user(cls.user_marketing).create({
+ 'name': 'Xmas Spam',
+ 'subject': 'Xmas Spam',
+ 'mailing_model_id': cls.env['ir.model']._get('mail.test.sms').id,
+ 'mailing_type': 'sms',
+ 'mailing_domain': '%s' % repr([('name', 'ilike', 'MassSMSTest')]),
+ 'sms_template_id': cls.sms_template.id,
+ 'sms_allow_unsubscribe': False,
+ })
+
+
+@tagged('mass_mailing')
+class TestMassSMSInternals(TestMassSMSCommon):
+
+ @users('user_marketing')
+ def test_mass_sms_domain(self):
+ 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',
+ })
+ self.assertEqual(literal_eval(mailing.mailing_domain), [])
+
+ mailing = self.env['mailing.mailing'].create({
+ 'name': 'Xmas Spam',
+ 'subject': 'Xmas Spam',
+ '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)])
+
+ @users('user_marketing')
+ def test_mass_sms_internals(self):
+ 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,
+ })
+
+ self.assertEqual(mailing.mailing_model_real, 'mail.test.sms')
+ self.assertEqual(mailing.medium_id, self.env.ref('mass_mailing_sms.utm_medium_sms'))
+ self.assertEqual(mailing.body_plaintext, self.sms_template.body)
+
+ remaining_res_ids = mailing._get_remaining_recipients()
+ self.assertEqual(set(remaining_res_ids), set(self.records.ids))
+
+ with self.mockSMSGateway():
+ 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
+ } for i, record in enumerate(self.records)],
+ mailing, self.records,
+ )
+
+ def test_mass_sms_internals_errors(self):
+ # same customer, specific different number on record -> should be valid
+ new_record_1 = self.env['mail.test.sms'].create({
+ 'name': 'MassSMSTest_nr1',
+ 'customer_id': self.partners[0].id,
+ 'phone_nbr': '0456999999',
+ })
+ void_record = self.env['mail.test.sms'].create({
+ 'name': 'MassSMSTest_void',
+ 'customer_id': False,
+ 'phone_nbr': '',
+ })
+ falsy_record_1 = self.env['mail.test.sms'].create({
+ 'name': 'MassSMSTest_falsy_1',
+ 'customer_id': False,
+ 'phone_nbr': 'abcd',
+ })
+ falsy_record_2 = self.env['mail.test.sms'].create({
+ 'name': 'MassSMSTest_falsy_2',
+ 'customer_id': False,
+ 'phone_nbr': '04561122',
+ })
+ bl_record_1 = self.env['mail.test.sms'].create({
+ 'name': 'MassSMSTest_bl_1',
+ 'customer_id': False,
+ 'phone_nbr': '0456110011',
+ })
+ self.env['phone.blacklist'].create({'number': '0456110011'})
+ # new customer, number already on record -> should be ignored
+ country_be_id = self.env.ref('base.be').id
+ nr2_partner = self.env['res.partner'].create({
+ 'name': 'Partner_nr2',
+ 'country_id': country_be_id,
+ 'mobile': '0456449999',
+ })
+ new_record_2 = self.env['mail.test.sms'].create({
+ 'name': 'MassSMSTest_nr2',
+ 'customer_id': nr2_partner.id,
+ 'phone_nbr': self.records[0].phone_nbr,
+ })
+ records_numbers = self.records_numbers + ['+32456999999']
+
+ with self.with_user('user_marketing'):
+ with self.mockSMSGateway():
+ self.mailing.action_send_sms()
+
+ 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)],
+ self.mailing, self.records | new_record_1,
+ )
+ # duplicates
+ self.assertSMSTraces(
+ [{'partner': new_record_2.customer_id, 'number': self.records_numbers[0],
+ 'content': 'Dear %s this is a mass SMS.' % new_record_2.display_name, 'state': 'ignored',
+ 'failure_type': 'sms_duplicate'}],
+ self.mailing, new_record_2,
+ )
+ # blacklist
+ self.assertSMSTraces(
+ [{'partner': self.env['res.partner'], '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, 'state': 'ignored',
+ 'failure_type': 'sms_blacklist'}],
+ self.mailing, bl_record_1,
+ )
+ # missing number
+ self.assertSMSTraces(
+ [{'partner': self.env['res.partner'], 'number': False,
+ 'content': 'Dear %s this is a mass SMS.' % void_record.display_name, 'state': 'exception',
+ 'failure_type': 'sms_number_missing'}],
+ self.mailing, void_record,
+ )
+ # wrong values
+ self.assertSMSTraces(
+ [{'partner': self.env['res.partner'], 'number': record.phone_nbr,
+ 'content': 'Dear %s this is a mass SMS.' % record.display_name, 'state': 'bounced',
+ 'failure_type': 'sms_number_format'}
+ for record in falsy_record_1 + falsy_record_2],
+ self.mailing, falsy_record_1 + falsy_record_2,
+ )
+
+ def test_mass_sms_internals_done_ids(self):
+ with self.with_user('user_marketing'):
+ with self.mockSMSGateway():
+ self.mailing.action_send_sms(res_ids=self.records[:5].ids)
+
+ traces = self.env['mailing.trace'].search([('mass_mailing_id', 'in', self.mailing.ids)])
+ self.assertEqual(len(traces), 5)
+ # new traces generated
+ self.assertSMSTraces(
+ [{'partner': record.customer_id, 'number': self.records_numbers[i],
+ 'content': 'Dear %s this is a mass SMS.' % record.display_name}
+ for i, record in enumerate(self.records[:5])],
+ self.mailing, self.records[:5],
+ )
+
+ with self.with_user('user_marketing'):
+ with self.mockSMSGateway():
+ self.mailing.action_send_sms(res_ids=self.records.ids)
+
+ # delete old traces (for testing purpose: ease check by deleting old ones)
+ traces.unlink()
+ # new failed traces generated for duplicates
+ self.assertSMSTraces(
+ [{'partner': record.customer_id, 'number': self.records_numbers[i],
+ 'content': 'Dear %s this is a mass SMS.' % record.display_name, 'state': 'ignored',
+ 'failure_type': 'sms_duplicate'}
+ for i, record in enumerate(self.records[:5])],
+ self.mailing, self.records[:5],
+ )
+ # new traces generated
+ self.assertSMSTraces(
+ [{'partner': record.customer_id, 'number': self.records_numbers[i+5],
+ 'content': 'Dear %s this is a mass SMS.' % record.display_name}
+ for i, record in enumerate(self.records[5:])],
+ self.mailing, self.records[5:],
+ )
+
+ @mute_logger('odoo.addons.mail.models.mail_render_mixin')
+ def test_mass_sms_test_button(self):
+ 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,
+ })
+
+ with self.with_user('user_marketing'):
+ with self.mockSMSGateway():
+ mailing_test.action_send_sms()
+
+ # Test if bad jinja in the body raises an error
+ mailing.write({
+ 'body_plaintext': 'Hello ${object.name_id.id}',
+ })
+
+ with self.with_user('user_marketing'):
+ with self.mock_mail_gateway(), self.assertRaises(Exception):
+ mailing_test.action_send_sms()
+
+
+@tagged('mass_mailing', 'mass_mailing_sms')
+class TestMassSMS(TestMassSMSCommon):
+
+ @users('user_marketing')
+ def test_mass_sms_links(self):
+ mailing = self.env['mailing.mailing'].browse(self.mailing.ids)
+ mailing.write({
+ 'body_plaintext': 'Dear ${object.display_name} this is a mass SMS with two links http://www.odoo.com/smstest and http://www.odoo.com/smstest/${object.name}',
+ 'sms_template_id': False,
+ 'sms_force_send': True,
+ 'sms_allow_unsubscribe': True,
+ })
+
+ with self.mockSMSGateway():
+ mailing.action_send_sms()
+
+ self.assertSMSTraces(
+ [{'partner': record.customer_id,
+ 'number': self.records_numbers[i],
+ 'state': 'sent',
+ 'content': 'Dear %s this is a mass SMS with two links' % record.display_name
+ } for i, record in enumerate(self.records)],
+ mailing, self.records,
+ sms_links_info=[[
+ ('http://www.odoo.com/smstest', True, {}),
+ ('http://www.odoo.com/smstest/%s' % record.name, True, {}),
+ # unsubscribe is not shortened and parsed at sending
+ ('unsubscribe', False, {}),
+ ] for record in self.records],
+ )
+
+ @users('user_marketing')
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_mass_sms_partner_only(self):
+ """ Check sending SMS marketing on models having only a partner_id fields
+ set is working. """
+ mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids)
+ mailing.write({
+ 'mailing_model_id': self.env['ir.model']._get('mail.test.sms.partner').id,
+ })
+
+ records = self.env['mail.test.sms.partner'].create([
+ {'name': 'SMSTest on %s' % partner.name,
+ 'customer_id': partner.id,
+ } for partner in self.partners
+ ])
+
+ with self.mockSMSGateway():
+ mailing.action_send_sms()
+
+ self.assertEqual(len(mailing.mailing_trace_ids), 10)
+ self.assertSMSTraces(
+ [{'partner': record.customer_id,
+ 'number': record.customer_id.phone_sanitized,
+ 'state': 'sent',
+ 'content': 'Dear %s this is a mass SMS with two links' % record.display_name
+ } for record in records],
+ mailing, records,
+ sms_links_info=[[
+ ('http://www.odoo.com/smstest', True, {}),
+ ('http://www.odoo.com/smstest/%s' % record.id, True, {}),
+ # unsubscribe is not shortened and parsed at sending
+ ('unsubscribe', False, {}),
+ ] for record in records],
+ )
+
+ # add a new record, send -> sent list should not resend traces
+ new_record = self.env['mail.test.sms.partner'].create([
+ {'name': 'Duplicate SMS on %s' % self.partners[0].name,
+ 'customer_id': self.partners[0].id,
+ }
+ ])
+ with self.mockSMSGateway():
+ mailing.action_send_sms()
+
+ self.assertEqual(len(mailing.mailing_trace_ids), 11)
+ self.assertSMSTraces(
+ [{'partner': new_record.customer_id,
+ 'number': new_record.customer_id.phone_sanitized,
+ 'state': 'sent',
+ 'content': 'Dear %s this is a mass SMS with two links' % new_record.display_name
+ }],
+ mailing, new_record,
+ sms_links_info=[[
+ ('http://www.odoo.com/smstest', True, {}),
+ ('http://www.odoo.com/smstest/%s' % new_record.id, True, {}),
+ # unsubscribe is not shortened and parsed at sending
+ ('unsubscribe', False, {}),
+ ]],
+ )
+
+ @users('user_marketing')
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_mass_sms_partner_only_m2m(self):
+ """ Check sending SMS marketing on models having only a m2m to partners
+ is currently not suppored. """
+ mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids)
+ mailing.write({
+ 'mailing_model_id': self.env['ir.model']._get('mail.test.sms.partner.2many').id,
+ })
+
+ records = self.env['mail.test.sms.partner.2many'].create([
+ {'name': 'SMSTest on %s' % partner.name,
+ 'customer_ids': [(4, partner.id)],
+ } for partner in self.partners
+ ])
+
+ with self.assertRaises(exceptions.UserError), self.mockSMSGateway():
+ mailing.action_send_sms()
+
+
+ @users('user_marketing')
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_mass_sms_w_opt_out(self):
+ mailing = self.env['mailing.mailing'].browse(self.mailing_sms.ids)
+ recipients = self._create_mailing_sms_test_records(model='mail.test.sms.bl.optout', count=5)
+
+ # optout records 0 and 1
+ (recipients[0] | recipients[1]).write({'opt_out': True})
+ # blacklist records 4
+ # TDE FIXME: sudo should not be necessary
+ self.env['phone.blacklist'].sudo().create({'number': recipients[4].phone_nbr})
+
+ mailing.write({
+ 'mailing_model_id': self.env['ir.model']._get('mail.test.sms.bl.optout'),
+ 'mailing_domain': [('id', 'in', recipients.ids)],
+ })
+
+ with self.mockSMSGateway():
+ mailing.action_send_sms()
+
+ self.assertSMSTraces(
+ [{'number': '+32456000000', 'state': 'ignored', 'failure_type': 'sms_blacklist'}, # TDE FIXME: should be opt_out
+ {'number': '+32456000101', 'state': 'ignored', 'failure_type': 'sms_blacklist'}, # TDE FIXME: should be opt_out
+ {'number': '+32456000202', 'state': 'sent'},
+ {'number': '+32456000303', 'state': 'sent'},
+ {'number': '+32456000404', 'state': 'ignored', 'failure_type': 'sms_blacklist'}],
+ mailing, recipients
+ )
+ self.assertEqual(mailing.ignored, 3)
diff --git a/addons/test_mail_full/tests/test_odoobot.py b/addons/test_mail_full/tests/test_odoobot.py
new file mode 100644
index 00000000..ddd6015b
--- /dev/null
+++ b/addons/test_mail_full/tests/test_odoobot.py
@@ -0,0 +1,143 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from unittest.mock import patch
+
+from odoo.addons.test_mail.tests.common import TestMailCommon, TestRecipients
+from odoo.tests import tagged
+from odoo.tools import mute_logger
+
+
+@tagged("odoobot")
+class TestOdoobot(TestMailCommon, TestRecipients):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestOdoobot, cls).setUpClass()
+ cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
+
+ cls.odoobot = cls.env.ref("base.partner_root")
+ cls.message_post_default_kwargs = {
+ 'body': '',
+ 'attachment_ids': [],
+ 'message_type': 'comment',
+ 'partner_ids': [],
+ 'subtype_xmlid': 'mail.mt_comment'
+ }
+ cls.odoobot_ping_body = '<a href="http://odoo.com/web#model=res.partner&amp;id=%s" class="o_mail_redirect" data-oe-id="%s" data-oe-model="res.partner" target="_blank">@OdooBot</a>' % (cls.odoobot.id, cls.odoobot.id)
+ cls.test_record_employe = cls.test_record.with_user(cls.user_employee)
+
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_fetch_listener(self):
+ channel = self.env['mail.channel'].with_user(self.user_employee).init_odoobot()
+ partners = self.env['mail.channel'].channel_fetch_listeners(channel.uuid)
+ odoobot = self.env.ref("base.partner_root")
+ odoobot_in_fetch_listeners = [partner for partner in partners if partner['id'] == odoobot.id]
+ self.assertEqual(len(odoobot_in_fetch_listeners), 1, 'odoobot should appear only once in channel_fetch_listeners')
+
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_odoobot_ping(self):
+ kwargs = self.message_post_default_kwargs.copy()
+ kwargs.update({'body': self.odoobot_ping_body, 'partner_ids': [self.odoobot.id, self.user_admin.partner_id.id]})
+
+ with patch('random.choice', lambda x: x[0]):
+ self.assertNextMessage(
+ self.test_record_employe.with_context({'mail_post_autofollow': True}).message_post(**kwargs),
+ sender=self.odoobot,
+ answer=False
+ )
+ # Odoobot should not be a follower but user_employee and user_admin should
+ follower = self.test_record.message_follower_ids.mapped('partner_id')
+ self.assertNotIn(self.odoobot, follower)
+ self.assertIn(self.user_employee.partner_id, follower)
+ self.assertIn(self.user_admin.partner_id, follower)
+
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_onboarding_flow(self):
+ kwargs = self.message_post_default_kwargs.copy()
+ channel = self.env['mail.channel'].with_user(self.user_employee).init_odoobot()
+
+ kwargs['body'] = 'tagada 😊'
+ last_message = self.assertNextMessage(
+ channel.message_post(**kwargs),
+ sender=self.odoobot,
+ answer=("help",)
+ )
+ channel.execute_command(command="help")
+ self.assertNextMessage(
+ last_message, # no message will be post with command help, use last odoobot message instead
+ sender=self.odoobot,
+ answer=("@OdooBot",)
+ )
+ kwargs['body'] = ''
+ kwargs['partner_ids'] = [self.env['ir.model.data'].xmlid_to_res_id("base.partner_root")]
+ self.assertNextMessage(
+ channel.message_post(**kwargs),
+ sender=self.odoobot,
+ answer=("attachment",)
+ )
+ kwargs['body'] = ''
+ attachment = self.env['ir.attachment'].with_user(self.user_employee).create({
+ 'datas': 'bWlncmF0aW9uIHRlc3Q=',
+ 'name': 'picture_of_your_dog.doc',
+ 'res_model': 'mail.compose.message',
+ })
+ kwargs['attachment_ids'] = [attachment.id]
+ # For the end of the flow, we only test that the state changed, but not to which
+ # one since it depends on the intalled apps, which can add more steps (like livechat)
+ channel.message_post(**kwargs)
+ self.assertNotEqual(self.user_employee.odoobot_state, 'onboarding_attachement')
+
+ # Test miscellaneous messages
+ self.user_employee.odoobot_state = "idle"
+ kwargs['partner_ids'] = []
+ kwargs['body'] = "I love you"
+ self.assertNextMessage(
+ channel.message_post(**kwargs),
+ sender=self.odoobot,
+ answer=("too human for me",)
+ )
+ kwargs['body'] = "Go fuck yourself"
+ self.assertNextMessage(
+ channel.message_post(**kwargs),
+ sender=self.odoobot,
+ answer=("I have feelings",)
+ )
+ kwargs['body'] = "help me"
+ self.assertNextMessage(
+ channel.message_post(**kwargs),
+ sender=self.odoobot,
+ answer=("If you need help",)
+ )
+
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_odoobot_no_default_answer(self):
+ kwargs = self.message_post_default_kwargs.copy()
+ kwargs.update({'body': "I'm not talking to @odoobot right now", 'partner_ids': []})
+ self.assertNextMessage(
+ self.test_record_employe.message_post(**kwargs),
+ answer=False
+ )
+
+ def assertNextMessage(self, message, answer=None, sender=None):
+ last_message = self.env['mail.message'].search([('id', '=', message.id + 1)])
+ if last_message:
+ body = last_message.body.replace('<p>', '').replace('</p>', '')
+ else:
+ self.assertFalse(answer, "No last message found when an answer was expect")
+ if answer is not None:
+ if answer and not last_message:
+ self.assertTrue(False, "No last message found")
+ if isinstance(answer, list):
+ self.assertIn(body, answer)
+ elif isinstance(answer, tuple):
+ for elem in answer:
+ self.assertIn(elem, body)
+ elif not answer:
+ self.assertFalse(last_message, "No answer should have been post")
+ return
+ else:
+ self.assertEqual(body, answer)
+ if sender:
+ self.assertEqual(sender, last_message.author_id)
+ return last_message
diff --git a/addons/test_mail_full/tests/test_phone_blacklist.py b/addons/test_mail_full/tests/test_phone_blacklist.py
new file mode 100644
index 00000000..adf37bdb
--- /dev/null
+++ b/addons/test_mail_full/tests/test_phone_blacklist.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients
+
+
+class TestPhoneBlacklist(TestMailFullCommon, TestRecipients):
+ """ TODO """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestPhoneBlacklist, cls).setUpClass()
+ cls._test_body = 'VOID CONTENT'
+
+ cls.test_record = cls.env['mail.test.sms.bl'].with_context(**cls._test_context).create({
+ 'name': 'Test',
+ 'customer_id': cls.partner_1.id,
+ 'mobile_nbr': cls.test_numbers[0],
+ 'phone_nbr': cls.test_numbers[1],
+ })
+ cls.test_record = cls._reset_mail_context(cls.test_record)
+
+ def test_phone_blacklist_internals(self):
+ with self.with_user('employee'):
+ test_record = self.env['mail.test.sms.bl'].browse(self.test_record.id)
+ self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[1])
+ self.assertFalse(test_record.phone_sanitized_blacklisted)
+
+ bl_record = self.env['phone.blacklist'].sudo().create([{'number': self.test_numbers_san[1]}])
+ test_record.invalidate_cache()
+ self.assertTrue(test_record.phone_sanitized_blacklisted)
+
+ self.env['phone.blacklist'].sudo().remove(self.test_numbers_san[1])
+ self.assertFalse(bl_record.active)
+ test_record.invalidate_cache()
+ self.assertFalse(test_record.phone_sanitized_blacklisted)
+
+ self.env['phone.blacklist'].sudo().add(self.test_numbers_san[1])
+ self.assertTrue(bl_record.active)
+ test_record.invalidate_cache()
+ self.assertTrue(test_record.phone_sanitized_blacklisted)
+
+ bl_record_2 = self.env['phone.blacklist'].sudo().create([{'number': self.test_numbers_san[1]}])
+ self.assertEqual(bl_record, bl_record_2)
+
+ rec = self.env['mail.test.sms.bl'].search([('phone_sanitized_blacklisted', '=', True)])
+ self.assertEqual(rec, test_record)
+
+ bl_record.unlink()
+ rec = self.env['mail.test.sms.bl'].search([('phone_sanitized_blacklisted', '=', True)])
+ self.assertEqual(rec, self.env['mail.test.sms.bl'])
+
+ def test_phone_sanitize_api(self):
+ with self.with_user('employee'):
+ test_record = self.env['mail.test.sms.bl'].browse(self.test_record.id)
+ self.assertFalse(test_record.phone_sanitized_blacklisted)
+
+ test_record._phone_set_blacklisted()
+ test_record.invalidate_cache()
+ self.assertTrue(test_record.phone_sanitized_blacklisted)
+
+ test_record._phone_reset_blacklisted()
+ test_record.invalidate_cache()
+ self.assertFalse(test_record.phone_sanitized_blacklisted)
+
+ def test_phone_sanitize_internals(self):
+ with self.with_user('employee'):
+ test_record = self.env['mail.test.sms.bl'].browse(self.test_record.id)
+ self.assertEqual(test_record.phone_nbr, self.test_numbers[1])
+ self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[1])
+
+ test_record.write({'phone_nbr': 'incorrect'})
+ self.assertEqual(test_record.phone_nbr, 'incorrect')
+ self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[0])
+
+ test_record.write({'mobile_nbr': 'incorrect'})
+ self.assertEqual(test_record.mobile_nbr, 'incorrect')
+ self.assertEqual(test_record.phone_sanitized, False)
+
+ test_record.write({'phone_nbr': self.test_numbers[1]})
+ self.assertEqual(test_record.phone_nbr, self.test_numbers[1])
+ self.assertEqual(test_record.phone_sanitized, self.test_numbers_san[1])
diff --git a/addons/test_mail_full/tests/test_sms_composer.py b/addons/test_mail_full/tests/test_sms_composer.py
new file mode 100644
index 00000000..cf6b7821
--- /dev/null
+++ b/addons/test_mail_full/tests/test_sms_composer.py
@@ -0,0 +1,567 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients
+
+
+class TestSMSComposerComment(TestMailFullCommon, TestRecipients):
+ """ TODO LIST
+
+ * add test for default_res_model / default_res_id and stuff like that;
+ * add test for comment put in queue;
+ * add test for language support (set template lang context);
+ * add test for sanitized / wrong numbers;
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSMSComposerComment, cls).setUpClass()
+ cls._test_body = 'VOID CONTENT'
+
+ cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
+ 'name': 'Test',
+ 'customer_id': cls.partner_1.id,
+ 'mobile_nbr': cls.test_numbers[0],
+ 'phone_nbr': cls.test_numbers[1],
+ })
+ cls.test_record = cls._reset_mail_context(cls.test_record)
+
+ cls.sms_template = cls.env['sms.template'].create({
+ 'name': 'Test Template',
+ 'model_id': cls.env['ir.model']._get('mail.test.sms').id,
+ 'body': 'Dear ${object.display_name} this is an SMS.',
+ })
+
+ def test_composer_comment_not_mail_thread(self):
+ with self.with_user('employee'):
+ record = self.env['test_performance.base'].create({'name': 'TestBase'})
+ composer = self.env['sms.composer'].with_context(
+ active_model='test_performance.base', active_id=record.id
+ ).create({
+ 'body': self._test_body,
+ 'numbers': ','.join(self.random_numbers),
+ })
+
+ with self.mockSMSGateway():
+ composer._action_send_sms()
+
+ # use sms.api directly, does not create sms.sms
+ self.assertNoSMS()
+ self.assertSMSIapSent(self.random_numbers_san, self._test_body)
+
+ def test_composer_comment_default(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ active_model='mail.test.sms', active_id=self.test_record.id
+ ).create({
+ 'body': self._test_body,
+ })
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ self.assertSMSNotification([{'partner': self.test_record.customer_id, 'number': self.test_numbers_san[1]}], self._test_body, messages)
+
+ def test_composer_comment_field_1(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ active_model='mail.test.sms', active_id=self.test_record.id,
+ ).create({
+ 'body': self._test_body,
+ 'number_field_name': 'mobile_nbr',
+ })
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ self.assertSMSNotification([{'partner': self.test_record.customer_id, 'number': self.test_numbers_san[0]}], self._test_body, messages)
+
+ def test_composer_comment_field_2(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ active_model='mail.test.sms', active_id=self.test_record.id,
+ ).create({
+ 'body': self._test_body,
+ 'number_field_name': 'phone_nbr',
+ })
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ self.assertSMSNotification([{'partner': self.test_record.customer_id, 'number': self.test_numbers_san[1]}], self._test_body, messages)
+
+ def test_composer_comment_field_w_numbers(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ active_model='mail.test.sms', active_id=self.test_record.id,
+ default_number_field_name='mobile_nbr',
+ ).create({
+ 'body': self._test_body,
+ 'numbers': ','.join(self.random_numbers),
+ })
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ self.assertSMSNotification([
+ {'partner': self.test_record.customer_id, 'number': self.test_record.mobile_nbr},
+ {'number': self.random_numbers_san[0]}, {'number': self.random_numbers_san[1]}], self._test_body, messages)
+
+ def test_composer_comment_field_w_template(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ active_model='mail.test.sms', active_id=self.test_record.id,
+ default_template_id=self.sms_template.id,
+ default_number_field_name='mobile_nbr',
+ ).create({})
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ self.assertSMSNotification([{'partner': self.test_record.customer_id, 'number': self.test_record.mobile_nbr}], 'Dear %s this is an SMS.' % self.test_record.display_name, messages)
+
+ def test_composer_internals(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_res_model='mail.test.sms', default_res_id=self.test_record.id,
+ ).create({
+ 'body': self._test_body,
+ 'number_field_name': 'phone_nbr',
+ })
+
+ self.assertEqual(composer.res_model, self.test_record._name)
+ self.assertEqual(composer.res_id, self.test_record.id)
+ self.assertEqual(composer.number_field_name, 'phone_nbr')
+ self.assertTrue(composer.comment_single_recipient)
+ self.assertEqual(composer.recipient_single_description, self.test_record.customer_id.display_name)
+ self.assertEqual(composer.recipient_single_number, self.test_numbers[1])
+ self.assertEqual(composer.recipient_single_number_itf, self.test_numbers[1])
+ self.assertTrue(composer.recipient_single_valid)
+ self.assertEqual(composer.recipient_valid_count, 1)
+ self.assertEqual(composer.recipient_invalid_count, 0)
+
+ with self.with_user('employee'):
+ composer.update({'recipient_single_number_itf': '0123456789'})
+
+ self.assertFalse(composer.recipient_single_valid)
+
+ with self.with_user('employee'):
+ composer.update({'recipient_single_number_itf': self.random_numbers[0]})
+
+ self.assertTrue(composer.recipient_single_valid)
+
+ with self.with_user('employee'):
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ self.test_record.flush()
+ self.assertEqual(self.test_record.phone_nbr, self.random_numbers[0])
+
+ def test_composer_comment_wo_partner_wo_value_update(self):
+ """ Test record without partner and without phone values: should allow updating first found phone field """
+ self.test_record.write({
+ 'customer_id': False,
+ 'phone_nbr': False,
+ 'mobile_nbr': False,
+ })
+ default_field_name = self.env['mail.test.sms']._sms_get_number_fields()[0]
+
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ active_model='mail.test.sms', active_id=self.test_record.id,
+ default_composition_mode='comment',
+ ).create({
+ 'body': self._test_body,
+ })
+ self.assertFalse(composer.recipient_single_number_itf)
+ self.assertFalse(composer.recipient_single_number)
+ self.assertEqual(composer.number_field_name, default_field_name)
+
+ composer.write({
+ 'recipient_single_number_itf': self.random_numbers_san[0],
+ })
+ self.assertEqual(composer.recipient_single_number_itf, self.random_numbers_san[0])
+ self.assertFalse(composer.recipient_single_number)
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ self.assertEqual(self.test_record[default_field_name], self.random_numbers_san[0])
+ self.assertSMSNotification([{'partner': self.env['res.partner'], 'number': self.random_numbers_san[0]}], self._test_body, messages)
+
+ def test_composer_numbers_no_model(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='numbers'
+ ).create({
+ 'body': self._test_body,
+ 'numbers': ','.join(self.random_numbers),
+ })
+
+ with self.mockSMSGateway():
+ composer._action_send_sms()
+
+ # use sms.api directly, does not create sms.sms
+ self.assertNoSMS()
+ self.assertSMSIapSent(self.random_numbers_san, self._test_body)
+
+
+class TestSMSComposerBatch(TestMailFullCommon):
+ @classmethod
+ def setUpClass(cls):
+ super(TestSMSComposerBatch, cls).setUpClass()
+ cls._test_body = 'Zizisse an SMS.'
+
+ cls._create_records_for_batch('mail.test.sms', 3)
+ cls.sms_template = cls._create_sms_template('mail.test.sms')
+
+ def test_composer_batch_active_domain(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='comment',
+ default_res_model='mail.test.sms',
+ default_use_active_domain=True,
+ active_domain=[('id', 'in', self.records.ids)],
+ ).create({
+ 'body': self._test_body,
+ })
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ for record in self.records:
+ self.assertSMSNotification([{'partner': r.customer_id} for r in self.records], 'Zizisse an SMS.', messages)
+
+ def test_composer_batch_active_ids(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='comment',
+ default_res_model='mail.test.sms',
+ active_ids=self.records.ids
+ ).create({
+ 'body': self._test_body,
+ })
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ for record in self.records:
+ self.assertSMSNotification([{'partner': r.customer_id} for r in self.records], 'Zizisse an SMS.', messages)
+
+ def test_composer_batch_domain(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='comment',
+ default_res_model='mail.test.sms',
+ default_use_active_domain=True,
+ default_active_domain=repr([('id', 'in', self.records.ids)]),
+ ).create({
+ 'body': self._test_body,
+ })
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ for record in self.records:
+ self.assertSMSNotification([{'partner': r.customer_id} for r in self.records], 'Zizisse an SMS.', messages)
+
+ def test_composer_batch_res_ids(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='comment',
+ default_res_model='mail.test.sms',
+ default_res_ids=repr(self.records.ids),
+ ).create({
+ 'body': self._test_body,
+ })
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ for record in self.records:
+ self.assertSMSNotification([{'partner': r.customer_id} for r in self.records], 'Zizisse an SMS.', messages)
+
+
+class TestSMSComposerMass(TestMailFullCommon):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSMSComposerMass, cls).setUpClass()
+ cls._test_body = 'Zizisse an SMS.'
+
+ cls._create_records_for_batch('mail.test.sms', 3)
+ cls.sms_template = cls._create_sms_template('mail.test.sms')
+
+ def test_composer_mass_active_domain(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ default_use_active_domain=True,
+ active_domain=[('id', 'in', self.records.ids)],
+ ).create({
+ 'body': self._test_body,
+ 'mass_keep_log': False,
+ })
+
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ for record in self.records:
+ self.assertSMSOutgoing(record.customer_id, None, content=self._test_body)
+
+ def test_composer_mass_active_domain_w_template(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ default_use_active_domain=True,
+ active_domain=[('id', 'in', self.records.ids)],
+ default_template_id=self.sms_template.id,
+ ).create({
+ 'mass_keep_log': False,
+ })
+
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ for record in self.records:
+ self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name)
+
+ def test_composer_mass_active_ids(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ active_ids=self.records.ids,
+ ).create({
+ 'body': self._test_body,
+ 'mass_keep_log': False,
+ })
+
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ for partner in self.partners:
+ self.assertSMSOutgoing(partner, None, content=self._test_body)
+
+ def test_composer_mass_active_ids_w_blacklist(self):
+ self.env['phone.blacklist'].create([{
+ 'number': p.phone_sanitized,
+ 'active': True,
+ } for p in self.partners[:5]])
+
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ active_ids=self.records.ids,
+ ).create({
+ 'body': self._test_body,
+ 'mass_keep_log': False,
+ 'mass_use_blacklist': True,
+ })
+
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ for partner in self.partners[5:]:
+ self.assertSMSOutgoing(partner, partner.phone_sanitized, content=self._test_body)
+ for partner in self.partners[:5]:
+ self.assertSMSCanceled(partner, partner.phone_sanitized, error_code='sms_blacklist', content=self._test_body)
+
+ def test_composer_mass_active_ids_wo_blacklist(self):
+ self.env['phone.blacklist'].create([{
+ 'number': p.phone_sanitized,
+ 'active': True,
+ } for p in self.partners[:5]])
+
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ active_ids=self.records.ids,
+ ).create({
+ 'body': self._test_body,
+ 'mass_keep_log': False,
+ 'mass_use_blacklist': False,
+ })
+
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ for partner in self.partners:
+ self.assertSMSOutgoing(partner, partner.phone_sanitized, content=self._test_body)
+
+ def test_composer_mass_active_ids_w_blacklist_and_done(self):
+ self.env['phone.blacklist'].create([{
+ 'number': p.phone_sanitized,
+ 'active': True,
+ } for p in self.partners[:5]])
+ for p in self.partners[8:]:
+ p.mobile = self.partners[8].mobile
+ self.assertEqual(p.phone_sanitized, self.partners[8].phone_sanitized)
+
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ active_ids=self.records.ids,
+ ).create({
+ 'body': self._test_body,
+ 'mass_keep_log': False,
+ 'mass_use_blacklist': True,
+ })
+
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ for partner in self.partners[8:]:
+ self.assertSMSOutgoing(partner, partner.phone_sanitized, content=self._test_body)
+ for partner in self.partners[5:8]:
+ self.assertSMSCanceled(partner, partner.phone_sanitized, error_code='sms_duplicate', content=self._test_body)
+ for partner in self.partners[:5]:
+ self.assertSMSCanceled(partner, partner.phone_sanitized, error_code='sms_blacklist', content=self._test_body)
+
+ def test_composer_mass_active_ids_w_template(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ active_ids=self.records.ids,
+ default_template_id=self.sms_template.id,
+ ).create({
+ 'mass_keep_log': False,
+ })
+
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ for record in self.records:
+ self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name)
+
+ def test_composer_mass_active_ids_w_template_and_lang(self):
+ self.env['res.lang']._activate_lang('fr_FR')
+ self.env['ir.translation'].create({
+ 'type': 'model',
+ 'name': 'sms.template,body',
+ 'lang': 'fr_FR',
+ 'res_id': self.sms_template.id,
+ 'src': self.sms_template.body,
+ 'value': 'Cher·e· ${object.display_name} ceci est un SMS.',
+ })
+ # set template to try to use customer lang
+ self.sms_template.write({
+ 'lang': '${object.customer_id.lang}',
+ })
+ # set one customer as french speaking
+ self.partners[2].write({'lang': 'fr_FR'})
+
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ active_ids=self.records.ids,
+ default_template_id=self.sms_template.id,
+ ).create({
+ 'mass_keep_log': False,
+ })
+
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ for record in self.records:
+ if record.customer_id == self.partners[2]:
+ self.assertSMSOutgoing(record.customer_id, None, content='Cher·e· %s ceci est un SMS.' % record.display_name)
+ else:
+ self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name)
+
+ def test_composer_mass_active_ids_w_template_and_log(self):
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ active_ids=self.records.ids,
+ default_template_id=self.sms_template.id,
+ ).create({
+ 'mass_keep_log': True,
+ })
+
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ for record in self.records:
+ self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name)
+ self.assertSMSLogged(record, 'Dear %s this is an SMS.' % record.display_name)
+
+ def test_composer_template_context_action(self):
+ """ Test the context action from a SMS template (Add context action button)
+ and the usage with the sms composer """
+ # Create the lang info
+ self.env['res.lang']._activate_lang('fr_FR')
+ self.env['ir.translation'].create({
+ 'type': 'model',
+ 'name': 'sms.template,body',
+ 'lang': 'fr_FR',
+ 'res_id': self.sms_template.id,
+ 'src': self.sms_template.body,
+ 'value': "Hello ${object.display_name} ceci est en français.",
+ })
+ # set template to try to use customer lang
+ self.sms_template.write({
+ 'lang': '${object.customer_id.lang}',
+ })
+ # create a second record linked to a customer in another language
+ self.partners[2].write({'lang': 'fr_FR'})
+ test_record_2 = self.env['mail.test.sms'].create({
+ 'name': 'Test',
+ 'customer_id': self.partners[2].id,
+ })
+ test_record_1 = self.env['mail.test.sms'].create({
+ 'name': 'Test',
+ 'customer_id': self.partners[1].id,
+ })
+ # Composer creation with context from a template context action (simulate) - comment (single recipient)
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ sms_composition_mode='guess',
+ default_res_ids=[test_record_2.id],
+ default_res_id=test_record_2.id,
+ active_ids=[test_record_2.id],
+ active_id=test_record_2.id,
+ active_model='mail.test.sms',
+ default_template_id=self.sms_template.id,
+ ).create({
+ 'mass_keep_log': False,
+ })
+ self.assertEqual(composer.composition_mode, "comment")
+ self.assertEqual(composer.body, "Hello %s ceci est en français." % test_record_2.display_name)
+
+ with self.mockSMSGateway():
+ messages = composer._action_send_sms()
+
+ number = self.partners[2].phone_get_sanitized_number()
+ self.assertSMSNotification([{'partner': test_record_2.customer_id, 'number': number}], "Hello %s ceci est en français." % test_record_2.display_name, messages)
+
+ # Composer creation with context from a template context action (simulate) - mass (multiple recipient)
+ with self.with_user('employee'):
+ composer = self.env['sms.composer'].with_context(
+ sms_composition_mode='guess',
+ default_res_ids=[test_record_1.id, test_record_2.id],
+ default_res_id=test_record_1.id,
+ active_ids=[test_record_1.id, test_record_2.id],
+ active_id=test_record_1.id,
+ active_model='mail.test.sms',
+ default_template_id=self.sms_template.id,
+ ).create({
+ 'mass_keep_log': True,
+ })
+ self.assertEqual(composer.composition_mode, "mass")
+ # In english because by default but when sinding depending of record
+ self.assertEqual(composer.body, "Dear ${object.display_name} this is an SMS.")
+
+ with self.mockSMSGateway():
+ composer.action_send_sms()
+
+ self.assertSMSOutgoing(test_record_1.customer_id, None, content='Dear %s this is an SMS.' % test_record_1.display_name)
+ self.assertSMSOutgoing(test_record_2.customer_id, None, content="Hello %s ceci est en français." % test_record_2.display_name)
diff --git a/addons/test_mail_full/tests/test_sms_management.py b/addons/test_mail_full/tests/test_sms_management.py
new file mode 100644
index 00000000..e0d09797
--- /dev/null
+++ b/addons/test_mail_full/tests/test_sms_management.py
@@ -0,0 +1,116 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients
+
+
+class TestSMSWizards(TestMailFullCommon, TestRecipients):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSMSWizards, cls).setUpClass()
+ cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
+ 'name': 'Test',
+ 'customer_id': cls.partner_1.id,
+ })
+ cls.test_record = cls._reset_mail_context(cls.test_record)
+ cls.msg = cls.test_record.message_post(body='TEST BODY', author_id=cls.partner_employee.id)
+ cls.notif_p1 = cls.env['mail.notification'].create({
+ 'mail_message_id': cls.msg.id,
+ 'res_partner_id': cls.partner_1.id,
+ 'sms_number': cls.partner_1.mobile,
+ 'notification_type': 'sms',
+ 'notification_status': 'exception',
+ 'failure_type': 'sms_number_format',
+ })
+ cls.notif_p2 = cls.env['mail.notification'].create({
+ 'mail_message_id': cls.msg.id,
+ 'res_partner_id': cls.partner_2.id,
+ 'sms_number': cls.partner_2.mobile,
+ 'notification_type': 'sms',
+ 'notification_status': 'exception',
+ 'failure_type': 'sms_credit',
+ })
+
+ def test_sms_resend(self):
+ self._reset_bus()
+
+ with self.with_user('employee'):
+ wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({})
+ wizard.write({'recipient_ids': [(1, r.id, {'resend': True}) for r in wizard.recipient_ids]})
+ with self.mockSMSGateway():
+ wizard.action_resend()
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'sent'},
+ {'partner': self.partner_2, 'state': 'sent'}
+ ], 'TEST BODY', self.msg, check_sms=True)
+ self.assertMessageBusNotifications(self.msg)
+
+ def test_sms_resend_update_number(self):
+ self._reset_bus()
+
+ with self.with_user('employee'):
+ wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({})
+ wizard.write({'recipient_ids': [(1, r.id, {'resend': True, 'sms_number': self.random_numbers[idx]}) for idx, r in enumerate(wizard.recipient_ids.sorted())]})
+ with self.mockSMSGateway():
+ wizard.action_resend()
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'sent', 'number': self.random_numbers_san[0]},
+ {'partner': self.partner_2, 'state': 'sent', 'number': self.random_numbers_san[1]}
+ ], 'TEST BODY', self.msg, check_sms=True)
+ self.assertMessageBusNotifications(self.msg)
+
+ def test_sms_resend_cancel(self):
+ self._reset_bus()
+
+ with self.with_user('employee'):
+ wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({})
+ with self.mockSMSGateway():
+ wizard.action_cancel()
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'canceled', 'number': self.notif_p1.sms_number, 'failure_type': 'sms_number_format'},
+ {'partner': self.partner_2, 'state': 'canceled', 'number': self.notif_p2.sms_number, 'failure_type': 'sms_credit'}
+ ], 'TEST BODY', self.msg, check_sms=False)
+ self.assertMessageBusNotifications(self.msg)
+
+ def test_sms_resend_internals(self):
+ self._reset_bus()
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'exception', 'number': self.notif_p1.sms_number, 'failure_type': 'sms_number_format'},
+ {'partner': self.partner_2, 'state': 'exception', 'number': self.notif_p2.sms_number, 'failure_type': 'sms_credit'}
+ ], 'TEST BODY', self.msg, check_sms=False)
+
+ with self.with_user('employee'):
+ wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({})
+ self.assertTrue(wizard.has_insufficient_credit)
+ self.assertEqual(set(wizard.mapped('recipient_ids.partner_name')), set((self.partner_1 | self.partner_2).mapped('display_name')))
+ wizard.write({'recipient_ids': [(1, r.id, {'resend': True}) for r in wizard.recipient_ids]})
+ with self.mockSMSGateway():
+ wizard.action_resend()
+
+ def test_sms_resend_w_cancel(self):
+ self._reset_bus()
+
+ with self.with_user('employee'):
+ wizard = self.env['sms.resend'].with_context(default_mail_message_id=self.msg.id).create({})
+ wizard.write({'recipient_ids': [(1, r.id, {'resend': True if r.partner_id == self.partner_1 else False}) for r in wizard.recipient_ids]})
+ with self.mockSMSGateway():
+ wizard.action_resend()
+
+ self.assertSMSNotification([{'partner': self.partner_1, 'state': 'sent'}], 'TEST BODY', self.msg, check_sms=True)
+ self.assertSMSNotification([{'partner': self.partner_2, 'state': 'canceled', 'number': self.notif_p2.sms_number, 'failure_type': 'sms_credit'}], 'TEST BODY', self.msg, check_sms=False)
+ self.assertMessageBusNotifications(self.msg)
+
+ def test_sms_cancel(self):
+ self._reset_bus()
+
+ with self.mockSMSGateway(), self.with_user('employee'):
+ wizard = self.env['sms.cancel'].with_context(default_model=self.msg.model).create({})
+ wizard.action_cancel()
+
+ self.assertEqual((self.notif_p1 | self.notif_p2).mapped('notification_status'), ['canceled', 'canceled'])
+
+ self.assertMessageBusNotifications(self.msg)
diff --git a/addons/test_mail_full/tests/test_sms_performance.py b/addons/test_mail_full/tests/test_sms_performance.py
new file mode 100644
index 00000000..cfa30321
--- /dev/null
+++ b/addons/test_mail_full/tests/test_sms_performance.py
@@ -0,0 +1,163 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.sms.tests import common as sms_common
+from odoo.addons.test_mail.tests.test_performance import BaseMailPerformance
+from odoo.tests.common import users, warmup
+from odoo.tests import tagged
+from odoo.tools import mute_logger
+
+
+@tagged('mail_performance')
+class TestSMSPerformance(BaseMailPerformance, sms_common.SMSCase):
+
+ def setUp(self):
+ super(TestSMSPerformance, self).setUp()
+ self.user_employee.write({
+ 'login': 'employee',
+ 'country_id': self.env.ref('base.be').id,
+ })
+ self.admin = self.env.user
+
+ self.customer = self.env['res.partner'].with_context(self._quick_create_ctx).create({
+ 'name': 'Test Customer',
+ 'email': 'test@example.com',
+ 'mobile': '0456123456',
+ 'country_id': self.env.ref('base.be').id,
+ })
+ self.test_record = self.env['mail.test.sms'].with_context(self._quick_create_ctx).create({
+ 'name': 'Test',
+ 'customer_id': self.customer.id,
+ 'phone_nbr': '0456999999',
+ })
+
+ # prepare recipients to test for more realistic workload
+ Partners = self.env['res.partner'].with_context(self._quick_create_ctx)
+ self.partners = self.env['res.partner']
+ for x in range(0, 10):
+ self.partners |= Partners.create({
+ 'name': 'Test %s' % x,
+ 'email': 'test%s@example.com' % x,
+ 'mobile': '0456%s%s0000' % (x, x),
+ 'country_id': self.env.ref('base.be').id,
+ })
+
+ # patch registry to simulate a ready environment
+ self.patch(self.env.registry, 'ready', True)
+
+ @mute_logger('odoo.addons.sms.models.sms_sms')
+ @users('employee')
+ @warmup
+ def test_message_sms_record_1_partner(self):
+ record = self.test_record.with_user(self.env.user)
+ pids = self.customer.ids
+ with self.mockSMSGateway(sms_allow_unlink=True), self.assertQueryCount(employee=23): # test_mail_enterprise: 18
+ messages = record._message_sms(
+ body='Performance Test',
+ partner_ids=pids,
+ )
+
+ self.assertEqual(record.message_ids[0].body, '<p>Performance Test</p>')
+ self.assertSMSNotification([{'partner': self.customer}], 'Performance Test', messages, sent_unlink=True)
+
+ @mute_logger('odoo.addons.sms.models.sms_sms')
+ @users('employee')
+ @warmup
+ def test_message_sms_record_10_partners(self):
+ record = self.test_record.with_user(self.env.user)
+ pids = self.partners.ids
+ with self.mockSMSGateway(sms_allow_unlink=True), self.assertQueryCount(employee=41):
+ messages = record._message_sms(
+ body='Performance Test',
+ partner_ids=pids,
+ )
+
+ self.assertEqual(record.message_ids[0].body, '<p>Performance Test</p>')
+ self.assertSMSNotification([{'partner': partner} for partner in self.partners], 'Performance Test', messages, sent_unlink=True)
+
+ @mute_logger('odoo.addons.sms.models.sms_sms')
+ @users('employee')
+ @warmup
+ def test_message_sms_record_default(self):
+ record = self.test_record.with_user(self.env.user)
+ with self.mockSMSGateway(sms_allow_unlink=True), self.assertQueryCount(employee=26):
+ messages = record._message_sms(
+ body='Performance Test',
+ )
+
+ self.assertEqual(record.message_ids[0].body, '<p>Performance Test</p>')
+ self.assertSMSNotification([{'partner': self.customer}], 'Performance Test', messages, sent_unlink=True)
+
+
+@tagged('mail_performance')
+class TestSMSMassPerformance(BaseMailPerformance, sms_common.MockSMS):
+
+ def setUp(self):
+ super(TestSMSMassPerformance, self).setUp()
+ be_country_id = self.env.ref('base.be').id,
+ self.user_employee.write({
+ 'login': 'employee',
+ 'country_id': be_country_id,
+ })
+ self.admin = self.env.user
+ self.admin.write({
+ 'country_id': be_country_id,
+ })
+
+ self._test_body = 'MASS SMS'
+
+ records = self.env['mail.test.sms']
+ partners = self.env['res.partner']
+ for x in range(50):
+ partners += self.env['res.partner'].with_context(**self._quick_create_ctx).create({
+ 'name': 'Partner_%s' % (x),
+ 'email': '_test_partner_%s@example.com' % (x),
+ 'country_id': be_country_id,
+ 'mobile': '047500%02d%02d' % (x, x)
+ })
+ records += self.env['mail.test.sms'].with_context(**self._quick_create_ctx).create({
+ 'name': 'Test_%s' % (x),
+ 'customer_id': partners[x].id,
+ })
+ self.partners = partners
+ self.records = records
+
+ self.sms_template = self.env['sms.template'].create({
+ 'name': 'Test Template',
+ 'model_id': self.env['ir.model']._get('mail.test.sms').id,
+ 'body': 'Dear ${object.display_name} this is an SMS.',
+ })
+
+ @mute_logger('odoo.addons.sms.models.sms_sms')
+ @users('employee')
+ @warmup
+ def test_composer_mass_active_domain(self):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ default_use_active_domain=True,
+ active_domain=[('id', 'in', self.records.ids)],
+ ).create({
+ 'body': self._test_body,
+ 'mass_keep_log': False,
+ })
+
+ with self.mockSMSGateway(sms_allow_unlink=True), self.assertQueryCount(employee=106):
+ composer.action_send_sms()
+
+ @mute_logger('odoo.addons.sms.models.sms_sms')
+ @users('employee')
+ @warmup
+ def test_composer_mass_active_domain_w_log(self):
+ composer = self.env['sms.composer'].with_context(
+ default_composition_mode='mass',
+ default_res_model='mail.test.sms',
+ default_use_active_domain=True,
+ active_domain=[('id', 'in', self.records.ids)],
+ ).create({
+ 'body': self._test_body,
+ 'mass_keep_log': True,
+ })
+
+ with self.mockSMSGateway(sms_allow_unlink=True), self.assertQueryCount(employee=157):
+ composer.action_send_sms()
diff --git a/addons/test_mail_full/tests/test_sms_post.py b/addons/test_mail_full/tests/test_sms_post.py
new file mode 100644
index 00000000..ad39f0c2
--- /dev/null
+++ b/addons/test_mail_full/tests/test_sms_post.py
@@ -0,0 +1,437 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients
+
+
+class TestSMSPost(TestMailFullCommon, TestRecipients):
+ """ TODO
+
+ * add tests for new mail.message and mail.thread fields;
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSMSPost, cls).setUpClass()
+ cls._test_body = 'VOID CONTENT'
+
+ cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
+ 'name': 'Test',
+ 'customer_id': cls.partner_1.id,
+ 'mobile_nbr': cls.test_numbers[0],
+ 'phone_nbr': cls.test_numbers[1],
+ })
+ cls.test_record = cls._reset_mail_context(cls.test_record)
+
+ def test_message_sms_internals_body(self):
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms('<p>Mega SMS<br/>Top moumoutte</p>', partner_ids=self.partner_1.ids)
+
+ self.assertEqual(messages.body, '<p>Mega SMS<br>Top moumoutte</p>')
+ self.assertEqual(messages.subtype_id, self.env.ref('mail.mt_note'))
+ self.assertSMSNotification([{'partner': self.partner_1}], 'Mega SMS\nTop moumoutte', messages)
+
+ def test_message_sms_internals_check_existing(self):
+ with self.with_user('employee'), self.mockSMSGateway(sim_error='wrong_number_format'):
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=self.partner_1.ids)
+
+ self.assertSMSNotification([{'partner': self.partner_1, 'state': 'exception', 'failure_type': 'sms_number_format'}], self._test_body, messages)
+
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ test_record._notify_record_by_sms(messages, {'partners': [{'id': self.partner_1.id, 'notif': 'sms'}]}, check_existing=True)
+ self.assertSMSNotification([{'partner': self.partner_1}], self._test_body, messages)
+
+ def test_message_sms_internals_sms_numbers(self):
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=self.partner_1.ids, sms_numbers=self.random_numbers)
+
+ self.assertSMSNotification([{'partner': self.partner_1}, {'number': self.random_numbers_san[0]}, {'number': self.random_numbers_san[1]}], self._test_body, messages)
+
+ def test_message_sms_internals_subtype(self):
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms('<p>Mega SMS<br/>Top moumoutte</p>', subtype_id=self.env.ref('mail.mt_comment').id, partner_ids=self.partner_1.ids)
+
+ self.assertEqual(messages.body, '<p>Mega SMS<br>Top moumoutte</p>')
+ self.assertEqual(messages.subtype_id, self.env.ref('mail.mt_comment'))
+ self.assertSMSNotification([{'partner': self.partner_1}], 'Mega SMS\nTop moumoutte', messages)
+
+ def test_message_sms_internals_pid_to_number(self):
+ pid_to_number = {
+ self.partner_1.id: self.random_numbers[0],
+ self.partner_2.id: self.random_numbers[1],
+ }
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2).ids, sms_pid_to_number=pid_to_number)
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'number': self.random_numbers_san[0]},
+ {'partner': self.partner_2, 'number': self.random_numbers_san[1]}],
+ self._test_body, messages)
+
+ def test_message_sms_model_partner(self):
+ with self.with_user('employee'), self.mockSMSGateway():
+ messages = self.partner_1._message_sms(self._test_body)
+ messages |= self.partner_2._message_sms(self._test_body)
+ self.assertSMSNotification([{'partner': self.partner_1}, {'partner': self.partner_2}], self._test_body, messages)
+
+ def test_message_sms_model_partner_fallback(self):
+ self.partner_1.write({'mobile': False, 'phone': self.random_numbers[0]})
+
+ with self.mockSMSGateway():
+ messages = self.partner_1._message_sms(self._test_body)
+ messages |= self.partner_2._message_sms(self._test_body)
+
+ self.assertSMSNotification([{'partner': self.partner_1, 'number': self.random_numbers_san[0]}, {'partner': self.partner_2}], self._test_body, messages)
+
+ def test_message_sms_model_w_partner_only(self):
+ with self.with_user('employee'):
+ record = self.env['mail.test.sms.partner'].create({'customer_id': self.partner_1.id})
+
+ with self.mockSMSGateway():
+ messages = record._message_sms(self._test_body)
+
+ self.assertSMSNotification([{'partner': self.partner_1}], self._test_body, messages)
+
+ def test_message_sms_model_w_partner_only_void(self):
+ with self.with_user('employee'):
+ record = self.env['mail.test.sms.partner'].create({'customer_id': False})
+
+ with self.mockSMSGateway():
+ messages = record._message_sms(self._test_body)
+
+ # should not crash but have a failed notification
+ self.assertSMSNotification([{'partner': self.env['res.partner'], 'number': False, 'state': 'exception', 'failure_type': 'sms_number_missing'}], self._test_body, messages)
+
+ def test_message_sms_model_w_partner_m2m_only(self):
+ with self.with_user('employee'):
+ record = self.env['mail.test.sms.partner.2many'].create({'customer_ids': [(4, self.partner_1.id)]})
+
+ with self.mockSMSGateway():
+ messages = record._message_sms(self._test_body)
+
+ self.assertSMSNotification([{'partner': self.partner_1}], self._test_body, messages)
+
+ # TDE: should take first found one according to partner ordering
+ with self.with_user('employee'):
+ record = self.env['mail.test.sms.partner.2many'].create({'customer_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]})
+
+ with self.mockSMSGateway():
+ messages = record._message_sms(self._test_body)
+
+ self.assertSMSNotification([{'partner': self.partner_2}], self._test_body, messages)
+
+ def test_message_sms_on_field_w_partner(self):
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, number_field='mobile_nbr')
+
+ self.assertSMSNotification([{'partner': self.partner_1, 'number': self.test_record.mobile_nbr}], self._test_body, messages)
+
+ def test_message_sms_on_field_wo_partner(self):
+ self.test_record.write({'customer_id': False})
+
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, number_field='mobile_nbr')
+
+ self.assertSMSNotification([{'number': self.test_record.mobile_nbr}], self._test_body, messages)
+
+ def test_message_sms_on_field_wo_partner_wo_value(self):
+ """ Test record without a partner and without phone values. """
+ self.test_record.write({
+ 'customer_id': False,
+ 'phone_nbr': False,
+ 'mobile_nbr': False,
+ })
+
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body)
+
+ # should not crash but have a failed notification
+ self.assertSMSNotification([{'partner': self.env['res.partner'], 'number': False, 'state': 'exception', 'failure_type': 'sms_number_missing'}], self._test_body, messages)
+
+ def test_message_sms_on_field_wo_partner_default_field(self):
+ self.test_record.write({'customer_id': False})
+
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body)
+
+ self.assertSMSNotification([{'number': self.test_numbers_san[1]}], self._test_body, messages)
+
+ def test_message_sms_on_field_wo_partner_default_field_2(self):
+ self.test_record.write({'customer_id': False, 'phone_nbr': False})
+
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body)
+
+ self.assertSMSNotification([{'number': self.test_numbers_san[0]}], self._test_body, messages)
+
+ def test_message_sms_on_numbers(self):
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, sms_numbers=self.random_numbers_san)
+ self.assertSMSNotification([{'number': self.random_numbers_san[0]}, {'number': self.random_numbers_san[1]}], self._test_body, messages)
+
+ def test_message_sms_on_numbers_sanitization(self):
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, sms_numbers=self.random_numbers)
+ self.assertSMSNotification([{'number': self.random_numbers_san[0]}, {'number': self.random_numbers_san[1]}], self._test_body, messages)
+
+ def test_message_sms_on_partner_ids(self):
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2).ids)
+
+ self.assertSMSNotification([{'partner': self.partner_1}, {'partner': self.partner_2}], self._test_body, messages)
+
+ def test_message_sms_on_partner_ids_default(self):
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body)
+
+ self.assertSMSNotification([{'partner': self.test_record.customer_id, 'number': self.test_numbers_san[1]}], self._test_body, messages)
+
+ def test_message_sms_on_partner_ids_w_numbers(self):
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=self.partner_1.ids, sms_numbers=self.random_numbers[:1])
+
+ self.assertSMSNotification([{'partner': self.partner_1}, {'number': self.random_numbers_san[0]}], self._test_body, messages)
+
+ def test_message_sms_with_template(self):
+ sms_template = self.env['sms.template'].create({
+ 'name': 'Test Template',
+ 'model_id': self.env['ir.model']._get('mail.test.sms').id,
+ 'body': 'Dear ${object.display_name} this is an SMS.',
+ })
+
+ with self.with_user('employee'):
+ with self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms_with_template(template=sms_template)
+
+ self.assertSMSNotification([{'partner': self.partner_1, 'number': self.test_numbers_san[1]}], 'Dear %s this is an SMS.' % self.test_record.display_name, messages)
+
+ def test_message_sms_with_template_fallback(self):
+ with self.with_user('employee'):
+ with self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms_with_template(template_xmlid='test_mail_full.this_should_not_exists', template_fallback='Fallback for ${object.id}')
+
+ self.assertSMSNotification([{'partner': self.partner_1, 'number': self.test_numbers_san[1]}], 'Fallback for %s' % self.test_record.id, messages)
+
+ def test_message_sms_with_template_xmlid(self):
+ sms_template = self.env['sms.template'].create({
+ 'name': 'Test Template',
+ 'model_id': self.env['ir.model']._get('mail.test.sms').id,
+ 'body': 'Dear ${object.display_name} this is an SMS.',
+ })
+ self.env['ir.model.data'].create({
+ 'name': 'this_should_exists',
+ 'module': 'test_mail_full',
+ 'model': sms_template._name,
+ 'res_id': sms_template.id,
+ })
+
+ with self.with_user('employee'):
+ with self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms_with_template(template_xmlid='test_mail_full.this_should_exists')
+
+ self.assertSMSNotification([{'partner': self.partner_1, 'number': self.test_numbers_san[1]}], 'Dear %s this is an SMS.' % self.test_record.display_name, messages)
+
+
+class TestSMSPostException(TestMailFullCommon, TestRecipients):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSMSPostException, cls).setUpClass()
+ cls._test_body = 'VOID CONTENT'
+
+ cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
+ 'name': 'Test',
+ 'customer_id': cls.partner_1.id,
+ })
+ cls.test_record = cls._reset_mail_context(cls.test_record)
+ cls.partner_3 = cls.env['res.partner'].with_context({
+ 'mail_create_nolog': True,
+ 'mail_create_nosubscribe': True,
+ 'mail_notrack': True,
+ 'no_reset_password': True,
+ }).create({
+ 'name': 'Ernestine Loubine',
+ 'email': 'ernestine.loubine@agrolait.com',
+ 'country_id': cls.env.ref('base.be').id,
+ 'mobile': '0475556644',
+ })
+
+ def test_message_sms_w_numbers_invalid(self):
+ random_numbers = self.random_numbers + ['6988754']
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, sms_numbers=random_numbers)
+
+ # invalid numbers are still given to IAP currently as they are
+ self.assertSMSNotification([{'number': self.random_numbers_san[0]}, {'number': self.random_numbers_san[1]}, {'number': random_numbers[2]}], self._test_body, messages)
+
+ def test_message_sms_w_partners_nocountry(self):
+ self.test_record.customer_id.write({
+ 'mobile': self.random_numbers[0],
+ 'phone': self.random_numbers[1],
+ 'country_id': False,
+ })
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=self.test_record.customer_id.ids)
+
+ self.assertSMSNotification([{'partner': self.test_record.customer_id}], self._test_body, messages)
+
+ def test_message_sms_w_partners_falsy(self):
+ # TDE FIXME: currently sent to IAP
+ self.test_record.customer_id.write({
+ 'mobile': 'youpie',
+ 'phone': 'youpla',
+ })
+ with self.with_user('employee'), self.mockSMSGateway():
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=self.test_record.customer_id.ids)
+
+ # self.assertSMSNotification({self.test_record.customer_id: {}}, {}, self._test_body, messages)
+
+ def test_message_sms_w_numbers_sanitization_duplicate(self):
+ pass
+ # TDE FIXME: not sure
+ # random_numbers = self.random_numbers + [self.random_numbers[1]]
+ # random_numbers_san = self.random_numbers_san + [self.random_numbers_san[1]]
+ # with self.with_user('employee'), self.mockSMSGateway():
+ # messages = self.test_record._message_sms(self._test_body, sms_numbers=random_numbers)
+ # self.assertSMSNotification({}, {random_numbers_san[0]: {}, random_numbers_san[1]: {}, random_numbers_san[2]: {}}, self._test_body, messages)
+
+ def test_message_sms_crash_credit(self):
+ with self.with_user('employee'), self.mockSMSGateway(sim_error='credit'):
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2).ids)
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'exception', 'failure_type': 'sms_credit'},
+ {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_credit'},
+ ], self._test_body, messages)
+
+ def test_message_sms_crash_credit_single(self):
+ with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): 'credit'}):
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids)
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'sent'},
+ {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_credit'},
+ {'partner': self.partner_3, 'state': 'sent'},
+ ], self._test_body, messages)
+
+ def test_message_sms_crash_server_crash(self):
+ with self.with_user('employee'), self.mockSMSGateway(sim_error='jsonrpc_exception'):
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids)
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'exception', 'failure_type': 'sms_server'},
+ {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_server'},
+ {'partner': self.partner_3, 'state': 'exception', 'failure_type': 'sms_server'},
+ ], self._test_body, messages)
+
+ def test_message_sms_crash_unregistered(self):
+ with self.with_user('employee'), self.mockSMSGateway(sim_error='unregistered'):
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2).ids)
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'exception', 'failure_type': 'sms_acc'},
+ {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_acc'},
+ ], self._test_body, messages)
+
+ def test_message_sms_crash_unregistered_single(self):
+ with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): 'unregistered'}):
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids)
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'sent'},
+ {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_acc'},
+ {'partner': self.partner_3, 'state': 'sent'},
+ ], self._test_body, messages)
+
+ def test_message_sms_crash_wrong_number(self):
+ with self.with_user('employee'), self.mockSMSGateway(sim_error='wrong_number_format'):
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2).ids)
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'exception', 'failure_type': 'sms_number_format'},
+ {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_number_format'},
+ ], self._test_body, messages)
+
+ def test_message_sms_crash_wrong_number_single(self):
+ with self.with_user('employee'), self.mockSMSGateway(nbr_t_error={self.partner_2.phone_get_sanitized_number(): 'wrong_number_format'}):
+ test_record = self.env['mail.test.sms'].browse(self.test_record.id)
+ messages = test_record._message_sms(self._test_body, partner_ids=(self.partner_1 | self.partner_2 | self.partner_3).ids)
+
+ self.assertSMSNotification([
+ {'partner': self.partner_1, 'state': 'sent'},
+ {'partner': self.partner_2, 'state': 'exception', 'failure_type': 'sms_number_format'},
+ {'partner': self.partner_3, 'state': 'sent'},
+ ], self._test_body, messages)
+
+
+class TestSMSApi(TestMailFullCommon):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSMSApi, cls).setUpClass()
+ cls._test_body = 'Zizisse an SMS.'
+
+ cls._create_records_for_batch('mail.test.sms', 3)
+ cls.sms_template = cls._create_sms_template('mail.test.sms')
+
+ def test_message_schedule_sms(self):
+ with self.with_user('employee'):
+ with self.mockSMSGateway():
+ self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(body=self._test_body, mass_keep_log=False)
+
+ for record in self.records:
+ self.assertSMSOutgoing(record.customer_id, None, content=self._test_body)
+
+ def test_message_schedule_sms_w_log(self):
+ with self.with_user('employee'):
+ with self.mockSMSGateway():
+ self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(body=self._test_body, mass_keep_log=True)
+
+ for record in self.records:
+ self.assertSMSOutgoing(record.customer_id, None, content=self._test_body)
+ self.assertSMSLogged(record, self._test_body)
+
+ def test_message_schedule_sms_w_template(self):
+ with self.with_user('employee'):
+ with self.mockSMSGateway():
+ self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(template=self.sms_template, mass_keep_log=False)
+
+ for record in self.records:
+ self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name)
+
+ def test_message_schedule_sms_w_template_and_log(self):
+ with self.with_user('employee'):
+ with self.mockSMSGateway():
+ self.env['mail.test.sms'].browse(self.records.ids)._message_sms_schedule_mass(template=self.sms_template, mass_keep_log=True)
+
+ for record in self.records:
+ self.assertSMSOutgoing(record.customer_id, None, content='Dear %s this is an SMS.' % record.display_name)
+ self.assertSMSLogged(record, 'Dear %s this is an SMS.' % record.display_name)
diff --git a/addons/test_mail_full/tests/test_sms_server_actions.py b/addons/test_mail_full/tests/test_sms_server_actions.py
new file mode 100644
index 00000000..1c06123e
--- /dev/null
+++ b/addons/test_mail_full/tests/test_sms_server_actions.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients
+
+
+class TestServerAction(TestMailFullCommon, TestRecipients):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestServerAction, cls).setUpClass()
+ cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
+ 'name': 'Test',
+ 'customer_id': cls.partner_1.id,
+ })
+ cls.test_record_2 = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
+ 'name': 'Test Record 2',
+ 'customer_id': False,
+ 'phone_nbr': cls.test_numbers[0],
+ })
+
+ cls.sms_template = cls._create_sms_template('mail.test.sms')
+ cls.action = cls.env['ir.actions.server'].create({
+ 'name': 'Test SMS Action',
+ 'model_id': cls.env['ir.model']._get('mail.test.sms').id,
+ 'state': 'sms',
+ 'sms_template_id': cls.sms_template.id,
+ 'groups_id': cls.env.ref('base.group_user'),
+ })
+
+ def test_action_sms(self):
+ context = {
+ 'active_model': 'mail.test.sms',
+ 'active_ids': (self.test_record | self.test_record_2).ids,
+ }
+
+ with self.with_user('employee'), self.mockSMSGateway():
+ self.action.with_user(self.env.user).with_context(**context).run()
+
+ self.assertSMSOutgoing(self.test_record.customer_id, None, content='Dear %s this is an SMS.' % self.test_record.display_name)
+ self.assertSMSOutgoing(self.env['res.partner'], self.test_numbers_san[0], content='Dear %s this is an SMS.' % self.test_record_2.display_name)
+
+ def test_action_sms_single(self):
+ context = {
+ 'active_model': 'mail.test.sms',
+ 'active_id': self.test_record.id,
+ }
+
+ with self.with_user('employee'), self.mockSMSGateway():
+ self.action.with_user(self.env.user).with_context(**context).run()
+ self.assertSMSOutgoing(self.test_record.customer_id, None, content='Dear %s this is an SMS.' % self.test_record.display_name)
+
+ def test_action_sms_w_log(self):
+ self.action.sms_mass_keep_log = True
+ context = {
+ 'active_model': 'mail.test.sms',
+ 'active_ids': (self.test_record | self.test_record_2).ids,
+ }
+
+ with self.with_user('employee'), self.mockSMSGateway():
+ self.action.with_user(self.env.user).with_context(**context).run()
+
+ self.assertSMSOutgoing(self.test_record.customer_id, None, content='Dear %s this is an SMS.' % self.test_record.display_name)
+ self.assertSMSLogged(self.test_record, 'Dear %s this is an SMS.' % self.test_record.display_name)
+
+ self.assertSMSOutgoing(self.env['res.partner'], self.test_numbers_san[0], content='Dear %s this is an SMS.' % self.test_record_2.display_name)
+ self.assertSMSLogged(self.test_record_2, 'Dear %s this is an SMS.' % self.test_record_2.display_name)
diff --git a/addons/test_mail_full/tests/test_sms_sms.py b/addons/test_mail_full/tests/test_sms_sms.py
new file mode 100644
index 00000000..2c009a47
--- /dev/null
+++ b/addons/test_mail_full/tests/test_sms_sms.py
@@ -0,0 +1,162 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import werkzeug
+
+from unittest.mock import patch
+from unittest.mock import DEFAULT
+
+from odoo import exceptions
+from odoo.addons.sms.models.sms_sms import SmsSms as SmsSms
+from odoo.addons.test_mail_full.tests.common import TestMailFullCommon
+from odoo.tests import common
+
+
+class LinkTrackerMock(common.BaseCase):
+
+ def setUp(self):
+ super(LinkTrackerMock, self).setUp()
+
+ def _get_title_from_url(u):
+ return "Test_TITLE"
+
+ self.env['ir.config_parameter'].sudo().set_param('web.base.url', 'https://test.odoo.com')
+
+ link_tracker_title_patch = patch('odoo.addons.link_tracker.models.link_tracker.LinkTracker._get_title_from_url', wraps=_get_title_from_url)
+ link_tracker_title_patch.start()
+ self.addCleanup(link_tracker_title_patch.stop)
+
+ self.utm_c = self.env.ref('utm.utm_campaign_fall_drive')
+ self.utm_m = self.env.ref('mass_mailing_sms.utm_medium_sms')
+ self.tracker_values = {
+ 'campaign_id': self.utm_c.id,
+ 'medium_id': self.utm_m.id,
+ }
+
+ def assertLinkTracker(self, url, url_params):
+ links = self.env['link.tracker'].sudo().search([('url', '=', url)])
+ self.assertEqual(len(links), 1)
+
+ # check UTMS are correctly set on redirect URL
+ original_url = werkzeug.urls.url_parse(url)
+ redirect_url = werkzeug.urls.url_parse(links.redirected_url)
+ redirect_params = redirect_url.decode_query().to_dict(flat=True)
+ self.assertEqual(redirect_url.scheme, original_url.scheme)
+ self.assertEqual(redirect_url.decode_netloc(), original_url.decode_netloc())
+ self.assertEqual(redirect_url.path, original_url.path)
+ self.assertEqual(redirect_params, url_params)
+
+
+class TestSMSPost(TestMailFullCommon, LinkTrackerMock):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSMSPost, cls).setUpClass()
+ cls._test_body = 'VOID CONTENT'
+
+ cls.sms_all = cls.env['sms.sms']
+ for x in range(10):
+ cls.sms_all |= cls.env['sms.sms'].create({
+ 'number': '+324560000%s%s' % (x, x),
+ 'body': cls._test_body,
+ })
+
+ def test_body_link_shorten(self):
+ link = 'http://www.example.com'
+ self.env['link.tracker'].search([('url', '=', link)]).unlink()
+ new_body = self.env['mail.render.mixin']._shorten_links_text('Welcome to %s !' % link, self.tracker_values)
+ self.assertNotIn(link, new_body)
+ self.assertLinkTracker(link, {'utm_campaign': self.utm_c.name, 'utm_medium': self.utm_m.name})
+ link = self.env['link.tracker'].search([('url', '=', link)])
+ self.assertIn(link.short_url, new_body)
+
+ link = 'https://test.odoo.com/my/super_page?test[0]=42&toto=áâà#title3'
+ self.env['link.tracker'].search([('url', '=', link)]).unlink()
+ new_body = self.env['mail.render.mixin']._shorten_links_text('Welcome to %s !' % link, self.tracker_values)
+ self.assertNotIn(link, new_body)
+ self.assertLinkTracker(link, {
+ 'utm_campaign': self.utm_c.name,
+ 'utm_medium': self.utm_m.name,
+ 'test[0]': '42',
+ 'toto': 'áâà',
+ })
+ link = self.env['link.tracker'].search([('url', '=', link)])
+ self.assertIn(link.short_url, new_body)
+
+ def test_body_link_shorten_wshort(self):
+ link = 'https://test.odoo.com/r/RAOUL'
+ self.env['link.tracker'].search([('url', '=', link)]).unlink()
+ new_body = self.env['mail.render.mixin']._shorten_links_text('Welcome to %s !' % link, self.tracker_values)
+ self.assertIn(link, new_body)
+ self.assertFalse(self.env['link.tracker'].search([('url', '=', link)]))
+
+ def test_body_link_shorten_wunsubscribe(self):
+ link = 'https://test.odoo.com/sms/3/'
+ self.env['link.tracker'].search([('url', '=', link)]).unlink()
+ new_body = self.env['mail.render.mixin']._shorten_links_text('Welcome to %s !' % link, self.tracker_values)
+ self.assertIn(link, new_body)
+ self.assertFalse(self.env['link.tracker'].search([('url', '=', link)]))
+
+ def test_sms_body_link_shorten_suffix(self):
+ mailing = self.env['mailing.mailing'].create({
+ 'subject': 'Minimal mailing',
+ 'mailing_model_id': self.env['ir.model']._get('mail.test.sms').id,
+ 'mailing_type': 'sms',
+ })
+
+ sms_0 = self.env['sms.sms'].create({
+ 'body': 'Welcome to https://test.odoo.com',
+ 'number': '12',
+ 'mailing_id': mailing.id,
+ })
+ sms_1 = self.env['sms.sms'].create({
+ 'body': 'Welcome to https://test.odoo.com/r/RAOUL',
+ 'number': '12',
+ })
+ sms_2 = self.env['sms.sms'].create({
+ 'body': 'Welcome to https://test.odoo.com/r/RAOUL',
+ 'number': '12', 'mailing_id': mailing.id,
+ })
+ sms_3 = self.env['sms.sms'].create({
+ 'body': 'Welcome to https://test.odoo.com/leodagan/r/RAOUL',
+ 'number': '12', 'mailing_id': mailing.id,
+ })
+
+ res = (sms_0 | sms_1 | sms_2 | sms_3)._update_body_short_links()
+ self.assertEqual(res[sms_0.id], 'Welcome to https://test.odoo.com')
+ self.assertEqual(res[sms_1.id], 'Welcome to https://test.odoo.com/r/RAOUL')
+ self.assertEqual(res[sms_2.id], 'Welcome to https://test.odoo.com/r/RAOUL/s/%s' % sms_2.id)
+ self.assertEqual(res[sms_3.id], 'Welcome to https://test.odoo.com/leodagan/r/RAOUL')
+
+ def test_sms_send_batch_size(self):
+ self.count = 0
+
+ def _send(sms_self, delete_all=False, raise_exception=False):
+ self.count += 1
+ return DEFAULT
+
+ self.env['ir.config_parameter'].set_param('sms.session.batch.size', '3')
+ with patch.object(SmsSms, '_send', autospec=True, side_effect=_send) as send_mock:
+ self.env['sms.sms'].browse(self.sms_all.ids).send()
+
+ self.assertEqual(self.count, 4)
+
+ def test_sms_send_crash_employee(self):
+ with self.assertRaises(exceptions.AccessError):
+ self.env['sms.sms'].with_user(self.user_employee).browse(self.sms_all.ids).send()
+
+ def test_sms_send_delete_all(self):
+ with self.mockSMSGateway(sms_allow_unlink=True, sim_error='jsonrpc_exception'):
+ self.env['sms.sms'].browse(self.sms_all.ids).send(delete_all=True, raise_exception=False)
+ self.assertFalse(len(self.sms_all.exists()))
+
+ def test_sms_send_raise(self):
+ with self.assertRaises(exceptions.AccessError):
+ with self.mockSMSGateway(sim_error='jsonrpc_exception'):
+ self.env['sms.sms'].browse(self.sms_all.ids).send(raise_exception=True)
+ self.assertEqual(set(self.sms_all.mapped('state')), set(['outgoing']))
+
+ def test_sms_send_raise_catch(self):
+ with self.mockSMSGateway(sim_error='jsonrpc_exception'):
+ self.env['sms.sms'].browse(self.sms_all.ids).send(raise_exception=False)
+ self.assertEqual(set(self.sms_all.mapped('state')), set(['error']))
diff --git a/addons/test_mail_full/tests/test_sms_template.py b/addons/test_mail_full/tests/test_sms_template.py
new file mode 100644
index 00000000..c57ae3cf
--- /dev/null
+++ b/addons/test_mail_full/tests/test_sms_template.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.test_mail_full.tests.common import TestMailFullCommon, TestRecipients
+
+
+class TestSmsTemplate(TestMailFullCommon, TestRecipients):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestSmsTemplate, cls).setUpClass()
+ cls.test_record = cls.env['mail.test.sms'].with_context(**cls._test_context).create({
+ 'name': 'Test',
+ 'customer_id': cls.partner_1.id,
+ })
+ cls.test_record = cls._reset_mail_context(cls.test_record)
+
+ cls.body_en = 'Dear ${object.display_name} this is an SMS.'
+ cls.body_fr = u"Hello ${object.display_name} ceci est en français."
+ cls.sms_template = cls._create_sms_template('mail.test.sms', body=cls.body_en)
+
+ def test_sms_template_render(self):
+ rendered_body = self.sms_template._render_template(self.sms_template.body, self.sms_template.model, self.test_record.ids)
+ self.assertEqual(rendered_body[self.test_record.id], 'Dear %s this is an SMS.' % self.test_record.display_name)
+
+ def test_sms_template_lang(self):
+ self.env['res.lang']._activate_lang('fr_FR')
+ self.user_admin.write({'lang': 'en_US'})
+ self.env['ir.translation'].create({
+ 'type': 'model',
+ 'name': 'sms.template,body',
+ 'lang': 'fr_FR',
+ 'res_id': self.sms_template.id,
+ 'src': self.sms_template.body,
+ 'value': self.body_fr,
+ })
+ # set template to try to use customer lang
+ self.sms_template.write({
+ 'lang': '${object.customer_id.lang}',
+ })
+ # create a second record linked to a customer in another language
+ self.partner_2.write({
+ 'lang': 'fr_FR',
+ })
+ test_record_2 = self.env['mail.test.sms'].create({
+ 'name': 'Test',
+ 'customer_id': self.partner_2.id,
+ })
+
+ self.assertEqual(self.sms_template.body, self.body_en)
+ self.assertEqual(self.sms_template.with_context(lang='fr_FR').body, self.body_fr)
+
+ rid_to_lang = self.sms_template._render_lang((self.test_record | test_record_2).ids)
+ self.assertEqual(set(rid_to_lang.keys()), set((self.test_record | test_record_2).ids))
+ for rid, lang in rid_to_lang.items():
+ # TDE FIXME: False or en_US ?
+ if rid == self.test_record.id:
+ self.assertEqual(lang, 'en_US')
+ elif rid == test_record_2.id:
+ self.assertEqual(lang, 'fr_FR')
+ else:
+ self.assertTrue(False)
+
+ tpl_to_rids = self.sms_template._classify_per_lang((self.test_record | test_record_2).ids)
+ for lang, (tpl, rids) in tpl_to_rids.items():
+ # TDE FIXME: False or en_US ?
+ if lang == 'en_US':
+ self.assertEqual(rids, self.test_record.ids)
+ elif lang == 'fr_FR':
+ self.assertEqual(rids, test_record_2.ids)
+ else:
+ self.assertTrue(False, 'Should not return lang %s' % lang)
+
+ def test_sms_template_create_and_unlink_sidebar_action(self):
+ ActWindow = self.env['ir.actions.act_window']
+ self.sms_template.action_create_sidebar_action()
+ action_id = self.sms_template.sidebar_action_id.id
+
+ self.assertNotEqual(action_id, False)
+ self.assertEqual(ActWindow.search_count([('id', '=', action_id)]), 1)
+
+ self.sms_template.action_unlink_sidebar_action()
+ self.assertEqual(ActWindow.search_count([('id', '=', action_id)]), 0)
+
+ def test_sms_template_unlink_with_action(self):
+ ActWindow = self.env['ir.actions.act_window']
+ self.sms_template.action_create_sidebar_action()
+ action_id = self.sms_template.sidebar_action_id.id
+
+ self.sms_template.unlink()
+ self.assertEqual(ActWindow.search_count([('id', '=', action_id)]), 0)