diff --git a/app/__init__.py b/app/__init__.py index 702081e38..bea857cc2 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -344,7 +344,9 @@ def format_notification_status(status, template_type): 'delivered': 'Delivered', 'sending': 'Sending', 'created': 'Sending', - 'sent': 'Delivered' + 'sent': 'Delivered', + 'pending-virus-check': 'Pending virus check', + 'virus-scan-failed': 'Virus detected', } }[template_type].get(status, status) @@ -368,6 +370,8 @@ def format_notification_status_as_field_status(status, notification_type): 'sending': None, 'created': None, 'accepted': None, + 'pending-virus-check': None, + 'virus-scan-failed': 'error', } }.get( notification_type, diff --git a/app/templates/components/table.html b/app/templates/components/table.html index 9c08bc00e..e43f2bfa9 100644 --- a/app/templates/components/table.html +++ b/app/templates/components/table.html @@ -140,7 +140,7 @@ {% if notification.status|format_notification_status_as_url %} {% endif %} - {% if notification['notification_type'] != "letter" %} + {% if notification['notification_type'] != "letter" or notification.status == 'virus-scan-failed' %} {{ notification.status|format_notification_status( notification.template.template_type ) }} diff --git a/app/templates/views/activity/notifications.html b/app/templates/views/activity/notifications.html index 239b7dca0..0002ac632 100644 --- a/app/templates/views/activity/notifications.html +++ b/app/templates/views/activity/notifications.html @@ -16,7 +16,11 @@ field_headings_visible=False ) %} {% call row_heading() %} - {{ item.to }} + {% if item.status in ('pending-virus-check', 'virus-scan-failed') %} + {{ item.to }} + {% else %} + {{ item.to }} + {% endif %}

{{ item.preview_of_content }}

diff --git a/app/templates/views/api/index.html b/app/templates/views/api/index.html index 0fe91f2a3..ebd5781f1 100644 --- a/app/templates/views/api/index.html +++ b/app/templates/views/api/index.html @@ -73,7 +73,7 @@
{{ notification[key] }}
{% endif %} {% endfor %} - {% if notification['notification_type'] == 'letter' %} + {% if notification['notification_type'] == 'letter' and notification.status not in ('pending-virus-check', 'virus-scan-failed') %} View letter {% endif %} diff --git a/app/utils.py b/app/utils.py index a7075adbb..979ddf62a 100644 --- a/app/utils.py +++ b/app/utils.py @@ -27,9 +27,9 @@ from notifications_utils.template import ( from orderedset._orderedset import OrderedSet from werkzeug.datastructures import MultiDict -SENDING_STATUSES = ['created', 'pending', 'sending'] +SENDING_STATUSES = ['created', 'pending', 'sending', 'pending-virus-check'] DELIVERED_STATUSES = ['delivered', 'sent'] -FAILURE_STATUSES = ['failed', 'temporary-failure', 'permanent-failure', 'technical-failure'] +FAILURE_STATUSES = ['failed', 'temporary-failure', 'permanent-failure', 'technical-failure', 'virus-scan-failed'] REQUESTED_STATUSES = SENDING_STATUSES + DELIVERED_STATUSES + FAILURE_STATUSES diff --git a/tests/app/main/views/test_activity.py b/tests/app/main/views/test_activity.py index c97953302..6bcecc6f5 100644 --- a/tests/app/main/views/test_activity.py +++ b/tests/app/main/views/test_activity.py @@ -26,14 +26,14 @@ from app.main.views.jobs import get_status_filters, get_time_left ( '', [ - 'created', 'pending', 'sending', + 'created', 'pending', 'sending', 'pending-virus-check', 'delivered', 'sent', - 'failed', 'temporary-failure', 'permanent-failure', 'technical-failure', + 'failed', 'temporary-failure', 'permanent-failure', 'technical-failure', 'virus-scan-failed', ] ), ( 'sending', - ['sending', 'created', 'pending'] + ['sending', 'created', 'pending', 'pending-virus-check'] ), ( 'delivered', @@ -41,7 +41,7 @@ from app.main.views.jobs import get_status_filters, get_time_left ), ( 'failed', - ['failed', 'temporary-failure', 'permanent-failure', 'technical-failure'] + ['failed', 'temporary-failure', 'permanent-failure', 'technical-failure', 'virus-scan-failed'] ) ] ) @@ -135,6 +135,60 @@ def test_can_show_notifications( assert json_content.keys() == {'counts', 'notifications'} +def test_letters_with_status_virus_scan_failed_shows_a_failure_description( + mocker, + active_user_with_permissions, + logged_in_client, + service_one, + mock_get_detailed_service, +): + mock_get_notifications( + mocker, + active_user_with_permissions, + is_precompiled_letter=True, + noti_status='virus-scan-failed' + ) + response = logged_in_client.get(url_for( + 'main.view_notifications', + service_id=service_one['id'], + message_type='letter', + status='', + )) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + error_description = page.find('div', attrs={'class': 'table-field-status-error'}).text.strip() + assert 'Virus detected\n' in error_description + + +@pytest.mark.parametrize('letter_status', [ + 'pending-virus-check', 'virus-scan-failed' +]) +def test_should_not_show_preview_link_for_precompiled_letters_in_virus_states( + mocker, + active_user_with_permissions, + logged_in_client, + service_one, + mock_get_detailed_service, + letter_status, +): + mock_get_notifications( + mocker, + active_user_with_permissions, + is_precompiled_letter=True, + noti_status=letter_status + ) + response = logged_in_client.get(url_for( + 'main.view_notifications', + service_id=service_one['id'], + message_type='letter', + status='', + )) + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert not page.find('a', attrs={'class': 'file-list-filename'}) + + def test_shows_message_when_no_notifications( client_request, mock_get_detailed_service, diff --git a/tests/app/main/views/test_api_integration.py b/tests/app/main/views/test_api_integration.py index 4c038d69d..1a9e5a9e4 100644 --- a/tests/app/main/views/test_api_integration.py +++ b/tests/app/main/views/test_api_integration.py @@ -89,6 +89,27 @@ def test_letter_notifications_should_have_link_to_view_letter( assert (page.select_one('details a') is not None) == has_links +@pytest.mark.parametrize('status', [ + 'pending-virus-check', 'virus-scan-failed' +]) +def test_should_not_have_link_to_view_letter_for_precompiled_letters_in_virus_states( + client_request, + api_user_active, + fake_uuid, + mock_has_permissions, + mocker, + status +): + mock_get_notifications(mocker, api_user_active, noti_status=status) + + page = client_request.get( + 'main.api_integration', + service_id=fake_uuid, + ) + + assert not page.select_one('details a') + + @pytest.mark.parametrize('client_reference, shows_ref', [ ('foo', True), (None, False), diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py index f3a4c084b..81eaf52a3 100644 --- a/tests/app/main/views/test_jobs.py +++ b/tests/app/main/views/test_jobs.py @@ -49,14 +49,14 @@ def test_get_jobs_shows_page_links( ( '', [ - 'created', 'pending', 'sending', + 'created', 'pending', 'sending', 'pending-virus-check', 'delivered', 'sent', - 'failed', 'temporary-failure', 'permanent-failure', 'technical-failure', + 'failed', 'temporary-failure', 'permanent-failure', 'technical-failure', 'virus-scan-failed', ] ), ( 'sending', - ['sending', 'created', 'pending'] + ['sending', 'created', 'pending', 'pending-virus-check'] ), ( 'delivered', @@ -64,7 +64,7 @@ def test_get_jobs_shows_page_links( ), ( 'failed', - ['failed', 'temporary-failure', 'permanent-failure', 'technical-failure'] + ['failed', 'temporary-failure', 'permanent-failure', 'technical-failure', 'virus-scan-failed'] ) ] ) @@ -200,12 +200,14 @@ def test_should_show_letter_job( 'created', 'pending', 'sending', + 'pending-virus-check', 'delivered', 'sent', 'failed', 'temporary-failure', 'permanent-failure', 'technical-failure', + 'virus-scan-failed', ], ) diff --git a/tests/conftest.py b/tests/conftest.py index a975d1de1..f62f2ec7b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1766,6 +1766,7 @@ def mock_get_notifications( redact_personalisation=False, is_precompiled_letter=False, client_reference=None, + noti_status=None, ): def _get_notifications( service_id, @@ -1807,6 +1808,7 @@ def mock_get_notifications( personalisation=personalisation, template_type=diff_template_type, client_reference=client_reference, + status=noti_status, ) return mocker.patch(