2021-03-10 13:55:06 +00:00
from collections import namedtuple
2024-05-23 13:59:51 -07:00
from datetime import timedelta
2021-07-08 14:10:58 +01:00
from unittest import mock
2021-09-23 15:05:07 +01:00
from unittest . mock import ANY , call
2017-06-07 16:31:51 +01:00
2018-11-15 17:24:37 +00:00
import pytest
2017-06-07 16:31:51 +01:00
2016-06-20 13:33:53 +01:00
from app . celery import scheduled_tasks
2017-01-27 12:30:56 +00:00
from app . celery . scheduled_tasks import (
2021-03-10 13:55:06 +00:00
check_for_missing_rows_in_completed_jobs ,
check_for_services_with_high_failure_rates_or_sending_to_tv_numbers ,
2017-10-16 12:32:44 +01:00
check_job_status ,
2017-01-27 12:30:56 +00:00
delete_verify_codes ,
2023-11-10 16:14:03 -05:00
expire_or_delete_invitations ,
2019-06-11 13:16:34 +01:00
replay_created_notifications ,
2021-03-10 13:55:06 +00:00
run_scheduled_jobs ,
2017-11-09 14:13:42 +00:00
)
2023-03-02 20:20:31 -05:00
from app . config import QueueNames , Test
2016-08-24 17:03:56 +01:00
from app . dao . jobs_dao import dao_get_job_by_id
2024-02-09 12:24:09 -05:00
from app . enums import JobStatus , NotificationStatus , TemplateType
2024-05-23 13:59:51 -07:00
from app . utils import utc_now
2024-05-16 10:17:45 -04:00
from notifications_utils . clients . zendesk . zendesk_client import NotifySupportTicket
2019-11-05 16:47:00 +00:00
from tests . app import load_example_csv
2022-10-05 01:12:35 +00:00
from tests . app . db import create_job , create_notification , create_template
2016-06-20 13:33:53 +01:00
2024-12-27 10:28:08 -08:00
CHECK_JOB_STATUS_TOO_OLD_MINUTES = 241
2016-06-20 13:33:53 +01:00
2023-08-23 10:35:43 -07:00
def test_should_call_delete_codes_on_delete_verify_codes_task (
notify_db_session , mocker
) :
mocker . patch (
" app.celery.scheduled_tasks.delete_codes_older_created_more_than_a_day_ago "
)
2016-06-20 13:33:53 +01:00
delete_verify_codes ( )
2023-08-23 10:35:43 -07:00
assert (
scheduled_tasks . delete_codes_older_created_more_than_a_day_ago . call_count == 1
)
2016-06-20 13:33:53 +01:00
2023-11-10 16:14:03 -05:00
def test_should_call_expire_or_delete_invotations_on_expire_or_delete_invitations_task (
2023-08-23 10:35:43 -07:00
notify_db_session , mocker
) :
mocker . patch (
2023-11-07 15:28:27 -05:00
" app.celery.scheduled_tasks.expire_invitations_created_more_than_two_days_ago "
2023-08-23 10:35:43 -07:00
)
2023-11-07 15:28:27 -05:00
expire_or_delete_invitations ( )
2023-08-23 10:35:43 -07:00
assert (
2023-11-07 15:28:27 -05:00
scheduled_tasks . expire_invitations_created_more_than_two_days_ago . call_count
2023-08-23 10:35:43 -07:00
== 1
)
2016-06-20 13:33:53 +01:00
2019-10-30 10:51:07 +00:00
def test_should_update_scheduled_jobs_and_put_on_queue ( mocker , sample_template ) :
2023-08-23 10:35:43 -07:00
mocked = mocker . patch ( " app.celery.tasks.process_job.apply_async " )
2016-08-24 17:03:56 +01:00
2024-05-23 13:59:51 -07:00
one_minute_in_the_past = utc_now ( ) - timedelta ( minutes = 1 )
2023-08-23 10:35:43 -07:00
job = create_job (
2024-02-13 11:04:02 -05:00
sample_template ,
job_status = JobStatus . SCHEDULED ,
scheduled_for = one_minute_in_the_past ,
2023-08-23 10:35:43 -07:00
)
2016-08-24 17:03:56 +01:00
run_scheduled_jobs ( )
updated_job = dao_get_job_by_id ( job . id )
2024-02-09 12:24:09 -05:00
assert updated_job . job_status == JobStatus . PENDING
2017-05-25 10:51:49 +01:00
mocked . assert_called_with ( [ str ( job . id ) ] , queue = " job-tasks " )
2016-08-24 17:03:56 +01:00
2019-10-30 10:51:07 +00:00
def test_should_update_all_scheduled_jobs_and_put_on_queue ( sample_template , mocker ) :
2023-08-23 10:35:43 -07:00
mocked = mocker . patch ( " app.celery.tasks.process_job.apply_async " )
2016-08-24 17:03:56 +01:00
2024-05-23 13:59:51 -07:00
one_minute_in_the_past = utc_now ( ) - timedelta ( minutes = 1 )
ten_minutes_in_the_past = utc_now ( ) - timedelta ( minutes = 10 )
twenty_minutes_in_the_past = utc_now ( ) - timedelta ( minutes = 20 )
2023-08-23 10:35:43 -07:00
job_1 = create_job (
2024-02-13 11:04:02 -05:00
sample_template ,
job_status = JobStatus . SCHEDULED ,
scheduled_for = one_minute_in_the_past ,
2023-08-23 10:35:43 -07:00
)
job_2 = create_job (
2024-02-13 11:04:02 -05:00
sample_template ,
job_status = JobStatus . SCHEDULED ,
scheduled_for = ten_minutes_in_the_past ,
2023-08-23 10:35:43 -07:00
)
job_3 = create_job (
sample_template ,
2024-02-09 12:24:09 -05:00
job_status = JobStatus . SCHEDULED ,
2023-08-23 10:35:43 -07:00
scheduled_for = twenty_minutes_in_the_past ,
)
2016-08-24 17:03:56 +01:00
run_scheduled_jobs ( )
2024-02-09 12:24:09 -05:00
assert dao_get_job_by_id ( job_1 . id ) . job_status == JobStatus . PENDING
assert dao_get_job_by_id ( job_2 . id ) . job_status == JobStatus . PENDING
assert dao_get_job_by_id ( job_2 . id ) . job_status == JobStatus . PENDING
2016-08-24 17:03:56 +01:00
2023-08-23 10:35:43 -07:00
mocked . assert_has_calls (
[
call ( [ str ( job_3 . id ) ] , queue = " job-tasks " ) ,
call ( [ str ( job_2 . id ) ] , queue = " job-tasks " ) ,
call ( [ str ( job_1 . id ) ] , queue = " job-tasks " ) ,
]
)
2016-09-07 15:36:59 +01:00
2020-07-22 17:00:20 +01:00
def test_check_job_status_task_calls_process_incomplete_jobs ( mocker , sample_template ) :
2023-08-23 10:35:43 -07:00
mock_celery = mocker . patch ( " app.celery.tasks.process_incomplete_jobs.apply_async " )
job = create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
processing_started = utc_now ( )
- timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . IN_PROGRESS ,
2023-08-23 10:35:43 -07:00
)
2017-10-12 16:21:08 +01:00
create_notification ( template = sample_template , job = job )
2020-07-22 17:00:20 +01:00
check_job_status ( )
2017-10-12 16:21:08 +01:00
2023-08-23 10:35:43 -07:00
mock_celery . assert_called_once_with ( [ [ str ( job . id ) ] ] , queue = QueueNames . JOBS )
2017-10-13 16:46:17 +01:00
2017-10-12 16:21:08 +01:00
2020-07-22 17:00:20 +01:00
def test_check_job_status_task_calls_process_incomplete_jobs_when_scheduled_job_is_not_complete (
mocker , sample_template
) :
2023-08-23 10:35:43 -07:00
mock_celery = mocker . patch ( " app.celery.tasks.process_incomplete_jobs.apply_async " )
job = create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( hours = 5 ) ,
scheduled_for = utc_now ( ) - timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
processing_started = utc_now ( )
- timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . IN_PROGRESS ,
2023-08-23 10:35:43 -07:00
)
2020-07-22 17:00:20 +01:00
check_job_status ( )
2017-10-12 16:21:08 +01:00
2023-08-23 10:35:43 -07:00
mock_celery . assert_called_once_with ( [ [ str ( job . id ) ] ] , queue = QueueNames . JOBS )
2017-10-13 16:46:17 +01:00
2017-10-12 16:21:08 +01:00
2021-03-17 14:53:34 +00:00
def test_check_job_status_task_calls_process_incomplete_jobs_for_pending_scheduled_jobs (
mocker , sample_template
) :
2023-08-23 10:35:43 -07:00
mock_celery = mocker . patch ( " app.celery.tasks.process_incomplete_jobs.apply_async " )
job = create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( hours = 5 ) ,
scheduled_for = utc_now ( ) - timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . PENDING ,
2023-08-23 10:35:43 -07:00
)
2021-03-17 14:53:34 +00:00
check_job_status ( )
2023-08-23 10:35:43 -07:00
mock_celery . assert_called_once_with ( [ [ str ( job . id ) ] ] , queue = QueueNames . JOBS )
2021-03-17 14:53:34 +00:00
def test_check_job_status_task_does_not_call_process_incomplete_jobs_for_non_scheduled_pending_jobs (
mocker ,
sample_template ,
) :
2023-08-23 10:35:43 -07:00
mock_celery = mocker . patch ( " app.celery.tasks.process_incomplete_jobs.apply_async " )
2021-03-17 14:53:34 +00:00
create_job (
template = sample_template ,
notification_count = 3 ,
2024-05-23 13:59:51 -07:00
created_at = utc_now ( ) - timedelta ( hours = 2 ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . PENDING ,
2021-03-17 14:53:34 +00:00
)
check_job_status ( )
assert not mock_celery . called
2023-08-23 10:35:43 -07:00
def test_check_job_status_task_calls_process_incomplete_jobs_for_multiple_jobs (
mocker , sample_template
) :
mock_celery = mocker . patch ( " app.celery.tasks.process_incomplete_jobs.apply_async " )
job = create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( hours = 5 ) ,
scheduled_for = utc_now ( ) - timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
processing_started = utc_now ( )
- timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . IN_PROGRESS ,
2023-08-23 10:35:43 -07:00
)
job_2 = create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( hours = 5 ) ,
scheduled_for = utc_now ( ) - timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
processing_started = utc_now ( )
- timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . IN_PROGRESS ,
2023-08-23 10:35:43 -07:00
)
2020-07-22 17:00:20 +01:00
check_job_status ( )
2017-10-12 16:21:08 +01:00
2017-10-13 16:46:17 +01:00
mock_celery . assert_called_once_with (
2023-08-23 10:35:43 -07:00
[ [ str ( job . id ) , str ( job_2 . id ) ] ] , queue = QueueNames . JOBS
2017-10-13 16:46:17 +01:00
)
2017-11-09 10:32:39 +00:00
2018-03-09 16:30:50 +00:00
def test_check_job_status_task_only_sends_old_tasks ( mocker , sample_template ) :
2023-08-23 10:35:43 -07:00
mock_celery = mocker . patch ( " app.celery.tasks.process_incomplete_jobs.apply_async " )
2018-03-09 16:30:50 +00:00
job = create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( hours = 5 ) ,
scheduled_for = utc_now ( ) - timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
processing_started = utc_now ( )
- timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . IN_PROGRESS ,
2018-03-09 16:30:50 +00:00
)
2020-07-22 17:00:20 +01:00
create_job (
2018-03-09 16:30:50 +00:00
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( minutes = 300 ) ,
processing_started = utc_now ( ) - timedelta ( minutes = 239 ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . IN_PROGRESS ,
2018-03-09 16:30:50 +00:00
)
2021-03-17 14:53:34 +00:00
create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( minutes = 300 ) ,
scheduled_for = utc_now ( ) - timedelta ( minutes = 239 ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . PENDING ,
2021-03-17 14:53:34 +00:00
)
2020-07-22 17:00:20 +01:00
check_job_status ( )
2018-03-09 16:30:50 +00:00
2021-03-17 14:53:34 +00:00
# jobs 2 and 3 were created less than 30 minutes ago, so are not sent to Celery task
2023-08-23 10:35:43 -07:00
mock_celery . assert_called_once_with ( [ [ str ( job . id ) ] ] , queue = QueueNames . JOBS )
2018-03-09 16:30:50 +00:00
2018-03-09 16:34:47 +00:00
def test_check_job_status_task_sets_jobs_to_error ( mocker , sample_template ) :
2023-08-23 10:35:43 -07:00
mock_celery = mocker . patch ( " app.celery.tasks.process_incomplete_jobs.apply_async " )
2018-03-09 16:34:47 +00:00
job = create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( hours = 5 ) ,
scheduled_for = utc_now ( ) - timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
processing_started = utc_now ( )
- timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . IN_PROGRESS ,
2018-03-09 16:34:47 +00:00
)
job_2 = create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( minutes = 300 ) ,
processing_started = utc_now ( ) - timedelta ( minutes = 239 ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . IN_PROGRESS ,
2018-03-09 16:34:47 +00:00
)
2020-07-22 17:00:20 +01:00
check_job_status ( )
2018-03-09 16:34:47 +00:00
# job 2 not in celery task
2023-08-23 10:35:43 -07:00
mock_celery . assert_called_once_with ( [ [ str ( job . id ) ] ] , queue = QueueNames . JOBS )
2024-01-16 14:46:17 -05:00
assert job . job_status == JobStatus . ERROR
assert job_2 . job_status == JobStatus . IN_PROGRESS
2018-03-09 16:34:47 +00:00
2018-03-23 15:38:35 +00:00
def test_replay_created_notifications ( notify_db_session , sample_service , mocker ) :
2023-08-23 10:35:43 -07:00
email_delivery_queue = mocker . patch (
" app.celery.provider_tasks.deliver_email.apply_async "
)
sms_delivery_queue = mocker . patch (
" app.celery.provider_tasks.deliver_sms.apply_async "
)
2018-03-23 15:38:35 +00:00
2024-02-13 11:04:02 -05:00
sms_template = create_template (
service = sample_service , template_type = TemplateType . SMS
)
email_template = create_template (
service = sample_service , template_type = TemplateType . EMAIL
)
2019-11-21 15:51:27 +00:00
older_than = ( 60 * 60 ) + ( 60 * 15 ) # 1 hour 15 minutes
2018-03-23 15:38:35 +00:00
# notifications expected to be resent
2023-08-23 10:35:43 -07:00
old_sms = create_notification (
template = sms_template ,
2024-05-23 13:59:51 -07:00
created_at = utc_now ( ) - timedelta ( seconds = older_than ) ,
2024-02-09 12:24:09 -05:00
status = NotificationStatus . CREATED ,
2023-08-23 10:35:43 -07:00
)
old_email = create_notification (
template = email_template ,
2024-05-23 13:59:51 -07:00
created_at = utc_now ( ) - timedelta ( seconds = older_than ) ,
2024-02-09 12:24:09 -05:00
status = NotificationStatus . CREATED ,
2023-08-23 10:35:43 -07:00
)
2018-03-23 15:38:35 +00:00
# notifications that are not to be resent
2023-08-23 10:35:43 -07:00
create_notification (
template = sms_template ,
2024-05-23 13:59:51 -07:00
created_at = utc_now ( ) - timedelta ( seconds = older_than ) ,
2024-02-09 12:24:09 -05:00
status = NotificationStatus . SENDING ,
2023-08-23 10:35:43 -07:00
)
create_notification (
template = email_template ,
2024-05-23 13:59:51 -07:00
created_at = utc_now ( ) - timedelta ( seconds = older_than ) ,
2024-02-09 12:24:09 -05:00
status = NotificationStatus . DELIVERED ,
2023-08-23 10:35:43 -07:00
)
create_notification (
2024-02-13 11:04:02 -05:00
template = sms_template ,
2024-05-23 13:59:51 -07:00
created_at = utc_now ( ) ,
2024-02-13 11:04:02 -05:00
status = NotificationStatus . CREATED ,
2023-08-23 10:35:43 -07:00
)
create_notification (
2024-02-13 11:04:02 -05:00
template = email_template ,
2024-05-23 13:59:51 -07:00
created_at = utc_now ( ) ,
2024-02-13 11:04:02 -05:00
status = NotificationStatus . CREATED ,
2023-08-23 10:35:43 -07:00
)
2018-03-23 15:38:35 +00:00
replay_created_notifications ( )
2023-08-23 10:35:43 -07:00
email_delivery_queue . assert_called_once_with (
[ str ( old_email . id ) ] , queue = " send-email-tasks "
)
sms_delivery_queue . assert_called_once_with (
[ str ( old_sms . id ) ] , queue = " send-sms-tasks "
)
2019-01-14 17:22:41 +00:00
def test_check_job_status_task_does_not_raise_error ( sample_template ) :
create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( hours = 5 ) ,
scheduled_for = utc_now ( ) - timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
processing_started = utc_now ( )
- timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . FINISHED ,
2023-08-23 10:35:43 -07:00
)
2019-01-14 17:22:41 +00:00
create_job (
template = sample_template ,
notification_count = 3 ,
2024-12-27 10:28:08 -08:00
created_at = utc_now ( ) - timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
processing_started = utc_now ( )
- timedelta ( minutes = CHECK_JOB_STATUS_TOO_OLD_MINUTES ) ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . FINISHED ,
2023-08-23 10:35:43 -07:00
)
2019-01-14 17:22:41 +00:00
check_job_status ( )
2019-06-11 13:16:34 +01:00
2023-08-23 10:35:43 -07:00
@pytest.mark.parametrize (
" offset " ,
(
timedelta ( days = 1 ) ,
pytest . param ( timedelta ( hours = 23 , minutes = 59 ) , marks = pytest . mark . xfail ) ,
pytest . param ( timedelta ( minutes = 20 ) , marks = pytest . mark . xfail ) ,
timedelta ( minutes = 19 ) ,
) ,
)
2020-09-26 11:06:44 +01:00
def test_check_for_missing_rows_in_completed_jobs_ignores_old_and_new_jobs (
mocker ,
sample_email_template ,
offset ,
) :
2023-08-23 10:35:43 -07:00
mocker . patch (
" app.celery.tasks.s3.get_job_and_metadata_from_s3 " ,
return_value = ( load_example_csv ( " multiple_email " ) , { " sender_id " : None } ) ,
)
mocker . patch ( " app.encryption.encrypt " , return_value = " something_encrypted " )
process_row = mocker . patch ( " app.celery.scheduled_tasks.process_row " )
2020-09-26 11:06:44 +01:00
job = create_job (
template = sample_email_template ,
notification_count = 5 ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . FINISHED ,
2024-05-23 13:59:51 -07:00
processing_finished = utc_now ( ) - offset ,
2020-09-26 11:06:44 +01:00
)
for i in range ( 0 , 4 ) :
create_notification ( job = job , job_row_number = i )
check_for_missing_rows_in_completed_jobs ( )
assert process_row . called is False
2019-11-05 16:47:00 +00:00
def test_check_for_missing_rows_in_completed_jobs ( mocker , sample_email_template ) :
2023-08-23 10:35:43 -07:00
mocker . patch (
" app.celery.tasks.s3.get_job_and_metadata_from_s3 " ,
return_value = ( load_example_csv ( " multiple_email " ) , { " sender_id " : None } ) ,
)
mocker . patch ( " app.encryption.encrypt " , return_value = " something_encrypted " )
process_row = mocker . patch ( " app.celery.scheduled_tasks.process_row " )
job = create_job (
template = sample_email_template ,
notification_count = 5 ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . FINISHED ,
2024-05-23 13:59:51 -07:00
processing_finished = utc_now ( ) - timedelta ( minutes = 20 ) ,
2023-08-23 10:35:43 -07:00
)
2019-11-05 16:47:00 +00:00
for i in range ( 0 , 4 ) :
create_notification ( job = job , job_row_number = i )
check_for_missing_rows_in_completed_jobs ( )
process_row . assert_called_once_with (
2019-11-15 15:32:16 +00:00
mock . ANY , mock . ANY , job , job . service , sender_id = None
2019-11-05 16:47:00 +00:00
)
2023-08-23 10:35:43 -07:00
def test_check_for_missing_rows_in_completed_jobs_calls_save_email (
mocker , sample_email_template
) :
mocker . patch (
" app.celery.tasks.s3.get_job_and_metadata_from_s3 " ,
return_value = ( load_example_csv ( " multiple_email " ) , { " sender_id " : None } ) ,
)
save_email_task = mocker . patch ( " app.celery.tasks.save_email.apply_async " )
mocker . patch ( " app.encryption.encrypt " , return_value = " something_encrypted " )
mocker . patch ( " app.celery.tasks.create_uuid " , return_value = " uuid " )
2019-11-05 16:47:00 +00:00
2023-08-23 10:35:43 -07:00
job = create_job (
template = sample_email_template ,
notification_count = 5 ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . FINISHED ,
2024-05-23 13:59:51 -07:00
processing_finished = utc_now ( ) - timedelta ( minutes = 20 ) ,
2023-08-23 10:35:43 -07:00
)
2019-11-05 16:47:00 +00:00
for i in range ( 0 , 4 ) :
create_notification ( job = job , job_row_number = i )
check_for_missing_rows_in_completed_jobs ( )
save_email_task . assert_called_once_with (
(
str ( job . service_id ) ,
" uuid " ,
" something_encrypted " ,
) ,
{ } ,
2023-08-23 10:35:43 -07:00
queue = " database-tasks " ,
2024-12-04 08:04:34 -08:00
expires = ANY ,
2019-11-05 16:47:00 +00:00
)
2019-11-15 15:32:16 +00:00
2023-08-23 10:35:43 -07:00
def test_check_for_missing_rows_in_completed_jobs_uses_sender_id (
mocker , sample_email_template , fake_uuid
) :
mocker . patch (
" app.celery.tasks.s3.get_job_and_metadata_from_s3 " ,
return_value = ( load_example_csv ( " multiple_email " ) , { " sender_id " : fake_uuid } ) ,
)
mock_process_row = mocker . patch ( " app.celery.scheduled_tasks.process_row " )
2019-11-15 15:32:16 +00:00
2023-08-23 10:35:43 -07:00
job = create_job (
template = sample_email_template ,
notification_count = 5 ,
2024-01-16 14:46:17 -05:00
job_status = JobStatus . FINISHED ,
2024-05-23 13:59:51 -07:00
processing_finished = utc_now ( ) - timedelta ( minutes = 20 ) ,
2023-08-23 10:35:43 -07:00
)
2019-11-15 15:32:16 +00:00
for i in range ( 0 , 4 ) :
create_notification ( job = job , job_row_number = i )
check_for_missing_rows_in_completed_jobs ( )
mock_process_row . assert_called_once_with (
mock . ANY , mock . ANY , job , job . service , sender_id = fake_uuid
)
2019-11-29 21:24:17 +00:00
2019-12-03 10:26:59 +00:00
MockServicesSendingToTVNumbers = namedtuple (
2023-08-23 10:35:43 -07:00
" ServicesSendingToTVNumbers " ,
2019-12-03 10:26:59 +00:00
[
2023-08-23 10:35:43 -07:00
" service_id " ,
" notification_count " ,
] ,
2019-12-03 10:26:59 +00:00
)
2019-12-05 16:07:06 +00:00
MockServicesWithHighFailureRate = namedtuple (
2023-08-23 10:35:43 -07:00
" ServicesWithHighFailureRate " ,
2019-12-05 16:07:06 +00:00
[
2023-08-23 10:35:43 -07:00
" service_id " ,
" permanent_failure_rate " ,
] ,
2019-12-05 16:07:06 +00:00
)
2019-12-03 10:26:59 +00:00
2023-08-23 10:35:43 -07:00
@pytest.mark.parametrize (
" failure_rates, sms_to_tv_numbers, expected_message " ,
2019-11-29 21:24:17 +00:00
[
2023-08-23 10:35:43 -07:00
[
[ MockServicesWithHighFailureRate ( " 123 " , 0.3 ) ] ,
[ ] ,
" 1 service(s) have had high permanent-failure rates for sms messages in last "
" 24 hours: \n service: {} /services/ {} failure rate: 0.3, \n " . format (
Test . ADMIN_BASE_URL , " 123 "
) ,
] ,
[
[ ] ,
[ MockServicesSendingToTVNumbers ( " 123 " , 300 ) ] ,
" 1 service(s) have sent over 500 sms messages to tv numbers in last 24 hours: \n "
" service: {} /services/ {} count of sms to tv numbers: 300, \n " . format (
Test . ADMIN_BASE_URL , " 123 "
) ,
] ,
2019-12-03 10:26:59 +00:00
] ,
2023-08-23 10:35:43 -07:00
)
2019-11-29 21:24:17 +00:00
def test_check_for_services_with_high_failure_rates_or_sending_to_tv_numbers (
2019-12-03 10:26:59 +00:00
mocker , notify_db_session , failure_rates , sms_to_tv_numbers , expected_message
2019-11-29 21:24:17 +00:00
) :
2023-08-23 10:35:43 -07:00
mock_logger = mocker . patch ( " app.celery.tasks.current_app.logger.warning " )
mock_create_ticket = mocker . spy ( NotifySupportTicket , " __init__ " )
2021-09-23 16:35:12 +01:00
mock_send_ticket_to_zendesk = mocker . patch (
2023-08-23 10:35:43 -07:00
" app.celery.scheduled_tasks.zendesk_client.send_ticket_to_zendesk " ,
2021-09-23 16:35:12 +01:00
autospec = True ,
)
2019-11-29 21:24:17 +00:00
mock_failure_rates = mocker . patch (
2023-08-23 10:35:43 -07:00
" app.celery.scheduled_tasks.dao_find_services_with_high_failure_rates " ,
return_value = failure_rates ,
2019-11-29 21:24:17 +00:00
)
2019-12-03 10:26:59 +00:00
mock_sms_to_tv_numbers = mocker . patch (
2023-08-23 10:35:43 -07:00
" app.celery.scheduled_tasks.dao_find_services_sending_to_tv_numbers " ,
return_value = sms_to_tv_numbers ,
2019-12-03 10:26:59 +00:00
)
2019-11-29 21:24:17 +00:00
2019-12-17 11:47:23 +00:00
zendesk_actions = " \n You can find instructions for this ticket in our manual: \n https://github.com/alphagov/notifications-manuals/wiki/Support-Runbook#Deal-with-services-with-high-failure-rates-or-sending-sms-to-tv-numbers " # noqa
2019-12-03 16:18:07 +00:00
2019-11-29 21:24:17 +00:00
check_for_services_with_high_failure_rates_or_sending_to_tv_numbers ( )
assert mock_failure_rates . called
2019-12-03 10:26:59 +00:00
assert mock_sms_to_tv_numbers . called
2019-11-29 21:24:17 +00:00
mock_logger . assert_called_once_with ( expected_message )
mock_create_ticket . assert_called_with (
2021-09-23 16:35:12 +01:00
ANY ,
2019-12-03 16:18:07 +00:00
message = expected_message + zendesk_actions ,
2019-11-29 21:24:17 +00:00
subject = " [test] High failure rates for sms spotted for services " ,
2023-08-23 10:35:43 -07:00
ticket_type = " incident " ,
technical_ticket = True ,
2019-11-29 21:24:17 +00:00
)
2021-09-23 16:35:12 +01:00
mock_send_ticket_to_zendesk . assert_called_once ( )