from unittest import mock
import pytest
from faker import Faker
from fansifter_common.email import EmailType
from email_campaigns.campaigns.handlers import GetCampaignPublicPreviewHandler
from email_campaigns.campaigns.models import EmailCampaign
from email_campaigns.emails.enums import TokenDecryptionStatus
from email_campaigns.emails.exceptions import (
EmailContentMissingError,
EmailNotFoundError,
EmailPublicPreviewTokenExpiredError,
)
from email_campaigns.emails.handlers import GetEmailPublicPreviewRequest
from email_campaigns.emails.tokens import TokenDecryptionResult
from tests.unit.types import CreateModel
class TestGetCampaignPublicPreviewHandler:
@pytest.mark.db
def test_get_campaign_public_preview(
self,
handler: GetCampaignPublicPreviewHandler,
create_model: CreateModel,
campaign_public_preview_cache_mock: mock.MagicMock,
stripo_client_mock: mock.MagicMock,
sendgrid_client_mock: mock.MagicMock,
tokens_mock: mock.MagicMock,
) -> None:
stripo_client_mock.reset_mock()
campaign_public_preview_cache_mock.reset_mock()
sendgrid_client_mock.reset_mock()
campaign = create_model(
EmailCampaign,
html_content="
html content
-privacyFooterBlock-",
css_content="p { color: red; }",
)
tokens_mock.decrypt_token.return_value = TokenDecryptionResult(
status=TokenDecryptionStatus.VALID,
email_id=campaign.id,
)
campaign_public_preview_cache_mock.get.return_value = None
compressed_html = (
"compressed
-privacyFooterBlock-"
)
stripo_client_mock.compress.return_value = compressed_html
footer_html = "Privacy Footer
"
sendgrid_client_mock.get_user_privacy_footer_block.return_value = footer_html
response = handler.handle(
GetEmailPublicPreviewRequest(
email_id=campaign.id,
token="valid_token",
)
)
stripo_client_mock.compress.assert_called_once_with(
campaign.html_content,
campaign.css_content,
)
sendgrid_client_mock.get_user_privacy_footer_block.assert_called_once()
expected_html = compressed_html.replace("-privacyFooterBlock-", footer_html)
campaign_public_preview_cache_mock.set.assert_called_once_with(
campaign.id, EmailType.CAMPAIGN, expected_html
)
assert response.html == expected_html
assert footer_html in response.html
@pytest.mark.db
def test_get_campaign_public_preview_no_token(
self,
handler: GetCampaignPublicPreviewHandler,
create_model: CreateModel,
) -> None:
campaign = create_model(EmailCampaign)
with pytest.raises(EmailNotFoundError):
handler.handle(
GetEmailPublicPreviewRequest(
email_id=campaign.id,
token=None,
)
)
@pytest.mark.db
def test_get_campaign_public_preview_invalid_token(
self,
handler: GetCampaignPublicPreviewHandler,
create_model: CreateModel,
tokens_mock: mock.MagicMock,
) -> None:
campaign = create_model(EmailCampaign)
tokens_mock.decrypt_token.return_value = TokenDecryptionResult(
status=TokenDecryptionStatus.INVALID,
email_id=None,
)
with pytest.raises(EmailNotFoundError):
handler.handle(
GetEmailPublicPreviewRequest(
email_id=campaign.id,
token="invalid_token",
)
)
@pytest.mark.db
def test_get_campaign_public_preview_mismatched_campaign_id(
self,
handler: GetCampaignPublicPreviewHandler,
create_model: CreateModel,
faker: Faker,
tokens_mock: mock.MagicMock,
) -> None:
campaign = create_model(EmailCampaign)
different_id = faker.uuid4()
tokens_mock.decrypt_token.return_value = TokenDecryptionResult(
status=TokenDecryptionStatus.VALID,
email_id=different_id,
)
with pytest.raises(EmailNotFoundError):
handler.handle(
GetEmailPublicPreviewRequest(
email_id=campaign.id,
token="token_with_different_id",
)
)
@pytest.mark.db
def test_get_campaign_public_preview_campaign_deleted(
self,
handler: GetCampaignPublicPreviewHandler,
faker: Faker,
tokens_mock: mock.MagicMock,
) -> None:
campaign_id = faker.uuid4()
tokens_mock.decrypt_token.return_value = TokenDecryptionResult(
status=TokenDecryptionStatus.VALID,
email_id=campaign_id,
)
with pytest.raises(EmailNotFoundError):
handler.handle(
GetEmailPublicPreviewRequest(
email_id=campaign_id,
token="valid_token",
)
)
@pytest.mark.db
def test_get_campaign_public_preview_missing_content(
self,
handler: GetCampaignPublicPreviewHandler,
create_model: CreateModel,
campaign_public_preview_cache_mock: mock.MagicMock,
tokens_mock: mock.MagicMock,
) -> None:
campaign = create_model(
EmailCampaign,
html_content=None,
css_content=None,
)
tokens_mock.decrypt_token.return_value = TokenDecryptionResult(
status=TokenDecryptionStatus.VALID,
email_id=campaign.id,
)
campaign_public_preview_cache_mock.get.return_value = None
with pytest.raises(EmailContentMissingError):
handler.handle(
GetEmailPublicPreviewRequest(
email_id=campaign.id,
token="valid_token",
)
)
@pytest.mark.db
def test_get_campaign_public_preview_expired_token(
self,
handler: GetCampaignPublicPreviewHandler,
create_model: CreateModel,
tokens_mock: mock.MagicMock,
) -> None:
campaign = create_model(EmailCampaign)
tokens_mock.decrypt_token.return_value = TokenDecryptionResult(
status=TokenDecryptionStatus.EXPIRED,
email_id=campaign.id,
)
with pytest.raises(EmailPublicPreviewTokenExpiredError):
handler.handle(
GetEmailPublicPreviewRequest(
email_id=campaign.id,
token="expired_token",
)
)
@pytest.mark.db
def test_get_campaign_public_preview_replaces_first_name_with_fallback(
self,
handler: GetCampaignPublicPreviewHandler,
create_model: CreateModel,
campaign_public_preview_cache_mock: mock.MagicMock,
stripo_client_mock: mock.MagicMock,
sendgrid_client_mock: mock.MagicMock,
tokens_mock: mock.MagicMock,
) -> None:
stripo_client_mock.reset_mock()
campaign_public_preview_cache_mock.reset_mock()
sendgrid_client_mock.reset_mock()
campaign = create_model(
EmailCampaign,
html_content="Hi -firstName-!
-privacyFooterBlock-",
css_content="p { color: red; }",
)
tokens_mock.decrypt_token.return_value = TokenDecryptionResult(
status=TokenDecryptionStatus.VALID,
email_id=campaign.id,
)
campaign_public_preview_cache_mock.get.return_value = None
stripo_client_mock.compress.return_value = (
"Hi -firstName-!
-privacyFooterBlock-"
)
sendgrid_client_mock.get_user_privacy_footer_block.return_value = ""
response = handler.handle(
GetEmailPublicPreviewRequest(
email_id=campaign.id,
token="valid_token",
)
)
assert "-firstName-" not in response.html
assert "Hi Fan!" in response.html
@pytest.mark.db
def test_get_campaign_public_preview_from_cache(
self,
handler: GetCampaignPublicPreviewHandler,
create_model: CreateModel,
campaign_public_preview_cache_mock: mock.MagicMock,
stripo_client_mock: mock.MagicMock,
sendgrid_client_mock: mock.MagicMock,
tokens_mock: mock.MagicMock,
) -> None:
stripo_client_mock.reset_mock()
campaign_public_preview_cache_mock.reset_mock()
sendgrid_client_mock.reset_mock()
campaign = create_model(
EmailCampaign,
html_content="html content
-privacyFooterBlock-",
css_content="p { color: red; }",
)
tokens_mock.decrypt_token.return_value = TokenDecryptionResult(
status=TokenDecryptionStatus.VALID,
email_id=campaign.id,
)
cached_html = "cached
Privacy Footer
"
campaign_public_preview_cache_mock.get.return_value = cached_html
response = handler.handle(
GetEmailPublicPreviewRequest(
email_id=campaign.id,
token="valid_token",
)
)
stripo_client_mock.compress.assert_not_called()
sendgrid_client_mock.get_user_privacy_footer_block.assert_not_called()
campaign_public_preview_cache_mock.set.assert_not_called()
assert response.html == cached_html