"""Tests for the email_utils module.""" import base64 import json import os from unittest.mock import MagicMock from freezegun import freeze_time from segment import analytics from notifications_delivery import config from notifications_delivery.constants.notifications import TRENDING_TRACKS_REGION_MAPPING from notifications_delivery.utils import email_utils def test_format_notification_for_rendering_analytics_digest(): """Test formatting for analytics digest.""" notification = { 'template': 'analytics_digest', 'payload': { 'top_new_releases': [ { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } }, { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } } ], 'top_releases': [ { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } }, { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } } ], 'top_tracks': [ { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } }, { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } } ] } } email_utils.format_notification_for_rendering(notification) assert notification == { 'template': 'analytics_digest', 'payload': { 'top_new_releases': [ { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } }, { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } } ], 'top_releases': [ { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } }, { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } } ], 'top_tracks': [ { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': 'N/A' } }, { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': 'N/A' } } ], 'stores': ['spotify', 'itunes', 'deezer'] } } def test_format_notification_for_rendering_analytics_digest_missing_items(): """Test formatting for analytics digest when some items are missing.""" notification = { 'template': 'analytics_digest', 'payload': { 'top_releases': [], 'top_tracks': [ { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } }, { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 } } }, ] } } email_utils.format_notification_for_rendering(notification) assert notification == { 'template': 'analytics_digest', 'payload': { 'top_releases': [], 'top_tracks': [ { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': 'N/A' } }, { 'store_percentages': { 'spotify': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'itunes': { 'percentage': '25%', 'streams_per_store': 2000, 'growth_percentage_per_store': 11.5 }, 'deezer': 'N/A' } } ], 'stores': ['spotify', 'itunes', 'deezer'] } } @freeze_time('2018-01-01 12:20:54') def test_format_notification_for_auth0_sso(): """Test formatting for auth0_sso.""" notification = { 'template': 'auth0_sso', 'payload': { 'label_names': [ 'Red ink Sony Canada', 'Protest the Hero (SME Canada)' ] }, 'users_info': [{ 'user_id': 'alw:55281', 'auth0_id': '5b7719d8ccde104ca215f8d3', 'email': 'test@theorchard.com' }] } email_utils.format_notification_for_rendering(notification) assert notification == { 'template': 'auth0_sso', 'payload': { 'label_names': [ 'Red ink Sony Canada', 'Protest the Hero (SME Canada)' ], 'signin_url': 'https://workstation.qaorch.com/login/single-signon?' 'token=eyJ1c2VyX2lkIjogImFsdzo1NTI4MSIsICJhdXRoMF9pZ' 'CI6ICI1Yjc3MTlkOGNjZGUxMDRjYTIxNWY4ZDMiLCAid2hlbiI6' 'IDE1MTQ4MDkyNTQuMH0=', 'user_email': 'test@theorchard.com', }, 'users_info': [{ 'user_id': 'alw:55281', 'auth0_id': '5b7719d8ccde104ca215f8d3', 'email': 'test@theorchard.com' }] } def test_format_notification_for_rendering_trending_tracks(): """Test formatting for trending tracks.""" notification = { 'template': 'trending_tracks', 'payload': { 'country_group_name': 'United States', 'global_trending_tracks': [], 'trending_tracks': [] } } email_utils.format_notification_for_rendering(notification) assert notification == { 'template': 'trending_tracks', 'payload': { 'country_group_name': 'United States', 'global_trending_tracks': [], 'trending_tracks': [], 'region_emoji': TRENDING_TRACKS_REGION_MAPPING[ 'United States']['emoji'], 'region_label': TRENDING_TRACKS_REGION_MAPPING[ 'United States']['label'] } } def test_format_notification_for_rendering_video_product_rejection(): """Test formatting for video product rejection.""" notification = { 'template': 'video_product_rejection', 'payload': { 'project_id': 123, 'product_id': 456 } } email_utils.format_notification_for_rendering(notification) assert notification == { 'template': 'video_product_rejection', 'payload': { 'project_id': 123, 'product_id': 456, 'product_url': ('https://workstation.qaorch.com' '/project/123/product/456/video') } } def test_format_notification_for_rendering_video_product_approval(): """Test formatting for video product approval.""" notification = { 'template': 'video_product_approval', 'payload': { 'project_id': 123, 'product_id': 456 } } email_utils.format_notification_for_rendering(notification) assert notification == { 'template': 'video_product_approval', 'payload': { 'project_id': 123, 'product_id': 456, 'product_url': ('https://workstation.qaorch.com' '/project/123/product/456/video') } } def test_get_email_subject_analytics_digest(mocker): """Test email subject for analytics digest.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'analytics_digest' payload = { 'start_date': '2018-06-15', 'end_date': '2018-06-22', 'account_name': 'ACCOUNT NAME' } user_locale = 'en' result = email_utils.get_email_subject(template, payload, user_locale) assert result == '[test] Weekly Analytics Digest Jun 15 - Jun 22 for ACCOUNT NAME' def test_get_email_subject_spike_detector(mocker): """Test email subject for spike detector.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'spike_detector' payload = { 'date': '2018-06-15', 'trending_tracks': [{'test1': 'test1'}, {'test2': 'test2'}]} user_locale = 'en' result = email_utils.get_email_subject(template, payload, user_locale) assert result == '[test] Spike Detection: 2 Tracks on June 15 📈' def test_get_email_subject_spike_detector_with_one_track(mocker): """Test email subject for spike detector.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'spike_detector' payload = { 'date': '2018-06-15', 'trending_tracks': [{'test1': 'test1'}]} user_locale = 'en' result = email_utils.get_email_subject(template, payload, user_locale) assert result == '[test] Spike Detection: 1 Track on June 15 📈' def test_get_email_subject_auth0_sso(mocker): """Test email subject for auth0_sso.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'auth0_sso' payload = {} user_locale = 'en' result = email_utils.get_email_subject(template, payload, user_locale) assert result == '[test] Activate Single Sign On Workstation Experience' def test_get_email_subject_trending_tracks(mocker): """Test email subject for trending_tracks.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'trending_tracks' payload = {'country_group_name': 'France'} user_locale = 'en' result = email_utils.get_email_subject(template, payload, user_locale) assert result == \ '[test] The Orchard - This Week’s Trending Tracks in France' def test_get_email_subject_video_product_rejection(mocker): """Test email subject for video_product_rejection.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'video_product_rejection' payload = {} user_locale = 'en' result = email_utils.get_email_subject(template, payload, user_locale) assert result == '[test] Action Required: Your Video Has Been Rejected' def test_get_email_subject_video_product_approval(mocker): """Test email subject for video_product_approval.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'video_product_approval' payload = {} user_locale = 'en' result = email_utils.get_email_subject(template, payload, user_locale) assert result == '[test] Your Video Has Been Approved' def test_get_email_subject_release_approval(mocker): """Test email subject for release_approval.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'release_approval' payload = { 'approved_releases': [] } user_locale = 'en' result = email_utils.get_email_subject(template, payload, user_locale) assert result == '[test] Your product has been approved.' def test_get_preview_text_analytics_digest(mocker): """Test preview text for analytics digest.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'analytics_digest' payload = { 'start_date': '2018-06-15', 'end_date': '2018-06-22' } user_locale = 'en' result = email_utils.get_preview_text(template, payload, user_locale) assert result == 'Weekly performance from Jun 15 - Jun 22' def test_get_preview_text_trending_tracks_no_track(mocker): """Test preview text for trending tracks when 0 track is trending.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'trending_tracks' payload = { 'trending_tracks': [] } user_locale = 'en' result = email_utils.get_preview_text(template, payload, user_locale) assert result == '' def test_get_preview_text_spike_detector_one_track(mocker): """Test preview text for spike detector when 1 track is trending.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'spike_detector' payload = { 'trending_tracks': [ {'track_name': 'Track123'} ] } user_locale = 'en' result = email_utils.get_preview_text(template, payload, user_locale) assert result == 'Track123 is trending' def test_get_preview_text_spike_detector_two_tracks(mocker): """Test preview text for spike detector when 2 tracks are trending.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'spike_detector' payload = { 'trending_tracks': [ {'track_name': 'Track123'}, {'track_name': 'Track456'} ] } user_locale = 'en' result = email_utils.get_preview_text(template, payload, user_locale) assert result == 'Track123 + 1 other track are trending' def test_get_preview_text_spike_detector_multiple_tracks(mocker): """Test preview text for spike detector when >2 tracks are trending.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'spike_detector' payload = { 'trending_tracks': [ {'track_name': 'Track123'}, {'track_name': 'Track456'}, {'track_name': 'Track789'} ] } user_locale = 'en' result = email_utils.get_preview_text(template, payload, user_locale) assert result == 'Track123 + 2 other tracks are trending' def test_get_preview_text_trending_tracks(mocker): """Test preview text for trending_tracks when >2 tracks are trending.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'trending_tracks' payload = { 'trending_tracks': [ {'track_name': 'Track123'}, {'track_name': 'Track456'}, {'track_name': 'Track789'} ] } user_locale = 'en' result = email_utils.get_preview_text(template, payload, user_locale) assert result == 'Track123 + 2 other tracks are trending' def test_get_preview_text_video_product_rejection(mocker): """Test preview text for video_product_rejection.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'video_product_rejection' payload = {} user_locale = 'en' result = email_utils.get_preview_text(template, payload, user_locale) assert result == 'Action Required: Your Video Has Been Rejected' def test_get_preview_text_video_product_approval(mocker): """Test preview text for video_product_approval.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'video_product_approval' payload = {} user_locale = 'en' result = email_utils.get_preview_text(template, payload, user_locale) assert result == 'Your Video Has Been Approved' def test_get_preview_text_release_approval(mocker): """Test preview text for release_approval.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) template = 'release_approval' payload = { 'approved_releases': [] } user_locale = 'en' result = email_utils.get_preview_text(template, payload, user_locale) assert result == 'Product Approved' def test_generate_unsubscribe_url(monkeypatch): """Test generating unubscribe url.""" monkeypatch.setattr( config, 'BFF_NOTIFICATIONS_URL', 'https://bff-notifications.theorchard.io') user_id = 'alw:123' feed_name = 'label_analytics_digest' feed_id = 'vendor_123' expected_url = 'https://bff-notifications.theorchard.io/unsubscribe' expected_url += '?user_id={0}'.format(user_id) expected_url += '&feed_name={0}'.format(feed_name) expected_url += '&feed_id={0}'.format(feed_id) expected_url += '&key={0}'.format( 'bf046eb3db79f809132460d8dfe58a40026bcbc4') result = email_utils.generate_unsubscribe_url( user_id, feed_name, feed_id) assert result == expected_url def test_generate_identify_url(monkeypatch): """Test generating the identify url.""" monkeypatch.setattr( config, 'SEGMENT_WRITE_KEY', 'WRITE KEY') user_info = { 'user_id': 'alw:123', 'first_name': 'John', 'last_name': 'Doe', 'email': 'johndoe@theorchard.com', 'account': { 'vendor_id': 123, 'subaccount_id': None } } expected_params = { 'writeKey': 'WRITE KEY', 'userId': 'alw:123', 'traits': { 'email': 'johndoe@theorchard.com', 'first_name': 'John', 'last_name': 'Doe', 'vendor_id': 123, 'subaccount_id': None, 'user_id': 'alw:123' } } expected_data = base64.b64encode( json.dumps(expected_params).encode()).decode('utf-8') expected_url = 'https://api.segment.io/v1/pixel/identify?data={0}'.format( expected_data) result = email_utils.generate_identify_url(user_info) assert result == expected_url def test_generate_tracking_url(monkeypatch): """Test generating the tracking url.""" monkeypatch.setattr( config, 'SEGMENT_WRITE_KEY', 'WRITE KEY') user_id = 'alw:123' feed_name = 'label_analytics_digest' feed_id = 'vendor_123' email_subject = 'Analytics Digest Notification' expected_params = { 'writeKey': 'WRITE KEY', 'userId': user_id, 'event': 'Email Opened', 'properties': { 'subject': email_subject, 'feed_name': feed_name, 'feed_id': feed_id, 'userId': user_id } } expected_data = base64.b64encode( json.dumps(expected_params).encode()).decode('utf-8') expected_url = 'https://api.segment.io/v1/pixel/track?data={0}'.format( expected_data) result = email_utils.generate_tracking_url( user_id, feed_name, feed_id, email_subject) assert result == expected_url def test_track_email_sent(monkeypatch): """Test trackinf the email sent.""" monkeypatch.setattr( config, 'SEGMENT_WRITE_KEY', 'WRITE KEY') monkeypatch.setattr(analytics, 'identify', MagicMock()) monkeypatch.setattr(analytics, 'track', MagicMock()) user_id = 'alw:123' feed_name = 'label_analytics_digest' feed_id = 'vendor_123' email_utils.track_email_sent(user_id, feed_name, feed_id) analytics.identify.assert_called_once_with(user_id) analytics.track.assert_called_once_with(user_id, 'Email Delivered', { 'feed_name': feed_name, 'feed_id': feed_id, 'userId': user_id }) def test_track_generic_email_sent(monkeypatch): """Test tracking the generic email sent.""" monkeypatch.setattr( config, 'SEGMENT_WRITE_KEY', 'WRITE KEY') monkeypatch.setattr(analytics, 'identify', MagicMock()) monkeypatch.setattr(analytics, 'track', MagicMock()) user_id = 'alw:123' subject = 'TEST SUBJECT' email_utils.track_generic_email_sent(user_id, subject) analytics.identify.assert_called_once_with(user_id) analytics.track.assert_called_once_with(user_id, 'Email Delivered', { 'subject': subject, 'userId': user_id }) def test_track_email_bounce(monkeypatch): """Test track if the email bounced.""" monkeypatch.setattr( config, 'SEGMENT_WRITE_KEY', 'WRITE KEY') monkeypatch.setattr(analytics, 'track', MagicMock()) email = 'EMAIL' email_utils.track_email_bounce(email) analytics.track.assert_called_once_with(email, 'Email Bounced', { 'email': email }) def test_track_email_complaint(monkeypatch): """Test track if the email complained.""" monkeypatch.setattr( config, 'SEGMENT_WRITE_KEY', 'WRITE KEY') monkeypatch.setattr(analytics, 'track', MagicMock()) email = 'EMAIL' email_utils.track_email_complaint(email) analytics.track.assert_called_once_with(email, 'Email Complained', { 'email': email }) def test_extract_bounced_emails_from_ses_notification(): """Test extract emails from ses notification.""" message_body = { 'Type': 'Notification', 'Message': "{\"notificationType\":\"Bounce\",\"bounce\":{\"bouncedRecipients\":[{\"emailAddress\":\"bounce@simulator.amazonses.com\"},{\"emailAddress\":\"bounce2@simulator.amazonses.com\"}]}}", # noqa } expected_emails = [ 'bounce@simulator.amazonses.com', 'bounce2@simulator.amazonses.com'] result = email_utils.\ extract_bounced_emails_from_ses_notification(message_body) assert result == expected_emails def test_extract_complaint_emails_from_ses_notification(): """Test extract emails from complaint ses notification.""" message_body = { 'Type': 'Notification', 'Message': "{\"notificationType\":\"Complaint\",\"complaint\":{\"complainedRecipients\":[{\"emailAddress\":\"complaint@simulator.amazonses.com\"},{\"emailAddress\":\"complaint2@simulator.amazonses.com\"}]}}", # noqa } expected_emails = [ 'complaint@simulator.amazonses.com', 'complaint2@simulator.amazonses.com'] result = email_utils.\ extract_complaint_emails_from_ses_notification(message_body) assert result == expected_emails def test_format_notification_for_release_rejection(): """Test formatting for release_rejection.""" notification = { 'template': 'release_rejection', 'payload': { 'project_id': 123, 'product_id': 456 } } email_utils.format_notification_for_rendering(notification) assert notification == { 'template': 'release_rejection', 'payload': { 'project_id': 123, 'product_id': 456, 'product_url': 'https://workstation.qaorch.com/project/123/product/456/overview' # noqa } } def test_format_notification_for_digital_rejection(): """Test formatting for digital_rejection.""" notification = { 'template': 'digital_rejection', 'payload': { 'project_id': 123, 'product_id': 456, 'rejection_reasons': [ { 'title': 'General Comments', 'comments': 'This is a general comment\n\nI have some newlines\n\nfin.' } ] } } email_utils.format_notification_for_rendering(notification) assert notification == { 'template': 'digital_rejection', 'payload': { 'project_id': 123, 'product_id': 456, 'pref_base_url': 'https://settings.qaorch.com', 'product_url': 'https://workstation.qaorch.com/project/123/product/456/overview', # noqa 'rejection_reasons': [ { 'title': 'General Comments', 'comments': 'This is a general comment

I have some newlines

fin.' } ] } } def test_get_email_subject_release_rejection(mocker): """Test email subject for release_rejection.""" mocker.patch( 'notifications_delivery.utils.template_renderer.get_translations', MagicMock(gettext=MagicMock(return_value=lambda s: s)) ) # monkeypatch.setattr( # email_utils, # 'get_translations', # MagicMock(return_value=lambda s: s)) template = 'release_rejection' payload = { 'project_id': 123, 'product_id': 456, } user_locale = 'en' result = email_utils.get_email_subject(template, payload, user_locale) assert result == "[test] Action Needed: We've encountered an issue with one of your releases." def test_send_smtp_email(monkeypatch): """Test sending smtp email.""" monkeypatch.setattr(config, 'ENVIRONMENT', 'dev') monkeypatch.setenv('TEST_SMTP_SERVER_HOST', 'smtp.gmail.com') monkeypatch.setenv('TEST_SMTP_SERVER_PORT', '587') monkeypatch.setenv('TEST_SMTP_SENDER_ADDRESS', 'test_sender@gmail.com') monkeypatch.setenv('TEST_SMTP_RECEIVER_ADDRESS', 'test_receiver@hotmail.com') monkeypatch.setenv('TEST_SMTP_SENDER_USERNAME', 'test_sender@gmail.com') monkeypatch.setenv('TEST_SMTP_SENDER_PASSWORD', 'mySecretP4SS') subject = 'test' html_content = '

Test

' with monkeypatch.context() as m: # Mock the smtplib.SMTP class m.setattr('smtplib.SMTP', lambda *args, **kwargs: MockSMTP(*args, **kwargs)) # Define a mock SMTP class class MockSMTP: def __init__(self, *args, **kwargs): pass def starttls(self): pass def login(self, username, password): pass def sendmail(self, from_addr, to_addrs, msg): pass def close(self): pass result = email_utils.send_smtp_email(subject, html_content) assert result is True def test_send_smtp_email_exception(): """Test exception handling when sending smtp email in prod environment.""" config.ENVIRONMENT = 'prod' subject = 'test' html_content = '

Test

' try: email_utils.send_smtp_email(subject, html_content) assert False # The exception should have been raised, so this line should not be executed except Exception as e: assert str(e) == 'Cannot send SMTP emails in production environment' def test_generate_html_file(monkeypatch): """Test generating an HTML file from a template.""" project_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../../..') template_name = 'welcome_onboard' html_content = '

Welcome to our website!

' def mock_abspath(path): return f'{path}/notifications_delivery/utils' monkeypatch.setattr(os.path, 'abspath', lambda path: mock_abspath(project_root)) email_utils.generate_html_file(template_name, html_content) # Check if the HTML file was created in the default output directory (project root) expected_file_path = os.path.join(project_root, f'{template_name}.html') assert os.path.exists(expected_file_path) def test_generate_html_file_exception(): """Test exception handling when generating an HTML file.""" template_name = 'welcome_onboard' html_content = '

Welcome to our website!

' output_dir_path = '/path/to/nonexistent_directory' try: email_utils.generate_html_file(template_name, html_content, output_dir_path) except FileNotFoundError as e: assert str(e) == 'Output directory does not exist'