Merge pull request #3497 from alphagov/broadcast-alert-earlier-181423293

Alert earlier when a broadcast is approved
This commit is contained in:
Ben Thorner
2022-04-07 10:34:23 +01:00
committed by GitHub
7 changed files with 184 additions and 197 deletions

View File

@@ -1,7 +1,8 @@
import pytest
from app.broadcast_message.utils import (
validate_and_update_broadcast_message_status,
_create_p1_zendesk_alert,
update_broadcast_message_status,
)
from app.errors import InvalidRequest
from app.models import (
@@ -15,9 +16,10 @@ from tests.app.db import (
create_template,
create_user,
)
from tests.conftest import set_config
def test_validate_and_update_broadcast_message_status_stores_approved_by_and_approved_at_and_queues_task(
def test_update_broadcast_message_status_stores_approved_by_and_approved_at_and_queues_task(
sample_broadcast_service,
mocker
):
@@ -34,7 +36,7 @@ def test_validate_and_update_broadcast_message_status_stores_approved_by_and_app
sample_broadcast_service.users.append(approver)
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
validate_and_update_broadcast_message_status(
update_broadcast_message_status(
broadcast_message, BroadcastStatusType.BROADCASTING, approver
)
@@ -54,7 +56,7 @@ def test_validate_and_update_broadcast_message_status_stores_approved_by_and_app
assert alert_event.transmitted_content == {"body": "emergency broadcast"}
def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_from_admin_interface(
def test_update_broadcast_message_status_for_cancelling_broadcast_from_admin_interface(
sample_broadcast_service,
mocker,
):
@@ -71,7 +73,7 @@ def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_f
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
validate_and_update_broadcast_message_status(
update_broadcast_message_status(
broadcast_message, BroadcastStatusType.CANCELLED, updating_user=canceller, api_key_id=None
)
@@ -89,7 +91,7 @@ def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_f
assert alert_event.message_type == BroadcastEventMessageType.CANCEL
def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_from_API_call(
def test_update_broadcast_message_status_for_cancelling_broadcast_from_API_call(
sample_broadcast_service,
mocker,
):
@@ -105,7 +107,7 @@ def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_f
)
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
validate_and_update_broadcast_message_status(
update_broadcast_message_status(
broadcast_message, BroadcastStatusType.CANCELLED, updating_user=None, api_key_id=api_key.id
)
@@ -123,7 +125,7 @@ def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_f
assert alert_event.message_type == BroadcastEventMessageType.CANCEL
def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_via_admin_interface(
def test_update_broadcast_message_status_for_rejecting_broadcast_via_admin_interface(
sample_broadcast_service,
mocker
):
@@ -138,7 +140,7 @@ def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_vi
)
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
validate_and_update_broadcast_message_status(
update_broadcast_message_status(
broadcast_message, BroadcastStatusType.REJECTED, updating_user=sample_broadcast_service.created_by
)
@@ -151,7 +153,7 @@ def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_vi
assert len(broadcast_message.events) == 0
def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_from_API_call(
def test_update_broadcast_message_status_for_rejecting_broadcast_from_API_call(
sample_broadcast_service,
mocker
):
@@ -167,7 +169,7 @@ def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_fr
)
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
validate_and_update_broadcast_message_status(
update_broadcast_message_status(
broadcast_message, BroadcastStatusType.REJECTED, api_key_id=api_key.id
)
@@ -209,7 +211,7 @@ def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_fr
(BroadcastStatusType.CANCELLED, BroadcastStatusType.BROADCASTING),
(BroadcastStatusType.CANCELLED, BroadcastStatusType.COMPLETED),
])
def test_validate_and_update_broadcast_message_status_restricts_status_transitions_to_explicit_list(
def test_update_broadcast_message_status_restricts_status_transitions_to_explicit_list(
sample_broadcast_service,
mocker,
current_status,
@@ -222,14 +224,14 @@ def test_validate_and_update_broadcast_message_status_restricts_status_transitio
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
with pytest.raises(expected_exception=InvalidRequest) as e:
validate_and_update_broadcast_message_status(broadcast_message, new_status, approver)
update_broadcast_message_status(broadcast_message, new_status, approver)
assert mock_task.called is False
assert f'from {current_status} to {new_status}' in str(e.value)
@pytest.mark.parametrize('is_platform_admin', [True, False])
def test_validate_and_update_broadcast_message_status_rejects_approval_from_creator(
def test_update_broadcast_message_status_rejects_approval_from_creator(
sample_broadcast_service,
mocker,
is_platform_admin
@@ -241,7 +243,7 @@ def test_validate_and_update_broadcast_message_status_rejects_approval_from_crea
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
with pytest.raises(expected_exception=InvalidRequest) as e:
validate_and_update_broadcast_message_status(
update_broadcast_message_status(
broadcast_message, BroadcastStatusType.BROADCASTING, creator_and_approver
)
@@ -249,7 +251,7 @@ def test_validate_and_update_broadcast_message_status_rejects_approval_from_crea
assert 'cannot approve their own broadcast' in str(e.value)
def test_validate_and_update_broadcast_message_status_rejects_approval_of_broadcast_with_no_areas(
def test_update_broadcast_message_status_rejects_approval_of_broadcast_with_no_areas(
admin_request,
sample_broadcast_service,
mocker
@@ -261,13 +263,13 @@ def test_validate_and_update_broadcast_message_status_rejects_approval_of_broadc
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
with pytest.raises(expected_exception=InvalidRequest) as e:
validate_and_update_broadcast_message_status(broadcast, BroadcastStatusType.BROADCASTING, approver)
update_broadcast_message_status(broadcast, BroadcastStatusType.BROADCASTING, approver)
assert mock_task.called is False
assert f'broadcast_message {broadcast.id} has no selected areas and so cannot be broadcasted.' in str(e.value)
def test_validate_and_update_broadcast_message_status_allows_trial_mode_services_to_approve_own_message(
def test_update_broadcast_message_status_allows_trial_mode_services_to_approve_own_message(
notify_db,
sample_broadcast_service,
mocker
@@ -282,7 +284,7 @@ def test_validate_and_update_broadcast_message_status_allows_trial_mode_services
creator_and_approver = sample_broadcast_service.created_by
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
validate_and_update_broadcast_message_status(
update_broadcast_message_status(
broadcast_message, BroadcastStatusType.BROADCASTING, creator_and_approver
)
@@ -298,7 +300,7 @@ def test_validate_and_update_broadcast_message_status_allows_trial_mode_services
(True, False),
(False, True),
])
def test_validate_and_update_broadcast_message_status_when_broadcast_message_is_stubbed_or_service_not_live(
def test_update_broadcast_message_status_when_broadcast_message_is_stubbed_or_service_not_live(
admin_request,
sample_broadcast_service,
mocker,
@@ -319,7 +321,7 @@ def test_validate_and_update_broadcast_message_status_when_broadcast_message_is_
sample_broadcast_service.restricted = service_restricted_before_approval
validate_and_update_broadcast_message_status(
update_broadcast_message_status(
broadcast_message, BroadcastStatusType.BROADCASTING, approver
)
assert broadcast_message.status == BroadcastStatusType.BROADCASTING
@@ -331,7 +333,7 @@ def test_validate_and_update_broadcast_message_status_when_broadcast_message_is_
assert len(mock_task.mock_calls) == 0
def test_validate_and_update_broadcast_message_status_creates_event_with_correct_content_if_broadcast_has_no_template(
def test_update_broadcast_message_status_creates_event_with_correct_content_if_broadcast_has_no_template(
admin_request,
sample_broadcast_service,
mocker
@@ -350,7 +352,7 @@ def test_validate_and_update_broadcast_message_status_creates_event_with_correct
sample_broadcast_service.users.append(approver)
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
validate_and_update_broadcast_message_status(
update_broadcast_message_status(
broadcast_message, BroadcastStatusType.BROADCASTING, approver
)
@@ -362,3 +364,93 @@ def test_validate_and_update_broadcast_message_status_creates_event_with_correct
mock_task.assert_called_once_with(kwargs={'broadcast_event_id': str(alert_event.id)}, queue='broadcast-tasks')
assert alert_event.transmitted_content == {"body": "tailor made emergency broadcast content"}
def test_update_broadcast_message_status_creates_zendesk_ticket(
mocker,
notify_api,
sample_broadcast_service
):
broadcast_message = create_broadcast_message(
service=sample_broadcast_service,
content='tailor made emergency broadcast content',
status=BroadcastStatusType.PENDING_APPROVAL,
areas={"names": ["England", "Scotland"], "simple_polygons": ['polygons']}
)
approver = create_user(email='approver@gov.uk')
sample_broadcast_service.users.append(approver)
mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
mock_send_ticket_to_zendesk = mocker.patch(
'app.broadcast_message.utils.zendesk_client.send_ticket_to_zendesk',
autospec=True,
)
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'live'):
update_broadcast_message_status(
broadcast_message, BroadcastStatusType.BROADCASTING, approver
)
mock_send_ticket_to_zendesk.assert_called_once()
def test_create_p1_zendesk_alert(sample_broadcast_service, mocker, notify_api):
broadcast_message = create_broadcast_message(
service=sample_broadcast_service,
content='tailor made emergency broadcast content',
status=BroadcastStatusType.BROADCASTING,
areas={"names": ["England", "Scotland"]}
)
mock_send_ticket_to_zendesk = mocker.patch(
'app.broadcast_message.utils.zendesk_client.send_ticket_to_zendesk',
autospec=True,
)
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'live'):
_create_p1_zendesk_alert(broadcast_message)
ticket = mock_send_ticket_to_zendesk.call_args_list[0].args[0]
assert ticket.subject == 'Live broadcast sent'
assert ticket.ticket_type == 'incident'
assert str(broadcast_message.id) in ticket.message
assert "Sent on channel severe to ['England', 'Scotland']" in ticket.message
assert 'Content starts "tailor made emergency' in ticket.message
def test_create_p1_zendesk_alert_doesnt_alert_when_cancelling(mocker, notify_api, sample_broadcast_service):
broadcast_message = create_broadcast_message(
service=sample_broadcast_service,
content='tailor made emergency broadcast content',
status=BroadcastStatusType.CANCELLED,
areas={"names": ["England", "Scotland"]}
)
mock_send_ticket_to_zendesk = mocker.patch(
'app.broadcast_message.utils.zendesk_client.send_ticket_to_zendesk',
autospec=True,
)
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'live'):
_create_p1_zendesk_alert(broadcast_message)
mock_send_ticket_to_zendesk.assert_not_called()
def test_create_p1_zendesk_alert_doesnt_alert_on_staging(mocker, notify_api, sample_broadcast_service):
broadcast_message = create_broadcast_message(
service=sample_broadcast_service,
content='tailor made emergency broadcast content',
status=BroadcastStatusType.BROADCASTING,
areas={"names": ["England", "Scotland"]}
)
mock_send_ticket_to_zendesk = mocker.patch(
'app.broadcast_message.utils.zendesk_client.send_ticket_to_zendesk',
autospec=True,
)
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'staging'):
_create_p1_zendesk_alert(broadcast_message)
mock_send_ticket_to_zendesk.assert_not_called()

View File

@@ -3,11 +3,7 @@ from unittest.mock import ANY, Mock, call
import pytest
from celery.exceptions import Retry
from flask import current_app
from freezegun import freeze_time
from notifications_utils.clients.zendesk.zendesk_client import (
NotifySupportTicket,
)
from app.celery.broadcast_message_tasks import (
BroadcastIntegrityError,
@@ -41,10 +37,6 @@ def test_send_broadcast_event_queues_up_for_active_providers(mocker, notify_api,
mocker.patch('app.celery.broadcast_message_tasks.notify_celery.send_task')
mock_send_ticket_to_zendesk = mocker.patch(
'app.celery.broadcast_message_tasks.zendesk_client.send_ticket_to_zendesk',
autospec=True,
)
mock_send_broadcast_provider_message = mocker.patch(
'app.celery.broadcast_message_tasks.send_broadcast_provider_message',
)
@@ -57,9 +49,6 @@ def test_send_broadcast_event_queues_up_for_active_providers(mocker, notify_api,
call(kwargs={'broadcast_event_id': event.id, 'provider': 'vodafone'}, queue='broadcast-tasks')
]
# we're on test env so this isn't called
assert mock_send_ticket_to_zendesk.called is False
@pytest.mark.parametrize('message_status', [
BroadcastStatusType.BROADCASTING,
@@ -71,10 +60,6 @@ def test_send_broadcast_event_calls_publish_govuk_alerts_task(
template = create_template(sample_broadcast_service, BROADCAST_TYPE)
broadcast_message = create_broadcast_message(template, status=message_status)
event = create_broadcast_event(broadcast_message)
mocker.patch(
'app.celery.broadcast_message_tasks.zendesk_client.send_ticket_to_zendesk',
autospec=True,
)
mocker.patch(
'app.celery.broadcast_message_tasks.send_broadcast_provider_message',
)
@@ -137,104 +122,6 @@ def test_send_broadcast_event_does_nothing_if_provider_set_on_service_isnt_enabl
assert mock_send_broadcast_provider_message.apply_async.called is False
@pytest.mark.parametrize('area_data,expected_message', [
({'names': ['England', 'Scotland']}, ['England', 'Scotland']),
({}, [])
])
def test_send_broadcast_event_creates_zendesk(
area_data,
expected_message,
mocker,
notify_api,
sample_broadcast_service
):
template = create_template(sample_broadcast_service, BROADCAST_TYPE)
broadcast_message = create_broadcast_message(
template,
status=BroadcastStatusType.BROADCASTING,
areas={**area_data, 'simple_polygons': []},
)
event = create_broadcast_event(broadcast_message)
mock_create_ticket = mocker.spy(NotifySupportTicket, '__init__')
mocker.patch('app.celery.broadcast_message_tasks.notify_celery.send_task')
mock_send_ticket_to_zendesk = mocker.patch(
'app.celery.broadcast_message_tasks.zendesk_client.send_ticket_to_zendesk',
autospec=True,
)
mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_provider_message')
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'live'):
send_broadcast_event(event.id)
mock_create_ticket.assert_called_once_with(
ANY,
subject='Live broadcast sent',
message=ANY,
ticket_type='incident',
technical_ticket=True,
org_id=current_app.config['BROADCAST_ORGANISATION_ID'],
org_type='central',
service_id=str(sample_broadcast_service.id)
)
ticket_message = mock_create_ticket.call_args_list[0][1]['message']
assert str(broadcast_message.id) in ticket_message
assert 'channel severe' in ticket_message
assert f"areas {expected_message}" in ticket_message
# the start of the content from the broadcast template
assert "Dear Sir/Madam" in ticket_message
mock_send_ticket_to_zendesk.assert_called_once()
def test_send_broadcast_event_doesnt_create_zendesk_when_cancelling(mocker, notify_api, sample_broadcast_service):
template = create_template(sample_broadcast_service, BROADCAST_TYPE)
broadcast_message = create_broadcast_message(
template,
status=BroadcastStatusType.BROADCASTING,
areas={'areas': ['wd20-S13002775', 'wd20-S13002773'], 'simple_polygons': []},
)
create_broadcast_event(broadcast_message, message_type=BroadcastEventMessageType.ALERT)
cancel_event = create_broadcast_event(broadcast_message, message_type=BroadcastEventMessageType.CANCEL)
mocker.patch('app.celery.broadcast_message_tasks.notify_celery.send_task')
mock_send_ticket_to_zendesk = mocker.patch(
'app.celery.broadcast_message_tasks.zendesk_client.send_ticket_to_zendesk',
autospec=True,
)
mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_provider_message')
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'live'):
send_broadcast_event(cancel_event.id)
assert mock_send_ticket_to_zendesk.called is False
def test_send_broadcast_event_doesnt_create_zendesk_on_staging(mocker, notify_api, sample_broadcast_service):
template = create_template(sample_broadcast_service, BROADCAST_TYPE)
broadcast_message = create_broadcast_message(template, status=BroadcastStatusType.BROADCASTING)
event = create_broadcast_event(broadcast_message)
mocker.patch('app.celery.broadcast_message_tasks.notify_celery.send_task')
mock_send_ticket_to_zendesk = mocker.patch(
'app.celery.broadcast_message_tasks.zendesk_client.send_ticket_to_zendesk',
autospec=True,
)
mock_send_broadcast_provider_message = mocker.patch(
'app.celery.broadcast_message_tasks.send_broadcast_provider_message',
)
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'staging'):
send_broadcast_event(event.id)
assert mock_send_broadcast_provider_message.apply_async.called is True
assert mock_send_ticket_to_zendesk.called is False
@freeze_time('2020-08-01 12:00')
@pytest.mark.parametrize('provider,provider_capitalised', [
['ee', 'EE'],

View File

@@ -124,7 +124,7 @@ def test_valid_post_cap_xml_broadcast_returns_201(
[True, "cancelled"],
[False, "rejected"]
])
def test_valid_cancel_broadcast_request_calls_validate_and_update_broadcast_message_status_and_returns_201(
def test_valid_cancel_broadcast_request_calls_update_broadcast_message_status_and_returns_201(
client,
sample_broadcast_service,
mocker,
@@ -153,7 +153,9 @@ def test_valid_cancel_broadcast_request_calls_validate_and_update_broadcast_mess
if is_approved:
broadcast_message.status = 'broadcasting'
mock_update = mocker.patch('app.v2.broadcast.post_broadcast.validate_and_update_broadcast_message_status')
mock_update = mocker.patch(
'app.v2.broadcast.post_broadcast.broadcast_utils.update_broadcast_message_status'
)
# cancel broadcast
response_for_cancel = client.post(