Move replies tests to new test case

Split SNSNotificationTest into three parts:

- SNSNotificationTestBase - shared setup and helper functions
- SNSNotificationIncomingTest - tests for emails to Relay users
- SNSNotificationRepliesTest - test for email replies from Relay users
This commit is contained in:
John Whitlock 2024-02-22 13:17:28 -06:00
Родитель b6d6dc748b
Коммит 37fad034cb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 082C735D154FB750
1 изменённых файлов: 113 добавлений и 96 удалений

Просмотреть файл

@ -288,24 +288,10 @@ def _replace_mime_boundaries(email: str) -> str:
@override_settings(RELAY_FROM_ADDRESS="reply@relay.example.com")
class SNSNotificationTest(TestCase):
def setUp(self) -> None:
self.user = baker.make(User, email="user@example.com")
self.profile = self.user.profile
self.profile.last_engagement = datetime.now(timezone.utc)
self.profile.save()
self.sa: SocialAccount = baker.make(
SocialAccount, user=self.user, provider="fxa"
)
self.ra = baker.make(
RelayAddress, user=self.user, address="ebsbdsan7", domain=2
)
self.premium_user = make_premium_test_user()
self.premium_profile = Profile.objects.get(user=self.premium_user)
self.premium_profile.subdomain = "subdomain"
self.premium_profile.last_engagement = datetime.now(timezone.utc)
self.premium_profile.save()
class SNSNotificationTestBase(TestCase):
"""Base class for tests of _sns_notification"""
def setUp(self) -> None:
remove_s3_patcher = patch("emails.views.remove_message_from_s3")
self.mock_remove_message_from_s3 = remove_s3_patcher.start()
self.addCleanup(remove_s3_patcher.stop)
@ -352,6 +338,28 @@ class SNSNotificationTest(TestCase):
last_key = key
raise Exception("Never found message body!")
class SNSNotificationIncomingTest(SNSNotificationTestBase):
"""Tests for _sns_notification for incoming emails to Relay users"""
def setUp(self) -> None:
super().setUp()
self.user = baker.make(User, email="user@example.com")
self.profile = self.user.profile
self.profile.last_engagement = datetime.now(timezone.utc)
self.profile.save()
self.sa: SocialAccount = baker.make(
SocialAccount, user=self.user, provider="fxa"
)
self.ra = baker.make(
RelayAddress, user=self.user, address="ebsbdsan7", domain=2
)
self.premium_user = make_premium_test_user()
self.premium_profile = Profile.objects.get(user=self.premium_user)
self.premium_profile.subdomain = "subdomain"
self.premium_profile.last_engagement = datetime.now(timezone.utc)
self.premium_profile.save()
def test_single_recipient_sns_notification(self) -> None:
pre_sns_notification_last_engagement = self.ra.user.profile.last_engagement
assert pre_sns_notification_last_engagement is not None
@ -540,85 +548,6 @@ class SNSNotificationTest(TestCase):
assert self.ra.num_forwarded == 0
assert self.ra.last_used_at is None
@patch("emails.views.get_message_content_from_s3")
def reply_test_implementation(
self, mock_get_content: Mock, text: str, expected_fixture_name: str
) -> str:
"""The headers of a reply refer to the Relay mask."""
# Create a premium user matching the s3_stored_replies sender
user = baker.make(User, email="source@sender.com")
user.profile.server_storage = True
user.profile.date_subscribed = datetime.now(tz=timezone.utc)
user.profile.last_engagement = datetime.now(tz=timezone.utc)
user.profile.save()
pre_reply_last_engagement = user.profile.last_engagement
upgrade_test_user_to_premium(user)
# Create a Reply record matching the s3_stored_replies headers
lookup_key, encryption_key = derive_reply_keys(
get_message_id_bytes("CA+J4FJFw0TXCr63y9dGcauvCGaZ7pXxspzOjEDhRpg5Zh4ziWg")
)
metadata = {
"message-id": str(uuid4()),
"from": "sender@external.example.com",
}
encrypted_metadata = encrypt_reply_metadata(encryption_key, metadata)
relay_address = baker.make(RelayAddress, user=user, address="a1b2c3d4")
Reply.objects.create(
lookup=b64_lookup_key(lookup_key),
encrypted_metadata=encrypted_metadata,
relay_address=relay_address,
)
# Mock loading a simple reply email message from S3
mock_get_content.return_value = create_email_from_notification(
EMAIL_SNS_BODIES["s3_stored_replies"], text=text
)
# Successfully reply to a previous sender
response = _sns_notification(EMAIL_SNS_BODIES["s3_stored_replies"])
assert response.status_code == 200
self.mock_remove_message_from_s3.assert_called_once()
mock_get_content.assert_called_once()
sender, recipient, headers, email = self.get_details_from_mock_send_raw_email()
assert sender == "a1b2c3d4@test.com"
assert recipient == "sender@external.example.com"
assert headers == {
"Content-Type": headers["Content-Type"],
"Subject": "Re: Test Mozilla User New Domain Address",
"MIME-Version": "1.0",
"From": sender,
"Reply-To": sender,
"To": recipient,
}
assert_email_equals(email, expected_fixture_name)
relay_address.refresh_from_db()
assert relay_address.num_replied == 1
last_used_at = relay_address.last_used_at
assert last_used_at
assert (datetime.now(tz=timezone.utc) - last_used_at).seconds < 2.0
assert relay_address.user.profile.last_engagement is not None
assert relay_address.user.profile.last_engagement > pre_reply_last_engagement
return email
def test_reply(self) -> None:
self.reply_test_implementation(
text="this is a text reply", expected_fixture_name="s3_stored_replies"
)
def test_reply_with_emoji_in_text(self) -> None:
"""An email with emoji text content is sent with UTF-8 encoding."""
email = self.reply_test_implementation(
text="👍 Thanks I got it!",
expected_fixture_name="s3_stored_replies_with_emoji",
)
assert 'Content-Type: text/plain; charset="utf-8"' in email
assert "Content-Transfer-Encoding: base64" in email
@patch("emails.views.generate_from_header", side_effect=InvalidFromHeader())
@patch("emails.views.info_logger")
def test_invalid_from_header(self, mock_logger, mock_generate_from_header) -> None:
@ -849,6 +778,94 @@ class SNSNotificationTest(TestCase):
)
class SNSNotificationRepliesTest(SNSNotificationTestBase):
"""Tests for _sns_notification for replies from Relay users"""
def setUp(self) -> None:
super().setUp()
# Create a premium user matching the s3_stored_replies sender
self.user = baker.make(User, email="source@sender.com")
self.user.profile.server_storage = True
self.user.profile.date_subscribed = datetime.now(tz=timezone.utc)
self.user.profile.last_engagement = datetime.now(tz=timezone.utc)
self.user.profile.save()
self.pre_reply_last_engagement = self.user.profile.last_engagement
upgrade_test_user_to_premium(self.user)
# Create a Reply record matching the s3_stored_replies headers
lookup_key, encryption_key = derive_reply_keys(
get_message_id_bytes("CA+J4FJFw0TXCr63y9dGcauvCGaZ7pXxspzOjEDhRpg5Zh4ziWg")
)
metadata = {
"message-id": str(uuid4()),
"from": "sender@external.example.com",
}
encrypted_metadata = encrypt_reply_metadata(encryption_key, metadata)
self.relay_address = baker.make(
RelayAddress, user=self.user, address="a1b2c3d4"
)
Reply.objects.create(
lookup=b64_lookup_key(lookup_key),
encrypted_metadata=encrypted_metadata,
relay_address=self.relay_address,
)
get_message_content_patcher = patch("emails.views.get_message_content_from_s3")
self.mock_get_content = get_message_content_patcher.start()
self.addCleanup(get_message_content_patcher.stop)
def success_test_implementation(self, text: str, expected_fixture_name: str) -> str:
"""The headers of a reply refer to the Relay mask."""
self.mock_get_content.return_value = create_email_from_notification(
EMAIL_SNS_BODIES["s3_stored_replies"], text=text
)
# Successfully reply to a previous sender
response = _sns_notification(EMAIL_SNS_BODIES["s3_stored_replies"])
assert response.status_code == 200
self.mock_remove_message_from_s3.assert_called_once()
self.mock_get_content.assert_called_once()
sender, recipient, headers, email = self.get_details_from_mock_send_raw_email()
assert sender == "a1b2c3d4@test.com"
assert recipient == "sender@external.example.com"
assert headers == {
"Content-Type": headers["Content-Type"],
"Subject": "Re: Test Mozilla User New Domain Address",
"MIME-Version": "1.0",
"From": sender,
"Reply-To": sender,
"To": recipient,
}
assert_email_equals(email, expected_fixture_name)
self.relay_address.refresh_from_db()
assert self.relay_address.num_replied == 1
last_used_at = self.relay_address.last_used_at
assert last_used_at
assert (datetime.now(tz=timezone.utc) - last_used_at).seconds < 2.0
assert (last_en := self.relay_address.user.profile.last_engagement) is not None
assert last_en > self.pre_reply_last_engagement
return email
def test_reply(self) -> None:
self.success_test_implementation(
text="this is a text reply", expected_fixture_name="s3_stored_replies"
)
def test_reply_with_emoji_in_text(self) -> None:
"""An email with emoji text content is sent with UTF-8 encoding."""
email = self.success_test_implementation(
text="👍 Thanks I got it!",
expected_fixture_name="s3_stored_replies_with_emoji",
)
assert 'Content-Type: text/plain; charset="utf-8"' in email
assert "Content-Transfer-Encoding: base64" in email
class BounceHandlingTest(TestCase):
def setUp(self):
self.user = baker.make(User, email="relayuser@test.com")