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(