def test_returns_error_payload_if_expected_already_subscribed_email_error(self, get_email_hash): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object( dm_mailchimp_client._client.lists.members, 'create_or_update', autospec=True ) as create_or_update: response = mock.MagicMock(__bool__=False) expected_error = "[email protected] is already a list member. Use PUT to insert or update list members." response.status_code = 400 response.message = ( "Bad Request for url: https://us5.api.mailchimp.com/3.0/lists/list_id/members/member_id" ) response.json.return_value = {"detail": expected_error} create_or_update.side_effect = HTTPError("400 Client Error", response=response) with assert_external_service_log_entry(successful_call=False, extra_modules=['mailchimp']) as log_catcher: res = dm_mailchimp_client.subscribe_new_email_to_list('list_id', '*****@*****.**') assert res == {"status": "error", "error_type": "already_subscribed", "status_code": 400} assert log_catcher.records[1].msg == ( "Expected error: Mailchimp failed to add user (foo) to list (list_id). " "API error: This email address is already subscribed." ) assert log_catcher.records[1].error == "400 Client Error" assert log_catcher.records[1].levelname == 'WARNING'
def test_returns_error_payload_if_user_previously_unsubscribed_error(self, get_email_hash): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object( dm_mailchimp_client._client.lists.members, 'create_or_update', autospec=True ) as create_or_update: response = mock.MagicMock(__bool__=False) expected_error = "[email protected] was permanently deleted and cannot be re-imported. " \ "The contact must re-subscribe to get back on the list." response.status_code = 400 response.message = ( "Bad Request for url: https://us5.api.mailchimp.com/3.0/lists/list_id/members/member_id" ) response.json.return_value = {"detail": expected_error} create_or_update.side_effect = HTTPError("400 Client Error", response=response) with assert_external_service_log_entry(successful_call=False, extra_modules=['mailchimp']) as log_catcher: res = dm_mailchimp_client.subscribe_new_email_to_list('list_id', '*****@*****.**') assert res == {"status": "error", "error_type": "deleted_user", "status_code": 400} assert log_catcher.records[1].msg == ( "Expected error: Mailchimp cannot automatically subscribe user (foo) to list (list_id) as the user " "has been permanently deleted." ) assert log_catcher.records[1].error == "400 Client Error" assert log_catcher.records[1].levelname == 'WARNING'
def test_success_does_not_perform_retry(): dm_mailchimp_client = DMMailChimpClient('username', 'api key', mock.MagicMock(), retries=2) with mock.patch.object(dm_mailchimp_client._client.lists.members, 'all', autospec=True) as all_members: all_members.side_effect = [ { "members": [ { "email_address": "*****@*****.**" }, { "email_address": "*****@*****.**" }, ] }, { "members": [] }, ] dm_mailchimp_client.get_email_addresses_from_list('a_list_id') assert all_members.mock_calls == [ mock.call('a_list_id', count=1000, offset=0), mock.call('a_list_id', count=1000, offset=1000), ]
def test_returns_error_payload_if_email_fails_validation(self, get_email_hash): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object( dm_mailchimp_client._client.lists.members, 'create_or_update', autospec=True ) as create_or_update: create_or_update.side_effect = MailChimpError( { "detail": "Please provide a valid email address.", 'title': 'Invalid Resource', 'status': 400 } ) with assert_external_service_log_entry(successful_call=False, extra_modules=['mailchimp']) as log_catcher: res = dm_mailchimp_client.subscribe_new_email_to_list('list_id', '*****@*****.**') assert res == {"status": "error", "error_type": "invalid_email", "status_code": 400} assert log_catcher.records[1].msg == ( "Expected error: Mailchimp failed to add user (foo) to list (list_id). " "API error: The email address was invalid." ) assert log_catcher.records[1].error == "{'detail': 'Please provide a valid email address.', " \ "'title': 'Invalid Resource', 'status': 400}" assert log_catcher.records[1].levelname == 'WARNING'
def test_get_email_addresses_from_list(): dm_mailchimp_client = DMMailChimpClient('username', 'api key', mock.MagicMock()) with mock.patch.object(dm_mailchimp_client._client.lists.members, 'all', autospec=True) as all_members: all_members.side_effect = [ { "members": [ { "email_address": "*****@*****.**" }, { "email_address": "*****@*****.**" }, ] }, { "members": [] }, ] res = dm_mailchimp_client.get_email_addresses_from_list('list_id') assert res == ["*****@*****.**", "*****@*****.**"] assert all_members.call_args_list == [ mock.call('list_id', count=1000, offset=0), mock.call('list_id', count=1000, offset=1000), ]
def test_get_email_addresses_from_list_generates_emails(self): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object(dm_mailchimp_client._client.lists.members, 'all', autospec=True) as all_members: all_members.side_effect = [ { "members": [ {"email_address": "*****@*****.**"}, {"email_address": "*****@*****.**"}, ] }, { "members": [] }, ] res = dm_mailchimp_client.get_email_addresses_from_list('list_id') assert isinstance(res, types.GeneratorType) assert all_members.call_args_list == [] with assert_external_service_log_entry(extra_modules=['mailchimp'], count=2): assert list(res) == ["*****@*****.**", "*****@*****.**"] assert all_members.call_args_list == [ mock.call('list_id', count=100, offset=0), mock.call('list_id', count=100, offset=100), ]
def join_open_framework_notification_mailing_list(): status = 200 form = EmailAddressForm() if form.validate_on_submit(): dmmc_client = DMMailChimpClient( current_app.config["DM_MAILCHIMP_USERNAME"], current_app.config["DM_MAILCHIMP_API_KEY"], current_app.logger, ) mc_response = dmmc_client.subscribe_new_email_to_list( current_app.config["DM_MAILCHIMP_OPEN_FRAMEWORK_NOTIFICATION_MAILING_LIST_ID"], form.data["email_address"], ) if mc_response.get('status') == 'success': data_api_client.create_audit_event( audit_type=AuditTypes.mailing_list_subscription, data={ "subscribedEmail": form.data["email_address"], "mailchimp": {k: mc_response.get(k) for k in ( "id", "unique_email_id", "timestamp_opt", "last_changed", "list_id", )}, }, ) flash(JOIN_OPEN_FRAMEWORK_NOTIFICATION_MAILING_LIST_SUCCESS_MESSAGE.format( email_address=form.data["email_address"], ), "success") return redirect("/") else: if mc_response.get('error_type') == 'already_subscribed': flash(JOIN_OPEN_FRAMEWORK_NOTIFICATION_MAILING_LIST_ALREADY_SUBSCRIBED_MESSAGE.format( support_email_address=current_app.config['SUPPORT_EMAIL_ADDRESS'] ), "error") elif mc_response.get('error_type') in ['deleted_user', 'invalid_email']: flash(JOIN_OPEN_FRAMEWORK_NOTIFICATION_MAILING_LIST_UNSUBSCRIBED_MESSAGE.format( support_email_address=current_app.config['SUPPORT_EMAIL_ADDRESS'] ), "error") else: flash(JOIN_OPEN_FRAMEWORK_NOTIFICATION_MAILING_LIST_ERROR_MESSAGE.format( support_email_address=current_app.config['SUPPORT_EMAIL_ADDRESS'] ), "error") # If no status code supplied, something has probably gone wrong status = mc_response.get('status_code', 503) # fall through to re-display form with error elif request.method == "POST": status = 400 # fall through to re-display form with errors return render_template( "suppliers/join_open_framework_notification_mailing_list.html", form=form, errors=get_errors_from_wtform(form), ), status
def test_timeout_exception_is_not_propagated_for_create_or_update(self): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object( dm_mailchimp_client._client.lists.members, 'create_or_update', autospec=True) as create_or_update: create_or_update.side_effect = ConnectTimeout() assert dm_mailchimp_client.subscribe_new_email_to_list('a_list_id', '*****@*****.**') == \ {"status": "error", "error_type": "unexpected_error", "status_code": 500} assert create_or_update.called is True
def test_send_campaign(self): campaign_id = "1" dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, mock.MagicMock()) with mock.patch.object(dm_mailchimp_client._client.campaigns.actions, 'send', autospec=True) as send: with assert_external_service_log_entry(): res = dm_mailchimp_client.send_campaign(campaign_id) assert res is True send.assert_called_once_with(campaign_id)
def test_create_campaign(): dm_mailchimp_client = DMMailChimpClient('username', 'api key', 'logger') with mock.patch.object(dm_mailchimp_client._client.campaigns, 'create', autospec=True) as create: create.return_value = {"id": "100"} res = dm_mailchimp_client.create_campaign({"example": "data"}) assert res == "100" create.assert_called_once_with({"example": "data"})
def test_default_timeout_retry_performs_no_retries(self): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object(dm_mailchimp_client._client.lists.members, 'all', autospec=True) as all_members: all_members.side_effect = HTTPError(response=mock.Mock(status_code=504)) with pytest.raises(HTTPError): with assert_external_service_log_entry(successful_call=False, extra_modules=['mailchimp']): list(dm_mailchimp_client.get_email_addresses_from_list('a_list_id')) assert all_members.call_args_list == [ mock.call('a_list_id', count=100, offset=0), ]
def test_create_campaign(self): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, 'logger') with mock.patch.object(dm_mailchimp_client._client.campaigns, 'create', autospec=True) as create: create.return_value = {"id": "100"} with assert_external_service_log_entry(): res = dm_mailchimp_client.create_campaign({"example": "data"}) assert res == "100" create.assert_called_once_with({"example": "data"})
def test_send_campaign(): campaign_id = "1" dm_mailchimp_client = DMMailChimpClient('username', 'api key', mock.MagicMock()) with mock.patch.object(dm_mailchimp_client._client.campaigns.actions, 'send', autospec=True) as send: res = dm_mailchimp_client.send_campaign(campaign_id) assert res is True send.assert_called_once_with(campaign_id)
def test_set_campaign_content(self): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, 'logger') with mock.patch.object(dm_mailchimp_client._client.campaigns.content, 'update', autospec=True) as update: campaign_id = '1' html_content = {'html': '<p>One or two words</p>'} update.return_value = html_content with assert_external_service_log_entry(): res = dm_mailchimp_client.set_campaign_content(campaign_id, html_content) assert res == html_content dm_mailchimp_client._client.campaigns.content.update.assert_called_once_with(campaign_id, html_content)
def test_log_error_message_if_error_setting_campaign_content(self, exception, expected_error): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object(dm_mailchimp_client._client.campaigns.content, 'update', autospec=True) as update: update.side_effect = exception with assert_external_service_log_entry(successful_call=False, extra_modules=['mailchimp']) as log_catcher: res = dm_mailchimp_client.set_campaign_content('1', {"html": "some html"}) assert res is False assert log_catcher.records[1].msg == "Mailchimp failed to set content for campaign id '1'" assert log_catcher.records[1].error == expected_error
def test_log_error_message_if_error_creating_campaign(self, exception, expected_error): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, mock.MagicMock()) with mock.patch.object(dm_mailchimp_client._client.campaigns, 'create', autospec=True) as create: create.side_effect = exception with mock.patch.object(dm_mailchimp_client.logger, 'error', autospec=True) as error: with assert_external_service_log_entry(successful_call=False): res = dm_mailchimp_client.create_campaign({"example": "data", 'settings': {'title': 'Foo'}}) assert res is False error.assert_called_once_with( "Mailchimp failed to create campaign for 'campaign title'", extra=expected_error )
def test_default_timeout_retry_performs_no_retries(): dm_mailchimp_client = DMMailChimpClient('username', 'api key', mock.MagicMock()) with mock.patch.object(dm_mailchimp_client._client.lists.members, 'all', autospec=True) as all_members: all_members.side_effect = HTTPError(response=mock.Mock( status_code=504)) with pytest.raises(HTTPError): dm_mailchimp_client.get_email_addresses_from_list('a_list_id') assert all_members.call_args_list == [ mock.call('a_list_id', count=1000, offset=0), ]
def test_subscribe_new_emails_to_list_tries_all_emails_returns_false_on_error(self): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, mock.MagicMock()) with mock.patch.object( dm_mailchimp_client, 'subscribe_new_email_to_list', autospec=True) as subscribe_new_email_to_list: subscribe_new_email_to_list.side_effect = [False, True] with assert_external_service_log_entry(count=2): res = dm_mailchimp_client.subscribe_new_emails_to_list('list_id', ['foo', '*****@*****.**']) calls = [mock.call('list_id', 'foo'), mock.call('list_id', '*****@*****.**')] assert res is False subscribe_new_email_to_list.assert_has_calls(calls)
def test_log_error_message_if_error_sending_campaign(logger): dm_mailchimp_client = DMMailChimpClient('username', 'api key', logger) with mock.patch.object(dm_mailchimp_client._client.campaigns.actions, 'send', autospec=True) as send: send.side_effect = RequestException("error sending") res = dm_mailchimp_client.send_campaign("1") assert res is False logger.error.assert_called_once_with( "Mailchimp failed to send campaign id '1'", extra={"error": "error sending"})
def test_log_error_message_if_error_sending_campaign(self, exception, expected_error): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object(dm_mailchimp_client._client.campaigns.actions, 'send', autospec=True) as send: send.side_effect = exception with assert_external_service_log_entry(successful_call=False, extra_modules=['mailchimp']) as log_catcher: res = dm_mailchimp_client.send_campaign("1") assert res is False assert log_catcher.records[1].msg == "Mailchimp failed to send campaign id '1'" assert log_catcher.records[1].levelname == 'ERROR' assert log_catcher.records[1].error == expected_error
def test_subscribe_new_emails_to_list(self): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, mock.MagicMock()) with mock.patch.object(dm_mailchimp_client, 'subscribe_new_email_to_list', autospec=True, return_value=True): with assert_external_service_log_entry(count=2): res = dm_mailchimp_client.subscribe_new_emails_to_list( 'list_id', ['*****@*****.**', '*****@*****.**'] ) assert res is True assert dm_mailchimp_client.subscribe_new_email_to_list.call_args_list == [ mock.call('list_id', '*****@*****.**'), mock.call('list_id', '*****@*****.**') ]
def test_set_campaign_content(): dm_mailchimp_client = DMMailChimpClient('username', 'api key', 'logger') with mock.patch.object(dm_mailchimp_client._client.campaigns.content, 'update', autospec=True) as update: campaign_id = '1' html_content = {'html': '<p>One or two words</p>'} update.return_value = html_content res = dm_mailchimp_client.set_campaign_content(campaign_id, html_content) assert res == html_content dm_mailchimp_client._client.campaigns.content.update.assert_called_once_with( campaign_id, html_content)
def test_log_error_message_if_error_setting_campaign_content(logger): dm_mailchimp_client = DMMailChimpClient('username', 'api key', logger) with mock.patch.object(dm_mailchimp_client._client.campaigns.content, 'update', autospec=True) as update: update.side_effect = RequestException("error message") res = dm_mailchimp_client.set_campaign_content('1', {"html": "some html"}) assert res is False logger.error.assert_called_once_with( "Mailchimp failed to set content for campaign id '1'", extra={"error": "error message"})
def test_log_mailchimp_error_unexpected_error_payload_if_error_subscribing_email_to_list(self, get_email_hash): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object( dm_mailchimp_client._client.lists.members, 'create_or_update', autospec=True) as create_or_update: create_or_update.side_effect = MailChimpError({'request': 'failed', 'status': 500}) with assert_external_service_log_entry(successful_call=False, extra_modules=['mailchimp']) as log_catcher: res = dm_mailchimp_client.subscribe_new_email_to_list('list_id', '*****@*****.**') assert res == {"status": "error", "error_type": "unexpected_error", "status_code": 500} assert log_catcher.records[1].msg == "Mailchimp failed to add user (foo) to list (list_id)" assert log_catcher.records[1].error == "{'request': 'failed', 'status': 500}" assert log_catcher.records[1].levelname == 'ERROR'
def test_handles_responses_with_invalid_json(self, get_email_hash): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object( dm_mailchimp_client._client.lists.members, 'create_or_update', autospec=True) as create_or_update: response = mock.Mock() response.json.side_effect = JSONDecodeError('msg', 'doc', 0) create_or_update.side_effect = RequestException("error sending", response=response) with assert_external_service_log_entry(successful_call=False, extra_modules=['mailchimp']) as log_catcher: res = dm_mailchimp_client.subscribe_new_email_to_list('list_id', '*****@*****.**') assert res == {'error_type': 'unexpected_error', 'status': 'error', 'status_code': 500} assert log_catcher.records[1].msg == 'Mailchimp failed to add user (foo) to list (list_id)' assert log_catcher.records[1].error == "error sending" assert log_catcher.records[1].levelname == 'ERROR'
def test_subscribe_new_email_to_list(get_email_hash): dm_mailchimp_client = DMMailChimpClient('username', 'api key', mock.MagicMock()) with mock.patch.object(dm_mailchimp_client._client.lists.members, 'create_or_update', autospec=True) as create_or_update: create_or_update.return_value = {"response": "data"} res = dm_mailchimp_client.subscribe_new_email_to_list( 'list_id', '*****@*****.**') assert res == {"response": "data"} create_or_update.assert_called_once_with('list_id', "foo", { "email_address": "*****@*****.**", "status_if_new": "subscribed" })
def test_subscribe_new_emails_to_list(): dm_mailchimp_client = DMMailChimpClient('username', 'api key', mock.MagicMock()) with mock.patch.object(dm_mailchimp_client, 'subscribe_new_email_to_list', autospec=True): dm_mailchimp_client.subscribe_new_email_to_list.return_value = True res = dm_mailchimp_client.subscribe_new_emails_to_list( 'list_id', ['*****@*****.**', '*****@*****.**']) calls = [ mock.call('list_id', '*****@*****.**'), mock.call('list_id', '*****@*****.**') ] assert res is True dm_mailchimp_client.subscribe_new_email_to_list.assert_has_calls(calls)
def test_log_request_exception_error_message_if_error_subscribing_email_to_list(self, get_email_hash): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object( dm_mailchimp_client._client.lists.members, 'create_or_update', autospec=True) as create_or_update: # The 400 response from MailChimp is actually falsey response = mock.MagicMock(__bool__=False) response.json.return_value = {"detail": "Unexpected error."} create_or_update.side_effect = RequestException("error sending", response=response) with assert_external_service_log_entry(successful_call=False, extra_modules=['mailchimp']) as log_catcher: res = dm_mailchimp_client.subscribe_new_email_to_list('list_id', '*****@*****.**') assert res == {"status": "error", "error_type": "unexpected_error", "status_code": 500} assert log_catcher.records[1].msg == "Mailchimp failed to add user (foo) to list (list_id)" assert log_catcher.records[1].error == "error sending" assert log_catcher.records[1].levelname == 'ERROR'
def test_subscribe_new_emails_to_list_tries_all_emails_returns_false_on_error( ): dm_mailchimp_client = DMMailChimpClient('username', 'api key', mock.MagicMock()) with mock.patch.object(dm_mailchimp_client, 'subscribe_new_email_to_list', autospec=True) as subscribe_new_email_to_list: subscribe_new_email_to_list.side_effect = [False, True] res = dm_mailchimp_client.subscribe_new_emails_to_list( 'list_id', ['foo', '*****@*****.**']) calls = [ mock.call('list_id', 'foo'), mock.call('list_id', '*****@*****.**') ] assert res is False subscribe_new_email_to_list.assert_has_calls(calls)
def test_create_or_update_returns_error_payload_for_expected_request_exception(self, get_email_hash): dm_mailchimp_client = DMMailChimpClient('username', DUMMY_MAILCHIMP_API_KEY, logging.getLogger('mailchimp')) with mock.patch.object( dm_mailchimp_client._client.lists.members, 'create_or_update', autospec=True) as create_or_update: response = mock.MagicMock(__bool__=False) response.json.return_value = {"detail": "foo looks fake or invalid, please enter a real email address."} create_or_update.side_effect = RequestException("error sending", response=response) with assert_external_service_log_entry(successful_call=False, extra_modules=['mailchimp']) as log_catcher: res = dm_mailchimp_client.subscribe_new_email_to_list('list_id', '*****@*****.**') assert res == {"status": "error", "error_type": "invalid_email", "status_code": 400} assert log_catcher.records[1].msg == ( "Expected error: Mailchimp failed to add user (foo) to list (list_id). " "API error: The email address looks fake or invalid, please enter a real email address." ) assert log_catcher.records[1].error == "error sending" assert log_catcher.records[1].levelname == 'WARNING'