2018-03-14 21:27:07 +00:00
|
|
|
from datetime import datetime
|
2018-03-05 14:11:37 +00:00
|
|
|
|
2018-03-14 21:27:07 +00:00
|
|
|
import boto3
|
2021-03-10 13:55:06 +00:00
|
|
|
import dateutil
|
|
|
|
|
import pytest
|
2018-03-14 21:27:07 +00:00
|
|
|
from flask import current_app
|
2018-03-14 18:15:00 +00:00
|
|
|
from freezegun import freeze_time
|
2018-03-14 21:27:07 +00:00
|
|
|
from moto import mock_s3
|
2018-03-14 18:15:00 +00:00
|
|
|
|
2018-03-19 13:28:16 +00:00
|
|
|
from app.letters.utils import (
|
2021-03-08 18:09:16 +00:00
|
|
|
LetterPDFNotFound,
|
2021-03-10 13:55:06 +00:00
|
|
|
ScanErrorType,
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
find_letter_pdf_filename,
|
|
|
|
|
generate_letter_pdf_filename,
|
2018-10-17 16:09:30 +01:00
|
|
|
get_bucket_name_and_prefix_for_notification,
|
2021-03-10 13:55:06 +00:00
|
|
|
get_folder_name,
|
2019-10-29 16:19:50 +00:00
|
|
|
get_letter_pdf_and_metadata,
|
2018-11-22 11:53:32 +00:00
|
|
|
letter_print_day,
|
2021-03-10 13:55:06 +00:00
|
|
|
move_failed_pdf,
|
2019-12-04 16:02:46 +00:00
|
|
|
move_sanitised_letter_to_test_or_live_pdf_bucket,
|
2018-03-19 13:28:16 +00:00
|
|
|
upload_letter_pdf,
|
2018-05-30 10:18:37 +01:00
|
|
|
)
|
2021-03-10 13:55:06 +00:00
|
|
|
from app.models import (
|
|
|
|
|
KEY_TYPE_NORMAL,
|
|
|
|
|
KEY_TYPE_TEST,
|
|
|
|
|
NOTIFICATION_VALIDATION_FAILED,
|
|
|
|
|
PRECOMPILED_TEMPLATE_NAME,
|
|
|
|
|
)
|
2018-09-25 11:04:58 +01:00
|
|
|
from tests.app.db import create_notification
|
2018-03-14 21:27:07 +00:00
|
|
|
|
|
|
|
|
FROZEN_DATE_TIME = "2018-03-14 17:00:00"
|
|
|
|
|
|
|
|
|
|
|
2018-10-17 16:09:30 +01:00
|
|
|
@pytest.fixture(name='sample_precompiled_letter_notification')
|
|
|
|
|
def _sample_precompiled_letter_notification(sample_letter_notification):
|
2018-03-14 21:27:07 +00:00
|
|
|
sample_letter_notification.template.hidden = True
|
|
|
|
|
sample_letter_notification.template.name = PRECOMPILED_TEMPLATE_NAME
|
|
|
|
|
sample_letter_notification.reference = 'foo'
|
2018-08-08 17:01:32 +01:00
|
|
|
with freeze_time(FROZEN_DATE_TIME):
|
|
|
|
|
sample_letter_notification.created_at = datetime.utcnow()
|
2019-09-05 11:47:31 +01:00
|
|
|
sample_letter_notification.updated_at = datetime.utcnow()
|
2018-03-14 21:27:07 +00:00
|
|
|
return sample_letter_notification
|
2018-03-05 14:11:37 +00:00
|
|
|
|
|
|
|
|
|
2018-10-17 16:09:30 +01:00
|
|
|
@pytest.fixture(name='sample_precompiled_letter_notification_using_test_key')
|
|
|
|
|
def _sample_precompiled_letter_notification_using_test_key(sample_precompiled_letter_notification):
|
|
|
|
|
sample_precompiled_letter_notification.key_type = KEY_TYPE_TEST
|
|
|
|
|
return sample_precompiled_letter_notification
|
2018-03-05 14:11:37 +00:00
|
|
|
|
|
|
|
|
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
@mock_s3
|
|
|
|
|
def test_find_letter_pdf_filename_returns_filename(sample_notification):
|
|
|
|
|
bucket_name = current_app.config['LETTERS_PDF_BUCKET_NAME']
|
|
|
|
|
s3 = boto3.client('s3', region_name='eu-west-1')
|
|
|
|
|
s3.create_bucket(
|
|
|
|
|
Bucket=bucket_name,
|
|
|
|
|
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
_, prefix = get_bucket_name_and_prefix_for_notification(sample_notification)
|
|
|
|
|
s3.put_object(Bucket=bucket_name, Key=f'{prefix}-and-then-some', Body=b'f')
|
|
|
|
|
|
|
|
|
|
assert find_letter_pdf_filename(sample_notification) == f'{prefix}-and-then-some'
|
|
|
|
|
|
|
|
|
|
|
2021-03-08 18:09:16 +00:00
|
|
|
@mock_s3
|
|
|
|
|
def test_find_letter_pdf_filename_raises_if_not_found(sample_notification):
|
|
|
|
|
bucket_name = current_app.config['LETTERS_PDF_BUCKET_NAME']
|
|
|
|
|
s3 = boto3.client('s3', region_name='eu-west-1')
|
|
|
|
|
s3.create_bucket(
|
|
|
|
|
Bucket=bucket_name,
|
|
|
|
|
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(LetterPDFNotFound):
|
|
|
|
|
find_letter_pdf_filename(sample_notification)
|
|
|
|
|
|
|
|
|
|
|
2018-11-27 12:21:41 +00:00
|
|
|
@pytest.mark.parametrize('created_at,folder', [
|
|
|
|
|
(datetime(2017, 1, 1, 17, 29), '2017-01-01'),
|
|
|
|
|
(datetime(2017, 1, 1, 17, 31), '2017-01-02'),
|
|
|
|
|
])
|
|
|
|
|
def test_get_bucket_name_and_prefix_for_notification_valid_notification(sample_notification, created_at, folder):
|
|
|
|
|
sample_notification.created_at = created_at
|
2019-09-05 11:47:31 +01:00
|
|
|
sample_notification.updated_at = created_at
|
2018-10-17 16:09:30 +01:00
|
|
|
|
|
|
|
|
bucket, bucket_prefix = get_bucket_name_and_prefix_for_notification(sample_notification)
|
|
|
|
|
|
|
|
|
|
assert bucket == current_app.config['LETTERS_PDF_BUCKET_NAME']
|
2018-03-05 14:11:37 +00:00
|
|
|
assert bucket_prefix == '{folder}/NOTIFY.{reference}'.format(
|
2018-11-27 12:21:41 +00:00
|
|
|
folder=folder,
|
2018-03-05 14:11:37 +00:00
|
|
|
reference=sample_notification.reference
|
|
|
|
|
).upper()
|
|
|
|
|
|
|
|
|
|
|
2019-09-16 14:20:40 +01:00
|
|
|
def test_get_bucket_name_and_prefix_for_notification_is_tomorrow_after_17_30(sample_notification):
|
2019-09-05 11:47:31 +01:00
|
|
|
sample_notification.created_at = datetime(2019, 8, 1, 17, 35)
|
|
|
|
|
sample_notification.sent_at = datetime(2019, 8, 2, 17, 45)
|
|
|
|
|
|
|
|
|
|
bucket, bucket_prefix = get_bucket_name_and_prefix_for_notification(sample_notification)
|
|
|
|
|
|
|
|
|
|
assert bucket == current_app.config['LETTERS_PDF_BUCKET_NAME']
|
|
|
|
|
assert bucket_prefix == '{folder}/NOTIFY.{reference}'.format(
|
|
|
|
|
folder='2019-08-02',
|
|
|
|
|
reference=sample_notification.reference
|
|
|
|
|
).upper()
|
|
|
|
|
|
|
|
|
|
|
2019-09-16 14:20:40 +01:00
|
|
|
def test_get_bucket_name_and_prefix_for_notification_is_today_before_17_30(sample_notification):
|
2019-09-09 14:18:08 +01:00
|
|
|
sample_notification.created_at = datetime(2019, 8, 1, 12, 00)
|
|
|
|
|
sample_notification.updated_at = datetime(2019, 8, 2, 12, 00)
|
|
|
|
|
sample_notification.sent_at = datetime(2019, 8, 3, 12, 00)
|
|
|
|
|
|
|
|
|
|
bucket, bucket_prefix = get_bucket_name_and_prefix_for_notification(sample_notification)
|
|
|
|
|
|
|
|
|
|
assert bucket == current_app.config['LETTERS_PDF_BUCKET_NAME']
|
|
|
|
|
assert bucket_prefix == '{folder}/NOTIFY.{reference}'.format(
|
2019-09-16 14:20:40 +01:00
|
|
|
folder='2019-08-01',
|
2019-09-09 14:18:08 +01:00
|
|
|
reference=sample_notification.reference
|
|
|
|
|
).upper()
|
|
|
|
|
|
|
|
|
|
|
2018-03-14 21:27:07 +00:00
|
|
|
@freeze_time(FROZEN_DATE_TIME)
|
2018-10-17 16:09:30 +01:00
|
|
|
def test_get_bucket_name_and_prefix_for_notification_precompiled_letter_using_test_key(
|
|
|
|
|
sample_precompiled_letter_notification_using_test_key
|
|
|
|
|
):
|
|
|
|
|
bucket, bucket_prefix = get_bucket_name_and_prefix_for_notification(
|
|
|
|
|
sample_precompiled_letter_notification_using_test_key)
|
|
|
|
|
|
|
|
|
|
assert bucket == current_app.config['TEST_LETTERS_BUCKET_NAME']
|
|
|
|
|
assert bucket_prefix == 'NOTIFY.{}'.format(
|
|
|
|
|
sample_precompiled_letter_notification_using_test_key.reference).upper()
|
|
|
|
|
|
|
|
|
|
|
2019-09-03 16:49:03 +01:00
|
|
|
@freeze_time(FROZEN_DATE_TIME)
|
|
|
|
|
def test_get_bucket_name_and_prefix_for_notification_templated_letter_using_test_key(sample_letter_notification):
|
|
|
|
|
sample_letter_notification.key_type = KEY_TYPE_TEST
|
|
|
|
|
|
|
|
|
|
bucket, bucket_prefix = get_bucket_name_and_prefix_for_notification(sample_letter_notification)
|
|
|
|
|
|
|
|
|
|
assert bucket == current_app.config['TEST_LETTERS_BUCKET_NAME']
|
|
|
|
|
assert bucket_prefix == 'NOTIFY.{}'.format(sample_letter_notification.reference).upper()
|
|
|
|
|
|
|
|
|
|
|
2018-10-17 16:09:30 +01:00
|
|
|
@freeze_time(FROZEN_DATE_TIME)
|
|
|
|
|
def test_get_bucket_name_and_prefix_for_failed_validation(sample_precompiled_letter_notification):
|
|
|
|
|
sample_precompiled_letter_notification.status = NOTIFICATION_VALIDATION_FAILED
|
|
|
|
|
bucket, bucket_prefix = get_bucket_name_and_prefix_for_notification(sample_precompiled_letter_notification)
|
|
|
|
|
|
|
|
|
|
assert bucket == current_app.config['INVALID_PDF_BUCKET_NAME']
|
|
|
|
|
assert bucket_prefix == 'NOTIFY.{}'.format(
|
|
|
|
|
sample_precompiled_letter_notification.reference).upper()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@freeze_time(FROZEN_DATE_TIME)
|
|
|
|
|
def test_get_bucket_name_and_prefix_for_test_noti_with_failed_validation(
|
2018-03-14 21:27:07 +00:00
|
|
|
sample_precompiled_letter_notification_using_test_key
|
|
|
|
|
):
|
2018-10-17 16:09:30 +01:00
|
|
|
sample_precompiled_letter_notification_using_test_key.status = NOTIFICATION_VALIDATION_FAILED
|
|
|
|
|
bucket, bucket_prefix = get_bucket_name_and_prefix_for_notification(
|
|
|
|
|
sample_precompiled_letter_notification_using_test_key
|
|
|
|
|
)
|
2018-03-14 21:27:07 +00:00
|
|
|
|
2018-10-17 16:09:30 +01:00
|
|
|
assert bucket == current_app.config['INVALID_PDF_BUCKET_NAME']
|
2018-03-14 21:27:07 +00:00
|
|
|
assert bucket_prefix == 'NOTIFY.{}'.format(
|
|
|
|
|
sample_precompiled_letter_notification_using_test_key.reference).upper()
|
|
|
|
|
|
|
|
|
|
|
2018-10-17 16:09:30 +01:00
|
|
|
def test_get_bucket_name_and_prefix_for_notification_invalid_notification():
|
2018-03-05 14:11:37 +00:00
|
|
|
with pytest.raises(AttributeError):
|
2018-10-17 16:09:30 +01:00
|
|
|
get_bucket_name_and_prefix_for_notification(None)
|
2018-03-14 18:15:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('crown_flag,expected_crown_text', [
|
|
|
|
|
(True, 'C'),
|
|
|
|
|
(False, 'N'),
|
|
|
|
|
])
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
def test_generate_letter_pdf_filename_returns_correct_filename(
|
2018-03-14 18:15:00 +00:00
|
|
|
notify_api, mocker, crown_flag, expected_crown_text):
|
2020-09-21 14:40:22 +01:00
|
|
|
created_at = datetime(2017, 12, 4, 17, 29)
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
filename = generate_letter_pdf_filename(reference='foo', crown=crown_flag, created_at=created_at)
|
2018-03-14 18:15:00 +00:00
|
|
|
|
|
|
|
|
assert filename == '2017-12-04/NOTIFY.FOO.D.2.C.{}.20171204172900.PDF'.format(expected_crown_text)
|
|
|
|
|
|
|
|
|
|
|
2018-09-25 11:04:58 +01:00
|
|
|
@pytest.mark.parametrize('postage,expected_postage', [
|
|
|
|
|
('second', 2),
|
|
|
|
|
('first', 1),
|
|
|
|
|
])
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
def test_generate_letter_pdf_filename_returns_correct_postage_for_filename(
|
2018-09-25 11:04:58 +01:00
|
|
|
notify_api, postage, expected_postage):
|
2020-09-21 14:40:22 +01:00
|
|
|
created_at = datetime(2017, 12, 4, 17, 29)
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
filename = generate_letter_pdf_filename(reference='foo', crown=True, created_at=created_at, postage=postage)
|
2018-09-25 11:04:58 +01:00
|
|
|
|
|
|
|
|
assert filename == '2017-12-04/NOTIFY.FOO.D.{}.C.C.20171204172900.PDF'.format(expected_postage)
|
|
|
|
|
|
|
|
|
|
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
def test_generate_letter_pdf_filename_returns_correct_filename_for_test_letters(
|
2018-03-14 18:15:00 +00:00
|
|
|
notify_api, mocker):
|
2020-09-21 14:40:22 +01:00
|
|
|
created_at = datetime(2017, 12, 4, 17, 29)
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
filename = generate_letter_pdf_filename(
|
|
|
|
|
reference='foo',
|
|
|
|
|
crown='C',
|
|
|
|
|
created_at=created_at,
|
|
|
|
|
ignore_folder=True
|
|
|
|
|
)
|
2018-03-14 18:15:00 +00:00
|
|
|
|
|
|
|
|
assert filename == 'NOTIFY.FOO.D.2.C.C.20171204172900.PDF'
|
|
|
|
|
|
|
|
|
|
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
def test_generate_letter_pdf_filename_returns_tomorrows_filename(notify_api, mocker):
|
2020-09-21 14:40:22 +01:00
|
|
|
created_at = datetime(2017, 12, 4, 17, 31)
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
filename = generate_letter_pdf_filename(reference='foo', crown=True, created_at=created_at)
|
2018-03-14 18:15:00 +00:00
|
|
|
|
|
|
|
|
assert filename == '2017-12-05/NOTIFY.FOO.D.2.C.C.20171204173100.PDF'
|
2018-03-14 21:27:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@mock_s3
|
|
|
|
|
@pytest.mark.parametrize('bucket_config_name,filename_format', [
|
|
|
|
|
('TEST_LETTERS_BUCKET_NAME', 'NOTIFY.FOO.D.2.C.C.%Y%m%d%H%M%S.PDF'),
|
|
|
|
|
('LETTERS_PDF_BUCKET_NAME', '%Y-%m-%d/NOTIFY.FOO.D.2.C.C.%Y%m%d%H%M%S.PDF')
|
|
|
|
|
])
|
|
|
|
|
@freeze_time(FROZEN_DATE_TIME)
|
|
|
|
|
def test_get_letter_pdf_gets_pdf_from_correct_bucket(
|
|
|
|
|
sample_precompiled_letter_notification_using_test_key,
|
|
|
|
|
bucket_config_name,
|
|
|
|
|
filename_format
|
|
|
|
|
):
|
|
|
|
|
if bucket_config_name == 'LETTERS_PDF_BUCKET_NAME':
|
|
|
|
|
sample_precompiled_letter_notification_using_test_key.key_type = KEY_TYPE_NORMAL
|
|
|
|
|
|
|
|
|
|
bucket_name = current_app.config[bucket_config_name]
|
|
|
|
|
filename = datetime.utcnow().strftime(filename_format)
|
|
|
|
|
conn = boto3.resource('s3', region_name='eu-west-1')
|
2020-12-07 15:03:41 +00:00
|
|
|
conn.create_bucket(
|
|
|
|
|
Bucket=bucket_name,
|
|
|
|
|
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
|
|
|
|
|
)
|
2018-03-14 21:27:07 +00:00
|
|
|
s3 = boto3.client('s3', region_name='eu-west-1')
|
|
|
|
|
s3.put_object(Bucket=bucket_name, Key=filename, Body=b'pdf_content')
|
|
|
|
|
|
2019-10-29 16:19:50 +00:00
|
|
|
file_data, metadata = get_letter_pdf_and_metadata(sample_precompiled_letter_notification_using_test_key)
|
2018-03-14 21:27:07 +00:00
|
|
|
|
2019-10-29 16:19:50 +00:00
|
|
|
assert file_data == b'pdf_content'
|
2018-03-13 14:08:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('is_precompiled_letter,bucket_config_name', [
|
|
|
|
|
(False, 'LETTERS_PDF_BUCKET_NAME'),
|
|
|
|
|
(True, 'LETTERS_SCAN_BUCKET_NAME')
|
|
|
|
|
])
|
|
|
|
|
def test_upload_letter_pdf_to_correct_bucket(
|
|
|
|
|
sample_letter_notification, mocker, is_precompiled_letter, bucket_config_name
|
|
|
|
|
):
|
|
|
|
|
if is_precompiled_letter:
|
|
|
|
|
sample_letter_notification.template.hidden = True
|
|
|
|
|
sample_letter_notification.template.name = PRECOMPILED_TEMPLATE_NAME
|
|
|
|
|
|
|
|
|
|
mock_s3 = mocker.patch('app.letters.utils.s3upload')
|
|
|
|
|
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
filename = generate_letter_pdf_filename(
|
2018-03-13 14:08:34 +00:00
|
|
|
reference=sample_letter_notification.reference,
|
2018-03-19 13:28:16 +00:00
|
|
|
crown=sample_letter_notification.service.crown,
|
2020-09-21 14:40:22 +01:00
|
|
|
created_at=sample_letter_notification.created_at,
|
2020-09-21 13:46:31 +01:00
|
|
|
ignore_folder=is_precompiled_letter
|
2018-03-13 14:08:34 +00:00
|
|
|
)
|
|
|
|
|
|
2018-04-09 13:56:44 +01:00
|
|
|
upload_letter_pdf(sample_letter_notification, b'\x00\x01', precompiled=is_precompiled_letter)
|
2018-03-13 14:08:34 +00:00
|
|
|
|
|
|
|
|
mock_s3.assert_called_once_with(
|
|
|
|
|
bucket_name=current_app.config[bucket_config_name],
|
|
|
|
|
file_location=filename,
|
|
|
|
|
filedata=b'\x00\x01',
|
2018-08-08 16:20:25 +01:00
|
|
|
region=current_app.config['AWS_REGION']
|
2018-03-13 14:08:34 +00:00
|
|
|
)
|
2018-03-19 13:28:16 +00:00
|
|
|
|
|
|
|
|
|
2018-09-25 11:04:58 +01:00
|
|
|
@pytest.mark.parametrize('postage,expected_postage', [
|
|
|
|
|
('second', 2),
|
|
|
|
|
('first', 1)
|
|
|
|
|
])
|
|
|
|
|
def test_upload_letter_pdf_uses_postage_from_notification(
|
|
|
|
|
sample_letter_template, mocker, postage, expected_postage
|
|
|
|
|
):
|
|
|
|
|
letter_notification = create_notification(template=sample_letter_template, postage=postage)
|
|
|
|
|
mock_s3 = mocker.patch('app.letters.utils.s3upload')
|
|
|
|
|
|
Relax lookup of letter PDFs in S3 buckets
Previously we generated the filename we expected a letter PDF to be
stored at in S3, and used that to retrieve it. However, the generated
filename can change over the course of a notification's lifetime e.g.
if the service changes from crown ('.C.') to non-crown ('.N.').
The prefix of the filename is stable: it's based on properties of the
notification - reference and creation - that don't change. This commit
changes the way we interact with letter PDFs in S3:
- Uploading uses the original method to generate the full file name.
The method is renamed to 'generate_' to distinguish it from the new one.
- Downloading uses a new 'find_' method to get the filename using just
its prefix, which makes it agnostic to changes in the filename suffix.
Making this change helps to decouple our code from the requirements DVLA
have on the filenames. While it means more traffic to S3, we rely on S3
in any case to download the files. From experience, we know S3 is highly
reliable and performant, so don't anticipate any issues.
In the tests we favour using moto to mock S3, so that the behaviour is
realistic. There are a couple of places where we just mock the method,
since what it returns isn't important for the test.
Note that, since the new method requires a notification object, we need
to change a query in one place, the columns of which were only selected
to appease the original method to generate a filename.
2021-03-08 15:23:37 +00:00
|
|
|
filename = generate_letter_pdf_filename(
|
2018-09-25 11:04:58 +01:00
|
|
|
reference=letter_notification.reference,
|
|
|
|
|
crown=letter_notification.service.crown,
|
2020-09-21 14:40:22 +01:00
|
|
|
created_at=letter_notification.created_at,
|
2020-09-21 13:46:31 +01:00
|
|
|
ignore_folder=False,
|
2018-09-25 11:04:58 +01:00
|
|
|
postage=letter_notification.postage
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
upload_letter_pdf(letter_notification, b'\x00\x01', precompiled=False)
|
|
|
|
|
|
|
|
|
|
mock_s3.assert_called_once_with(
|
|
|
|
|
bucket_name=current_app.config['LETTERS_PDF_BUCKET_NAME'],
|
|
|
|
|
file_location=filename,
|
|
|
|
|
filedata=b'\x00\x01',
|
|
|
|
|
region=current_app.config['AWS_REGION']
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2018-03-23 15:27:24 +00:00
|
|
|
@mock_s3
|
|
|
|
|
@freeze_time(FROZEN_DATE_TIME)
|
|
|
|
|
def test_move_failed_pdf_error(notify_api):
|
|
|
|
|
filename = 'test.pdf'
|
2018-03-29 14:28:06 +01:00
|
|
|
bucket_name = current_app.config['LETTERS_SCAN_BUCKET_NAME']
|
2018-03-23 15:27:24 +00:00
|
|
|
|
|
|
|
|
conn = boto3.resource('s3', region_name='eu-west-1')
|
2020-12-07 15:03:41 +00:00
|
|
|
bucket = conn.create_bucket(
|
|
|
|
|
Bucket=bucket_name,
|
|
|
|
|
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
|
|
|
|
|
)
|
2018-03-23 15:27:24 +00:00
|
|
|
|
|
|
|
|
s3 = boto3.client('s3', region_name='eu-west-1')
|
2018-03-29 14:28:06 +01:00
|
|
|
s3.put_object(Bucket=bucket_name, Key=filename, Body=b'pdf_content')
|
2018-03-23 15:27:24 +00:00
|
|
|
|
|
|
|
|
move_failed_pdf(filename, ScanErrorType.ERROR)
|
|
|
|
|
|
2018-03-29 14:28:06 +01:00
|
|
|
assert 'ERROR/' + filename in [o.key for o in bucket.objects.all()]
|
|
|
|
|
assert filename not in [o.key for o in bucket.objects.all()]
|
2018-03-23 15:27:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@mock_s3
|
|
|
|
|
@freeze_time(FROZEN_DATE_TIME)
|
|
|
|
|
def test_move_failed_pdf_scan_failed(notify_api):
|
|
|
|
|
filename = 'test.pdf'
|
2018-03-29 14:28:06 +01:00
|
|
|
bucket_name = current_app.config['LETTERS_SCAN_BUCKET_NAME']
|
2018-03-23 15:27:24 +00:00
|
|
|
|
|
|
|
|
conn = boto3.resource('s3', region_name='eu-west-1')
|
2020-12-07 15:03:41 +00:00
|
|
|
bucket = conn.create_bucket(
|
|
|
|
|
Bucket=bucket_name,
|
|
|
|
|
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
|
|
|
|
|
)
|
2018-03-23 15:27:24 +00:00
|
|
|
|
|
|
|
|
s3 = boto3.client('s3', region_name='eu-west-1')
|
2018-03-29 14:28:06 +01:00
|
|
|
s3.put_object(Bucket=bucket_name, Key=filename, Body=b'pdf_content')
|
2018-03-23 15:27:24 +00:00
|
|
|
|
|
|
|
|
move_failed_pdf(filename, ScanErrorType.FAILURE)
|
|
|
|
|
|
2018-03-29 14:28:06 +01:00
|
|
|
assert 'FAILURE/' + filename in [o.key for o in bucket.objects.all()]
|
|
|
|
|
assert filename not in [o.key for o in bucket.objects.all()]
|
2018-05-30 10:18:37 +01:00
|
|
|
|
|
|
|
|
|
2020-09-21 13:46:31 +01:00
|
|
|
@pytest.mark.parametrize("timestamp, expected_folder_name",
|
2018-05-30 10:18:37 +01:00
|
|
|
[("2018-04-01 17:50:00", "2018-04-02/"),
|
|
|
|
|
("2018-07-02 16:29:00", "2018-07-02/"),
|
|
|
|
|
("2018-07-02 16:30:00", "2018-07-02/"),
|
|
|
|
|
("2018-07-02 16:31:00", "2018-07-03/"),
|
|
|
|
|
("2018-01-02 16:31:00", "2018-01-02/"),
|
|
|
|
|
("2018-01-02 17:31:00", "2018-01-03/"),
|
2018-07-02 14:16:40 +01:00
|
|
|
|
|
|
|
|
("2018-07-02 22:30:00", "2018-07-03/"),
|
|
|
|
|
("2018-07-02 23:30:00", "2018-07-03/"),
|
|
|
|
|
("2018-07-03 00:30:00", "2018-07-03/"),
|
|
|
|
|
|
|
|
|
|
("2018-01-02 22:30:00", "2018-01-03/"),
|
|
|
|
|
("2018-01-02 23:30:00", "2018-01-03/"),
|
|
|
|
|
("2018-01-03 00:30:00", "2018-01-03/"),
|
2018-05-30 10:18:37 +01:00
|
|
|
])
|
2020-09-21 13:46:31 +01:00
|
|
|
def test_get_folder_name_in_british_summer_time(notify_api, timestamp, expected_folder_name):
|
|
|
|
|
timestamp = dateutil.parser.parse(timestamp)
|
2020-09-21 14:24:43 +01:00
|
|
|
folder_name = get_folder_name(created_at=timestamp)
|
2018-05-30 10:18:37 +01:00
|
|
|
assert folder_name == expected_folder_name
|
|
|
|
|
|
|
|
|
|
|
2019-12-04 16:02:46 +00:00
|
|
|
@mock_s3
|
|
|
|
|
def test_move_sanitised_letter_to_live_pdf_bucket(notify_api, mocker):
|
|
|
|
|
filename = 'my_letter.pdf'
|
|
|
|
|
source_bucket_name = current_app.config['LETTER_SANITISE_BUCKET_NAME']
|
|
|
|
|
target_bucket_name = current_app.config['LETTERS_PDF_BUCKET_NAME']
|
|
|
|
|
|
|
|
|
|
conn = boto3.resource('s3', region_name='eu-west-1')
|
2020-12-07 15:03:41 +00:00
|
|
|
source_bucket = conn.create_bucket(
|
|
|
|
|
Bucket=source_bucket_name,
|
|
|
|
|
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
|
|
|
|
|
)
|
|
|
|
|
target_bucket = conn.create_bucket(
|
|
|
|
|
Bucket=target_bucket_name,
|
|
|
|
|
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
|
|
|
|
|
)
|
2019-12-04 16:02:46 +00:00
|
|
|
|
|
|
|
|
s3 = boto3.client('s3', region_name='eu-west-1')
|
|
|
|
|
s3.put_object(Bucket=source_bucket_name, Key=filename, Body=b'pdf_content')
|
|
|
|
|
|
|
|
|
|
move_sanitised_letter_to_test_or_live_pdf_bucket(
|
|
|
|
|
filename=filename,
|
|
|
|
|
is_test_letter=False,
|
2020-09-15 16:17:33 +01:00
|
|
|
created_at=datetime.utcnow(),
|
|
|
|
|
new_filename=filename
|
2019-12-04 16:02:46 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert not [x for x in source_bucket.objects.all()]
|
|
|
|
|
assert len([x for x in target_bucket.objects.all()]) == 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@mock_s3
|
|
|
|
|
def test_move_sanitised_letter_to_test_pdf_bucket(notify_api, mocker):
|
|
|
|
|
filename = 'my_letter.pdf'
|
|
|
|
|
source_bucket_name = current_app.config['LETTER_SANITISE_BUCKET_NAME']
|
|
|
|
|
target_bucket_name = current_app.config['TEST_LETTERS_BUCKET_NAME']
|
|
|
|
|
|
|
|
|
|
conn = boto3.resource('s3', region_name='eu-west-1')
|
2020-12-07 15:03:41 +00:00
|
|
|
source_bucket = conn.create_bucket(
|
|
|
|
|
Bucket=source_bucket_name,
|
|
|
|
|
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
|
|
|
|
|
)
|
|
|
|
|
target_bucket = conn.create_bucket(
|
|
|
|
|
Bucket=target_bucket_name,
|
|
|
|
|
CreateBucketConfiguration={'LocationConstraint': 'eu-west-1'}
|
|
|
|
|
)
|
2019-12-04 16:02:46 +00:00
|
|
|
|
|
|
|
|
s3 = boto3.client('s3', region_name='eu-west-1')
|
|
|
|
|
s3.put_object(Bucket=source_bucket_name, Key=filename, Body=b'pdf_content')
|
|
|
|
|
|
|
|
|
|
move_sanitised_letter_to_test_or_live_pdf_bucket(
|
|
|
|
|
filename=filename,
|
|
|
|
|
is_test_letter=True,
|
2020-09-15 16:17:33 +01:00
|
|
|
created_at=datetime.utcnow(),
|
|
|
|
|
new_filename=filename
|
2019-12-04 16:02:46 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert not [x for x in source_bucket.objects.all()]
|
|
|
|
|
assert len([x for x in target_bucket.objects.all()]) == 1
|
|
|
|
|
|
|
|
|
|
|
2018-11-22 11:53:32 +00:00
|
|
|
@freeze_time('2017-07-07 20:00:00')
|
|
|
|
|
def test_letter_print_day_returns_today_if_letter_was_printed_after_1730_yesterday():
|
|
|
|
|
created_at = datetime(2017, 7, 6, 17, 30)
|
|
|
|
|
assert letter_print_day(created_at) == 'today'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@freeze_time('2017-07-07 16:30:00')
|
|
|
|
|
def test_letter_print_day_returns_today_if_letter_was_printed_today():
|
|
|
|
|
created_at = datetime(2017, 7, 7, 12, 0)
|
|
|
|
|
assert letter_print_day(created_at) == 'today'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('created_at, formatted_date', [
|
|
|
|
|
(datetime(2017, 7, 5, 16, 30), 'on 6 July'),
|
|
|
|
|
(datetime(2017, 7, 6, 16, 29), 'on 6 July'),
|
|
|
|
|
(datetime(2016, 8, 8, 10, 00), 'on 8 August'),
|
|
|
|
|
(datetime(2016, 12, 12, 17, 29), 'on 12 December'),
|
|
|
|
|
(datetime(2016, 12, 12, 17, 30), 'on 13 December'),
|
|
|
|
|
])
|
|
|
|
|
@freeze_time('2017-07-07 16:30:00')
|
|
|
|
|
def test_letter_print_day_returns_formatted_date_if_letter_printed_before_1730_yesterday(created_at, formatted_date):
|
|
|
|
|
assert letter_print_day(created_at) == formatted_date
|