def test_no_dupes(self, mock_post: mock.MagicMock) -> None: """Test that we do not send duplicate emails if we run the script twice.""" _db = pymongo.MongoClient('mongodb://my-db/db').db _db.user.drop() _db.user.insert_one(_USER_PENDING_NPS_DICT) mail_nps.main(self._now, '1') self.assertTrue(mailjetmock.get_all_sent_messages()) mailjetmock.clear_sent_messages() # Running the script again 10 minutes later. mail_nps.main(self._now + datetime.timedelta(minutes=10), '1') self.assertFalse(mailjetmock.get_all_sent_messages()) calls = [ mock.call( 'https://slack.example.com/webhook', json={ 'text': "Report for NPS blast: I've sent 1 emails (with 0 errors)." }, ), mock.call( 'https://slack.example.com/webhook', json={ 'text': "Report for NPS blast: I've sent 0 emails (with 0 errors)." }, ) ] self.assertEqual(calls, mock_post.mock_calls)
def test_no_comments(self) -> None: """Forwarded email has no #-starting lines.""" user_id = self.create_user(email='*****@*****.**') parse_email = dict( self.parse_email, **{ 'Subject': f'{user_id} Merci pour votre retour', 'Text-part': '''# This line will disappear # This line will disappear too, even though it has whitespace. This line will stay.''' }) response = self.app.post('/api/eval/mailjet', data=json.dumps(parse_email)) self.assertEqual(200, response.status_code) self.assertEqual(['*****@*****.**'], [ msg.recipient['Email'] for msg in mailjetmock.get_all_sent_messages() ]) self.assertEqual(['Merci pour votre retour'], [ msg.properties.get('Subject') for msg in mailjetmock.get_all_sent_messages() ]) self.assertEqual(['This line will stay.'], [ line.strip() for msg in mailjetmock.get_all_sent_messages() for line in msg.properties.get('TextPart', '').split('\n') ])
def test_send_email_to_everyone(self) -> None: """Send data to counselor and user when having both email adresses.""" self.assertFalse(mailjetmock.get_all_sent_messages()) response = self.app.post( '/api/ali/user', data= ('{"user_email": "*****@*****.**", "counselor_name": "Bobbie",' '"counselor_email": "*****@*****.**", "results_url": "http://www.foo.bar"}' ), content_type='application/json') post_response = self.json_from_response(response) mails_sent = mailjetmock.get_all_sent_messages() self.assertEqual(2, len(mails_sent), msg=mails_sent) self.assertEqual({ 'hasCounselorEmail': True, 'hasUserEmail': True }, post_response) self.assertEqual('*****@*****.**', mails_sent[0].recipient['Email']) self.assertEqual('*****@*****.**', mails_sent[1].recipient['Email']) data = mails_sent[0].properties['Variables'] self.assertEqual('*****@*****.**', data['userEmail']) self.assertEqual('*****@*****.**', data['counselorEmail']) self.assertEqual('Bobbie', data['counselorName']) self.assertIn('http://www.foo.bar', data['directLink'])
def test_send_only_once_a_month(self) -> None: """Sending focus emails to user on "once-a-month" frequency.""" self._db.user_test.user.update_one( {}, {'$set': { 'profile.coachingEmailFrequency': 'EMAIL_ONCE_A_MONTH' }}) focus.main(['send', '--disable-sentry']) self.mock_now.return_value += datetime.timedelta(days=15) mailjetmock.clear_sent_messages() focus.main(['send', '--disable-sentry']) # No email sent, even 15 days later. self.assertFalse(mailjetmock.get_all_sent_messages()) user_data = self._db.user_test.user.find_one() self.assertEqual(1, len(user_data.get('emailsSent'))) self.mock_now.return_value += datetime.timedelta(days=30) mailjetmock.clear_sent_messages() focus.main(['send', '--disable-sentry']) self.assertEqual(['*****@*****.**'], [ m.recipient['Email'] for m in mailjetmock.get_all_sent_messages() ]) user_data = self._db.user_test.user.find_one() self.assertEqual(2, len(user_data.get('emailsSent')))
def test_activation_email_for_guest_user(self, mock_now: mock.MagicMock) -> None: """Create an account with email + password from a guest user that has a diagnostic.""" mock_now.return_value = datetime.datetime(2018, 6, 10) self._db.advice_modules.insert_many([ { 'adviceId': 'spontaneous-application', 'categories': ['first'], 'triggerScoringModel': 'constant(2)', 'isReadyForProd': True, }, ]) user_id, auth_token = self.create_guest_user( first_name='Pascal', modifiers=[base_test.add_project]) self.assertFalse(mailjetmock.get_all_sent_messages()) # Create password. self.app.post( '/api/user/authenticate', data= f'{{"email": "*****@*****.**", "userId": "{user_id}", "authToken": "{auth_token}", ' f'"hashedPassword": "******"*****@*****.**", "psswd")}"}}', content_type='application/json') mails_sent = mailjetmock.get_all_sent_messages() self.assertEqual(1, len(mails_sent), msg=mails_sent) self.assertEqual('*****@*****.**', mails_sent[0].recipient['Email']) data = mails_sent[0].properties['Variables'] self.assertEqual('10 juin 2018', data['date']) self.assertEqual('Pascal', data['firstName'])
def test_send_all_focus_emails( self, unused_mock_logging: mock.MagicMock) -> None: """Sending all focus emails in 6 months.""" days_without_email = 0 sent_emails_count = 0 # Try sending emails until there has been a month without any email sent. while days_without_email < 30 and sent_emails_count <= len( _GOLDEN_FOCUS_CAMPAIGNS): focus.main(['send', '--disable-sentry']) emails_sent = mailjetmock.get_all_sent_messages() if len(emails_sent) > sent_emails_count: sent_emails_count = len(emails_sent) days_without_email = 0 else: days_without_email += 1 self.mock_now.return_value += datetime.timedelta(days=1) emails_sent = mailjetmock.get_all_sent_messages() self.assertEqual({'*****@*****.**'}, {m.recipient['Email'] for m in emails_sent}) self.assertLessEqual(len(emails_sent), len(_GOLDEN_FOCUS_CAMPAIGNS)) user_data = self._db.user_test.user.find_one() assert user_data campaigns_sent = [e.get('campaignId') for e in user_data['emailsSent']] self.assertCountEqual(set(campaigns_sent), campaigns_sent, msg='No duplicates') self.assertLessEqual(set(campaigns_sent), set(_GOLDEN_FOCUS_CAMPAIGNS)) # Try sending emails until the next check. next_date = datetime.datetime.fromisoformat( user_data['sendCoachingEmailAfter'][:-1]) while next_date >= self.mock_now.return_value: focus.main(['send', '--disable-sentry']) self.mock_now.return_value += datetime.timedelta(days=1) self.assertEqual( len(emails_sent), len(mailjetmock.get_all_sent_messages()), msg='No new messages.' ' There probably is an issue with time sensitive conditions on some emails' ) user_data = self._db.user_test.user.find_one() # Next check should be at least a month from now. self.assertLessEqual( self.mock_now.return_value + datetime.timedelta(days=30), datetime.datetime.fromisoformat( user_data['sendCoachingEmailAfter'][:-1]))
def test_recommend_advice_none(self, mock_logger: mock.MagicMock) -> None: """Test that the advisor does not recommend anyting if all modules score 0.""" project = project_pb2.Project() self.database.advice_modules.insert_many([ { 'adviceId': 'spontaneous-application', 'categories': ['first'], 'triggerScoringModel': 'constant(0)', 'isReadyForProd': True, }, { 'adviceId': 'other-work-env', 'categories': ['first'], 'triggerScoringModel': 'constant(0)', 'isReadyForProd': True, }, ]) advisor.maybe_advise(self.user, project, self.database) self.assertFalse(project.advices) self.assertFalse(mailjetmock.get_all_sent_messages()) mock_logger.assert_called_once()
def test_explained_advice(self, mock_scoring_models: Dict[str, Any]) -> None: """Test that the advisor gives explanations for the advices.""" mock_scoring_models['constant(1)'] = mock.MagicMock( spec=['get_advice_override', 'score_and_explain']) mock_scoring_models['constant(1)'].score_and_explain.return_value = \ scoring.ExplainedScore(1, ['voilà pourquoi', 'explication genré%eFeminine']) mock_scoring_models[ 'constant(1)'].get_advice_override.return_value = None project = project_pb2.Project() self.database.advice_modules.insert_one({ 'adviceId': 'network', 'categories': ['first'], 'triggerScoringModel': 'constant(1)', 'isReadyForProd': True, }) self.user.profile.gender = user_pb2.FEMININE advisor.maybe_advise(self.user, project, self.database) self.assertEqual(['network'], [a.advice_id for a in project.advices]) self.assertEqual(['voilà pourquoi', 'explication genrée'], project.advices[0].explanations) self.assertEqual(1, len(mailjetmock.get_all_sent_messages()))
def test_send_shuffle(self, mock_random_random: mock.MagicMock) -> None: """Send the mail with a better score first.""" mock_random_random.return_value = 0 self._db.test.focus_emails.drop() self._db.test.focus_emails.insert_many([ { 'campaignId': 'just-big', 'scoringModel': 'constant(0.5)' }, { 'campaignId': 'small-very-important', 'scoringModel': 'constant(3)' }, ]) focus.main([ 'send', '--disable-sentry', '--restrict-campaigns', 'just-big', 'small-very-important', ]) self.assertEqual(['*****@*****.**'], [ m.recipient['Email'] for m in mailjetmock.get_all_sent_messages() ]) user_data = self._db.user_test.user.find_one() assert user_data self.assertEqual(1, len(user_data.get('emailsSent'))) self.assertEqual('small-very-important', user_data['emailsSent'][0]['campaignId'])
def test_slack(self, mock_notify_slack: mock.MagicMock) -> None: """Send message to slack.""" self._db.test.focus_emails.drop() self._db.test.focus_emails.insert_many([ { 'campaignId': 'first-one', 'scoringModel': 'constant(3)' }, { 'campaignId': 'third-one', 'scoringModel': 'constant(.1)' }, ]) # Note that random will not be flaky: # the diff score is (3 - .1) / 3 * _SCORES_WEIGHT = 5 * 2.9 / 3 # the max random diff is _RANDOM_WEIGHTS = 4 # So the difference in score will always be bigger than the random diff and thus first-one # will always be selected. focus.main([ 'send', '--disable-sentry', ]) self.assertEqual(['*****@*****.**'], [ m.recipient['Email'] for m in mailjetmock.get_all_sent_messages() ]) mock_notify_slack.assert_called_once_with( textwrap.dedent('''\ Focus emails sent today: • *first-one*: 1 email • *third-one*: 0 email'''))
def test_blast_hash_start(self, mock_hasher: mock.MagicMock, unused_mock_logging: mock.MagicMock) -> None: """Send mail to users with given hash start.""" mock_user_db = pymongo.MongoClient( 'mongodb://myprivatedata.com/user_test').user_test mock_user_db.user.drop() hash_values = ['12345', '01234'] mock_hasher.reset_mock() mock_hasher().hexdigest.side_effect = hash_values mock_user_db.user.insert_many([{ 'registeredAt': '2017-04-15T00:00:00Z', 'profile': { 'name': f'user {hash_value}', 'email': f'email{hash_value}@corpet.net', }, } for hash_value in hash_values]) mail_blast.main([ 'fake-user-campaign', 'send', '--registered-from', '2017-04-01', '--registered-to', '2017-07-10', '--disable-sentry', '--user-hash', '1' ]) sent_messages = mailjetmock.get_all_sent_messages() self.assertEqual( ['*****@*****.**'], [m.recipient['Email'] for m in sent_messages], msg= f'1 email expected: only for the user whith hash starting with 1\n{sent_messages}' )
def test_setup_report(self, mock_setup_sentry: mock.MagicMock) -> None: """Make sure the report is setup.""" focus.main(['send']) mock_setup_sentry.assert_called_once_with('fake-sentry') self.assertTrue(mailjetmock.get_all_sent_messages())
def test_incompatible_advice_modules(self) -> None: """Test that the advisor discard incompatible advice modules.""" project = project_pb2.Project() self.database.advice_modules.insert_many([ { 'adviceId': 'other-work-env', 'airtableId': 'abc', 'categories': ['first'], 'triggerScoringModel': 'constant(2)', 'isReadyForProd': True, 'incompatibleAdviceIds': ['def'], }, { 'adviceId': 'spontaneous-application', 'categories': ['first'], 'airtableId': 'def', 'triggerScoringModel': 'constant(3)', 'isReadyForProd': True, 'incompatibleAdviceIds': ['abc'], }, { 'adviceId': 'final-one', 'categories': ['first'], 'airtableId': 'ghi', 'triggerScoringModel': 'constant(1)', 'isReadyForProd': True, }, ]) advisor.maybe_advise(self.user, project, self.database) self.assertEqual(['spontaneous-application', 'final-one'], [a.advice_id for a in project.advices]) self.assertEqual(1, len(mailjetmock.get_all_sent_messages()))
def test_main(self, mock_post: mock.MagicMock) -> None: """Overall test.""" _db = pymongo.MongoClient('mongodb://my-db/db').db _db.user.drop() _db.user.insert_one({ '_id': mongomock.ObjectId('5daf2298484ae6c93351b822'), 'profile': { 'name': 'Pascal', 'lastName': 'Corpet', 'email': '*****@*****.**', 'locale': 'en', }, 'registeredAt': datetime.datetime(2018, 1, 22, 10, 0, 0).isoformat() + 'Z', 'projects': [{ 'title': 'Project Title', }], }) mail_nps.main(self._now, '1') sent_messages = mailjetmock.get_all_sent_messages() self.assertEqual(['*****@*****.**'], [m.recipient['Email'] for m in sent_messages]) self.assertEqual(100819, sent_messages[0].properties['TemplateID']) template_vars = sent_messages[0].properties['Variables'] nps_form_urlstring = template_vars.pop('npsFormUrl') self.assertEqual( { 'baseUrl': 'http://localhost:3000', 'firstName': 'Pascal', }, template_vars) nps_form_url = parse.urlparse(nps_form_urlstring) self.assertEqual( 'http://localhost:3000/api/nps', parse.urlunparse(nps_form_url[:4] + ('', ) + nps_form_url[5:])) nps_form_args = parse.parse_qs(nps_form_url.query) self.assertEqual({'user', 'token', 'redirect'}, nps_form_args.keys()) self.assertEqual(['5daf2298484ae6c93351b822'], nps_form_args['user']) auth.check_token('5daf2298484ae6c93351b822', nps_form_args['token'][0], role='nps') self.assertEqual(['http://localhost:3000/retours?hl=en'], nps_form_args['redirect']) mock_post.assert_called_once_with( 'https://slack.example.com/webhook', json={ 'text': "Report for NPS blast: I've sent 1 emails (with 0 errors)." }, ) modified_user = user_pb2.User() proto.parse_from_mongo(_db.user.find_one(), modified_user) self.assertEqual( [sent_messages[0].message_id], [m.mailjet_message_id for m in modified_user.emails_sent]) self.assertEqual('nps', modified_user.emails_sent[0].campaign_id)
def test_no_incomplete(self, mock_post: mock.MagicMock) -> None: """Do not send if project is not complete.""" _db = pymongo.MongoClient('mongodb://my-db/db').db _db.user.drop() _db.user.insert_one({ 'profile': { 'name': 'Pascal', 'lastName': 'Corpet', 'email': '*****@*****.**', }, 'registeredAt': datetime.datetime(2018, 1, 22, 10, 0, 0).isoformat() + 'Z', 'projects': [{ 'title': 'Project Title', 'isIncomplete': True, }], }) mail_nps.main(self._now, '1') self.assertFalse(mailjetmock.get_all_sent_messages()) mock_post.assert_called_once_with( 'https://slack.example.com/webhook', json={ 'text': "Report for NPS blast: I've sent 0 emails (with 0 errors)." }, )
def test_no_missing_email(self, mock_post: mock.MagicMock) -> None: """Do not send if there's no email address.""" _db = pymongo.MongoClient('mongodb://my-db/db').db _db.user.drop() _db.user.insert_one({ '_id': '5daf2298484ae6c93351b822', 'profile': { 'name': 'Pascal', 'lastName': 'Corpet', }, 'registeredAt': datetime.datetime(2018, 1, 22, 10, 0, 0).isoformat() + 'Z', 'projects': [{ 'title': 'Project Title', }], }) mail_nps.main(self._now, '1') self.assertFalse(mailjetmock.get_all_sent_messages()) mock_post.assert_called_once_with( 'https://slack.example.com/webhook', json={ 'text': "Report for NPS blast: I've sent 0 emails (with 0 errors)." }, )
def test_list_emails(self, mock_logging: mock.MagicMock) -> None: """List uses logging extensively but does not send any email.""" focus.main(['list']) mock_logging.assert_called() self.assertFalse(mailjetmock.get_all_sent_messages())
def test_failed_setup_report(self, mock_error: mock.MagicMock) -> None: """Warn if the report is not correctly setup.""" focus.main(['send']) mock_error.assert_called_once_with( 'Please set SENTRY_DSN to enable logging to Sentry, or use --disable-sentry option' ) self.assertFalse(mailjetmock.get_all_sent_messages())
def test_list(self, mock_logging: mock.MagicMock) -> None: """Test a list for documents blast.""" mail_blast.main(['fake-document', 'list']) messages = mailjetmock.get_all_sent_messages() self.assertFalse(messages) mock_logging.assert_any_call('%s: %s %s', 'fake-document', '5b2173b9362bf80840db6c2a', '*****@*****.**')
def test_send_template_not_to_example(self) -> None: """Do not send template to test addresses.""" mail.send_template('12345', _Recipient('REDACTED', 'Primary', 'Recipient'), {'custom': 'var'}) sent_emails = mailjetmock.get_all_sent_messages() self.assertEqual([], sorted(m.recipient['Email'] for m in sent_emails)) mail.send_template( '12345', _Recipient('*****@*****.**', 'Primary', 'Recipient'), {'custom': 'var'}) sent_emails = mailjetmock.get_all_sent_messages() self.assertEqual([], sorted(m.recipient['Email'] for m in sent_emails))
def test_no_advice_if_project_incomplete(self) -> None: """Test that the advice do not get populated when the project is marked as incomplete.""" project = project_pb2.Project(is_incomplete=True) advisor.maybe_advise(self.user, project, self.database) self.assertEqual(len(project.advices), 0) self.assertFalse(mailjetmock.get_all_sent_messages())
def test_forward(self) -> None: """Basic usage of email-forwarder.""" user_id = self.create_user(email='*****@*****.**') parse_email = dict(self.parse_email, Subject=f'{user_id} Merci pour votre retour') response = self.app.post('/api/eval/mailjet', data=json.dumps(parse_email)) self.assertEqual(200, response.status_code) self.assertEqual(['*****@*****.**'], [ msg.recipient['Email'] for msg in mailjetmock.get_all_sent_messages() ]) self.assertEqual(['Merci pour votre retour'], [ msg.properties.get('Subject') for msg in mailjetmock.get_all_sent_messages() ])
def test_send(self) -> None: """Test really sending to a document owner.""" mail_blast.main(['fake-document', 'send', '--disable-sentry']) messages = mailjetmock.get_all_sent_messages() self.assertTrue(messages) self.assertEqual(1, len(messages)) message = messages.pop() self.assertEqual('Nathalie ', message.recipient['Name']) self.assertEqual('*****@*****.**', message.recipient['Email'])
def test_send_auth_email_to_guest_user(self) -> None: """Create an account with email from a guest user, then use this email to auth.""" user_id, auth_token = self.create_guest_user(first_name='Lascap') # Add an email to the guest user. self.app.post( '/api/user/authenticate', data= f'{{"email": "*****@*****.**", "userId": "{user_id}", "authToken": "{auth_token}"}}', content_type='application/json') self.assertFalse(mailjetmock.get_all_sent_messages()) # Try to connect with a password. salt = self._get_salt('*****@*****.**') request = \ f'{{"email": "*****@*****.**", "hashSalt": "{salt}", ' \ f'"hashedPassword": "******"*****@*****.**", "psswd"))}"}}' response = self.app.post('/api/user/authenticate', data=request, content_type='application/json') self.assertEqual(403, response.status_code) # Do not display the email in the error message. self.assertNotIn('*****@*****.**', response.get_data(as_text=True)) # Open the auth email. mails_sent = mailjetmock.get_all_sent_messages() self.assertEqual(1, len(mails_sent), msg=mails_sent) self.assertEqual('*****@*****.**', mails_sent[0].recipient['Email']) data = mails_sent[0].properties['Variables'] self._assert_all_template_variables(data) self.assertEqual('Lascap', data['firstname']) url_args = parse.parse_qs(parse.urlparse(data['authLink']).query) self.assertLessEqual({'userId', 'authToken'}, url_args.keys()) self.assertEqual([user_id], url_args['userId']) auth_token = url_args['authToken'][0] # Log in from email. response = self.app.post( '/api/user/authenticate', data=f'{{"userId": "{user_id}", "authToken": "{auth_token}"}}', content_type='application/json') self.json_from_response(response)
def test_dont_send_to_mistyped_emails(self) -> None: """Do not send focus emails to users with an incorrect email address.""" self._db.user_test.user.update_one( {}, {'$set': { 'profile.email': 'pascal@ corpet.net', }}) focus.main(['send', '--disable-sentry']) self.assertFalse(mailjetmock.get_all_sent_messages()) self._db.user_test.user.update_one( {}, {'$set': { 'profile.email': 'pascal@corpet', }}) focus.main(['send', '--disable-sentry']) self.assertFalse(mailjetmock.get_all_sent_messages())
def test_reset_password(self) -> None: """Full flow to reset user's password.""" user_id, auth_token = self.authenticate_new_user_token( first_name='Sil', email='*****@*****.**', password='******') # Ask to reset user's password. self.app.post( '/api/user/reset-password', data=f'{{"email": "*****@*****.**", "authToken":"{auth_token}"}}', content_type='application/json') # Retrieve the reset token from the sent email. mails_sent = mailjetmock.get_all_sent_messages() self.assertEqual(1, len(mails_sent), msg=mails_sent) self.assertEqual('*****@*****.**', mails_sent[0].recipient['Email']) data = mails_sent[0].properties['Variables'] self.assertTrue(data['resetLink']) reset_token = parse.parse_qs(parse.urlparse( data['resetLink']).query).get('resetToken', [])[0] mailjetmock.clear_sent_messages() # Reset password. request = f'{{"email": "*****@*****.**", "authToken":"{reset_token}", ' \ f'"hashedPassword": "******"*****@*****.**", "new password")}", "firstName": "Sil"}}' response = self.app.post('/api/user/authenticate', data=request) self.assertEqual(200, response.status_code, msg=response.get_data(as_text=True)) self.assertEqual([user_id], list( str(u['_id']) for u in self._user_db.user.find())) mails_sent = mailjetmock.get_all_sent_messages() self.assertEqual(1, len(mails_sent), msg=mails_sent) self.assertEqual('*****@*****.**', mails_sent[0].recipient['Email']) self.assertEqual(3643543, mails_sent[0].properties['TemplateID']) data = mails_sent[0].properties['Variables'] self.assertIn('senderName', data.keys()) self._assert_all_template_variables(data) self.assertEqual('Sil', data['firstName'])
def test_dont_send_to_example(self, mock_warning: mock.MagicMock) -> None: """Do not send focus emails to users with an example email address.""" self._db.user_test.user.update_one( {}, {'$set': { 'profile.email': '*****@*****.**', }}) focus.main(['send', '--disable-sentry']) self.assertFalse(mailjetmock.get_all_sent_messages()) mock_warning.assert_not_called()
def test_dry_run(self) -> None: """Test a dry run for documents.""" mail_blast.main([ 'fake-document', 'dry-run', '--dry-run-email', '*****@*****.**' ]) messages = mailjetmock.get_all_sent_messages() self.assertTrue(messages) self.assertEqual(1, len(messages)) message = messages.pop() self.assertEqual('Nathalie ', message.recipient['Name']) self.assertEqual('*****@*****.**', message.recipient['Email'])
def test_missing_sentry_dsn(self, mock_error: mock.MagicMock) -> None: """Log an error when used without SENTRY_DSN env var.""" mail_blast.main([ 'focus-network', 'send', '--registered-from', '2017-04-01', '--registered-to', '2017-07-10' ]) self.assertFalse(mailjetmock.get_all_sent_messages()) mock_error.assert_called_with( 'Please set SENTRY_DSN to enable logging to Sentry, or use --disable-sentry option' )
def _assert_user_receives_campaign( self, should_be_sent: bool = True, blast_from: Optional[str] = None, blast_to: Optional[str] = None, extra_args: Optional[list[str]] = None) -> None: json_user = json_format.MessageToDict(self.user) json_user['_id'] = mongomock.ObjectId(json_user.pop('userId')) self._user_database.user.insert_one(json_user) year = self.user.registered_at.ToDatetime().year if self.now: now_patcher = nowmock.patch(new=mock.MagicMock( return_value=self.now)) now_patcher.start() self.addCleanup(now_patcher.stop) mail_blast.main([ self.campaign_id, 'send', '--disable-sentry', '--registered-from', blast_from or str(year), '--registered-to', blast_to or str(year + 1), '--log-reason-on-error', ] + (extra_args or [])) all_sent_messages = mailjetmock.get_all_sent_messages() if not should_be_sent: self.assertFalse(all_sent_messages) return self.assertEqual(1, len(all_sent_messages), msg=all_sent_messages) self.assertEqual(self.campaign_id, all_sent_messages[0].properties['CustomCampaign']) self._variables = all_sent_messages[0].properties['Variables'] self._from = all_sent_messages[0].properties['From'] self.assertEqual(self._from['Name'], self._variables.pop('senderName')) # Test that variables used in the template are populated. template_id = str(all_sent_messages[0].properties['TemplateID']) template_path = campaign.get_campaign_folder( typing.cast(mailjet_templates.Id, self.campaign_id)) self.assertTrue(template_path, msg=f'No template for campaign "{self.campaign_id}"') assert template_path vars_filename = os.path.join(template_path, 'vars-example.json') with open(vars_filename, 'r', encoding='utf-8') as vars_file: template_vars = json.load(vars_file).keys() for template_var in template_vars: self.assertIn( template_var, self._variables, msg=f'Template error for campaign {self.campaign_id}, see ' f'https://app.mailjet.com/template/{template_id}/build')