-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d535233
commit e24cf04
Showing
2 changed files
with
126 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,23 @@ | ||
import concurrent.futures | ||
import email.mime.multipart | ||
import time | ||
from datetime import datetime, timezone | ||
from email.mime.multipart import MIMEMultipart | ||
from smtplib import SMTPException | ||
from unittest import mock | ||
from unittest.mock import MagicMock | ||
|
||
import pytest | ||
from django.test import TestCase, override_settings | ||
from parameterized import parameterized | ||
|
||
from mail.celery_tasks import get_lite_api_url, manage_inbox, notify_users_of_rejected_licences | ||
from mail.celery_tasks import ( | ||
MAX_RETRIES, | ||
get_lite_api_url, | ||
manage_inbox, | ||
notify_users_of_rejected_licences, | ||
send_email_task, | ||
) | ||
from mail.enums import ExtractTypeEnum, ReceptionStatusEnum, SourceEnum | ||
from mail.libraries.email_message_dto import EmailMessageDto | ||
from mail.libraries.routing_controller import check_and_route_emails | ||
|
@@ -223,3 +232,114 @@ def test_processing_of_licence_reply_with_rejected_licences( | |
self.assertEqual(message["From"], emails_data[index]["sender"]) | ||
self.assertEqual(message["To"], emails_data[index]["recipients"]) | ||
self.assertEqual(message["Subject"], emails_data[index]["subject"]) | ||
|
||
|
||
class SendEmailTestTests(TestCase): | ||
@pytest.fixture(autouse=True) | ||
def inject_fixtures(self, caplog): | ||
self.caplog = caplog | ||
|
||
@mock.patch("mail.celery_tasks.cache") | ||
@mock.patch("mail.servers.get_smtp_connection") | ||
def test_sends_email(self, mock_get_smtp_connection, mock_cache): | ||
mock_conn = mock_get_smtp_connection() | ||
message = { | ||
"From": "[email protected]", | ||
"To": "[email protected]", | ||
} | ||
send_email_task.apply(args=[message]) | ||
mock_conn.send_message.assert_called_with(message) | ||
mock_conn.quit.assert_called() | ||
mock_cache.lock.assert_called_with("global_send_email_lock", timeout=600) | ||
|
||
@parameterized.expand( | ||
[ | ||
(ConnectionResetError,), | ||
(SMTPException,), | ||
] | ||
) | ||
@mock.patch("mail.celery_tasks.cache") | ||
@mock.patch("mail.servers.get_smtp_connection") | ||
def test_sends_email_failed_then_succeeds(self, exception_class, mock_get_smtp_connection, mock_cache): | ||
mock_conn = mock_get_smtp_connection() | ||
message = { | ||
"From": "[email protected]", | ||
"To": "[email protected]", | ||
} | ||
mock_conn.send_message.side_effect = [exception_class(), None] | ||
send_email_task.apply(args=[message]) | ||
mock_conn.send_message.assert_called_with(message) | ||
self.assertEqual(mock_conn.send_message.call_count, 2) | ||
self.assertEqual(mock_conn.quit.call_count, 2) | ||
mock_cache.lock.assert_called_with("global_send_email_lock", timeout=600) | ||
|
||
@parameterized.expand( | ||
[ | ||
(ConnectionResetError,), | ||
(SMTPException,), | ||
] | ||
) | ||
@mock.patch("mail.celery_tasks.cache") | ||
@mock.patch("mail.servers.get_smtp_connection") | ||
def test_sends_email_max_retry_failures(self, exception_class, mock_get_smtp_connection, mock_cache): | ||
mock_conn = mock_get_smtp_connection() | ||
message = { | ||
"From": "[email protected]", | ||
"To": "[email protected]", | ||
} | ||
mock_conn.send_message.side_effect = exception_class() | ||
send_email_task.apply(args=[message]) | ||
mock_conn.send_message.assert_called_with(message) | ||
self.assertEqual( | ||
mock_conn.send_message.call_count, | ||
MAX_RETRIES + 1, | ||
) | ||
self.assertEqual(mock_conn.quit.call_count, MAX_RETRIES + 1) | ||
mock_cache.lock.assert_called_with("global_send_email_lock", timeout=600) | ||
|
||
@mock.patch("mail.servers.get_smtp_connection") | ||
def test_locking(self, mock_get_smtp_connection): | ||
results = [] | ||
|
||
def _sleepy(message): | ||
call = {} | ||
call["start"] = { | ||
"message": message, | ||
"time": time.monotonic(), | ||
} | ||
time.sleep(1) | ||
call["end"] = { | ||
"message": message, | ||
"time": time.monotonic(), | ||
} | ||
results.append(call) | ||
|
||
mock_conn = mock_get_smtp_connection() | ||
mock_conn.send_message.side_effect = _sleepy | ||
|
||
with concurrent.futures.ThreadPoolExecutor() as executor: | ||
message_1 = { | ||
"From": "[email protected]", | ||
"To": "[email protected]", | ||
} | ||
future_1 = executor.submit(send_email_task.apply, args=[message_1]) | ||
|
||
message_2 = { | ||
"From": "[email protected]", | ||
"To": "[email protected]", | ||
} | ||
future_2 = executor.submit(send_email_task.apply, args=[message_2]) | ||
|
||
future_1.result() | ||
future_2.result() | ||
|
||
first_call, second_call = results | ||
|
||
assert first_call["start"]["message"] == {"From": "[email protected]", "To": "[email protected]"} | ||
assert first_call["end"]["message"] == {"From": "[email protected]", "To": "[email protected]"} | ||
|
||
assert second_call["start"]["message"] == {"From": "[email protected]", "To": "[email protected]"} | ||
assert second_call["end"]["message"] == {"From": "[email protected]", "To": "[email protected]"} | ||
|
||
assert second_call["start"]["time"] > first_call["end"]["time"] | ||
assert second_call["start"]["time"] > first_call["start"]["time"] + 1 |