2016-04-06 14:31:33 +01:00
import uuid
2020-03-17 15:15:43 +00:00
from datetime import datetime
2016-04-06 14:31:33 +01:00
2018-02-07 17:49:15 +00:00
import pytest
2020-03-17 15:15:43 +00:00
from freezegun import freeze_time
2018-02-07 17:49:15 +00:00
2020-03-17 15:15:43 +00:00
from app import statsd_client
2021-03-10 13:55:06 +00:00
from app . celery . process_sms_client_response_tasks import (
process_sms_client_response ,
)
from app . celery . service_callback_tasks import (
create_delivery_status_callback_data ,
)
2018-02-07 17:49:15 +00:00
from app . clients import ClientException
2020-03-17 15:15:43 +00:00
from app . models import NOTIFICATION_TECHNICAL_FAILURE
2017-12-04 14:48:23 +00:00
from tests . app . db import create_service_callback_api
2016-04-06 14:31:33 +01:00
2020-03-17 15:15:43 +00:00
def test_process_sms_client_response_raises_error_if_reference_is_not_a_valid_uuid ( client ) :
with pytest . raises ( ValueError ) :
process_sms_client_response (
status = ' 000 ' , provider_reference = ' something-bad ' , client_name = ' sms-client ' )
2016-04-06 14:31:33 +01:00
2020-03-17 15:15:43 +00:00
@pytest.mark.parametrize ( ' client_name ' , ( ' Firetext ' , ' MMG ' ) )
def test_process_sms_response_raises_client_exception_for_unknown_status (
sample_notification ,
mocker ,
client_name ,
) :
with pytest . raises ( ClientException ) as e :
process_sms_client_response (
status = ' 000 ' ,
provider_reference = str ( sample_notification . id ) ,
client_name = client_name ,
)
assert f " { client_name } callback failed: status { ' 000 ' } not found. " in str ( e . value )
assert sample_notification . status == NOTIFICATION_TECHNICAL_FAILURE
2020-06-01 11:45:35 +01:00
@pytest.mark.parametrize ( ' status, detailed_status_code, sms_provider, expected_notification_status, reason ' , [
2020-05-27 18:03:55 +01:00
( ' 0 ' , None , ' Firetext ' , ' delivered ' , None ) ,
( ' 1 ' , ' 101 ' , ' Firetext ' , ' permanent-failure ' , ' Unknown Subscriber ' ) ,
( ' 2 ' , ' 102 ' , ' Firetext ' , ' pending ' , ' Absent Subscriber ' ) ,
( ' 2 ' , ' 1 ' , ' MMG ' , ' permanent-failure ' , " Number does not exist " ) ,
( ' 3 ' , ' 2 ' , ' MMG ' , ' delivered ' , " Delivered to operator " ) ,
( ' 4 ' , ' 27 ' , ' MMG ' , ' temporary-failure ' , " Absent Subscriber " ) ,
( ' 5 ' , ' 13 ' , ' MMG ' , ' permanent-failure ' , " Sender id blacklisted " ) ,
2020-03-17 15:15:43 +00:00
] )
def test_process_sms_client_response_updates_notification_status (
sample_notification ,
mocker ,
status ,
2020-06-01 11:45:35 +01:00
detailed_status_code ,
2020-03-17 15:15:43 +00:00
sms_provider ,
expected_notification_status ,
2020-05-27 18:03:55 +01:00
reason
2020-03-17 15:15:43 +00:00
) :
2020-05-27 18:03:55 +01:00
mock_logger = mocker . patch ( ' app.celery.tasks.current_app.logger.info ' )
2020-03-17 15:15:43 +00:00
sample_notification . status = ' sending '
2020-06-01 11:45:35 +01:00
process_sms_client_response ( status , str ( sample_notification . id ) , sms_provider , detailed_status_code )
2020-05-27 18:03:55 +01:00
2020-06-01 18:02:01 +01:00
message = f " { sms_provider } callback returned status of { expected_notification_status } ( { status } ): { reason } ( { detailed_status_code } ) for reference: { sample_notification . id } " # noqa
2020-05-27 18:03:55 +01:00
mock_logger . assert_any_call ( message )
2020-03-17 15:15:43 +00:00
assert sample_notification . status == expected_notification_status
2020-06-01 11:45:35 +01:00
@pytest.mark.parametrize ( ' detailed_status_code, expected_notification_status, reason ' , [
2020-04-20 18:20:01 +01:00
( ' 101 ' , ' permanent-failure ' , ' Unknown Subscriber ' ) ,
( ' 102 ' , ' temporary-failure ' , ' Absent Subscriber ' ) ,
( None , ' temporary-failure ' , None ) ,
2021-03-10 10:39:17 +00:00
( ' 000 ' , ' temporary-failure ' , ' No error reported ' )
2020-04-14 15:55:17 +01:00
] )
def test_process_sms_client_response_updates_notification_status_when_called_second_time (
sample_notification ,
mocker ,
2020-06-01 11:45:35 +01:00
detailed_status_code ,
2020-04-14 15:55:17 +01:00
expected_notification_status ,
2020-04-20 18:20:01 +01:00
reason
2020-04-14 15:55:17 +01:00
) :
2020-04-20 18:20:01 +01:00
mock_logger = mocker . patch ( ' app.celery.tasks.current_app.logger.info ' )
2020-04-14 15:55:17 +01:00
sample_notification . status = ' sending '
process_sms_client_response ( ' 2 ' , str ( sample_notification . id ) , ' Firetext ' )
2020-04-20 18:20:01 +01:00
2020-06-01 11:45:35 +01:00
process_sms_client_response ( ' 1 ' , str ( sample_notification . id ) , ' Firetext ' , detailed_status_code )
2020-04-14 15:55:17 +01:00
2021-03-10 10:39:17 +00:00
if detailed_status_code :
2020-04-20 18:20:01 +01:00
message = f ' Updating notification id { sample_notification . id } to status { expected_notification_status } , reason: { reason } ' # noqa
mock_logger . assert_called_with ( message )
2020-04-14 15:55:17 +01:00
assert sample_notification . status == expected_notification_status
2020-06-01 11:45:35 +01:00
@pytest.mark.parametrize ( ' detailed_status_code ' , [ ' 102 ' , None , ' 000 ' ] )
def test_process_sms_client_response_updates_notification_status_to_pending_with_and_without_failure_code_present (
2020-04-21 13:30:42 +01:00
sample_notification ,
mocker ,
2020-06-01 11:45:35 +01:00
detailed_status_code
2020-04-21 13:30:42 +01:00
) :
sample_notification . status = ' sending '
2020-06-01 11:45:35 +01:00
process_sms_client_response ( ' 2 ' , str ( sample_notification . id ) , ' Firetext ' , detailed_status_code )
2020-04-21 13:30:42 +01:00
assert sample_notification . status == ' pending '
2020-06-01 11:45:35 +01:00
def test_process_sms_client_response_updates_notification_status_when_detailed_status_code_not_recognised (
2020-04-20 18:20:01 +01:00
sample_notification ,
mocker ,
) :
2020-04-21 12:06:28 +01:00
mock_logger = mocker . patch ( ' app.celery.tasks.current_app.logger.warning ' )
2020-04-20 18:20:01 +01:00
sample_notification . status = ' sending '
process_sms_client_response ( ' 2 ' , str ( sample_notification . id ) , ' Firetext ' )
process_sms_client_response ( ' 1 ' , str ( sample_notification . id ) , ' Firetext ' , ' 789 ' )
mock_logger . assert_called_once_with ( ' Failure code 789 from Firetext not recognised ' )
assert sample_notification . status == ' temporary-failure '
2020-03-17 15:15:43 +00:00
def test_sms_response_does_not_send_callback_if_notification_is_not_in_the_db ( sample_service , mocker ) :
2017-12-05 13:57:46 +00:00
mocker . patch (
2020-03-17 15:15:43 +00:00
' app.celery.process_sms_client_response_tasks.get_service_delivery_status_callback_api_for_service ' ,
return_value = ' mock-delivery-callback-for-service ' )
2017-12-05 13:57:46 +00:00
send_mock = mocker . patch (
' app.celery.service_callback_tasks.send_delivery_status_to_service.apply_async '
)
reference = str ( uuid . uuid4 ( ) )
2018-03-21 18:11:10 +00:00
process_sms_client_response ( status = ' 3 ' , provider_reference = reference , client_name = ' MMG ' )
2017-12-05 13:57:46 +00:00
send_mock . assert_not_called ( )
2020-03-17 15:15:43 +00:00
@freeze_time ( ' 2001-01-01T12:00:00 ' )
def test_process_sms_client_response_records_statsd_metrics ( sample_notification , client , mocker ) :
mocker . patch ( ' app.statsd_client.incr ' )
mocker . patch ( ' app.statsd_client.timing_with_dates ' )
2016-04-06 14:31:33 +01:00
2020-03-17 15:15:43 +00:00
sample_notification . status = ' sending '
sample_notification . sent_at = datetime . utcnow ( )
2018-10-24 11:24:53 +01:00
2020-03-17 15:15:43 +00:00
process_sms_client_response ( ' 0 ' , str ( sample_notification . id ) , ' Firetext ' )
2018-10-24 11:24:53 +01:00
2020-03-17 15:15:43 +00:00
statsd_client . incr . assert_any_call ( " callback.firetext.delivered " )
statsd_client . timing_with_dates . assert_any_call (
" callback.firetext.elapsed-time " , datetime . utcnow ( ) , sample_notification . sent_at
)
2018-03-21 18:11:10 +00:00
2019-06-28 12:19:21 +01:00
def test_process_sms_updates_billable_units_if_zero ( sample_notification ) :
sample_notification . billable_units = 0
2020-03-17 15:15:43 +00:00
process_sms_client_response ( ' 3 ' , str ( sample_notification . id ) , ' MMG ' )
2019-06-28 12:19:21 +01:00
assert sample_notification . billable_units == 1
2020-03-17 15:15:43 +00:00
def test_process_sms_response_does_not_send_service_callback_for_pending_notifications ( sample_notification , mocker ) :
mocker . patch (
' app.celery.process_sms_client_response_tasks.get_service_delivery_status_callback_api_for_service ' ,
return_value = ' fake-callback ' )
send_mock = mocker . patch ( ' app.celery.service_callback_tasks.send_delivery_status_to_service.apply_async ' )
process_sms_client_response ( ' 2 ' , str ( sample_notification . id ) , ' Firetext ' )
send_mock . assert_not_called ( )
2016-04-06 14:31:33 +01:00
2020-03-17 15:15:43 +00:00
def test_outcome_statistics_called_for_successful_callback ( sample_notification , mocker ) :
send_mock = mocker . patch (
' app.celery.service_callback_tasks.send_delivery_status_to_service.apply_async '
)
callback_api = create_service_callback_api ( service = sample_notification . service , url = " https://original_url.com " )
reference = str ( sample_notification . id )
2016-04-06 14:31:33 +01:00
2020-03-17 15:15:43 +00:00
process_sms_client_response ( ' 3 ' , reference , ' MMG ' )
2017-05-09 22:03:57 +01:00
2020-03-17 15:15:43 +00:00
encrypted_data = create_delivery_status_callback_data ( sample_notification , callback_api )
send_mock . assert_called_once_with ( [ reference , encrypted_data ] ,
queue = " service-callbacks " )
2017-05-09 22:03:57 +01:00
2016-04-06 14:31:33 +01:00
2020-03-17 15:15:43 +00:00
def test_process_sms_updates_sent_by_with_client_name_if_not_in_noti ( sample_notification ) :
sample_notification . sent_by = None
process_sms_client_response ( ' 3 ' , str ( sample_notification . id ) , ' MMG ' )
2016-04-06 14:31:33 +01:00
2020-03-17 15:15:43 +00:00
assert sample_notification . sent_by == ' mmg '