diff --git a/app/__init__.py b/app/__init__.py index ada26eee3..748b6fdb1 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -80,7 +80,8 @@ def init_app(app): no_auth_req = [ url_for('status.show_status'), url_for('notifications.process_ses_response'), - url_for('notifications.process_firetext_response') + url_for('notifications.process_firetext_response'), + url_for('notifications.process_mmg_response') ] if request.path not in no_auth_req: from app.authentication import auth diff --git a/app/celery/tasks.py b/app/celery/tasks.py index c3e52e669..220e10ef2 100644 --- a/app/celery/tasks.py +++ b/app/celery/tasks.py @@ -313,9 +313,6 @@ def send_email(service_id, notification_id, subject, from_address, encrypted_not @notify_celery.task(name='send-sms-code') def send_sms_code(encrypted_verification): verification_message = encryption.decrypt(encrypted_verification) - # send_sms_via_firetext(validate_and_format_phone_number(verification_message['to']), - # verification_message['secret_code'], - # 'send-sms-code') try: mmg_client.send_sms(validate_and_format_phone_number(verification_message['to']), verification_message['secret_code'], diff --git a/app/clients/sms/firetext.py b/app/clients/sms/firetext.py index 87e0119a6..f0cb10c6f 100644 --- a/app/clients/sms/firetext.py +++ b/app/clients/sms/firetext.py @@ -6,34 +6,34 @@ from app.clients.sms import ( ) from flask import current_app from requests import request, RequestException, HTTPError -from app.clients import ClientResponse, STATISTICS_DELIVERED, STATISTICS_FAILURE +from app.clients import STATISTICS_DELIVERED, STATISTICS_FAILURE logger = logging.getLogger(__name__) +firetext_responses = { + '0': { + "message": 'Delivered', + "notification_statistics_status": STATISTICS_DELIVERED, + "success": True, + "notification_status": 'delivered' + }, + '1': { + "message": 'Declined', + "success": False, + "notification_statistics_status": STATISTICS_FAILURE, + "notification_status": 'failed' + }, + '2': { + "message": 'Undelivered (Pending with Network)', + "success": False, + "notification_statistics_status": None, + "notification_status": 'sent' + } +} -class FiretextResponses(ClientResponse): - def __init__(self): - ClientResponse.__init__(self) - self.__response_model__ = { - '0': { - "message": 'Delivered', - "notification_statistics_status": STATISTICS_DELIVERED, - "success": True, - "notification_status": 'delivered' - }, - '1': { - "message": 'Declined', - "success": False, - "notification_statistics_status": STATISTICS_FAILURE, - "notification_status": 'failed' - }, - '2': { - "message": 'Undelivered (Pending with Network)', - "success": False, - "notification_statistics_status": None, - "notification_status": 'sent' - } - } + +def get_firetext_responses(status): + return firetext_responses[status] class FiretextClientException(SmsClientException): diff --git a/app/clients/sms/mmg.py b/app/clients/sms/mmg.py index 0f6a9f031..14e2ef526 100644 --- a/app/clients/sms/mmg.py +++ b/app/clients/sms/mmg.py @@ -1,33 +1,27 @@ from flask import current_app from monotonic import monotonic from requests import (request, RequestException, HTTPError) -from app.clients import (ClientResponse, STATISTICS_DELIVERED, STATISTICS_FAILURE) +from app.clients import (STATISTICS_DELIVERED, STATISTICS_FAILURE) from app.clients.sms import (SmsClient, SmsClientException) +mmg_response_map = { + '00': { + "message": 'Delivered', + "notification_statistics_status": STATISTICS_DELIVERED, + "success": True, + "notification_status": 'delivered' + }, + 'default': { + "message": 'Declined', + "success": False, + "notification_statistics_status": STATISTICS_FAILURE, + "notification_status": 'failed' + } +} -class FiretextResponses(ClientResponse): - def __init__(self): - ClientResponse.__init__(self) - self.__response_model__ = { - '0': { - "message": 'Delivered', - "notification_statistics_status": STATISTICS_DELIVERED, - "success": True, - "notification_status": 'delivered' - }, - '1': { - "message": 'Declined', - "success": False, - "notification_statistics_status": STATISTICS_FAILURE, - "notification_status": 'failed' - }, - '2': { - "message": 'Undelivered (Pending with Network)', - "success": False, - "notification_statistics_status": None, - "notification_status": 'sent' - } - } + +def get_mmg_responses(status): + return mmg_response_map.get(status, mmg_response_map.get('default')) class MMGClientException(SmsClientException): diff --git a/app/notifications/process_client_response.py b/app/notifications/process_client_response.py new file mode 100644 index 000000000..a9afe00b7 --- /dev/null +++ b/app/notifications/process_client_response.py @@ -0,0 +1,71 @@ +import uuid +from flask import current_app +from app.dao import notifications_dao +from app.clients.sms.firetext import get_firetext_responses +from app.clients.sms.mmg import get_mmg_responses + +sms_response_mapper = {'MMG': get_mmg_responses, + 'Firetext': get_firetext_responses + } + + +def validate_callback_data(data, fields, client_name): + errors = [] + for f in fields: + if len(data.get(f, '')) <= 0: + error = "{} callback failed: {} missing".format(client_name, f) + errors.append(error) + return errors if len(errors) > 0 else None + + +def process_sms_client_response(status, reference, client_name): + success = None + errors = None + # validate reference + if reference == 'send-sms-code': + success = "{} callback succeeded: send-sms-code".format(client_name) + return success, errors + + try: + uuid.UUID(reference, version=4) + except ValueError: + message = "{} callback with invalid reference {}".format(client_name, reference) + return success, message + + try: + response_parser = sms_response_mapper[client_name] + except KeyError: + return success, 'unknown sms client: {}'.format(client_name) + + # validate status + try: + response_dict = response_parser(status) + current_app.logger.info('{} callback return status of {} for reference: {}'.format(client_name, + status, reference)) + except KeyError: + msg = "{} callback failed: status {} not found.".format(client_name, status) + return success, msg + + notification_status = response_dict['notification_status'] + notification_statistics_status = response_dict['notification_statistics_status'] + notification_status_message = response_dict['message'] + notification_success = response_dict['success'] + + # record stats + update_success = notifications_dao.update_notification_status_by_id(reference, + notification_status, + notification_statistics_status) + if update_success == 0: + status_error = "{} callback failed: notification {} not found. Status {}".format(client_name, + reference, + notification_status_message) + return success, status_error + + if not notification_success: + current_app.logger.info( + "{} delivery failed: notification {} has error found. Status {}".format(client_name, + reference, + notification_status_message)) + + success = "{} callback succeeded. reference {} updated".format(client_name, reference) + return success, errors diff --git a/app/notifications/rest.py b/app/notifications/rest.py index b3bbd3205..60b25a497 100644 --- a/app/notifications/rest.py +++ b/app/notifications/rest.py @@ -1,5 +1,4 @@ from datetime import datetime -import uuid from flask import ( Blueprint, @@ -11,7 +10,6 @@ from flask import ( ) from utils.template import Template -from app.clients.sms.firetext import FiretextResponses from app.clients.email.aws_ses import AwsSesResponses from app import api_user, encryption, create_uuid, DATETIME_FORMAT, DATE_FORMAT from app.authentication.auth import require_admin @@ -20,7 +18,10 @@ from app.dao import ( services_dao, notifications_dao ) - +from app.notifications.process_client_response import ( + validate_callback_data, + process_sms_client_response +) from app.schemas import ( email_notification_schema, sms_template_notification_schema, @@ -35,9 +36,7 @@ notifications = Blueprint('notifications', __name__) from app.errors import register_errors register_errors(notifications) - aws_response = AwsSesResponses() -firetext_response = FiretextResponses() @notifications.route('/notifications/email/ses', methods=['POST']) @@ -145,90 +144,43 @@ def is_not_a_notification(source): @notifications.route('/notifications/sms/mmg', methods=['POST']) def process_mmg_response(): - current_app.logger.info('MMG client callback form{}'.format(request.form)) - status, error1 = _get_from_response(form=request.form, field='status', client_name='MMG') - reference, error2 = _get_from_response(form=request.form, field='reference', client_name='MMG') - errors = [error1, error2] - errors.remove(None) - if len(errors) > 0: + client_name = 'MMG' + data = json.loads(request.data) + validation_errors = validate_callback_data(data=data, + fields=['status', 'CID'], + client_name=client_name) + if validation_errors: + [current_app.logger.info(e) for e in validation_errors] + return jsonify(result='error', message=validation_errors), 400 + + success, errors = process_sms_client_response(status=data.get('status'), + reference=data.get('CID'), + client_name='MMG') + if errors: + [current_app.logger.info(e) for e in errors] return jsonify(result='error', message=errors), 400 - if reference == 'send-sms-code': - return jsonify(result="success", message="MMG callback succeeded: send-sms-code"), 200 - - -def _get_from_response(form, field, client_name): - error = None - form_field = None - if len(form.get(field, '')) <= 0: - current_app.logger.info( - "{} callback failed: {} missing".format(client_name, field) - ) - error = "{} callback failed: {} missing".format(client_name, field) else: - form_field = form[field] - return form_field, error + return jsonify(result='success', message=success), 200 @notifications.route('/notifications/sms/firetext', methods=['POST']) def process_firetext_response(): - status, error1 = _get_from_response(form=request.form, field='status', client_name='Firetext') - reference, error2 = _get_from_response(form=request.form, field='reference', client_name='Firetext') - errors = [error1, error2] - errors = errors.remove(None) + client_name = 'Firetext' + validation_errors = validate_callback_data(data=request.form, + fields=['status', 'reference'], + client_name=client_name) + if validation_errors: + current_app.logger.info(validation_errors) + return jsonify(result='error', message=validation_errors), 400 - if len(errors) > 0: + success, errors = process_sms_client_response(status=request.form.get('status'), + reference=request.form.get('reference'), + client_name=client_name) + if errors: + [current_app.logger.info(e) for e in errors] return jsonify(result='error', message=errors), 400 - - if reference == 'send-sms-code': - return jsonify(result="success", message="Firetext callback succeeded: send-sms-code"), 200 - - try: - uuid.UUID(reference, version=4) - except ValueError: - current_app.logger.info( - "Firetext callback with invalid reference {}".format(reference) - ) - return jsonify( - result="error", message="Firetext callback with invalid reference {}".format(reference) - ), 400 - - try: - firetext_response.response_code_to_object(status) - except KeyError: - current_app.logger.info( - "Firetext callback failed: status {} not found.".format(status) - ) - return jsonify(result="error", message="Firetext callback failed: status {} not found.".format(status)), 400 - - notification_status = firetext_response.response_code_to_notification_status(status) - notification_statistics_status = firetext_response.response_code_to_notification_statistics_status(status) - - if notifications_dao.update_notification_status_by_id( - reference, - notification_status, - notification_statistics_status - ) == 0: - current_app.logger.info( - "Firetext callback failed: notification {} not found. Status {}".format(reference, status) - ) - return jsonify( - result="error", - message="Firetext callback failed: notification {} not found. Status {}".format( - reference, - firetext_response.response_code_to_message(status) - ) - ), 404 - - if not firetext_response.response_code_to_notification_success(status): - current_app.logger.info( - "Firetext delivery failed: notification {} has error found. Status {}".format( - reference, - FiretextResponses().response_code_to_message(status) - ) - ) - return jsonify( - result="success", message="Firetext callback succeeded. reference {} updated".format(reference) - ), 200 + else: + return jsonify(result='success', message=success), 200 @notifications.route('/notifications/', methods=['GET']) diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index a8b6890cd..0a3260592 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -46,6 +46,8 @@ class AnyStringWith(str): def firetext_error(): return {'code': 0, 'description': 'error'} +mmg_error = {'Error': '40', 'Description': 'error'} + def test_should_call_delete_successful_notifications_in_task(notify_api, mocker): mocker.patch('app.celery.tasks.delete_successful_notifications_created_more_than_a_day_ago') @@ -667,7 +669,7 @@ def test_should_throw_mmg_client_exception(mocker): 'secret_code': '12345'} encrypted_notification = encryption.encrypt(notification) - mocker.patch('app.mmg_client.send_sms', side_effect=MMGClientException(firetext_error())) + mocker.patch('app.mmg_client.send_sms', side_effect=MMGClientException(mmg_error)) send_sms_code(encrypted_notification) mmg_client.send_sms.assert_called_once_with(format_phone_number(validate_phone_number(notification['to'])), notification['secret_code'], diff --git a/tests/app/clients/test_firetext.py b/tests/app/clients/test_firetext.py index b68837a44..ba35762fc 100644 --- a/tests/app/clients/test_firetext.py +++ b/tests/app/clients/test_firetext.py @@ -1,32 +1,33 @@ import pytest -from app.clients.sms import firetext - -responses = firetext.FiretextResponses() +from app.clients.sms.firetext import get_firetext_responses def test_should_return_correct_details_for_delivery(): - assert responses.response_code_to_message('0') == 'Delivered' - assert responses.response_code_to_notification_status('0') == 'delivered' - assert responses.response_code_to_notification_statistics_status('0') == 'delivered' - assert responses.response_code_to_notification_success('0') + response_dict = get_firetext_responses('0') + assert response_dict['message'] == 'Delivered' + assert response_dict['notification_status'] == 'delivered' + assert response_dict['notification_statistics_status'] == 'delivered' + assert response_dict['success'] def test_should_return_correct_details_for_bounced(): - assert responses.response_code_to_message('1') == 'Declined' - assert responses.response_code_to_notification_status('1') == 'failed' - assert responses.response_code_to_notification_statistics_status('1') == 'failure' - assert not responses.response_code_to_notification_success('1') + response_dict = get_firetext_responses('1') + assert response_dict['message'] == 'Declined' + assert response_dict['notification_status'] == 'failed' + assert response_dict['notification_statistics_status'] == 'failure' + assert not response_dict['success'] def test_should_return_correct_details_for_complaint(): - assert responses.response_code_to_message('2') == 'Undelivered (Pending with Network)' - assert responses.response_code_to_notification_status('2') == 'sent' - assert not responses.response_code_to_notification_statistics_status('2') - assert not responses.response_code_to_notification_success('2') + response_dict = get_firetext_responses('2') + assert response_dict['message'] == 'Undelivered (Pending with Network)' + assert response_dict['notification_status'] == 'sent' + assert not response_dict['notification_statistics_status'] + assert not response_dict['success'] def test_should_be_none_if_unrecognised_status_code(): with pytest.raises(KeyError) as e: - responses.response_code_to_object('99') + get_firetext_responses('99') assert '99' in str(e.value) diff --git a/tests/app/clients/test_mmg.py b/tests/app/clients/test_mmg.py new file mode 100644 index 000000000..48ed402ef --- /dev/null +++ b/tests/app/clients/test_mmg.py @@ -0,0 +1,25 @@ +from app.clients.sms.mmg import get_mmg_responses + + +def test_should_return_correct_details_for_delivery(): + response_dict = get_mmg_responses('00') + assert response_dict['message'] == 'Delivered' + assert response_dict['notification_status'] == 'delivered' + assert response_dict['notification_statistics_status'] == 'delivered' + assert response_dict['success'] + + +def test_should_return_correct_details_for_bounced(): + response_dict = get_mmg_responses('50') + assert response_dict['message'] == 'Declined' + assert response_dict['notification_status'] == 'failed' + assert response_dict['notification_statistics_status'] == 'failure' + assert not response_dict['success'] + + +def test_should_be_none_if_unrecognised_status_code(): + response_dict = get_mmg_responses('blah') + assert response_dict['message'] == 'Declined' + assert response_dict['notification_status'] == 'failed' + assert response_dict['notification_statistics_status'] == 'failure' + assert not response_dict['success'] diff --git a/tests/app/notifications/test_process_client_response.py b/tests/app/notifications/test_process_client_response.py new file mode 100644 index 000000000..169161e6e --- /dev/null +++ b/tests/app/notifications/test_process_client_response.py @@ -0,0 +1,91 @@ +import uuid + +from app.models import NotificationStatistics +from app.notifications.process_client_response import ( + validate_callback_data, + process_sms_client_response +) + + +def test_validate_callback_data_returns_none_when_valid(): + form = {'status': 'good', + 'reference': 'send-sms-code'} + fields = ['status', 'reference'] + client_name = 'sms client' + + assert validate_callback_data(form, fields, client_name) is None + + +def test_validate_callback_data_return_errors_when_fields_are_empty(): + form = {'monkey': 'good'} + fields = ['status', 'cid'] + client_name = 'sms client' + + errors = validate_callback_data(form, fields, client_name) + assert len(errors) == 2 + assert "{} callback failed: {} missing".format(client_name, 'status') in errors + assert "{} callback failed: {} missing".format(client_name, 'cid') in errors + + +def test_process_sms_response_return_success_for_send_sms_code_reference(): + success, error = process_sms_client_response(status='000', reference='send-sms-code', client_name='sms-client') + assert success == "{} callback succeeded: send-sms-code".format('sms-client') + assert error is None + + +def test_process_sms_response_returns_error_bad_reference(): + success, error = process_sms_client_response(status='000', reference='something-bad', client_name='sms-client') + assert success is None + assert error == "{} callback with invalid reference {}".format('sms-client', 'something-bad') + + +def test_process_sms_response_returns_error_for_unknown_sms_client(): + success, error = process_sms_client_response(status='000', reference=str(uuid.uuid4()), client_name='sms-client') + assert success is None + assert error == 'unknown sms client: {}'.format('sms-client') + + +def test_process_sms_response_returns_error_for_unknown_status(): + success, error = process_sms_client_response(status='000', reference=str(uuid.uuid4()), client_name='Firetext') + assert success is None + assert error == "{} callback failed: status {} not found.".format('Firetext', '000') + + +def test_process_sms_response_updates_notification_stats_for_valid_request(notify_db, + notify_db_session, + sample_notification): + stats = NotificationStatistics.query.all() + assert len(stats) == 1 + assert stats[0].sms_requested == 1 + assert stats[0].sms_delivered == 0 + assert stats[0].sms_error == 0 + success, error = process_sms_client_response(status='0', reference=str(sample_notification.id), + client_name='Firetext') + assert error is None + assert success == "{} callback succeeded. reference {} updated".format('Firetext', sample_notification.id) + stats = NotificationStatistics.query.all() + assert len(stats) == 1 + assert stats[0].sms_requested == 1 + assert stats[0].sms_delivered == 1 + assert stats[0].sms_error == 0 + + +def test_process_sms_response_updates_notification_stats_for_valid_request_with_failed_status(notify_api, + notify_db, + notify_db_session, + sample_notification): + with notify_api.test_request_context(): + stats = NotificationStatistics.query.all() + assert len(stats) == 1 + assert stats[0].sms_requested == 1 + assert stats[0].sms_delivered == 0 + assert stats[0].sms_error == 0 + success, error = process_sms_client_response(status='1', reference=str(sample_notification.id), + client_name='Firetext') + assert success == "{} callback succeeded. reference {} updated".format('Firetext', sample_notification.id) + assert error is None + stats = NotificationStatistics.query.all() + assert len(stats) == 1 + assert stats[0].sms_requested == 1 + assert stats[0].sms_delivered == 0 + assert stats[0].sms_error == 1 diff --git a/tests/app/notifications/test_rest.py b/tests/app/notifications/test_rest.py index 10bb2daa7..4a07e0636 100644 --- a/tests/app/notifications/test_rest.py +++ b/tests/app/notifications/test_rest.py @@ -15,42 +15,6 @@ from app.dao.notifications_dao import get_notification_by_id, dao_get_notificati from freezegun import freeze_time -def test_get_status_name_from_response_return_status(): - from app.notifications.rest import _get_from_response - form = {'status': 'good'} - client_name = 'sms client' - status, error = _get_from_response(form, 'status', client_name) - expected = 'good' - assert expected == status - assert error is None - - -def test_get_status_name_returns_error(): - from app.notifications.rest import _get_from_response - form = {'status': '', - 'another': 'some'} - client_name = 'sms client' - errors = [] - status, error1 = _get_from_response(form, 'reference', client_name) - expected = "{} callback failed: reference missing".format(client_name) - assert expected == error1 - assert status is None - errors.append(error1) - status, error2 = _get_from_response(form, 'status', client_name) - errors.append(error2) - another, error3 = _get_from_response(form, 'another', client_name) - if error3: - errors.append(error3) - assert "sms client callback failed: status missing" == error2 - assert len(errors) == 2 - assert error1 in errors - assert error2 in errors - assert error3 not in errors - err = [error1, error2, error3] - err = filter(None, err) - assert len(list(err)) == 2 - - def test_get_notification_by_id(notify_api, sample_notification): with notify_api.test_request_context(): with notify_api.test_client() as client: @@ -1066,7 +1030,7 @@ def test_firetext_callback_should_return_400_if_empty_reference(notify_api): json_resp = json.loads(response.get_data(as_text=True)) assert response.status_code == 400 assert json_resp['result'] == 'error' - assert json_resp['message'] == 'Firetext callback failed: reference missing' + assert json_resp['message'] == ['Firetext callback failed: reference missing'] def test_firetext_callback_should_return_400_if_no_reference(notify_api): @@ -1080,7 +1044,7 @@ def test_firetext_callback_should_return_400_if_no_reference(notify_api): json_resp = json.loads(response.get_data(as_text=True)) assert response.status_code == 400 assert json_resp['result'] == 'error' - assert json_resp['message'] == 'Firetext callback failed: reference missing' + assert json_resp['message'] == ['Firetext callback failed: reference missing'] def test_firetext_callback_should_return_200_if_send_sms_reference(notify_api): @@ -1102,13 +1066,13 @@ def test_firetext_callback_should_return_400_if_no_status(notify_api): with notify_api.test_client() as client: response = client.post( path='/notifications/sms/firetext', - data='mobile=441234123123&time=2016-03-10 14:17:00', + data='mobile=441234123123&time=2016-03-10 14:17:00&reference=send-sms-code', headers=[('Content-Type', 'application/x-www-form-urlencoded')]) json_resp = json.loads(response.get_data(as_text=True)) assert response.status_code == 400 assert json_resp['result'] == 'error' - assert json_resp['message'] == 'Firetext callback failed: status missing' + assert json_resp['message'] == ['Firetext callback failed: status missing'] def test_firetext_callback_should_return_400_if_unknown_status(notify_api): @@ -1151,7 +1115,7 @@ def test_firetext_callback_should_return_404_if_cannot_find_notification_id(noti headers=[('Content-Type', 'application/x-www-form-urlencoded')]) json_resp = json.loads(response.get_data(as_text=True)) - assert response.status_code == 404 + assert response.status_code == 400 assert json_resp['result'] == 'error' assert json_resp['message'] == 'Firetext callback failed: notification {} not found. Status {}'.format( missing_notification_id, @@ -1272,6 +1236,60 @@ def test_firetext_callback_should_update_multiple_notification_status_sent(notif assert dao_get_notification_statistics_for_service(notification1.service_id)[0].sms_error == 0 +def test_process_mmg_response_return_200_when_cid_is_send_sms_code(notify_api): + with notify_api.test_request_context(): + data = json.dumps({"reference": "10100164", + "CID": "send-sms-code", + "MSISDN": "447775349060", + "status": "00", + "deliverytime": "2016-04-05 16:01:07"}) + + with notify_api.test_client() as client: + response = client.post(path='notifications/sms/mmg', + data=data, + headers=[('Content-Type', 'application/json')]) + assert response.status_code == 200 + json_data = json.loads(response.data) + assert json_data['result'] == 'success' + assert json_data['message'] == 'MMG callback succeeded: send-sms-code' + + +def test_process_mmg_response_returns_200_when_cid_is_valid_notification_id(notify_api, sample_notification): + with notify_api.test_client() as client: + data = json.dumps({"reference": "mmg_reference", + "CID": str(sample_notification.id), + "MSISDN": "447777349060", + "status": "00", + "deliverytime": "2016-04-05 16:01:07"}) + + response = client.post(path='notifications/sms/mmg', + data=data, + headers=[('Content-Type', 'application/json')]) + assert response.status_code == 200 + json_data = json.loads(response.data) + assert json_data['result'] == 'success' + assert json_data['message'] == 'MMG callback succeeded. reference {} updated'.format(sample_notification.id) + + +def test_process_mmg_response_returns_400_for_malformed_data(notify_api): + with notify_api.test_client() as client: + data = json.dumps({"reference": "mmg_reference", + "monkey": 'random thing', + "MSISDN": "447777349060", + "no_status": "00", + "deliverytime": "2016-04-05 16:01:07"}) + + response = client.post(path='notifications/sms/mmg', + data=data, + headers=[('Content-Type', 'application/json')]) + assert response.status_code == 400 + json_data = json.loads(response.data) + assert json_data['result'] == 'error' + assert len(json_data['message']) == 2 + assert "{} callback failed: {} missing".format('MMG', 'status') in json_data['message'] + assert "{} callback failed: {} missing".format('MMG', 'CID') in json_data['message'] + + def test_ses_callback_should_not_need_auth(notify_api): with notify_api.test_request_context(): with notify_api.test_client() as client: