434 lines
17 KiB
Python
434 lines
17 KiB
Python
# Copyright 2016 Tecnativa - Antonio Espinosa
|
|
# Copyright 2017 Tecnativa - David Vidal
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
|
|
from unittest import mock
|
|
|
|
from odoo.exceptions import UserError, ValidationError
|
|
from odoo.tests.common import TransactionCase
|
|
from odoo.tools import mute_logger
|
|
|
|
_packagepath = "odoo.addons.mail_tracking_mailgun"
|
|
|
|
|
|
class TestMailgun(TransactionCase):
|
|
def mail_send(self):
|
|
mail = self.env["mail.mail"].create(
|
|
{
|
|
"subject": "Test subject",
|
|
"email_from": "from@example.com",
|
|
"email_to": self.recipient,
|
|
"body_html": "<p>This is a test message</p>",
|
|
}
|
|
)
|
|
mail.send()
|
|
# Search tracking created
|
|
tracking_email = self.env["mail.tracking.email"].search(
|
|
[("mail_id", "=", mail.id)]
|
|
)
|
|
return mail, tracking_email
|
|
|
|
def setUp(self):
|
|
super(TestMailgun, self).setUp()
|
|
self.recipient = "to@example.com"
|
|
self.mail, self.tracking_email = self.mail_send()
|
|
self.api_key = "key-12345678901234567890123456789012"
|
|
self.domain = "example.com"
|
|
self.token = "f1349299097a51b9a7d886fcb5c2735b426ba200ada6e9e149"
|
|
self.timestamp = "1471021089"
|
|
self.signature = (
|
|
"4fb6d4dbbe10ce5d620265dcd7a3c0b8ca0dede1433103891bc1ae4086e9d5b2"
|
|
)
|
|
self.env["ir.config_parameter"].set_param("mailgun.apikey", self.api_key)
|
|
self.env["ir.config_parameter"].set_param("mail.catchall.domain", self.domain)
|
|
self.env["ir.config_parameter"].set_param(
|
|
"mailgun.validation_key", self.api_key
|
|
)
|
|
self.env["ir.config_parameter"].set_param(
|
|
"mailgun.auto_check_partner_email", ""
|
|
)
|
|
self.event = {
|
|
"Message-Id": "<xxx.xxx.xxx-openerp-xxx-res.partner@test_db>",
|
|
"X-Mailgun-Sid": "WyIwNjgxZSIsICJ0b0BleGFtcGxlLmNvbSIsICI3MGI0MWYiXQ==",
|
|
"token": self.token,
|
|
"timestamp": self.timestamp,
|
|
"signature": self.signature,
|
|
"domain": "example.com",
|
|
"message-headers": "[]",
|
|
"recipient": self.recipient,
|
|
"odoo_db": self.env.cr.dbname,
|
|
"tracking_email_id": "%s" % self.tracking_email.id,
|
|
}
|
|
self.metadata = {
|
|
"ip": "127.0.0.1",
|
|
"user_agent": False,
|
|
"os_family": False,
|
|
"ua_family": False,
|
|
}
|
|
self.partner = self.env["res.partner"].create(
|
|
{"name": "Mr. Odoo", "email": "mrodoo@example.com"}
|
|
)
|
|
self.response = {
|
|
"items": [
|
|
{
|
|
"log-level": "info",
|
|
"id": "oXAVv5URCF-dKv8c6Sa7T",
|
|
"timestamp": 1509119329.0,
|
|
"message": {
|
|
"headers": {
|
|
"to": "test@test.com",
|
|
"message-id": "test-id@f187c54734e8",
|
|
"from": "Mr. Odoo <mrodoo@odoo.com>",
|
|
"subject": "This is a test",
|
|
}
|
|
},
|
|
"event": "delivered",
|
|
"recipient": "to@example.com",
|
|
}
|
|
]
|
|
}
|
|
|
|
def event_search(self, event_type):
|
|
event = self.env["mail.tracking.event"].search(
|
|
[
|
|
("tracking_email_id", "=", self.tracking_email.id),
|
|
("event_type", "=", event_type),
|
|
]
|
|
)
|
|
self.assertTrue(event)
|
|
return event
|
|
|
|
def test_no_api_key(self):
|
|
self.env["ir.config_parameter"].set_param("mailgun.apikey", "")
|
|
self.test_event_delivered()
|
|
with self.assertRaises(ValidationError):
|
|
self.env["mail.tracking.email"]._mailgun_values()
|
|
|
|
def test_no_domain(self):
|
|
self.env["ir.config_parameter"].set_param("mail.catchall.domain", "")
|
|
self.test_event_delivered()
|
|
with self.assertRaises(ValidationError):
|
|
self.env["mail.tracking.email"]._mailgun_values()
|
|
# now we set an specific domain for Mailgun:
|
|
# i.e: we configure new EU zone without loosing old domain statistics
|
|
self.env["ir.config_parameter"].set_param("mailgun.domain", "eu.example.com")
|
|
self.test_event_delivered()
|
|
|
|
@mute_logger("odoo.addons.mail_tracking_mailgun.models.mail_tracking_email")
|
|
def test_bad_signature(self):
|
|
self.event.update({"event": "delivered", "signature": "bad_signature"})
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("ERROR: Signature", response)
|
|
|
|
@mute_logger("odoo.addons.mail_tracking_mailgun.models.mail_tracking_email")
|
|
def test_bad_event_type(self):
|
|
self.event.update({"event": "bad_event"})
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("ERROR: Event type not supported", response)
|
|
|
|
@mute_logger("odoo.addons.mail_tracking_mailgun.models.mail_tracking_email")
|
|
def test_bad_db(self):
|
|
self.event.update({"event": "delivered", "odoo_db": "bad_db"})
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("ERROR: Invalid DB", response)
|
|
|
|
def test_bad_ts(self):
|
|
timestamp = "7a" # Now time will be used instead
|
|
signature = "06cc05680f6e8110e59b41152b2d1c0f1045d755ef2880ff922344325c89a6d4"
|
|
self.event.update(
|
|
{"event": "delivered", "timestamp": timestamp, "signature": signature}
|
|
)
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("OK", response)
|
|
|
|
@mute_logger("odoo.addons.mail_tracking_mailgun.models.mail_tracking_email")
|
|
def test_tracking_not_found(self):
|
|
self.event.update({"event": "delivered", "tracking_email_id": "bad_id"})
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("ERROR: Tracking not found", response)
|
|
|
|
# https://documentation.mailgun.com/user_manual.html#tracking-deliveries
|
|
def test_event_delivered(self):
|
|
self.event.update({"event": "delivered"})
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("OK", response)
|
|
events = self.event_search("delivered")
|
|
for event in events:
|
|
self.assertEqual(event.timestamp, float(self.timestamp))
|
|
self.assertEqual(event.recipient, self.recipient)
|
|
|
|
# https://documentation.mailgun.com/user_manual.html#tracking-opens
|
|
def test_event_opened(self):
|
|
ip = "127.0.0.1"
|
|
user_agent = "Odoo Test/8.0 Gecko Firefox/11.0"
|
|
os_family = "Linux"
|
|
ua_family = "Firefox"
|
|
ua_type = "browser"
|
|
self.event.update(
|
|
{
|
|
"event": "opened",
|
|
"city": "Mountain View",
|
|
"country": "US",
|
|
"region": "CA",
|
|
"client-name": ua_family,
|
|
"client-os": os_family,
|
|
"client-type": ua_type,
|
|
"device-type": "desktop",
|
|
"ip": ip,
|
|
"user-agent": user_agent,
|
|
}
|
|
)
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("OK", response)
|
|
event = self.event_search("open")
|
|
self.assertEqual(event.timestamp, float(self.timestamp))
|
|
self.assertEqual(event.recipient, self.recipient)
|
|
self.assertEqual(event.ip, ip)
|
|
self.assertEqual(event.user_agent, user_agent)
|
|
self.assertEqual(event.os_family, os_family)
|
|
self.assertEqual(event.ua_family, ua_family)
|
|
self.assertEqual(event.ua_type, ua_type)
|
|
self.assertEqual(event.mobile, False)
|
|
self.assertEqual(event.user_country_id.code, "US")
|
|
|
|
# https://documentation.mailgun.com/user_manual.html#tracking-clicks
|
|
def test_event_clicked(self):
|
|
ip = "127.0.0.1"
|
|
user_agent = "Odoo Test/8.0 Gecko Firefox/11.0"
|
|
os_family = "Linux"
|
|
ua_family = "Firefox"
|
|
ua_type = "browser"
|
|
url = "https://odoo-community.org"
|
|
self.event.update(
|
|
{
|
|
"event": "clicked",
|
|
"city": "Mountain View",
|
|
"country": "US",
|
|
"region": "CA",
|
|
"client-name": ua_family,
|
|
"client-os": os_family,
|
|
"client-type": ua_type,
|
|
"device-type": "tablet",
|
|
"ip": ip,
|
|
"user-agent": user_agent,
|
|
"url": url,
|
|
}
|
|
)
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata, event_type="click"
|
|
)
|
|
self.assertEqual("OK", response)
|
|
event = self.event_search("click")
|
|
self.assertEqual(event.timestamp, float(self.timestamp))
|
|
self.assertEqual(event.recipient, self.recipient)
|
|
self.assertEqual(event.ip, ip)
|
|
self.assertEqual(event.user_agent, user_agent)
|
|
self.assertEqual(event.os_family, os_family)
|
|
self.assertEqual(event.ua_family, ua_family)
|
|
self.assertEqual(event.ua_type, ua_type)
|
|
self.assertEqual(event.mobile, True)
|
|
self.assertEqual(event.url, url)
|
|
|
|
# https://documentation.mailgun.com/user_manual.html#tracking-unsubscribes
|
|
def test_event_unsubscribed(self):
|
|
ip = "127.0.0.1"
|
|
user_agent = "Odoo Test/8.0 Gecko Firefox/11.0"
|
|
os_family = "Linux"
|
|
ua_family = "Firefox"
|
|
ua_type = "browser"
|
|
self.event.update(
|
|
{
|
|
"event": "unsubscribed",
|
|
"city": "Mountain View",
|
|
"country": "US",
|
|
"region": "CA",
|
|
"client-name": ua_family,
|
|
"client-os": os_family,
|
|
"client-type": ua_type,
|
|
"device-type": "mobile",
|
|
"ip": ip,
|
|
"user-agent": user_agent,
|
|
}
|
|
)
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("OK", response)
|
|
event = self.event_search("unsub")
|
|
self.assertEqual(event.timestamp, float(self.timestamp))
|
|
self.assertEqual(event.recipient, self.recipient)
|
|
self.assertEqual(event.ip, ip)
|
|
self.assertEqual(event.user_agent, user_agent)
|
|
self.assertEqual(event.os_family, os_family)
|
|
self.assertEqual(event.ua_family, ua_family)
|
|
self.assertEqual(event.ua_type, ua_type)
|
|
self.assertEqual(event.mobile, True)
|
|
|
|
# https://documentation.mailgun.com/
|
|
# user_manual.html#tracking-spam-complaints
|
|
def test_event_complained(self):
|
|
self.event.update({"event": "complained"})
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("OK", response)
|
|
event = self.event_search("spam")
|
|
self.assertEqual(event.timestamp, float(self.timestamp))
|
|
self.assertEqual(event.recipient, self.recipient)
|
|
self.assertEqual(event.error_type, "spam")
|
|
|
|
# https://documentation.mailgun.com/user_manual.html#tracking-bounces
|
|
def test_event_bounced(self):
|
|
code = "550"
|
|
error = (
|
|
"5.1.1 The email account does not exist.\n"
|
|
"5.1.1 double-checking the recipient's email address"
|
|
)
|
|
notification = "Please, check recipient's email address"
|
|
self.event.update(
|
|
{
|
|
"event": "bounced",
|
|
"code": code,
|
|
"error": error,
|
|
"notification": notification,
|
|
}
|
|
)
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("OK", response)
|
|
event = self.event_search("hard_bounce")
|
|
self.assertEqual(event.timestamp, float(self.timestamp))
|
|
self.assertEqual(event.recipient, self.recipient)
|
|
self.assertEqual(event.error_type, code)
|
|
self.assertEqual(event.error_description, error)
|
|
self.assertEqual(event.error_details, notification)
|
|
|
|
# https://documentation.mailgun.com/user_manual.html#tracking-failures
|
|
def test_event_dropped(self):
|
|
reason = "hardfail"
|
|
code = "605"
|
|
description = "Not delivering to previously bounced address"
|
|
self.event.update(
|
|
{
|
|
"event": "dropped",
|
|
"reason": reason,
|
|
"code": code,
|
|
"description": description,
|
|
}
|
|
)
|
|
response = self.env["mail.tracking.email"].event_process(
|
|
None, self.event, self.metadata
|
|
)
|
|
self.assertEqual("OK", response)
|
|
event = self.event_search("reject")
|
|
self.assertEqual(event.timestamp, float(self.timestamp))
|
|
self.assertEqual(event.recipient, self.recipient)
|
|
self.assertEqual(event.error_type, reason)
|
|
self.assertEqual(event.error_description, code)
|
|
self.assertEqual(event.error_details, description)
|
|
|
|
@mock.patch(_packagepath + ".models.res_partner.requests")
|
|
def test_email_validity(self, mock_request):
|
|
self.partner.email_bounced = False
|
|
mock_request.get.return_value.apparent_encoding = "ascii"
|
|
mock_request.get.return_value.status_code = 200
|
|
mock_request.get.return_value.json.return_value = {
|
|
"is_valid": True,
|
|
"mailbox_verification": "true",
|
|
}
|
|
# Trigger email auto validation in partner
|
|
self.env["ir.config_parameter"].set_param(
|
|
"mailgun.auto_check_partner_email", "True"
|
|
)
|
|
self.partner.email = "info@tecnativa.com"
|
|
self.assertFalse(self.partner.email_bounced)
|
|
self.partner.email = "xoxoxoxo@tecnativa.com"
|
|
# Not a valid mailbox
|
|
mock_request.get.return_value.json.return_value = {
|
|
"is_valid": True,
|
|
"mailbox_verification": "false",
|
|
}
|
|
with self.assertRaises(UserError):
|
|
self.partner.check_email_validity()
|
|
# Not a valid mail address
|
|
mock_request.get.return_value.json.return_value = {
|
|
"is_valid": False,
|
|
"mailbox_verification": "false",
|
|
}
|
|
with self.assertRaises(UserError):
|
|
self.partner.check_email_validity()
|
|
# Unable to fully validate
|
|
mock_request.get.return_value.json.return_value = {
|
|
"is_valid": True,
|
|
"mailbox_verification": "unknown",
|
|
}
|
|
with self.assertRaises(UserError):
|
|
self.partner.check_email_validity()
|
|
self.assertTrue(self.partner.email_bounced)
|
|
|
|
@mock.patch(_packagepath + ".models.res_partner.requests")
|
|
def test_email_validity_exceptions(self, mock_request):
|
|
mock_request.get.return_value.status_code = 404
|
|
with self.assertRaises(UserError):
|
|
self.partner.check_email_validity()
|
|
self.env["ir.config_parameter"].set_param("mailgun.validation_key", "")
|
|
with self.assertRaises(UserError):
|
|
self.partner.check_email_validity()
|
|
|
|
@mock.patch(_packagepath + ".models.res_partner.requests")
|
|
def test_bounced(self, mock_request):
|
|
self.partner.email_bounced = True
|
|
mock_request.get.return_value.status_code = 404
|
|
self.partner.check_email_bounced()
|
|
self.assertFalse(self.partner.email_bounced)
|
|
mock_request.get.return_value.status_code = 200
|
|
self.partner.force_set_bounced()
|
|
self.partner.check_email_bounced()
|
|
self.assertTrue(self.partner.email_bounced)
|
|
mock_request.delete.return_value.status_code = 200
|
|
self.partner.force_unset_bounced()
|
|
self.assertFalse(self.partner.email_bounced)
|
|
|
|
def test_email_bounced_set(self):
|
|
message_number = len(self.partner.message_ids) + 1
|
|
self.partner._email_bounced_set("test_error", False)
|
|
self.assertEqual(len(self.partner.message_ids), message_number)
|
|
self.partner.email = ""
|
|
self.partner._email_bounced_set("test_error", False)
|
|
self.assertEqual(len(self.partner.message_ids), message_number)
|
|
|
|
@mock.patch(_packagepath + ".models.mail_tracking_email.requests")
|
|
def test_manual_check(self, mock_request):
|
|
mock_request.get.return_value.json.return_value = self.response
|
|
mock_request.get.return_value.status_code = 200
|
|
self.tracking_email.action_manual_check_mailgun()
|
|
event = self.env["mail.tracking.event"].search(
|
|
[("mailgun_id", "=", self.response["items"][0]["id"])]
|
|
)
|
|
self.assertEqual(event.event_type, self.response["items"][0]["event"])
|
|
|
|
@mock.patch(_packagepath + ".models.mail_tracking_email.requests")
|
|
def test_manual_check_exceptions(self, mock_request):
|
|
mock_request.get.return_value.status_code = 404
|
|
with self.assertRaises(ValidationError):
|
|
self.tracking_email.action_manual_check_mailgun()
|
|
mock_request.get.return_value.status_code = 200
|
|
mock_request.get.return_value.json.return_value = {}
|
|
with self.assertRaises(ValidationError):
|
|
self.tracking_email.action_manual_check_mailgun()
|