Delete old 'process-virus-scan-passed-task'

This has been replaced by a new task, `sanitise-letter`, to this deletes
all the code in the old task and ensures that when antivirus is not
enabled locally we are calling the new task.
This commit is contained in:
Katie Smith
2020-03-10 15:15:58 +00:00
parent e07dea27e1
commit 6ac89c9a2f
5 changed files with 12 additions and 531 deletions

View File

@@ -1,7 +1,5 @@
import math import math
import base64
from datetime import datetime, timedelta from datetime import datetime, timedelta
from uuid import UUID
from hashlib import sha512 from hashlib import sha512
from base64 import urlsafe_b64encode from base64 import urlsafe_b64encode
@@ -13,7 +11,6 @@ from requests import (
) )
from celery.exceptions import MaxRetriesExceededError from celery.exceptions import MaxRetriesExceededError
from notifications_utils.statsd_decorators import statsd from notifications_utils.statsd_decorators import statsd
from notifications_utils.s3 import s3upload
from notifications_utils.letter_timings import LETTER_PROCESSING_DEADLINE from notifications_utils.letter_timings import LETTER_PROCESSING_DEADLINE
from notifications_utils.timezones import convert_utc_to_bst from notifications_utils.timezones import convert_utc_to_bst
@@ -32,10 +29,8 @@ from app.letters.utils import get_letter_pdf_filename
from app.errors import VirusScanError from app.errors import VirusScanError
from app.exceptions import NotificationTechnicalFailureException from app.exceptions import NotificationTechnicalFailureException
from app.letters.utils import ( from app.letters.utils import (
copy_redaction_failed_pdf,
get_billable_units_for_letter_page_count, get_billable_units_for_letter_page_count,
get_reference_from_filename, get_reference_from_filename,
get_folder_name,
upload_letter_pdf, upload_letter_pdf,
ScanErrorType, ScanErrorType,
move_failed_pdf, move_failed_pdf,
@@ -213,72 +208,6 @@ def group_letters(letter_pdfs):
yield list_of_files yield list_of_files
@notify_celery.task(bind=True, name='process-virus-scan-passed', max_retries=15, default_retry_delay=300)
def process_virus_scan_passed(self, filename):
reference = get_reference_from_filename(filename)
notification = dao_get_notification_by_reference(reference)
current_app.logger.info('notification id {} Virus scan passed: {}'.format(notification.id, filename))
is_test_key = notification.key_type == KEY_TYPE_TEST
scan_pdf_object = s3.get_s3_object(current_app.config['LETTERS_SCAN_BUCKET_NAME'], filename)
old_pdf = scan_pdf_object.get()['Body'].read()
sanitise_response, result = _sanitise_precompiled_pdf(self, notification, old_pdf)
new_pdf = None
if result == 'validation_passed':
new_pdf = base64.b64decode(sanitise_response["file"].encode())
redaction_failed_message = sanitise_response.get("redaction_failed_message")
if redaction_failed_message and not is_test_key:
current_app.logger.info('{} for notification id {} ({})'.format(
redaction_failed_message, notification.id, filename)
)
copy_redaction_failed_pdf(filename)
billable_units = get_billable_units_for_letter_page_count(sanitise_response.get("page_count"))
# TODO: Remove this once CYSP update their template to not cross over the margins
if notification.service_id == UUID('fe44178f-3b45-4625-9f85-2264a36dd9ec'): # CYSP
# Check your state pension submit letters with good addresses and notify tags, so just use their supplied pdf
new_pdf = old_pdf
if result == 'validation_failed' and not new_pdf:
current_app.logger.info('Invalid precompiled pdf received {} ({})'.format(notification.id, filename))
_move_invalid_letter_and_update_status(
notification=notification,
filename=filename,
scan_pdf_object=scan_pdf_object,
message=sanitise_response["message"],
invalid_pages=sanitise_response.get("invalid_pages"),
page_count=sanitise_response.get("page_count")
)
return
current_app.logger.info('notification id {} ({}) sanitised and ready to send'.format(notification.id, filename))
try:
_upload_pdf_to_test_or_live_pdf_bucket(
new_pdf,
filename,
is_test_letter=is_test_key,
created_at=notification.created_at
)
update_letter_pdf_status(
reference=reference,
status=NOTIFICATION_DELIVERED if is_test_key else NOTIFICATION_CREATED,
billable_units=billable_units,
recipient_address=sanitise_response.get("recipient_address")
)
scan_pdf_object.delete()
except BotoClientError:
current_app.logger.exception(
"Error uploading letter to live pdf bucket for notification: {}".format(notification.id)
)
update_notification_status_by_id(notification.id, NOTIFICATION_TECHNICAL_FAILURE)
@notify_celery.task(bind=True, name='sanitise-letter', max_retries=15, default_retry_delay=300) @notify_celery.task(bind=True, name='sanitise-letter', max_retries=15, default_retry_delay=300)
def sanitise_letter(self, filename): def sanitise_letter(self, filename):
try: try:
@@ -412,60 +341,6 @@ def _move_invalid_letter_and_update_status(
raise NotificationTechnicalFailureException raise NotificationTechnicalFailureException
def _upload_pdf_to_test_or_live_pdf_bucket(pdf_data, filename, is_test_letter, created_at):
target_bucket_config = 'TEST_LETTERS_BUCKET_NAME' if is_test_letter else 'LETTERS_PDF_BUCKET_NAME'
target_bucket_name = current_app.config[target_bucket_config]
target_filename = get_folder_name(created_at, dont_use_sending_date=is_test_letter) + filename
s3upload(
filedata=pdf_data,
region=current_app.config['AWS_REGION'],
bucket_name=target_bucket_name,
file_location=target_filename
)
def _sanitise_precompiled_pdf(self, notification, precompiled_pdf):
try:
response = requests_post(
'{}/precompiled/sanitise'.format(
current_app.config['TEMPLATE_PREVIEW_API_HOST']
),
data=precompiled_pdf,
headers={'Authorization': 'Token {}'.format(current_app.config['TEMPLATE_PREVIEW_API_KEY']),
'Service-ID': str(notification.service_id),
'Notification-ID': str(notification.id)}
)
response.raise_for_status()
return response.json(), "validation_passed"
except RequestException as ex:
if ex.response is not None and ex.response.status_code == 400:
message = "sanitise_precompiled_pdf validation error for notification: {}. ".format(notification.id)
if response.json().get("message"):
message += response.json()["message"]
if response.json().get("invalid_pages"):
message += (" on pages: " + ", ".join(map(str, response.json()["invalid_pages"])))
current_app.logger.info(
message
)
return response.json(), "validation_failed"
try:
current_app.logger.exception(
"sanitise_precompiled_pdf failed for notification: {}".format(notification.id)
)
self.retry(queue=QueueNames.RETRY)
except MaxRetriesExceededError:
current_app.logger.error(
"RETRY FAILED: sanitise_precompiled_pdf failed for notification {}".format(notification.id),
)
notification.status = NOTIFICATION_TECHNICAL_FAILURE
dao_update_notification(notification)
raise
@notify_celery.task(name='process-virus-scan-failed') @notify_celery.task(name='process-virus-scan-failed')
def process_virus_scan_failed(filename): def process_virus_scan_failed(filename):
move_failed_pdf(filename, ScanErrorType.FAILURE) move_failed_pdf(filename, ScanErrorType.FAILURE)
@@ -529,9 +404,9 @@ def replay_letters_in_error(filename=None):
) )
else: else:
# stub out antivirus in dev # stub out antivirus in dev
process_virus_scan_passed.apply_async( sanitise_letter.apply_async(
kwargs={'filename': filename}, [filename],
queue=QueueNames.LETTERS, queue=QueueNames.LETTERS
) )
else: else:
error_files = get_file_names_from_error_bucket() error_files = get_file_names_from_error_bucket()
@@ -548,7 +423,7 @@ def replay_letters_in_error(filename=None):
) )
else: else:
# stub out antivirus in dev # stub out antivirus in dev
process_virus_scan_passed.apply_async( sanitise_letter.apply_async(
kwargs={'filename': moved_file_name}, [filename],
queue=QueueNames.LETTERS, queue=QueueNames.LETTERS
) )

View File

@@ -114,14 +114,6 @@ def move_failed_pdf(source_filename, scan_error_type):
_move_s3_object(scan_bucket, source_filename, scan_bucket, target_filename) _move_s3_object(scan_bucket, source_filename, scan_bucket, target_filename)
def copy_redaction_failed_pdf(source_filename):
scan_bucket = current_app.config['LETTERS_SCAN_BUCKET_NAME']
target_filename = 'REDACTION_FAILURE/' + source_filename
_copy_s3_object(scan_bucket, source_filename, scan_bucket, target_filename)
def move_error_pdf_to_scan_bucket(source_filename): def move_error_pdf_to_scan_bucket(source_filename):
scan_bucket = current_app.config['LETTERS_SCAN_BUCKET_NAME'] scan_bucket = current_app.config['LETTERS_SCAN_BUCKET_NAME']
error_file = 'ERROR/' + source_filename error_file = 'ERROR/' + source_filename
@@ -213,22 +205,6 @@ def _move_s3_object(source_bucket, source_filename, target_bucket, target_filena
source_bucket, source_filename, target_bucket, target_filename)) source_bucket, source_filename, target_bucket, target_filename))
def _copy_s3_object(source_bucket, source_filename, target_bucket, target_filename):
s3 = boto3.resource('s3')
copy_source = {'Bucket': source_bucket, 'Key': source_filename}
target_bucket = s3.Bucket(target_bucket)
obj = target_bucket.Object(target_filename)
# Tags are copied across but the expiration time is reset in the destination bucket
# e.g. if a file has 5 days left to expire on a ONE_WEEK retention in the source bucket,
# in the destination bucket the expiration time will be reset to 7 days left to expire
obj.copy(copy_source, ExtraArgs={'ServerSideEncryption': 'AES256'})
current_app.logger.info("Copied letter PDF: {}/{} to {}/{}".format(
source_bucket, source_filename, target_bucket, target_filename))
def letter_print_day(created_at): def letter_print_day(created_at):
bst_print_datetime = convert_utc_to_bst(created_at) + timedelta(hours=6, minutes=30) bst_print_datetime = convert_utc_to_bst(created_at) + timedelta(hours=6, minutes=30)
bst_print_date = bst_print_datetime.date() bst_print_date = bst_print_datetime.date()

View File

@@ -17,7 +17,7 @@ from app import (
encryption, encryption,
DATETIME_FORMAT DATETIME_FORMAT
) )
from app.celery.letters_pdf_tasks import create_letters_pdf, process_virus_scan_passed from app.celery.letters_pdf_tasks import create_letters_pdf, sanitise_letter
from app.celery.research_mode_tasks import create_fake_letter_response_file from app.celery.research_mode_tasks import create_fake_letter_response_file
from app.celery.tasks import save_api_email from app.celery.tasks import save_api_email
from app.clients.document_download import DocumentDownloadError from app.clients.document_download import DocumentDownloadError
@@ -407,9 +407,9 @@ def process_precompiled_letter_notifications(*, letter_data, api_key, template,
) )
else: else:
# stub out antivirus in dev # stub out antivirus in dev
process_virus_scan_passed.apply_async( sanitise_letter.apply_async(
kwargs={'filename': filename}, [filename],
queue=QueueNames.LETTERS, queue=QueueNames.LETTERS
) )
return notification return notification

View File

@@ -1,6 +1,5 @@
from unittest.mock import Mock, call, ANY from unittest.mock import call
import base64
import boto3 import boto3
from datetime import datetime, timedelta from datetime import datetime, timedelta
from moto import mock_s3 from moto import mock_s3
@@ -9,7 +8,7 @@ from freezegun import freeze_time
import pytest import pytest
import requests_mock import requests_mock
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
from celery.exceptions import MaxRetriesExceededError, Retry from celery.exceptions import MaxRetriesExceededError
from requests import RequestException from requests import RequestException
from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm.exc import NoResultFound
@@ -23,13 +22,11 @@ from app.celery.letters_pdf_tasks import (
get_key_and_size_of_letters_to_be_sent_to_print, get_key_and_size_of_letters_to_be_sent_to_print,
group_letters, group_letters,
process_sanitised_letter, process_sanitised_letter,
process_virus_scan_passed,
process_virus_scan_failed, process_virus_scan_failed,
process_virus_scan_error, process_virus_scan_error,
replay_letters_in_error, replay_letters_in_error,
sanitise_letter, sanitise_letter,
_move_invalid_letter_and_update_status, _move_invalid_letter_and_update_status,
_sanitise_precompiled_pdf
) )
from app.config import QueueNames, TaskNames from app.config import QueueNames, TaskNames
from app.letters.utils import ScanErrorType from app.letters.utils import ScanErrorType
@@ -53,7 +50,6 @@ from tests.conftest import set_config_values
def test_should_have_decorated_tasks_functions(): def test_should_have_decorated_tasks_functions():
assert create_letters_pdf.__wrapped__.__name__ == 'create_letters_pdf' assert create_letters_pdf.__wrapped__.__name__ == 'create_letters_pdf'
assert collate_letter_pdfs_to_be_sent.__wrapped__.__name__ == 'collate_letter_pdfs_to_be_sent' assert collate_letter_pdfs_to_be_sent.__wrapped__.__name__ == 'collate_letter_pdfs_to_be_sent'
assert process_virus_scan_passed.__wrapped__.__name__ == 'process_virus_scan_passed'
assert process_virus_scan_failed.__wrapped__.__name__ == 'process_virus_scan_failed' assert process_virus_scan_failed.__wrapped__.__name__ == 'process_virus_scan_failed'
assert process_virus_scan_error.__wrapped__.__name__ == 'process_virus_scan_error' assert process_virus_scan_error.__wrapped__.__name__ == 'process_virus_scan_error'
assert sanitise_letter.__wrapped__.__name__ == 'sanitise_letter' assert sanitise_letter.__wrapped__.__name__ == 'sanitise_letter'
@@ -521,256 +517,6 @@ def test_group_letters_with_no_letters():
assert list(group_letters([])) == [] assert list(group_letters([])) == []
@freeze_time('2018-01-01 18:00')
@mock_s3
@pytest.mark.parametrize('key_type,noti_status,bucket_config_name,destination_folder', [
(KEY_TYPE_NORMAL, NOTIFICATION_CREATED, 'LETTERS_PDF_BUCKET_NAME', '2018-01-02/'),
(KEY_TYPE_TEST, NOTIFICATION_DELIVERED, 'TEST_LETTERS_BUCKET_NAME', '')
])
def test_process_letter_task_check_virus_scan_passed(
sample_letter_template, mocker, key_type, noti_status, bucket_config_name, destination_folder
):
letter_notification = create_notification(template=sample_letter_template, billable_units=0,
status='pending-virus-check', key_type=key_type,
reference='{} letter'.format(key_type))
filename = 'NOTIFY.{}'.format(letter_notification.reference)
source_bucket_name = current_app.config['LETTERS_SCAN_BUCKET_NAME']
target_bucket_name = current_app.config[bucket_config_name]
conn = boto3.resource('s3', region_name='eu-west-1')
conn.create_bucket(Bucket=source_bucket_name)
conn.create_bucket(Bucket=target_bucket_name)
s3 = boto3.client('s3', region_name='eu-west-1')
s3.put_object(Bucket=source_bucket_name, Key=filename, Body=b'old_pdf')
mock_s3upload = mocker.patch('app.celery.letters_pdf_tasks.s3upload')
endpoint = 'http://localhost:9999/precompiled/sanitise'
with requests_mock.mock() as rmock:
rmock.request(
"POST",
endpoint,
json={
"file": base64.b64encode(b"new_pdf").decode("utf-8"),
"recipient_address": "Bugs Bunny",
"validation_passed": True,
"message": "",
"invalid_pages": [],
"page_count": 1
},
status_code=200
)
process_virus_scan_passed(filename)
assert letter_notification.status == noti_status
assert letter_notification.billable_units == 1
assert rmock.called
assert rmock.request_history[0].url == endpoint
mock_s3upload.assert_called_once_with(
bucket_name=target_bucket_name,
filedata=b'new_pdf',
file_location=destination_folder + filename,
region='eu-west-1',
)
@freeze_time('2018-01-01 18:00')
@mock_s3
@pytest.mark.parametrize('key_type', [KEY_TYPE_NORMAL, KEY_TYPE_TEST])
def test_process_letter_task_check_virus_scan_passed_when_sanitise_fails(
sample_letter_notification, mocker, key_type
):
filename = 'NOTIFY.{}'.format(sample_letter_notification.reference)
source_bucket_name = current_app.config['LETTERS_SCAN_BUCKET_NAME']
target_bucket_name = current_app.config['INVALID_PDF_BUCKET_NAME']
conn = boto3.resource('s3', region_name='eu-west-1')
conn.create_bucket(Bucket=source_bucket_name)
conn.create_bucket(Bucket=target_bucket_name)
s3 = boto3.client('s3', region_name='eu-west-1')
s3.put_object(Bucket=source_bucket_name, Key=filename, Body=b'pdf_content')
sample_letter_notification.status = NOTIFICATION_PENDING_VIRUS_CHECK
sample_letter_notification.key_type = key_type
mock_move_s3 = mocker.patch('app.letters.utils._move_s3_object')
sanitise_response = {
"file": base64.b64encode(b"nyan").decode("utf-8"),
"validation_passed": False,
"message": "content-outside-printable-area",
"invalid_pages": [1, 2],
"page_count": 2
}
mock_sanitise = mocker.patch(
'app.celery.letters_pdf_tasks._sanitise_precompiled_pdf', return_value=(sanitise_response, "validation_failed")
)
process_virus_scan_passed(filename)
assert sample_letter_notification.status == NOTIFICATION_VALIDATION_FAILED
assert sample_letter_notification.billable_units == 0
mock_sanitise.assert_called_once_with(
ANY,
sample_letter_notification,
b'pdf_content'
)
mock_move_s3.assert_called_once_with(
source_bucket=source_bucket_name, source_filename=filename,
target_bucket=target_bucket_name, target_filename=filename, metadata={
"message": "content-outside-printable-area",
"invalid_pages": "[1, 2]",
"page_count": "2"
}
)
@freeze_time('2018-01-01 18:00')
@mock_s3
@pytest.mark.parametrize('key_type,notification_status,bucket_config_name', [
(KEY_TYPE_NORMAL, NOTIFICATION_CREATED, 'LETTERS_PDF_BUCKET_NAME'),
(KEY_TYPE_TEST, NOTIFICATION_DELIVERED, 'TEST_LETTERS_BUCKET_NAME')
])
def test_process_letter_task_check_virus_scan_passed_when_redaction_fails(
sample_letter_notification, mocker, key_type, notification_status, bucket_config_name
):
filename = 'NOTIFY.{}'.format(sample_letter_notification.reference)
bucket_name = current_app.config['LETTERS_SCAN_BUCKET_NAME']
target_bucket_name = current_app.config[bucket_config_name]
conn = boto3.resource('s3', region_name='eu-west-1')
conn.create_bucket(Bucket=bucket_name)
conn.create_bucket(Bucket=target_bucket_name)
s3 = boto3.client('s3', region_name='eu-west-1')
s3.put_object(Bucket=bucket_name, Key=filename, Body=b'pdf_content')
sample_letter_notification.status = NOTIFICATION_PENDING_VIRUS_CHECK
sample_letter_notification.key_type = key_type
mock_copy_s3 = mocker.patch('app.letters.utils._copy_s3_object')
endpoint = 'http://localhost:9999/precompiled/sanitise'
with requests_mock.mock() as rmock:
rmock.request(
"POST",
endpoint,
json={
"file": base64.b64encode(b"new_pdf").decode("utf-8"),
"validation_passed": True,
"redaction_failed_message": "No matches for address block during redaction procedure",
"message": "",
"invalid_pages": "",
"page_count": 3
},
status_code=200
)
process_virus_scan_passed(filename)
assert sample_letter_notification.billable_units == 2
assert sample_letter_notification.status == notification_status
if key_type == KEY_TYPE_NORMAL:
mock_copy_s3.assert_called_once_with(
bucket_name, filename,
bucket_name, 'REDACTION_FAILURE/' + filename
)
else:
mock_copy_s3.assert_not_called()
@freeze_time('2018-01-01 18:00')
@mock_s3
@pytest.mark.parametrize('key_type', [KEY_TYPE_NORMAL, KEY_TYPE_TEST])
def test_process_letter_task_check_virus_scan_passed_when_file_cannot_be_opened(
sample_letter_notification, mocker, key_type
):
filename = 'NOTIFY.{}'.format(sample_letter_notification.reference)
source_bucket_name = current_app.config['LETTERS_SCAN_BUCKET_NAME']
target_bucket_name = current_app.config['INVALID_PDF_BUCKET_NAME']
conn = boto3.resource('s3', region_name='eu-west-1')
conn.create_bucket(Bucket=source_bucket_name)
conn.create_bucket(Bucket=target_bucket_name)
s3 = boto3.client('s3', region_name='eu-west-1')
s3.put_object(Bucket=source_bucket_name, Key=filename, Body=b'pdf_content')
sample_letter_notification.status = NOTIFICATION_PENDING_VIRUS_CHECK
sample_letter_notification.key_type = key_type
mock_move_s3 = mocker.patch('app.letters.utils._move_s3_object')
endpoint = 'http://localhost:9999/precompiled/sanitise'
with requests_mock.mock() as rmock:
rmock.request(
"POST",
endpoint,
json={
"page_count": None,
"recipient_address": None,
"message": 'unable-to-read-the-file',
"invalid_pages": None,
"file": None
},
status_code=400
)
process_virus_scan_passed(filename)
mock_move_s3.assert_called_once_with(
source_bucket=source_bucket_name, source_filename=filename,
target_bucket=target_bucket_name, target_filename=filename, metadata={'message': 'unable-to-read-the-file'}
)
assert sample_letter_notification.status == NOTIFICATION_VALIDATION_FAILED
assert sample_letter_notification.billable_units == 0
@mock_s3
def test_process_virus_scan_passed_logs_error_and_sets_tech_failure_if_s3_error_uploading_to_live_bucket(
mocker,
sample_letter_notification,
):
mock_logger = mocker.patch('app.celery.tasks.current_app.logger.exception')
sample_letter_notification.status = NOTIFICATION_PENDING_VIRUS_CHECK
filename = 'NOTIFY.{}'.format(sample_letter_notification.reference)
source_bucket_name = current_app.config['LETTERS_SCAN_BUCKET_NAME']
conn = boto3.resource('s3', region_name='eu-west-1')
conn.create_bucket(Bucket=source_bucket_name)
s3 = boto3.client('s3', region_name='eu-west-1')
s3.put_object(Bucket=source_bucket_name, Key=filename, Body=b'pdf_content')
error_response = {
'Error': {
'Code': 'InvalidParameterValue',
'Message': 'some error message from amazon',
'Type': 'Sender'
}
}
mocker.patch('app.celery.letters_pdf_tasks._upload_pdf_to_test_or_live_pdf_bucket',
side_effect=ClientError(error_response, 'operation_name'))
endpoint = 'http://localhost:9999/precompiled/sanitise'
with requests_mock.mock() as rmock:
rmock.request(
"POST",
endpoint,
json={
"file": base64.b64encode(b"new_pdf").decode("utf-8"),
"validation_passed": True,
"message": "",
"invalid_pages": [],
"page_count": 1
},
status_code=200
)
process_virus_scan_passed(filename)
assert sample_letter_notification.status == NOTIFICATION_TECHNICAL_FAILURE
mock_logger.assert_called_once_with(
'Error uploading letter to live pdf bucket for notification: {}'.format(sample_letter_notification.id)
)
def test_move_invalid_letter_and_update_status_logs_error_and_sets_tech_failure_state_if_s3_error( def test_move_invalid_letter_and_update_status_logs_error_and_sets_tech_failure_state_if_s3_error(
mocker, mocker,
sample_letter_notification, sample_letter_notification,
@@ -1087,100 +833,3 @@ def test_replay_letters_in_error_for_one_file(notify_api, mocker):
replay_letters_in_error("file_name") replay_letters_in_error("file_name")
mock_move.assert_called_once_with('file_name') mock_move.assert_called_once_with('file_name')
mock_celery.assert_called_once_with(name='scan-file', kwargs={'filename': 'file_name'}, queue='antivirus-tasks') mock_celery.assert_called_once_with(name='scan-file', kwargs={'filename': 'file_name'}, queue='antivirus-tasks')
def test_sanitise_precompiled_pdf_returns_data_from_template_preview(rmock, sample_letter_notification):
sample_letter_notification.status = NOTIFICATION_PENDING_VIRUS_CHECK
endpoint = 'http://localhost:9999/precompiled/sanitise'
with requests_mock.mock() as rmock:
rmock.request(
"POST",
endpoint,
json={
"file": base64.b64encode(b"new_pdf").decode("utf-8"),
"validation_passed": True,
"message": "",
"invalid_pages": [],
"page_count": 1
},
status_code=200
)
mock_celery = Mock(**{'retry.side_effect': Retry})
response, result = _sanitise_precompiled_pdf(mock_celery, sample_letter_notification, b'old_pdf')
assert rmock.called
assert rmock.request_history[0].url == endpoint
assert result == "validation_passed"
assert base64.b64decode(response["file"].encode()) == b"new_pdf"
assert rmock.last_request.text == 'old_pdf'
def test_sanitise_precompiled_pdf_return_validation_error(rmock, sample_letter_notification):
sample_letter_notification.status = NOTIFICATION_PENDING_VIRUS_CHECK
endpoint = 'http://localhost:9999/precompiled/sanitise'
response_json = {
"file": base64.b64encode(b"nyan").decode("utf-8"),
"validation_passed": False,
"message": "content-outside-printable-area",
"invalid_pages": [1],
"page_count": 1
}
with requests_mock.mock() as rmock:
rmock.request(
"POST",
endpoint,
json=response_json,
status_code=400
)
mock_celery = Mock(**{'retry.side_effect': Retry})
response, result = _sanitise_precompiled_pdf(mock_celery, sample_letter_notification, b'old_pdf')
assert rmock.called
assert rmock.request_history[0].url == endpoint
assert result == "validation_failed"
assert response == response_json
def test_sanitise_precompiled_pdf_passes_the_service_id_and_notification_id_to_template_preview(
mocker,
sample_letter_notification,
):
tp_mock = mocker.patch('app.celery.letters_pdf_tasks.requests_post')
sample_letter_notification.status = NOTIFICATION_PENDING_VIRUS_CHECK
mock_celery = Mock(**{'retry.side_effect': Retry})
_sanitise_precompiled_pdf(mock_celery, sample_letter_notification, b'old_pdf')
service_id = str(sample_letter_notification.service_id)
notification_id = str(sample_letter_notification.id)
tp_mock.assert_called_once_with(
'http://localhost:9999/precompiled/sanitise',
data=b'old_pdf',
headers={'Authorization': 'Token my-secret-key',
'Service-ID': service_id,
'Notification-ID': notification_id}
)
def test_sanitise_precompiled_pdf_retries_on_http_error(rmock, sample_letter_notification):
sample_letter_notification.status = NOTIFICATION_PENDING_VIRUS_CHECK
rmock.post('http://localhost:9999/precompiled/sanitise', content=b'new_pdf', status_code=500)
mock_celery = Mock(**{'retry.side_effect': Retry})
with pytest.raises(Retry):
_sanitise_precompiled_pdf(mock_celery, sample_letter_notification, b'old_pdf')
def test_sanitise_precompiled_pdf_sets_notification_to_technical_failure_after_too_many_errors(
rmock,
sample_letter_notification
):
sample_letter_notification.status = NOTIFICATION_PENDING_VIRUS_CHECK
rmock.post('http://localhost:9999/precompiled/sanitise', content=b'new_pdf', status_code=500)
mock_celery = Mock(**{'retry.side_effect': MaxRetriesExceededError})
with pytest.raises(MaxRetriesExceededError):
_sanitise_precompiled_pdf(mock_celery, sample_letter_notification, b'old_pdf')
assert sample_letter_notification.status == NOTIFICATION_TECHNICAL_FAILURE

View File

@@ -7,7 +7,6 @@ from freezegun import freeze_time
from moto import mock_s3 from moto import mock_s3
from app.letters.utils import ( from app.letters.utils import (
copy_redaction_failed_pdf,
get_bucket_name_and_prefix_for_notification, get_bucket_name_and_prefix_for_notification,
get_letter_pdf_filename, get_letter_pdf_filename,
get_letter_pdf_and_metadata, get_letter_pdf_and_metadata,
@@ -294,24 +293,6 @@ def test_move_failed_pdf_scan_failed(notify_api):
assert filename not in [o.key for o in bucket.objects.all()] assert filename not in [o.key for o in bucket.objects.all()]
@mock_s3
@freeze_time(FROZEN_DATE_TIME)
def test_copy_redaction_failed_pdf(notify_api):
filename = 'test.pdf'
bucket_name = current_app.config['LETTERS_SCAN_BUCKET_NAME']
conn = boto3.resource('s3', region_name='eu-west-1')
bucket = conn.create_bucket(Bucket=bucket_name)
s3 = boto3.client('s3', region_name='eu-west-1')
s3.put_object(Bucket=bucket_name, Key=filename, Body=b'pdf_content')
copy_redaction_failed_pdf(filename)
assert 'REDACTION_FAILURE/' + filename in [o.key for o in bucket.objects.all()]
assert filename in [o.key for o in bucket.objects.all()]
@pytest.mark.parametrize("freeze_date, expected_folder_name", @pytest.mark.parametrize("freeze_date, expected_folder_name",
[("2018-04-01 17:50:00", "2018-04-02/"), [("2018-04-01 17:50:00", "2018-04-02/"),
("2018-07-02 16:29:00", "2018-07-02/"), ("2018-07-02 16:29:00", "2018-07-02/"),