Files
notifications-admin/tests/app/main/views/test_templates.py
2025-06-09 12:28:08 -07:00

2137 lines
59 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
from unittest.mock import ANY, Mock
import pytest
from bs4 import BeautifulSoup
from flask import url_for
from freezegun import freeze_time
from notifications_python_client.errors import HTTPError
from tests import template_json, validate_route_permission
from tests.app.main.views.test_template_folders import (
CHILD_FOLDER_ID,
FOLDER_TWO_ID,
PARENT_FOLDER_ID,
_folder,
_template,
)
from tests.conftest import (
SERVICE_ONE_ID,
SERVICE_TWO_ID,
TEMPLATE_ONE_ID,
ElementNotFound,
create_active_caseworking_user,
create_active_user_view_permissions,
create_template,
normalize_spaces,
)
@pytest.mark.parametrize(
("permissions", "expected_message"),
[
(
["email"],
(
"Every message starts with a template. You can change it later. "
"You need a template before you can send messages."
),
),
(
["sms"],
(
"Every message starts with a template. You can change it later. "
"You need a template before you can send messages."
),
),
(
["email", "sms"],
(
"Every message starts with a template. You can change it later. "
"You need a template before you can send messages."
),
),
],
)
def test_should_show_empty_page_when_no_templates(
client_request,
service_one,
mock_get_service_templates_when_no_templates_exist,
mock_get_template_folders,
mock_get_no_api_keys,
permissions,
expected_message,
):
service_one["permissions"] = permissions
page = client_request.get(
"main.choose_template",
service_id=service_one["id"],
)
assert normalize_spaces(page.select_one("h1").text) == (
"Select or create a template"
)
assert normalize_spaces(page.select_one("main p").text) == (expected_message)
assert page.select_one("#add_new_folder_form")
assert page.select_one("#add_new_template_form")
def test_should_show_add_template_form_if_service_has_folder_permission(
client_request,
service_one,
mock_get_service_templates_when_no_templates_exist,
mock_get_template_folders,
mock_get_no_api_keys,
):
page = client_request.get(
"main.choose_template",
service_id=service_one["id"],
)
assert normalize_spaces(page.select_one("h1").text) == (
"Select or create a template"
)
assert normalize_spaces(page.select_one("main p").text) == (
"Every message starts with a template. You can change it later. "
"You need a template before you can send messages."
)
assert [(item["name"], item["value"]) for item in page.select("[type=radio]")] == [
# ('add_template_by_template_type', 'email'),
("add_template_by_template_type", "sms"),
]
assert not page.select("main a")
@pytest.mark.parametrize(
(
"user",
"expected_page_title",
"extra_args",
"expected_nav_links",
"expected_templates",
),
[
(
create_active_user_view_permissions(),
"Select or create a template",
{},
["Email", "Text message"],
[
"sms_template_one",
"sms_template_two",
"email_template_one",
"email_template_two",
],
),
(
create_active_user_view_permissions(),
"Select or create a template",
{"template_type": "sms"},
["All", "Email"],
["sms_template_one", "sms_template_two"],
),
(
create_active_user_view_permissions(),
"Select or create a template",
{"template_type": "email"},
["All", "Text message"],
["email_template_one", "email_template_two"],
),
(
create_active_caseworking_user(),
"Select or create a template",
{},
["Email", "Text message"],
[
"sms_template_one",
"sms_template_two",
"email_template_one",
"email_template_two",
],
),
(
create_active_caseworking_user(),
"Select or create a template",
{"template_type": "email"},
["All", "Text message"],
["email_template_one", "email_template_two"],
),
],
)
def test_should_show_page_for_choosing_a_template(
client_request,
mock_get_service_templates,
mock_get_template_folders,
mock_has_no_jobs,
mock_get_no_api_keys,
extra_args,
expected_nav_links,
expected_templates,
service_one,
mocker,
user,
expected_page_title,
):
client_request.login(user)
page = client_request.get(
"main.choose_template", service_id=service_one["id"], **extra_args
)
assert normalize_spaces(page.select_one("h1").text) == expected_page_title
links_in_page = page.select(".pill a:not(.pill-item--selected)")
assert len(links_in_page) == len(expected_nav_links)
for index, expected_link in enumerate(expected_nav_links):
assert links_in_page[index].text.strip() == expected_link
template_links = page.select("#template-list a, .template-list-item a")
assert len(template_links) == len(expected_templates)
for index, expected_template in enumerate(expected_templates):
assert template_links[index].text.strip() == expected_template
mock_get_service_templates.assert_called_once_with(SERVICE_ONE_ID)
mock_get_template_folders.assert_called_once_with(SERVICE_ONE_ID)
def test_choose_template_can_pass_through_an_initial_state_to_templates_and_folders_selection_form(
client_request,
mock_get_template_folders,
mock_get_service_templates,
mock_get_no_api_keys,
):
page = client_request.get(
"main.choose_template",
service_id=SERVICE_ONE_ID,
initial_state="add-new-template",
)
templates_and_folders_form = page.find("form")
assert templates_and_folders_form["data-prev-state"] == "add-new-template"
def test_should_not_show_template_nav_if_only_one_type_of_template(
client_request,
mock_get_template_folders,
mock_get_service_templates_with_only_one_template,
mock_get_no_api_keys,
):
page = client_request.get(
"main.choose_template",
service_id=SERVICE_ONE_ID,
)
assert not page.select(".pill")
def test_should_not_show_live_search_if_list_of_templates_fits_onscreen(
client_request,
mock_get_template_folders,
mock_get_service_templates,
mock_get_no_api_keys,
):
page = client_request.get(
"main.choose_template",
service_id=SERVICE_ONE_ID,
)
assert not page.select(".live-search")
def test_should_show_live_search_if_list_of_templates_taller_than_screen(
client_request,
mock_get_template_folders,
mock_get_more_service_templates_than_can_fit_onscreen,
mock_get_no_api_keys,
):
page = client_request.get(
"main.choose_template",
service_id=SERVICE_ONE_ID,
)
search = page.select_one(".live-search")
assert search["data-module"] == "live-search"
assert search["data-targets"] == "#template-list .template-list-item"
assert normalize_spaces(search.select_one("label").text) == ("Search by name")
assert (
len(page.select(search["data-targets"]))
== len(page.select("#template-list .usa-checkbox"))
== 20
)
def test_should_label_search_by_id_for_services_with_api_keys(
client_request,
mock_get_template_folders,
mock_get_more_service_templates_than_can_fit_onscreen,
mock_get_api_keys,
):
page = client_request.get(
"main.choose_template",
service_id=SERVICE_ONE_ID,
)
assert normalize_spaces(page.select_one(".live-search label").text) == (
"Search by name or ID"
)
def test_should_show_live_search_if_service_has_lots_of_folders(
client_request,
mock_get_template_folders,
mock_get_service_templates, # returns 4 templates
mock_get_no_api_keys,
):
mock_get_template_folders.return_value = [
_folder("one", PARENT_FOLDER_ID),
_folder("two", None, parent=PARENT_FOLDER_ID),
_folder("three", None, parent=PARENT_FOLDER_ID),
_folder("four", None, parent=PARENT_FOLDER_ID),
]
page = client_request.get(
"main.choose_template",
service_id=SERVICE_ONE_ID,
)
count_of_templates_and_folders = len(page.select("#template-list .usa-checkbox"))
count_of_folders = len(page.select(".template-list-folder:first-of-type"))
count_of_templates = count_of_templates_and_folders - count_of_folders
assert len(page.select(".live-search")) == 1
assert count_of_folders == 4
assert count_of_templates == 4
@pytest.mark.parametrize(
("service_permissions", "expected_values", "expected_labels"),
[
pytest.param(
["email", "sms"],
[
# 'email',
"sms",
"copy-existing",
],
[
# 'Email',
"Start with a blank template",
"Copy an existing template",
],
),
# TODO This is a duplicate of above. Why?
# pytest.param(
# ["email", "sms"],
# [
# # 'email',
# "sms",
# "copy-existing",
# ],
# [
# # 'Email',
# "Start with a blank template",
# "Copy an existing template",
# ],
# ),
],
)
def test_should_show_new_template_choices_if_service_has_folder_permission(
client_request,
service_one,
mock_get_service_templates,
mock_get_template_folders,
mock_get_no_api_keys,
service_permissions,
expected_values,
expected_labels,
):
service_one["permissions"] = service_permissions
page = client_request.get(
"main.choose_template",
service_id=SERVICE_ONE_ID,
)
if not page.select("#add_new_template_form"):
raise ElementNotFound()
assert normalize_spaces(
page.select_one("#add_new_template_form fieldset legend").text
) == ("New template")
assert [
choice["value"]
for choice in page.select("#add_new_template_form input[type=radio]")
] == expected_values
assert [
normalize_spaces(choice.text)
for choice in page.select("#add_new_template_form label")
] == expected_labels
@pytest.mark.parametrize(
("permissions", "are_data_attrs_added"),
[
(["sms"], True),
(["email"], True),
(["sms", "email"], False),
],
)
def test_should_add_data_attributes_for_services_that_only_allow_one_type_of_notifications(
client_request,
service_one,
mock_get_service_templates,
mock_get_template_folders,
mock_get_no_api_keys,
permissions,
are_data_attrs_added,
):
service_one["permissions"] = permissions
page = client_request.get(
"main.choose_template",
service_id=SERVICE_ONE_ID,
)
if not page.select("#add_new_template_form"):
raise ElementNotFound()
if are_data_attrs_added:
assert (
page.find(id="add_new_template_form").attrs["data-channel"]
== permissions[0]
)
assert (
page.find(id="add_new_template_form").attrs["data-service"]
== SERVICE_ONE_ID
)
else:
assert page.find(id="add_new_template_form").attrs.get("data-channel") is None
assert page.find(id="add_new_template_form").attrs.get("data-service") is None
def test_should_show_page_for_one_template(
client_request,
mock_get_service_template,
fake_uuid,
):
template_id = fake_uuid
page = client_request.get(
".edit_service_template",
service_id=SERVICE_ONE_ID,
template_id=template_id,
)
assert page.select_one("input[type=text]")["value"] == "Two week reminder"
assert "Template <em>content</em> with & entity" in str(
page.select_one("textarea")
)
assert page.select_one("textarea")["data-module"] == "enhanced-textbox"
assert page.select_one("textarea")["data-highlight-placeholders"] == "true"
assert "priority" not in str(page.select_one("main"))
assert (
(page.select_one("[data-module=update-status]")["data-target"])
== (page.select_one("textarea")["id"])
== ("template_content")
)
assert (
page.select_one("[data-module=update-status]")["data-updates-url"]
) == url_for(
".count_content_length",
service_id=SERVICE_ONE_ID,
template_type="sms",
)
assert (page.select_one("[data-module=update-status]")["aria-live"]) == ("polite")
mock_get_service_template.assert_called_with(SERVICE_ONE_ID, template_id, None)
def test_caseworker_redirected_to_set_sender_for_one_off(
client_request,
mock_get_service_templates,
mock_get_service_template,
mocker,
fake_uuid,
active_caseworking_user,
):
client_request.login(active_caseworking_user)
client_request.get(
"main.view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_expected_status=302,
_expected_redirect=url_for(
"main.set_sender",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
),
)
@freeze_time("2020-01-01 20:00")
def test_caseworker_sees_template_page_if_template_is_deleted(
client_request,
mock_get_deleted_template,
fake_uuid,
mocker,
active_caseworking_user,
):
mocker.patch("app.user_api_client.get_user", return_value=active_caseworking_user)
template_id = fake_uuid
page = client_request.get(
".view_template",
service_id=SERVICE_ONE_ID,
template_id=template_id,
_test_page_title=False,
)
content = str(page)
assert (
url_for("main.send_one_off", service_id=SERVICE_ONE_ID, template_id=fake_uuid)
not in content
)
assert (
page.select("p.hint")[0].text.strip()
== "This template was deleted today at 15:00 America/New_York."
)
mock_get_deleted_template.assert_called_with(SERVICE_ONE_ID, template_id, None)
def test_user_with_only_send_and_view_redirected_to_set_sender_for_one_off(
client_request,
mock_get_service_templates,
mock_get_service_template,
active_user_with_permissions,
mocker,
fake_uuid,
):
active_user_with_permissions["permissions"][SERVICE_ONE_ID] = [
"send_messages",
"view_activity",
]
client_request.login(active_user_with_permissions)
client_request.get(
"main.view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_expected_status=302,
_expected_redirect=url_for(
"main.set_sender",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
),
)
@pytest.mark.parametrize(
("permissions", "links_to_be_shown", "permissions_warning_to_be_shown"),
[
(
["view_activity"],
[],
"If you need to send this text message or edit this template, contact your manager.",
),
(
["manage_api_keys"],
[],
None,
),
(
["manage_templates"],
[
(".edit_service_template", "Edit this template"),
],
None,
),
(
["send_messages", "manage_templates"],
[
(".set_sender", "Use this template"),
(".edit_service_template", "Edit this template"),
],
None,
),
],
)
def test_should_be_able_to_view_a_template_with_links(
client_request,
mock_get_service_template,
mock_get_template_folders,
active_user_with_permissions,
fake_uuid,
permissions,
links_to_be_shown,
permissions_warning_to_be_shown,
):
active_user_with_permissions["permissions"][SERVICE_ONE_ID] = permissions + [
"view_activity"
]
client_request.login(active_user_with_permissions)
page = client_request.get(
".view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_test_page_title=False,
)
assert normalize_spaces(page.select_one("h1").text) == ("Confirm your template")
assert normalize_spaces(page.select_one("title").text) == (
"Confirm your template service one Notify.gov"
)
assert [
(link["href"], normalize_spaces(link.text))
for link in page.select(".usa-pill-separate-item")
] == [
(
url_for(
endpoint,
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
),
text,
)
for endpoint, text in links_to_be_shown
]
assert normalize_spaces(page.select_one("main p").text) == (
"To: phone number" or permissions_warning_to_be_shown
)
@pytest.mark.skip(
reason="Hiding the copy-to-clipboard widget until API access is opened up"
)
def test_should_show_template_id_on_template_page(
client_request,
mock_get_service_template,
mock_get_template_folders,
fake_uuid,
):
page = client_request.get(
".view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_test_page_title=False,
)
assert fake_uuid in page.select(".copy-to-clipboard__value")[0].text
def test_should_show_sms_template_with_downgraded_unicode_characters(
client_request,
mocker,
service_one,
mock_get_template_folders,
fake_uuid,
):
msg = "here:\tare some “fancy quotes” and zero\u200bwidth\u200bspaces"
rendered_msg = "here: are some “fancy quotes” and zerowidthspaces"
mocker.patch(
"app.service_api_client.get_service_template",
return_value={
"data": template_json(
service_one["id"], fake_uuid, type_="sms", content=msg
)
},
)
page = client_request.get(
".view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_test_page_title=False,
)
assert rendered_msg in page.text
@pytest.mark.parametrize(
"prefix_sms", [True, pytest.param(False, marks=pytest.mark.xfail())]
)
def test_should_show_message_with_prefix_hint_if_enabled_for_service(
client_request,
mocker,
mock_get_service_template,
mock_get_users_by_service,
service_one,
fake_uuid,
prefix_sms,
):
service_one["prefix_sms"] = prefix_sms
page = client_request.get(
".edit_service_template",
service_id=service_one["id"],
template_id=fake_uuid,
)
assert "Your service name will be added to the start of your message" in page.text
def test_should_show_page_template_with_priority_select_if_platform_admin(
client_request,
platform_admin_user,
mocker,
mock_get_service_template,
service_one,
fake_uuid,
):
mocker.patch(
"app.user_api_client.get_users_for_service", return_value=[platform_admin_user]
)
template_id = fake_uuid
client_request.login(platform_admin_user)
page = client_request.get(
".edit_service_template",
service_id=service_one["id"],
template_id=template_id,
)
assert page.select_one("input[name=name]")["value"] == "Two week reminder"
assert "Template <em>content</em> with & entity" in str(
page.select_one("textarea")
)
assert "Use priority queue?" not in page.text
mock_get_service_template.assert_called_with(service_one["id"], template_id, None)
def test_choosing_to_copy_redirects(
client_request,
service_one,
mock_get_service_templates,
mock_get_template_folders,
):
client_request.post(
"main.choose_template",
service_id=SERVICE_ONE_ID,
_data={
"operation": "add-new-template",
"add_template_by_template_type": "copy-existing",
},
_expected_status=302,
_expected_redirect=url_for(
"main.choose_template_to_copy",
service_id=SERVICE_ONE_ID,
),
)
def test_choose_a_template_to_copy(
client_request,
mock_get_service_templates,
mock_get_template_folders,
mock_get_no_api_keys,
mock_get_just_services_for_user,
):
page = client_request.get(
"main.choose_template_to_copy",
service_id=SERVICE_ONE_ID,
)
assert page.select(".folder-heading") == []
expected = [
(""),
("Service 1 sms_template_one"),
("Service 1 sms_template_two"),
("Service 1 email_template_one"),
("Service 1 email_template_two"),
(""),
("Service 2 sms_template_one"),
("Service 2 sms_template_two"),
("Service 2 email_template_one"),
("Service 2 email_template_two"),
]
actual = page.select(".template-list-item")
assert len(actual) == len(expected)
zipobject = zip(actual, expected)
for actual, expected in zipobject:
assert normalize_spaces(actual.text) == expected
links = page.select("main nav a")
assert links[0]["href"] == url_for(
"main.choose_template_to_copy",
service_id=SERVICE_ONE_ID,
from_service=SERVICE_TWO_ID,
)
def test_choose_a_template_to_copy_when_user_has_one_service(
client_request,
mock_get_service_templates,
mock_get_template_folders,
mock_get_no_api_keys,
mock_get_empty_organizations_and_one_service_for_user,
):
page = client_request.get(
"main.choose_template_to_copy",
service_id=SERVICE_ONE_ID,
)
assert page.select(".folder-heading") == []
expected = [
("sms_template_one"),
("sms_template_two"),
("email_template_one"),
("email_template_two"),
]
actual = page.select(".template-list-item")
assert len(actual) == len(expected)
zipobject = zip(actual, expected)
for actual, expected in zipobject:
assert normalize_spaces(actual.text) == expected
assert page.select("main nav a")[0]["href"] == url_for(
"main.copy_template",
service_id=SERVICE_ONE_ID,
template_id=TEMPLATE_ONE_ID,
from_service=SERVICE_TWO_ID,
)
def test_choose_a_template_to_copy_from_folder_within_service(
mocker,
client_request,
mock_get_template_folders,
mock_get_non_empty_organizations_and_services_for_user,
mock_get_no_api_keys,
):
mock_get_template_folders.return_value = [
_folder("Parent folder", PARENT_FOLDER_ID),
_folder("", CHILD_FOLDER_ID, parent=PARENT_FOLDER_ID),
_folder("Child folder non-empty", FOLDER_TWO_ID, parent=PARENT_FOLDER_ID),
]
mocker.patch(
"app.service_api_client.get_service_templates",
return_value={
"data": [
_template(
"sms",
"Should not appear in list (at service root)",
),
_template(
"sms",
"Should appear in list (at same level)",
parent=PARENT_FOLDER_ID,
),
_template(
"sms",
"Should appear in list (nested)",
parent=FOLDER_TWO_ID,
template_id=TEMPLATE_ONE_ID,
),
]
},
)
page = client_request.get(
"main.choose_template_to_copy",
service_id=SERVICE_ONE_ID,
from_service=SERVICE_ONE_ID,
from_folder=PARENT_FOLDER_ID,
)
assert normalize_spaces(page.select_one(".folder-heading").text) == (
"service one Parent folder"
)
breadcrumb_links = page.select(".folder-heading a")
assert len(breadcrumb_links) == 1
assert breadcrumb_links[0]["href"] == url_for(
"main.choose_template_to_copy",
service_id=SERVICE_ONE_ID,
from_service=SERVICE_ONE_ID,
)
expected = [
(""),
(""),
("Child folder non-empty Should appear in list (nested)"),
("Should appear in list (at same level)"),
]
actual = page.select(".template-list-item")
assert len(actual) == len(expected)
zipobject = zip(actual, expected)
for actual, expected in zipobject:
assert normalize_spaces(actual.text) == expected
links = page.select("main nav a")
assert links[0]["href"] == url_for(
"main.choose_template_to_copy",
service_id=SERVICE_ONE_ID,
from_folder=FOLDER_TWO_ID,
)
# assert links[1]["href"] == url_for(
# "main.choose_template_to_copy",
# service_id=SERVICE_ONE_ID,
# from_service=SERVICE_ONE_ID,
# from_folder=PARENT_FOLDER_ID,
# )
# assert links[2]["href"] == url_for(
# "main.choose_template_to_copy",
# service_id=SERVICE_ONE_ID,
# from_folder=FOLDER_TWO_ID,
# )
# assert links[3]["href"] == url_for(
# "main.copy_template",
# service_id=SERVICE_ONE_ID,
# template_id=TEMPLATE_ONE_ID,
# from_service=SERVICE_ONE_ID,
# )
@pytest.mark.parametrize(
("existing_template_names", "expected_name"),
[
(["Two week reminder"], "Two week reminder (copy)"),
(["Two week reminder (copy)"], "Two week reminder (copy 2)"),
(
["Two week reminder", "Two week reminder (copy)"],
"Two week reminder (copy 2)",
),
(
["Two week reminder (copy 8)", "Two week reminder (copy 9)"],
"Two week reminder (copy 10)",
),
(
["Two week reminder (copy)", "Two week reminder (copy 9)"],
"Two week reminder (copy 10)",
),
(
["Two week reminder (copy)", "Two week reminder (copy 10)"],
"Two week reminder (copy 2)",
),
],
)
def test_load_edit_template_with_copy_of_template(
client_request,
active_user_with_permission_to_two_services,
mock_get_service_templates,
mock_get_service_email_template,
mock_get_non_empty_organizations_and_services_for_user,
existing_template_names,
expected_name,
):
mock_get_service_templates.side_effect = lambda service_id: {
"data": [
{"name": existing_template_name, "template_type": "sms"}
for existing_template_name in existing_template_names
]
}
client_request.login(active_user_with_permission_to_two_services)
page = client_request.get(
"main.copy_template",
service_id=SERVICE_ONE_ID,
template_id=TEMPLATE_ONE_ID,
from_service=SERVICE_TWO_ID,
)
assert page.select_one("form")["method"] == "post"
assert page.select_one("input")["value"] == (expected_name)
assert page.select_one("textarea").text.strip() == ("Your ((thing)) is due soon")
mock_get_service_email_template.assert_called_once_with(
SERVICE_TWO_ID,
TEMPLATE_ONE_ID,
)
def test_copy_template_loads_template_from_within_subfolder(
client_request,
active_user_with_permission_to_two_services,
mock_get_service_templates,
mock_get_non_empty_organizations_and_services_for_user,
mocker,
):
template = template_json(
SERVICE_TWO_ID, TEMPLATE_ONE_ID, name="foo", folder=PARENT_FOLDER_ID
)
mock_get_service_template = mocker.patch(
"app.service_api_client.get_service_template", return_value={"data": template}
)
mock_get_template_folder = mocker.patch(
"app.template_folder_api_client.get_template_folder",
return_value=_folder("Parent folder", PARENT_FOLDER_ID),
)
client_request.login(active_user_with_permission_to_two_services)
page = client_request.get(
"main.copy_template",
service_id=SERVICE_ONE_ID,
template_id=TEMPLATE_ONE_ID,
from_service=SERVICE_TWO_ID,
)
assert page.select_one("input")["value"] == "foo (copy)"
mock_get_service_template.assert_called_once_with(SERVICE_TWO_ID, TEMPLATE_ONE_ID)
mock_get_template_folder.assert_called_once_with(SERVICE_TWO_ID, PARENT_FOLDER_ID)
def test_cant_copy_template_from_non_member_service(
client_request,
mock_get_service_email_template,
mock_get_organizations_and_services_for_user,
):
client_request.get(
"main.copy_template",
service_id=SERVICE_ONE_ID,
template_id=TEMPLATE_ONE_ID,
from_service=SERVICE_TWO_ID,
_expected_status=403,
)
assert mock_get_service_email_template.call_args_list == []
@pytest.mark.parametrize(
("service_permissions", "data", "expected_error"),
[
(
["email"],
{
"operation": "add-new-template",
"add_template_by_template_type": "sms",
},
"Sending text messages has been disabled for your service.",
),
],
)
def test_should_not_allow_creation_of_template_through_form_without_correct_permission(
client_request,
service_one,
mock_get_service_templates,
mock_get_template_folders,
service_permissions,
data,
expected_error,
fake_uuid,
):
service_one["permissions"] = service_permissions
page = client_request.post(
"main.choose_template",
service_id=SERVICE_ONE_ID,
_data=data,
_follow_redirects=True,
_expected_status=403,
)
assert normalize_spaces(page.select("main p")[0].text) == expected_error
assert page.select(".usa-back-link")[0].text == "Back"
assert page.select(".usa-back-link")[0]["href"] == url_for(
".choose_template",
service_id=SERVICE_ONE_ID,
)
@pytest.mark.parametrize("method", ["get", "post"])
@pytest.mark.parametrize(
("type_of_template", "expected_error"),
[
("email", "Sending emails has been disabled for your service."),
("sms", "Sending text messages has been disabled for your service."),
],
)
def test_should_not_allow_creation_of_a_template_without_correct_permission(
client_request,
service_one,
mocker,
method,
type_of_template,
expected_error,
):
service_one["permissions"] = []
page = getattr(client_request, method)(
".add_service_template",
service_id=SERVICE_ONE_ID,
template_type=type_of_template,
_follow_redirects=True,
_expected_status=403,
)
assert page.select("main p")[0].text.strip() == expected_error
assert page.select(".usa-back-link")[0].text == "Back"
assert page.select(".usa-back-link")[0]["href"] == url_for(
".choose_template",
service_id=service_one["id"],
)
def test_should_redirect_when_saving_a_template(
client_request,
mock_get_service_template,
mock_get_api_keys,
mock_update_service_template,
fake_uuid,
):
name = "new name"
content = "template <em>content</em> with & entity"
client_request.post(
".edit_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_data={
"id": fake_uuid,
"name": name,
"template_content": content,
"template_type": "sms",
"service": SERVICE_ONE_ID,
},
_expected_status=302,
_expected_redirect=url_for(
".view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
),
)
mock_update_service_template.assert_called_with(
fake_uuid, name, "sms", content, SERVICE_ONE_ID, None
)
def test_should_edit_content_when_process_type_is_priority_not_platform_admin(
client_request,
mock_get_service_template_with_priority,
mock_update_service_template,
fake_uuid,
):
client_request.post(
".edit_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_data={
"id": fake_uuid,
"name": "new name",
"template_content": "new template <em>content</em> with & entity",
"template_type": "sms",
"service": SERVICE_ONE_ID,
},
_expected_status=302,
_expected_redirect=url_for(
".view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
),
)
mock_update_service_template.assert_called_with(
fake_uuid,
"new name",
"sms",
"new template <em>content</em> with & entity",
SERVICE_ONE_ID,
None,
)
def test_should_not_allow_template_edits_without_correct_permission(
client_request,
mock_get_service_template,
service_one,
fake_uuid,
):
service_one["permissions"] = ["email"]
page = client_request.get(
".edit_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_follow_redirects=True,
_expected_status=403,
)
assert (
page.select("main p")[0].text.strip()
== "Sending text messages has been disabled for your service."
)
assert page.select(".usa-back-link")[0].text == "Back"
assert page.select(".usa-back-link")[0]["href"] == url_for(
".view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
)
def test_should_403_when_edit_template_with_process_type_of_priority_for_non_platform_admin(
client_request,
active_user_with_permissions,
mocker,
mock_get_service_template,
mock_update_service_template,
fake_uuid,
service_one,
):
service_one["users"] = [active_user_with_permissions]
client_request.login(active_user_with_permissions)
mocker.patch(
"app.user_api_client.get_users_for_service",
return_value=[active_user_with_permissions],
)
template_id = fake_uuid
data = {
"id": template_id,
"name": "new name",
"template_content": "template <em>content</em> with & entity",
"template_type": "sms",
"service": service_one["id"],
"process_type": "priority",
}
client_request.post(
".edit_service_template",
service_id=service_one["id"],
template_id=template_id,
_data=data,
_expected_status=403,
)
assert mock_update_service_template.called is False
def test_should_403_when_create_template_with_process_type_of_priority_for_non_platform_admin(
client_request,
active_user_with_permissions,
mocker,
mock_get_service_template,
mock_update_service_template,
fake_uuid,
service_one,
):
service_one["users"] = [active_user_with_permissions]
client_request.login(active_user_with_permissions, service_one)
mocker.patch(
"app.user_api_client.get_users_for_service",
return_value=[active_user_with_permissions],
)
template_id = fake_uuid
data = {
"id": template_id,
"name": "new name",
"template_content": "template <em>content</em> with & entity",
"template_type": "sms",
"service": service_one["id"],
"process_type": "priority",
}
client_request.post(
".add_service_template",
service_id=service_one["id"],
template_type="sms",
_data=data,
_expected_status=403,
)
assert mock_update_service_template.called is False
@pytest.mark.parametrize(
("old_content", "new_content", "expected_paragraphs"),
[
(
"my favorite color is blue",
"my favorite color is ((color))",
[
"You added ((color))",
"Before you send any messages, make sure your API calls include color.",
],
),
(
"hello ((name))",
"hello ((first name)) ((middle name)) ((last name))",
[
"You removed ((name))",
"You added ((first name)) ((middle name)) and ((last name))",
"Before you send any messages, make sure your API calls include first name, middle name and last name.",
],
),
],
)
def test_should_show_interstitial_when_making_breaking_change(
client_request,
mock_update_service_template,
mock_get_user_by_email,
mock_get_api_keys,
fake_uuid,
mocker,
new_content,
old_content,
expected_paragraphs,
):
email_template = create_template(
template_id=fake_uuid,
template_type="email",
subject="Your ((thing)) is due soon",
content=old_content,
)
mocker.patch(
"app.service_api_client.get_service_template",
return_value={"data": email_template},
)
data = {
"id": fake_uuid,
"name": "new name",
"template_content": new_content,
"template_type": "email",
"subject": "reminder '\" <span> & ((thing))",
"service": SERVICE_ONE_ID,
"process_type": "normal",
}
page = client_request.post(
".edit_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_data=data,
_expected_status=200,
)
assert page.h1.string.strip() == "Confirm changes"
assert page.find("a", {"class": "usa-back-link"})["href"] == url_for(
".edit_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
)
assert [
normalize_spaces(paragraph.text) for paragraph in page.select("main p")
] == expected_paragraphs
for key, value in {
"name": "new name",
"subject": "reminder '\" <span> & ((thing))",
"template_content": new_content,
"confirm": "true",
}.items():
assert page.find("input", {"name": key})["value"] == value
# BeautifulSoup returns the value attribute as unencoded, lets make
# sure that it is properly encoded in the HTML
assert str(page.find("input", {"name": "subject"})) == (
"""<input name="subject" type="hidden" value="reminder '&quot; &lt;span&gt; &amp; ((thing))"/>"""
)
def test_removing_placeholders_is_not_a_breaking_change(
client_request,
mock_get_service_email_template,
mock_update_service_template,
fake_uuid,
):
existing_template = mock_get_service_email_template(0, 0)["data"]
client_request.post(
".edit_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_data={
"name": existing_template["name"],
"template_content": "no placeholders",
"subject": existing_template["subject"],
},
_expected_status=302,
_expected_redirect=url_for(
"main.view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
),
)
assert mock_update_service_template.called is True
def test_should_not_create_too_big_template(
client_request,
mock_get_service_template,
mock_create_service_template_content_too_big,
fake_uuid,
):
page = client_request.post(
".add_service_template",
service_id=SERVICE_ONE_ID,
template_type="sms",
_data={
"name": "new name",
"template_content": "template content",
"template_type": "sms",
"service": SERVICE_ONE_ID,
"process_type": "normal",
},
_expected_status=200,
)
assert "Content has a character count greater than the limit of 459" in page.text
def test_should_not_update_too_big_template(
client_request,
mock_get_service_template,
mock_update_service_template_400_content_too_big,
fake_uuid,
):
page = client_request.post(
".edit_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_data={
"id": fake_uuid,
"name": "new name",
"template_content": "template content",
"service": SERVICE_ONE_ID,
"template_type": "sms",
"process_type": "normal",
},
_expected_status=200,
)
assert "Content has a character count greater than the limit of 459" in page.text
def test_should_redirect_when_saving_a_template_email(
client_request,
mock_get_service_email_template,
mock_update_service_template,
mock_get_user_by_email,
fake_uuid,
):
name = "new name"
content = "template <em>content</em> with & entity ((thing)) ((date))"
subject = "subject & entity"
client_request.post(
".edit_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_data={
"id": fake_uuid,
"name": name,
"template_content": content,
"template_type": "email",
"service": SERVICE_ONE_ID,
"subject": subject,
},
_expected_status=302,
_expected_redirect=url_for(
".view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
),
)
mock_update_service_template.assert_called_with(
fake_uuid, name, "email", content, SERVICE_ONE_ID, subject
)
def test_should_show_delete_template_page_with_time_block(
client_request,
mock_get_service_template,
mock_get_template_folders,
mocker,
fake_uuid,
):
mocker.patch(
"app.template_statistics_client.get_last_used_date_for_template",
return_value="2012-01-01 12:00:00",
)
with freeze_time("2012-01-01 12:10:00"):
page = client_request.get(
".delete_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_test_page_title=False,
)
assert (
"Are you sure you want to delete Two week reminder?"
in page.select(".banner-dangerous")[0].text
)
assert normalize_spaces(page.select(".banner-dangerous p")[0].text) == (
"This template was last used 10 minutes ago."
)
assert normalize_spaces(page.select(".sms-message-wrapper")[0].text) == (
"service one: Template <em>content</em> with & entity"
)
mock_get_service_template.assert_called_with(SERVICE_ONE_ID, fake_uuid, None)
def test_should_show_delete_template_page_with_time_block_for_empty_notification(
client_request,
mock_get_service_template,
mock_get_template_folders,
mocker,
fake_uuid,
):
mocker.patch(
"app.template_statistics_client.get_last_used_date_for_template",
return_value=None,
)
with freeze_time("2012-01-01 11:00:00"):
page = client_request.get(
".delete_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_test_page_title=False,
)
assert (
"Are you sure you want to delete Two week reminder?"
in page.select(".banner-dangerous")[0].text
)
assert normalize_spaces(page.select(".banner-dangerous p")[0].text) == (
"This template has never been used."
)
assert normalize_spaces(page.select(".sms-message-wrapper")[0].text) == (
"service one: Template <em>content</em> with & entity"
)
mock_get_service_template.assert_called_with(SERVICE_ONE_ID, fake_uuid, None)
def test_should_show_delete_template_page_with_never_used_block(
client_request,
mock_get_service_template,
mock_get_template_folders,
fake_uuid,
mocker,
):
mocker.patch(
"app.template_statistics_client.get_last_used_date_for_template",
side_effect=HTTPError(
response=Mock(status_code=404), message="Default message"
),
)
page = client_request.get(
".delete_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_test_page_title=False,
)
assert (
"Are you sure you want to delete Two week reminder?"
in page.select(".banner-dangerous")[0].text
)
assert not page.select(".banner-dangerous p")
assert normalize_spaces(page.select(".sms-message-wrapper")[0].text) == (
"service one: Template <em>content</em> with & entity"
)
mock_get_service_template.assert_called_with(SERVICE_ONE_ID, fake_uuid, None)
@pytest.mark.parametrize("parent", [PARENT_FOLDER_ID, None])
def test_should_redirect_when_deleting_a_template(
mocker,
client_request,
mock_delete_service_template,
mock_get_template_folders,
parent,
):
mock_get_template_folders.return_value = [
{
"id": PARENT_FOLDER_ID,
"name": "Folder",
"parent": None,
"users_with_permission": [ANY],
}
]
mock_get_service_template = mocker.patch(
"app.service_api_client.get_service_template",
return_value={
"data": _template(
"sms",
"Hello",
parent=parent,
)
},
)
client_request.post(
".delete_service_template",
service_id=SERVICE_ONE_ID,
template_id=TEMPLATE_ONE_ID,
_expected_status=302,
_expected_redirect=url_for(
".choose_template",
service_id=SERVICE_ONE_ID,
template_folder_id=parent,
),
)
mock_get_service_template.assert_called_with(SERVICE_ONE_ID, TEMPLATE_ONE_ID, None)
mock_delete_service_template.assert_called_with(SERVICE_ONE_ID, TEMPLATE_ONE_ID)
@freeze_time("2016-01-01T20:00")
def test_should_show_page_for_a_deleted_template(
client_request,
mock_get_template_folders,
mock_get_deleted_template,
mock_get_user,
mock_get_user_by_email,
mock_has_permissions,
fake_uuid,
):
template_id = fake_uuid
page = client_request.get(
".view_template",
service_id=SERVICE_ONE_ID,
template_id=template_id,
_test_page_title=False,
)
content = str(page)
assert (
url_for(
"main.edit_service_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
)
not in content
)
assert (
url_for("main.send_one_off", service_id=SERVICE_ONE_ID, template_id=fake_uuid)
not in content
)
assert (
page.select("p.hint")[0].text.strip()
== "This template was deleted today at 15:00 America/New_York."
)
assert "Delete this template" not in page.select_one("main").text
mock_get_deleted_template.assert_called_with(SERVICE_ONE_ID, template_id, None)
@pytest.mark.parametrize(
"route",
[
"main.add_service_template",
"main.edit_service_template",
"main.delete_service_template",
],
)
def test_route_permissions(
route,
mocker,
notify_admin,
client_request,
api_user_active,
service_one,
mock_get_service_template,
mock_get_template_folders,
fake_uuid,
):
mocker.patch(
"app.template_statistics_client.get_last_used_date_for_template",
return_value="2012-01-01 12:00:00",
)
validate_route_permission(
mocker,
notify_admin,
"GET",
200,
url_for(
route,
service_id=service_one["id"],
template_type="sms",
template_id=fake_uuid,
),
["manage_templates"],
api_user_active,
service_one,
)
def test_route_permissions_for_choose_template(
mocker,
notify_admin,
client_request,
api_user_active,
mock_get_template_folders,
service_one,
mock_get_service_templates,
mock_get_no_api_keys,
):
mocker.patch("app.job_api_client.get_job")
validate_route_permission(
mocker,
notify_admin,
"GET",
200,
url_for(
"main.choose_template",
service_id=service_one["id"],
),
[],
api_user_active,
service_one,
)
@pytest.mark.parametrize(
"route",
[
"main.add_service_template",
"main.edit_service_template",
"main.delete_service_template",
],
)
def test_route_invalid_permissions(
route,
mocker,
notify_admin,
client_request,
api_user_active,
service_one,
mock_get_service_template,
fake_uuid,
):
validate_route_permission(
mocker,
notify_admin,
"GET",
403,
url_for(
route,
service_id=service_one["id"],
template_type="sms",
template_id=fake_uuid,
),
["view_activity"],
api_user_active,
service_one,
)
@pytest.mark.parametrize(
("template_type", "expected"),
[
("email", "New email template"),
("sms", "New text message template"),
],
)
def test_add_template_page_title(
client_request,
service_one,
template_type,
expected,
):
service_one["permissions"] += [template_type]
page = client_request.get(
".add_service_template",
service_id=SERVICE_ONE_ID,
template_type=template_type,
)
assert normalize_spaces(page.select_one("h1").text) == expected
# def test_can_create_email_template_with_emoji(
# client_request, mock_create_service_template
# ):
# client_request.post(
# ".add_service_template",
# service_id=SERVICE_ONE_ID,
# template_type="email",
# _data={
# "name": "new name",
# "subject": "Food incoming!",
# "template_content": "here's a burrito 🌯",
# "template_type": "email",
# "service": SERVICE_ONE_ID,
# "process_type": "normal",
# },
# _expected_status=302,
# )
# assert mock_create_service_template.called is True
# @pytest.mark.parametrize(
# ("template_type", "expected_error"),
# [
# (
# "sms",
# (
# "Please remove the unaccepted character 🍜 in your message, then save again"
# ),
# ),
# ],
# )
# def test_should_not_create_sms_template_with_emoji(
# client_request,
# service_one,
# mock_create_service_template,
# template_type,
# expected_error,
# ):
# service_one["permissions"] += [template_type]
# page = client_request.post(
# ".add_service_template",
# service_id=SERVICE_ONE_ID,
# template_type=template_type,
# _data={
# "name": "new name",
# "template_content": "here are some noodles 🍜",
# "template_type": "sms",
# "service": SERVICE_ONE_ID,
# "process_type": "normal",
# },
# _expected_status=200,
# )
# # print(page.main.prettify())
# assert expected_error in normalize_spaces(
# page.select_one("#template_content-error").text
# )
# assert mock_create_service_template.called is False
# @pytest.mark.parametrize(
# ("template_type", "expected_error"),
# [
# (
# "sms",
# (
# "Please remove the unaccepted character 🍔 in your message, then save again"
# ),
# ),
# ],
# )
# def test_should_not_update_sms_template_with_emoji(
# mocker,
# client_request,
# service_one,
# mock_get_service_template,
# mock_update_service_template,
# fake_uuid,
# template_type,
# expected_error,
# ):
# service_one["permissions"] += [template_type]
# return mocker.patch(
# "app.service_api_client.get_service_template",
# return_value=template_json(
# SERVICE_ONE_ID,
# fake_uuid,
# type_=template_type,
# ),
# )
# page = client_request.post(
# ".edit_service_template",
# service_id=SERVICE_ONE_ID,
# template_id=fake_uuid,
# _data={
# "id": fake_uuid,
# "name": "new name",
# "template_content": "here's a burger 🍔",
# "service": SERVICE_ONE_ID,
# "template_type": template_type,
# "process_type": "normal",
# },
# _expected_status=200,
# )
# assert expected_error in page.text
# assert mock_update_service_template.called is False
@pytest.mark.parametrize(
"template_type",
[
"sms",
],
)
def test_should_create_sms_template_without_downgrading_unicode_characters(
client_request,
service_one,
mock_create_service_template,
template_type,
):
service_one["permissions"] += [template_type]
msg = "here:\tare some “fancy quotes” and non\u200bbreaking\u200bspaces"
client_request.post(
".add_service_template",
service_id=SERVICE_ONE_ID,
template_type="sms",
_data={
"name": "new name",
"template_content": msg,
"template_type": template_type,
"service": SERVICE_ONE_ID,
},
expected_status=302,
)
mock_create_service_template.assert_called_with(
ANY, # name
ANY, # type
msg, # content
ANY, # service_id
ANY, # subject
ANY, # parent_folder_id
)
def test_should_show_message_before_redacting_template(
client_request,
mock_get_service_template,
service_one,
fake_uuid,
):
page = client_request.get(
"main.redact_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_test_page_title=False,
)
assert (
"Are you sure you want to hide all personalized and conditional"
" content after sending for increased privacy protection?"
) in page.select(".banner-dangerous")[0].text
form = page.select(".banner-dangerous form")[0]
assert "action" not in form
assert form["method"] == "post"
def test_should_show_redact_template(
client_request,
mock_get_service_template,
mock_get_template_folders,
mock_redact_template,
service_one,
fake_uuid,
):
page = client_request.post(
"main.redact_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_follow_redirects=True,
)
assert normalize_spaces(page.select(".banner-default-with-tick")[0].text) == (
"Personalized content will be hidden for messages sent with this template"
)
mock_redact_template.assert_called_once_with(SERVICE_ONE_ID, fake_uuid)
def test_should_show_hint_once_template_redacted(
client_request,
mocker,
service_one,
mock_get_template_folders,
fake_uuid,
):
template = create_template(
template_type="email", content="hi ((name))", redact_personalisation=True
)
mocker.patch(
"app.service_api_client.get_service_template", return_value={"data": template}
)
page = client_request.get(
"main.view_template",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_test_page_title=False,
)
assert page.select(".hint")[0].text == "Personalization is hidden after sending"
def test_set_template_sender(
client_request,
fake_uuid,
mock_update_service_template_sender,
mock_get_service_template,
single_sms_sender,
):
data = {
"sender": "1234",
}
client_request.post(
"main.set_template_sender",
service_id=SERVICE_ONE_ID,
template_id=fake_uuid,
_data=data,
)
mock_update_service_template_sender.assert_called_once_with(
SERVICE_ONE_ID,
fake_uuid,
"1234",
)
@pytest.mark.parametrize(
("template_type", "prefix_sms", "content", "expected_message", "expected_class"),
[
(
"sms",
False,
"a" * 160,
"Will be charged as 1 text message",
None,
),
(
"sms",
False,
"a" * 161,
"Will be charged as 2 text messages",
None,
),
(
"sms",
True,
"a" * 147,
"Will be charged as 1 text message",
None,
),
(
# service name takes 13 characters, 148 + 13 = 161
"sms",
True,
"a" * 148,
"Will be charged as 2 text messages",
None,
),
(
"sms",
False,
"a" * 918,
"Will be charged as 6 text messages",
None,
),
(
# Service name increases fragment count but doesnt count
# against total character limit
"sms",
True,
"a" * 918,
"Will be charged as 7 text messages",
None,
),
(
# Cant make a 7 fragment text template from content alone
"sms",
False,
"a" * 919,
"You have 1 character too many",
"usa-error-message",
),
(
# Service name increases content count but character count
# is based on content alone
"sms",
True,
"a" * 919,
"You have 1 character too many",
"usa-error-message",
),
(
# Service name increases content count but character count
# is based on content alone
"sms",
True,
"a" * 920,
"You have 2 characters too many",
"usa-error-message",
),
(
"sms",
False,
"" * 70,
"Will be charged as 1 text message.", # noqa E501
None,
),
(
"sms",
False,
"" * 71,
"Will be charged as 2 text messages",
None,
),
(
"sms",
False,
"" * 918,
"Will be charged as 14 text messages",
None,
),
(
"sms",
False,
"" * 919,
"You have 1 character too many",
"usa-error-message",
),
(
"sms",
False,
"Hello ((name))",
"Will be charged as 1 text message (not including personalization).",
None,
),
(
# Length of placeholder body doesnt count towards fragment count
"sms",
False,
f'Hello (( {"a" * 999} ))',
"Will be charged as 1 text message (not including personalization).",
None,
),
],
)
def test_content_count_json_endpoint(
client_request,
service_one,
template_type,
prefix_sms,
content,
expected_message,
expected_class,
):
service_one["prefix_sms"] = prefix_sms
response = client_request.post_response(
"main.count_content_length",
service_id=SERVICE_ONE_ID,
template_type=template_type,
_data={
"template_content": content,
},
_expected_status=200,
)
html = json.loads(response.get_data(as_text=True))["html"]
snippet = BeautifulSoup(html, "html.parser").select_one("span")
assert expected_message in normalize_spaces(snippet.text)
if snippet.has_attr("class"):
assert snippet["class"] == [expected_class]
else:
assert expected_class is None
@pytest.mark.parametrize(
"template_type",
[
"email",
"banana",
],
)
def test_content_count_json_endpoint_for_unsupported_template_types(
client_request,
template_type,
):
client_request.post(
"main.count_content_length",
service_id=SERVICE_ONE_ID,
template_type=template_type,
content="foo",
_expected_status=404,
)