diff --git a/app/main/__init__.py b/app/main/__init__.py index 2a6bff976..6d431a63a 100644 --- a/app/main/__init__.py +++ b/app/main/__init__.py @@ -29,6 +29,7 @@ from app.main.views import ( # noqa find_users, platform_admin, email_branding, + letter_branding, conversation, organisations, notifications, diff --git a/app/main/forms.py b/app/main/forms.py index 52aeffb41..4cc423401 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -851,6 +851,21 @@ class ServiceUpdateEmailBranding(StripWhitespaceForm): raise ValidationError('This field is required') +class SVGFileUpload(StripWhitespaceForm): + file = FileField_wtf( + 'Upload an SVG logo', + validators=[ + FileAllowed(['svg'], 'SVG Images only!'), + DataRequired(message="You need to upload a file to submit") + ] + ) + + +class ServiceLetterBrandingDetails(StripWhitespaceForm): + name = StringField('Name of brand', validators=[DataRequired()]) + domain = GovernmentDomainField('Domain') + + class PDFUploadForm(StripWhitespaceForm): file = FileField_wtf( 'Upload a letter in PDF format to check if it fits in the printable area', diff --git a/app/main/views/letter_branding.py b/app/main/views/letter_branding.py new file mode 100644 index 000000000..0e9002f2d --- /dev/null +++ b/app/main/views/letter_branding.py @@ -0,0 +1,103 @@ +from flask import ( + current_app, + redirect, + render_template, + request, + session, + url_for, +) +from flask_login import login_required +from requests import get as requests_get + +from app import letter_branding_client +from app.main import main +from app.main.forms import ServiceLetterBrandingDetails, SVGFileUpload +from app.s3_client.s3_logo_client import ( + LETTER_TEMP_TAG, + delete_letter_temp_file, + delete_letter_temp_files_created_by, + get_letter_filename_with_no_path_or_extension, + letter_filename_for_db, + permanent_letter_logo_name, + persist_logo, + upload_letter_png_logo, + upload_letter_temp_logo, +) +from app.utils import get_logo_cdn_domain, user_is_platform_admin + + +@main.route("/letter-branding/create", methods=['GET', 'POST']) +@main.route("/letter-branding/create/", methods=['GET', 'POST']) +@login_required +@user_is_platform_admin +def create_letter_branding(logo=None): + file_upload_form = SVGFileUpload() + letter_branding_details_form = ServiceLetterBrandingDetails() + + file_upload_form_submitted = file_upload_form.file.data + details_form_submitted = request.form.get('operation') == 'branding-details' + + if file_upload_form_submitted and file_upload_form.validate_on_submit(): + upload_filename = upload_letter_temp_logo( + file_upload_form.file.data.filename, + file_upload_form.file.data, + current_app.config['AWS_REGION'], + user_id=session["user_id"] + ) + + if logo and logo.startswith(LETTER_TEMP_TAG.format(user_id=session['user_id'])): + delete_letter_temp_file(logo) + + return redirect(url_for('.create_letter_branding', logo=upload_filename)) + + if details_form_submitted and letter_branding_details_form.validate_on_submit(): + if logo: + db_filename = letter_filename_for_db(logo, session['user_id']) + + letter_branding_client.create_letter_branding( + filename=db_filename, + name=letter_branding_details_form.name.data, + domain=letter_branding_details_form.domain.data, + ) + + png_file = get_png_file_from_svg(logo) + + persist_logo(logo, permanent_letter_logo_name(db_filename, 'svg')) + + upload_letter_png_logo( + permanent_letter_logo_name(db_filename, 'png'), + png_file, + current_app.config['AWS_REGION'], + ) + + delete_letter_temp_files_created_by(session['user_id']) + + # TODO: redirect to all letter branding page once it exists + return redirect(url_for('main.platform_admin')) + + # Show error on upload form if trying to submit with no logo + file_upload_form.validate() + + return render_template( + 'views/letter-branding/manage-letter-branding.html', + file_upload_form=file_upload_form, + letter_branding_details_form=letter_branding_details_form, + cdn_url=get_logo_cdn_domain(), + logo=logo + ) + + +def get_png_file_from_svg(filename): + filename_for_template_preview = get_letter_filename_with_no_path_or_extension(filename) + + template_preview_svg_endpoint = '{}/{}.svg.png'.format( + current_app.config['TEMPLATE_PREVIEW_API_HOST'], + filename_for_template_preview + ) + + response = requests_get( + template_preview_svg_endpoint, + headers={'Authorization': 'Token {}'.format(current_app.config['TEMPLATE_PREVIEW_API_KEY'])} + ) + + return response.content diff --git a/app/navigation.py b/app/navigation.py index 05e43e9f2..f784360fe 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -75,6 +75,7 @@ class HeaderNavigation(Navigation): 'platform-admin': { 'add_organisation', 'create_email_branding', + 'create_letter_branding', 'email_branding', 'find_users_by_email', 'live_services', @@ -405,6 +406,7 @@ class MainNavigation(Navigation): 'conversation_updates', 'cookies', 'create_email_branding', + 'create_letter_branding', 'data_retention', 'delete_template_folder', 'delivery_and_failure', @@ -583,6 +585,7 @@ class CaseworkNavigation(Navigation): 'copy_template', 'create_api_key', 'create_email_branding', + 'create_letter_branding', 'data_retention', 'delete_service_template', 'delete_template_folder', @@ -817,6 +820,7 @@ class OrgNavigation(Navigation): 'copy_template', 'create_api_key', 'create_email_branding', + 'create_letter_branding', 'data_retention', 'delete_service_template', 'delete_template_folder', diff --git a/app/s3_client/s3_logo_client.py b/app/s3_client/s3_logo_client.py index 63dadb10d..bb8626bae 100644 --- a/app/s3_client/s3_logo_client.py +++ b/app/s3_client/s3_logo_client.py @@ -6,6 +6,9 @@ from notifications_utils.s3 import s3upload as utils_s3upload TEMP_TAG = 'temp-{user_id}_' EMAIL_LOGO_LOCATION_STRUCTURE = '{temp}{unique_id}-{filename}' +LETTER_PREFIX = 'letters/static/images/letter-template/' +LETTER_TEMP_TAG = LETTER_PREFIX + TEMP_TAG +LETTER_TEMP_LOGO_LOCATION = 'letters/static/images/letter-template/temp-{user_id}_{unique_id}-{filename}' def get_s3_object(bucket_name, filename): @@ -33,10 +36,14 @@ def get_s3_objects_filter_by_prefix(prefix): return s3.Bucket(bucket_name).objects.filter(Prefix=prefix) -def get_temp_truncated_email_filename(filename, user_id): +def get_temp_truncated_filename(filename, user_id): return filename[len(TEMP_TAG.format(user_id=user_id)):] +def get_letter_filename_with_no_path_or_extension(filename): + return filename[len(LETTER_PREFIX):-4] + + def upload_email_logo(filename, filedata, region, user_id): upload_file_name = EMAIL_LOGO_LOCATION_STRUCTURE.format( temp=TEMP_TAG.format(user_id=user_id), @@ -55,20 +62,74 @@ def upload_email_logo(filename, filedata, region, user_id): return upload_file_name +def upload_letter_temp_logo(filename, filedata, region, user_id): + upload_filename = LETTER_TEMP_LOGO_LOCATION.format( + user_id=user_id, + unique_id=str(uuid.uuid4()), + filename=filename + ) + bucket_name = current_app.config['LOGO_UPLOAD_BUCKET_NAME'] + utils_s3upload( + filedata=filedata, + region=region, + bucket_name=bucket_name, + file_location=upload_filename, + content_type='image/svg+xml' + ) + + return upload_filename + + +def upload_letter_png_logo(filename, filedata, region): + bucket_name = current_app.config['LOGO_UPLOAD_BUCKET_NAME'] + utils_s3upload( + filedata=filedata, + region=region, + bucket_name=bucket_name, + file_location=filename, + content_type='image/png' + ) + + def permanent_email_logo_name(filename, user_id): if filename.startswith(TEMP_TAG.format(user_id=user_id)): - return get_temp_truncated_email_filename(filename=filename, user_id=user_id) + return get_temp_truncated_filename(filename=filename, user_id=user_id) else: return filename +def permanent_letter_logo_name(filename, extension): + return LETTER_PREFIX + filename + '.' + extension + + +def letter_filename_for_db(filename, user_id): + filename = get_letter_filename_with_no_path_or_extension(filename) + + if filename.startswith(TEMP_TAG.format(user_id=user_id)): + filename = get_temp_truncated_filename(filename=filename, user_id=user_id) + + return filename + + def delete_email_temp_files_created_by(user_id): for obj in get_s3_objects_filter_by_prefix(TEMP_TAG.format(user_id=user_id)): delete_s3_object(obj.key) +def delete_letter_temp_files_created_by(user_id): + for obj in get_s3_objects_filter_by_prefix(LETTER_TEMP_TAG.format(user_id=user_id)): + delete_s3_object(obj.key) + + def delete_email_temp_file(filename): if not filename.startswith(TEMP_TAG[:5]): raise ValueError('Not a temp file: {}'.format(filename)) delete_s3_object(filename) + + +def delete_letter_temp_file(filename): + if not filename.startswith(LETTER_TEMP_TAG[:43]): + raise ValueError('Not a temp file: {}'.format(filename)) + + delete_s3_object(filename) diff --git a/app/templates/views/letter-branding/manage-letter-branding.html b/app/templates/views/letter-branding/manage-letter-branding.html new file mode 100644 index 000000000..060054208 --- /dev/null +++ b/app/templates/views/letter-branding/manage-letter-branding.html @@ -0,0 +1,37 @@ +{% extends "views/platform-admin/_base_template.html" %} +{% from "components/file-upload.html" import file_upload %} +{% from "components/page-footer.html" import page_footer %} +{% from "components/textbox.html" import textbox %} +{% from "components/form.html" import form_wrapper %} + +{% block per_page_title %} + Create letter branding +{% endblock %} + +{% block platform_admin_content %} + +

Add letter branding

+
+
+ {% if logo %} +
+ +
+ {% endif %} + {{ file_upload(file_upload_form.file) }} + {% call form_wrapper() %} +
+
{{textbox(letter_branding_details_form.name)}}
+
{{textbox(letter_branding_details_form.domain)}}
+ {{ page_footer( + 'Save', + button_name='operation', + button_value='branding-details', + back_link=url_for('main.platform_admin'), + back_link_text='Back to letter branding selection', + ) }} +
+ {% endcall %} +
+ +{% endblock %} diff --git a/tests/app/main/views/test_letter_branding.py b/tests/app/main/views/test_letter_branding.py new file mode 100644 index 000000000..85be63ef6 --- /dev/null +++ b/tests/app/main/views/test_letter_branding.py @@ -0,0 +1,228 @@ +from io import BytesIO + +import pytest +from bs4 import BeautifulSoup +from flask import current_app, url_for + +from app.main.views.letter_branding import get_png_file_from_svg +from app.s3_client.s3_logo_client import LETTER_TEMP_LOGO_LOCATION + + +def test_create_letter_branding_does_not_show_branding_info(logged_in_platform_admin_client): + response = logged_in_platform_admin_client.get( + url_for('.create_letter_branding') + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert page.select_one('#logo-img > img') is None + assert page.select_one('#name').attrs.get('value') == '' + assert page.select_one('#domain').attrs.get('value') == '' + + +def test_create_letter_branding_when_uploading_valid_file( + mocker, + logged_in_platform_admin_client, + fake_uuid +): + with logged_in_platform_admin_client.session_transaction() as session: + user_id = session["user_id"] + + filename = 'test.svg' + expected_temp_filename = LETTER_TEMP_LOGO_LOCATION.format(user_id=user_id, unique_id=fake_uuid, filename=filename) + + mock_s3_upload = mocker.patch('app.s3_client.s3_logo_client.utils_s3upload') + mocker.patch('app.s3_client.s3_logo_client.uuid.uuid4', return_value=fake_uuid) + mock_delete_temp_files = mocker.patch('app.main.views.letter_branding.delete_letter_temp_file') + + response = logged_in_platform_admin_client.post( + url_for('.create_letter_branding'), + data={'file': (BytesIO(''.encode('utf-8')), filename)}, + follow_redirects=True + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert page.select_one('#logo-img > img').attrs['src'].endswith(expected_temp_filename) + assert mock_s3_upload.called + mock_delete_temp_files.assert_not_called() + + +def test_create_letter_branding_when_uploading_invalid_file(logged_in_platform_admin_client): + response = logged_in_platform_admin_client.post( + url_for('.create_letter_branding'), + data={'file': (BytesIO(''.encode('utf-8')), 'test.png')}, + follow_redirects=True + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert page.find('h1').text == 'Add letter branding' + assert page.select_one('.error-message').text.strip() == 'SVG Images only!' + + +def test_create_letter_branding_deletes_temp_files_when_uploading_a_new_file( + mocker, + logged_in_platform_admin_client, + fake_uuid, +): + with logged_in_platform_admin_client.session_transaction() as session: + user_id = session["user_id"] + + temp_logo = LETTER_TEMP_LOGO_LOCATION.format(user_id=user_id, unique_id=fake_uuid, filename='temp.svg') + + mock_s3_upload = mocker.patch('app.s3_client.s3_logo_client.utils_s3upload') + mock_delete_temp_files = mocker.patch('app.main.views.letter_branding.delete_letter_temp_file') + + response = logged_in_platform_admin_client.post( + url_for('.create_letter_branding', logo=temp_logo), + data={'file': (BytesIO(''.encode('utf-8')), 'new.svg')}, + follow_redirects=True + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert mock_s3_upload.called + assert mock_delete_temp_files.called + assert page.find('h1').text == 'Add letter branding' + + +def test_create_new_letter_branding_shows_preview_of_logo( + mocker, + logged_in_platform_admin_client, + fake_uuid +): + with logged_in_platform_admin_client.session_transaction() as session: + user_id = session["user_id"] + + temp_logo = LETTER_TEMP_LOGO_LOCATION.format(user_id=user_id, unique_id=fake_uuid, filename='temp.svg') + + response = logged_in_platform_admin_client.get( + url_for('.create_letter_branding', logo=temp_logo) + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert page.find('h1').text == 'Add letter branding' + assert page.select_one('#logo-img > img').attrs['src'].endswith(temp_logo) + + +def test_create_letter_branding_shows_an_error_when_submitting_details_with_no_logo( + logged_in_platform_admin_client, + fake_uuid +): + response = logged_in_platform_admin_client.post( + url_for('.create_letter_branding'), + data={ + 'name': 'Test brand', + 'domain': 'bl.uk', + 'operation': 'branding-details' + } + ) + + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + + assert page.find('h1').text == 'Add letter branding' + assert page.select_one('.error-message').text.strip() == 'You need to upload a file to submit' + + +@pytest.mark.parametrize('domain', ['bl.uk', '']) +def test_create_letter_branding_persists_logo_when_all_data_is_valid( + mocker, + logged_in_platform_admin_client, + fake_uuid, + domain +): + with logged_in_platform_admin_client.session_transaction() as session: + user_id = session["user_id"] + + temp_logo = LETTER_TEMP_LOGO_LOCATION.format(user_id=user_id, unique_id=fake_uuid, filename='test.svg') + + mock_letter_client = mocker.patch('app.main.views.letter_branding.letter_branding_client') + mock_template_preview = mocker.patch( + 'app.main.views.letter_branding.get_png_file_from_svg', + return_value='fake_png') + mock_persist_logo = mocker.patch('app.main.views.letter_branding.persist_logo') + mock_upload_png = mocker.patch('app.main.views.letter_branding.upload_letter_png_logo') + mock_delete_temp_files = mocker.patch('app.main.views.letter_branding.delete_letter_temp_files_created_by') + + # TODO: remove platform admin page mocks once we no longer redirect there + mocker.patch('app.main.views.platform_admin.make_columns') + mocker.patch('app.main.views.platform_admin.platform_stats_api_client.get_aggregate_platform_stats') + mocker.patch('app.main.views.platform_admin.complaint_api_client.get_complaint_count') + + response = logged_in_platform_admin_client.post( + url_for('.create_letter_branding', logo=temp_logo), + data={ + 'name': 'Test brand', + 'domain': 'bl.uk', + 'operation': 'branding-details' + }, + follow_redirects=True + ) + + assert response.status_code == 200 + + mock_letter_client.create_letter_branding.assert_called_once_with( + domain='bl.uk', filename='{}-test'.format(fake_uuid), name='Test brand' + ) + assert mock_template_preview.called + mock_persist_logo.assert_called_once_with( + temp_logo, + 'letters/static/images/letter-template/{}-test.svg'.format(fake_uuid) + ) + mock_upload_png.assert_called_once_with( + 'letters/static/images/letter-template/{}-test.png'.format(fake_uuid), + 'fake_png', + current_app.config['AWS_REGION'] + ) + mock_delete_temp_files.assert_called_once_with(user_id) + + +def test_create_letter_branding_shows_errors_on_name_and_domain_fields( + logged_in_platform_admin_client, + fake_uuid +): + with logged_in_platform_admin_client.session_transaction() as session: + user_id = session["user_id"] + + temp_logo = LETTER_TEMP_LOGO_LOCATION.format(user_id=user_id, unique_id=fake_uuid, filename='test.svg') + + response = logged_in_platform_admin_client.post( + url_for('.create_letter_branding', logo=temp_logo), + data={ + 'name': '', + 'domain': 'example.com', + 'operation': 'branding-details' + } + ) + + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + error_messages = page.find_all('span', class_='error-message') + + assert page.find('h1').text == 'Add letter branding' + assert len(error_messages) == 2 + assert error_messages[0].text.strip() == 'This field is required.' + assert error_messages[1].text.strip() == 'Not a known government domain (you might need to update domains.yml)' + + +def test_get_png_file_from_svg(client, mocker, fake_uuid): + mocker.patch.dict( + 'flask.current_app.config', + {'TEMPLATE_PREVIEW_API_HOST': 'localhost', 'TEMPLATE_PREVIEW_API_KEY': 'abc'} + ) + tp_mock = mocker.patch('app.main.views.letter_branding.requests_get') + filename = LETTER_TEMP_LOGO_LOCATION.format(user_id=fake_uuid, unique_id=fake_uuid, filename='test.svg') + + get_png_file_from_svg(filename) + + tp_mock.assert_called_once_with( + 'localhost/temp-{}_{}-test.svg.png'.format(fake_uuid, fake_uuid), + headers={'Authorization': 'Token abc'} + ) diff --git a/tests/app/s3_client/test_s3_logo_client.py b/tests/app/s3_client/test_s3_logo_client.py index 1a5755ad5..29cedd4a9 100644 --- a/tests/app/s3_client/test_s3_logo_client.py +++ b/tests/app/s3_client/test_s3_logo_client.py @@ -5,17 +5,25 @@ import pytest from app.s3_client.s3_logo_client import ( EMAIL_LOGO_LOCATION_STRUCTURE, + LETTER_TEMP_LOGO_LOCATION, + LETTER_TEMP_TAG, TEMP_TAG, delete_email_temp_file, delete_email_temp_files_created_by, + delete_letter_temp_file, + delete_letter_temp_files_created_by, + letter_filename_for_db, permanent_email_logo_name, persist_logo, upload_email_logo, + upload_letter_png_logo, + upload_letter_temp_logo, ) bucket = 'test_bucket' data = {'data': 'some_data'} filename = 'test.png' +svg_filename = 'test.svg' upload_id = 'test_uuid' region = 'eu-west1' @@ -26,6 +34,15 @@ def upload_filename(fake_uuid): temp=TEMP_TAG.format(user_id=fake_uuid), unique_id=upload_id, filename=filename) +@pytest.fixture +def letter_upload_filename(fake_uuid): + return LETTER_TEMP_LOGO_LOCATION.format( + user_id=fake_uuid, + unique_id=upload_id, + filename=svg_filename + ) + + def test_upload_email_logo_calls_correct_args(client, mocker, fake_uuid, upload_filename): mocker.patch('uuid.uuid4', return_value=upload_id) mocker.patch.dict('flask.current_app.config', {'LOGO_UPLOAD_BUCKET_NAME': bucket}) @@ -42,6 +59,38 @@ def test_upload_email_logo_calls_correct_args(client, mocker, fake_uuid, upload_ ) +def test_upload_letter_temp_logo_calls_correct_args(mocker, fake_uuid, letter_upload_filename): + mocker.patch('uuid.uuid4', return_value=upload_id) + mocker.patch.dict('flask.current_app.config', {'LOGO_UPLOAD_BUCKET_NAME': bucket}) + mocked_s3_upload = mocker.patch('app.s3_client.s3_logo_client.utils_s3upload') + + new_filename = upload_letter_temp_logo(filename=svg_filename, user_id=fake_uuid, filedata=data, region=region) + + mocked_s3_upload.assert_called_once_with( + filedata=data, + region=region, + bucket_name=bucket, + file_location=letter_upload_filename, + content_type='image/svg+xml' + ) + assert new_filename == 'letters/static/images/letter-template/temp-{}_test_uuid-test.svg'.format(fake_uuid) + + +def test_upload_letter_png_logo_calls_correct_args(mocker): + mocked_s3_upload = mocker.patch('app.s3_client.s3_logo_client.utils_s3upload') + mocker.patch.dict('flask.current_app.config', {'LOGO_UPLOAD_BUCKET_NAME': bucket}) + + upload_letter_png_logo(filename, data, region) + + mocked_s3_upload.assert_called_once_with( + filedata=data, + region=region, + bucket_name=bucket, + file_location=filename, + content_type='image/png' + ) + + def test_persist_logo(client, mocker, fake_uuid, upload_filename): mocker.patch.dict('flask.current_app.config', {'LOGO_UPLOAD_BUCKET_NAME': bucket}) mocked_get_s3_object = mocker.patch('app.s3_client.s3_logo_client.get_s3_object') @@ -79,6 +128,16 @@ def test_permanent_email_logo_name_does_not_change_filenames_with_no_TEMP_TAG(): assert new_name == filename +def test_letter_filename_for_db_when_file_has_a_temp_tag(fake_uuid): + temp_filename = LETTER_TEMP_LOGO_LOCATION.format(user_id=fake_uuid, unique_id=upload_id, filename=svg_filename) + assert letter_filename_for_db(temp_filename, fake_uuid) == 'test_uuid-test' + + +def test_letter_filename_for_db_when_file_does_not_have_a_temp_tag(fake_uuid): + filename = 'letters/static/images/letter-template/{}-test.svg'.format(fake_uuid) + assert letter_filename_for_db(filename, fake_uuid) == '{}-test'.format(fake_uuid) + + def test_delete_email_temp_files_created_by_user(client, mocker, fake_uuid): obj = namedtuple("obj", ["key"]) objs = [obj(key='test1'), obj(key='test2')] @@ -92,7 +151,20 @@ def test_delete_email_temp_files_created_by_user(client, mocker, fake_uuid): assert arg == call(objs[index].key) -def test_delete_single_temp_file(client, mocker, fake_uuid, upload_filename): +def test_delete_letter_temp_files_created_by_user(mocker, fake_uuid): + obj = namedtuple("obj", ["key"]) + objs = [obj(key='test1'), obj(key='test2')] + + mocker.patch('app.s3_client.s3_logo_client.get_s3_objects_filter_by_prefix', return_value=objs) + mocked_delete_s3_object = mocker.patch('app.s3_client.s3_logo_client.delete_s3_object') + + delete_letter_temp_files_created_by(fake_uuid) + + for index, arg in enumerate(mocked_delete_s3_object.call_args_list): + assert arg == call(objs[index].key) + + +def test_delete_single_email_temp_file(client, mocker, upload_filename): mocked_delete_s3_object = mocker.patch('app.s3_client.s3_logo_client.delete_s3_object') delete_email_temp_file(upload_filename) @@ -100,7 +172,7 @@ def test_delete_single_temp_file(client, mocker, fake_uuid, upload_filename): mocked_delete_s3_object.assert_called_with(upload_filename) -def test_does_not_delete_non_temp_file(client, mocker, fake_uuid): +def test_does_not_delete_non_temp_email_file(client, mocker): filename = 'logo.png' mocked_delete_s3_object = mocker.patch('app.s3_client.s3_logo_client.delete_s3_object') @@ -109,3 +181,23 @@ def test_does_not_delete_non_temp_file(client, mocker, fake_uuid): mocked_delete_s3_object.assert_not_called assert str(error.value) == 'Not a temp file: {}'.format(filename) + + +def test_delete_single_temp_letter_file(mocker, fake_uuid, upload_filename): + mocked_delete_s3_object = mocker.patch('app.s3_client.s3_logo_client.delete_s3_object') + + upload_filename = LETTER_TEMP_TAG.format(user_id=fake_uuid) + svg_filename + + delete_letter_temp_file(upload_filename) + + mocked_delete_s3_object.assert_called_with(upload_filename) + + +def test_does_not_delete_non_temp_letter_file(mocker, fake_uuid): + mocked_delete_s3_object = mocker.patch('app.s3_client.s3_logo_client.delete_s3_object') + + with pytest.raises(ValueError) as error: + delete_letter_temp_file(svg_filename) + + mocked_delete_s3_object.assert_not_called + assert str(error.value) == 'Not a temp file: {}'.format(svg_filename)