superset/tests/email_tests.py
Mahendra M 808622414c [SIP-3] Scheduled email reports for Slices / Dashboards (#5294)
* [scheduled reports] Add support for scheduled reports

* Scheduled email reports for slice and dashboard visualization
  (attachment or inline)
* Scheduled email reports for slice data (CSV attachment on inline table)
* Each schedule has a list of recipients (all of them can receive a single mail,
  or separate mails)
* All outgoing mails can have a mandatory bcc - for audit purposes.
* Each dashboard/slice can have multiple schedules.

In addition, this PR also makes a few minor improvements to the celery
infrastructure.
* Create a common celery app
* Added more celery annotations for the tasks
* Introduced celery beat
* Update docs about concurrency / pools

* [scheduled reports] - Debug mode for scheduled emails

* [scheduled reports] - Ability to send test mails

* [scheduled reports] - Test email functionality - minor improvements

* [scheduled reports] - Rebase with master. Minor fixes

* [scheduled reports] - Add warning messages

* [scheduled reports] - flake8

* [scheduled reports] - fix rebase

* [scheduled reports] - fix rebase

* [scheduled reports] - fix flake8

* [scheduled reports] Rebase in prep for merge

* Fixed alembic tree after rebase
* Updated requirements to latest version of packages (and tested)
* Removed py2 stuff

* [scheduled reports] - fix flake8

* [scheduled reports] - address review comments

* [scheduled reports] - rebase with master
2018-12-10 22:29:29 -08:00

155 lines
5.9 KiB
Python

# -*- coding: utf-8 -*-
"""Unit tests for email service in Superset"""
from email.mime.application import MIMEApplication
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
import logging
import tempfile
import unittest
import mock
from superset import app
from superset.utils import core as utils
from .utils import read_fixture
send_email_test = mock.Mock()
class EmailSmtpTest(unittest.TestCase):
def setUp(self):
app.config['smtp_ssl'] = False
@mock.patch('superset.utils.core.send_MIME_email')
def test_send_smtp(self, mock_send_mime):
attachment = tempfile.NamedTemporaryFile()
attachment.write(b'attachment')
attachment.seek(0)
utils.send_email_smtp(
'to', 'subject', 'content', app.config, files=[attachment.name])
assert mock_send_mime.called
call_args = mock_send_mime.call_args[0]
logging.debug(call_args)
assert call_args[0] == app.config.get('SMTP_MAIL_FROM')
assert call_args[1] == ['to']
msg = call_args[2]
assert msg['Subject'] == 'subject'
assert msg['From'] == app.config.get('SMTP_MAIL_FROM')
assert len(msg.get_payload()) == 2
mimeapp = MIMEApplication('attachment')
assert msg.get_payload()[-1].get_payload() == mimeapp.get_payload()
@mock.patch('superset.utils.core.send_MIME_email')
def test_send_smtp_data(self, mock_send_mime):
utils.send_email_smtp(
'to', 'subject', 'content', app.config, data={'1.txt': b'data'})
assert mock_send_mime.called
call_args = mock_send_mime.call_args[0]
logging.debug(call_args)
assert call_args[0] == app.config.get('SMTP_MAIL_FROM')
assert call_args[1] == ['to']
msg = call_args[2]
assert msg['Subject'] == 'subject'
assert msg['From'] == app.config.get('SMTP_MAIL_FROM')
assert len(msg.get_payload()) == 2
mimeapp = MIMEApplication('data')
assert msg.get_payload()[-1].get_payload() == mimeapp.get_payload()
@mock.patch('superset.utils.core.send_MIME_email')
def test_send_smtp_inline_images(self, mock_send_mime):
image = read_fixture('sample.png')
utils.send_email_smtp(
'to', 'subject', 'content', app.config, images=dict(blah=image))
assert mock_send_mime.called
call_args = mock_send_mime.call_args[0]
logging.debug(call_args)
assert call_args[0] == app.config.get('SMTP_MAIL_FROM')
assert call_args[1] == ['to']
msg = call_args[2]
assert msg['Subject'] == 'subject'
assert msg['From'] == app.config.get('SMTP_MAIL_FROM')
assert len(msg.get_payload()) == 2
mimeapp = MIMEImage(image)
assert msg.get_payload()[-1].get_payload() == mimeapp.get_payload()
@mock.patch('superset.utils.core.send_MIME_email')
def test_send_bcc_smtp(self, mock_send_mime):
attachment = tempfile.NamedTemporaryFile()
attachment.write(b'attachment')
attachment.seek(0)
utils.send_email_smtp(
'to', 'subject', 'content', app.config, files=[attachment.name],
cc='cc', bcc='bcc')
assert mock_send_mime.called
call_args = mock_send_mime.call_args[0]
assert call_args[0] == app.config.get('SMTP_MAIL_FROM')
assert call_args[1] == ['to', 'cc', 'bcc']
msg = call_args[2]
assert msg['Subject'] == 'subject'
assert msg['From'] == app.config.get('SMTP_MAIL_FROM')
assert len(msg.get_payload()) == 2
mimeapp = MIMEApplication('attachment')
assert msg.get_payload()[-1].get_payload() == mimeapp.get_payload()
@mock.patch('smtplib.SMTP_SSL')
@mock.patch('smtplib.SMTP')
def test_send_mime(self, mock_smtp, mock_smtp_ssl):
mock_smtp.return_value = mock.Mock()
mock_smtp_ssl.return_value = mock.Mock()
msg = MIMEMultipart()
utils.send_MIME_email('from', 'to', msg, app.config, dryrun=False)
mock_smtp.assert_called_with(
app.config.get('SMTP_HOST'),
app.config.get('SMTP_PORT'),
)
assert mock_smtp.return_value.starttls.called
mock_smtp.return_value.login.assert_called_with(
app.config.get('SMTP_USER'),
app.config.get('SMTP_PASSWORD'),
)
mock_smtp.return_value.sendmail.assert_called_with(
'from', 'to', msg.as_string())
assert mock_smtp.return_value.quit.called
@mock.patch('smtplib.SMTP_SSL')
@mock.patch('smtplib.SMTP')
def test_send_mime_ssl(self, mock_smtp, mock_smtp_ssl):
app.config['SMTP_SSL'] = True
mock_smtp.return_value = mock.Mock()
mock_smtp_ssl.return_value = mock.Mock()
utils.send_MIME_email(
'from', 'to', MIMEMultipart(), app.config, dryrun=False)
assert not mock_smtp.called
mock_smtp_ssl.assert_called_with(
app.config.get('SMTP_HOST'),
app.config.get('SMTP_PORT'),
)
@mock.patch('smtplib.SMTP_SSL')
@mock.patch('smtplib.SMTP')
def test_send_mime_noauth(self, mock_smtp, mock_smtp_ssl):
app.config['SMTP_USER'] = None
app.config['SMTP_PASSWORD'] = None
mock_smtp.return_value = mock.Mock()
mock_smtp_ssl.return_value = mock.Mock()
utils.send_MIME_email(
'from', 'to', MIMEMultipart(), app.config, dryrun=False)
assert not mock_smtp_ssl.called
mock_smtp.assert_called_with(
app.config.get('SMTP_HOST'),
app.config.get('SMTP_PORT'),
)
assert not mock_smtp.login.called
@mock.patch('smtplib.SMTP_SSL')
@mock.patch('smtplib.SMTP')
def test_send_mime_dryrun(self, mock_smtp, mock_smtp_ssl):
utils.send_MIME_email(
'from', 'to', MIMEMultipart(), app.config, dryrun=True)
assert not mock_smtp.called
assert not mock_smtp_ssl.called
if __name__ == '__main__':
unittest.main()