From f691bc2a923ec3879d1827a5d615c9bbd87b9bfa Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Thu, 17 Feb 2022 11:45:40 +0000 Subject: [PATCH] Only lookup broadcasts which can be cancelled It is possible that, among the references Environment Agency give us for which broadcast to cancel, there could be references for older, already expired broadcasts. This would be the case if someone cancelled a broadcast in Notify, then issued and try to re-cancel another broadcast to the same area. The Flood Warning Service has no way of knowing that the first broadcast has been cancelled in Notify already, so it would add the reference to the list of things to be cancelled. We can avoid this from happening by filtering-out already-cancelled and expired broadcasts before looking up which one should be cancelled. --- app/dao/broadcast_message_dao.py | 4 ++ tests/app/v2/broadcast/test_post_broadcast.py | 50 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/app/dao/broadcast_message_dao.py b/app/dao/broadcast_message_dao.py index 087702eb4..1281c0e75 100644 --- a/app/dao/broadcast_message_dao.py +++ b/app/dao/broadcast_message_dao.py @@ -26,6 +26,10 @@ def dao_get_broadcast_message_by_id_and_service_id(broadcast_message_id, service def dao_get_broadcast_message_by_references_and_service_id(references_to_original_broadcast, service_id): return BroadcastMessage.query.filter( + BroadcastMessage.status.in_(( + BroadcastStatusType.PENDING_APPROVAL, + BroadcastStatusType.BROADCASTING, + )), BroadcastMessage.reference.in_(references_to_original_broadcast), BroadcastMessage.service_id == service_id ).one() diff --git a/tests/app/v2/broadcast/test_post_broadcast.py b/tests/app/v2/broadcast/test_post_broadcast.py index e6e2c89be..fcf8aad78 100644 --- a/tests/app/v2/broadcast/test_post_broadcast.py +++ b/tests/app/v2/broadcast/test_post_broadcast.py @@ -253,6 +253,56 @@ def test_cancel_request_does_not_cancel_broadcast_if_service_id_does_not_match( assert response_for_cancel.status_code == 404 +@pytest.mark.parametrize("is_approved, expected_cancel_tasks", ( + (True, 1), + (False, 0), +)) +def test_same_broadcast_cant_be_cancelled_twice( + mocker, + client, + sample_broadcast_service, + is_approved, + expected_cancel_tasks, +): + mock_send_broadcast_event_task = mocker.patch( + 'app.celery.broadcast_message_tasks.send_broadcast_event.apply_async' + ) + auth_header = create_service_authorization_header(service_id=sample_broadcast_service.id) + + # create a broadcast + response_for_create = client.post( + path='/v2/broadcast', + data=sample_cap_xml_documents.WAINFLEET, + headers=[('Content-Type', 'application/cap+xml'), auth_header], + ) + assert response_for_create.status_code == 201 + + response_json_for_create = json.loads(response_for_create.get_data(as_text=True)) + + broadcast_message = dao_get_broadcast_message_by_id_and_service_id( + response_json_for_create["id"], response_json_for_create["service_id"] + ) + # approve broadcast + if is_approved: + broadcast_message.status = 'broadcasting' + + first_response_for_cancel = client.post( + path='/v2/broadcast', + data=sample_cap_xml_documents.WAINFLEET_CANCEL_WITH_REFERENCES, + headers=[('Content-Type', 'application/cap+xml'), auth_header], + ) + assert first_response_for_cancel.status_code == 201 + + second_response_for_cancel = client.post( + path='/v2/broadcast', + data=sample_cap_xml_documents.WAINFLEET_CANCEL_WITH_REFERENCES, + headers=[('Content-Type', 'application/cap+xml'), auth_header], + ) + assert second_response_for_cancel.status_code == 404 + + assert len(mock_send_broadcast_event_task.call_args_list) == expected_cancel_tasks + + def test_large_polygon_is_simplified( client, sample_broadcast_service,