Files
notifications-api/tests/app/template_folder/test_template_folder_rest.py
Beverly Nguyen 15fb5a8ecb pytest
2025-08-19 13:24:31 -07:00

510 lines
16 KiB
Python

import uuid
import pytest
from sqlalchemy import func, select
from app import db
from app.dao.service_user_dao import dao_get_service_user
from app.models import TemplateFolder
from tests.app.db import (
create_service,
create_template,
create_template_folder,
create_user,
)
def test_get_folders_for_service(admin_request, notify_db_session):
s1 = create_service(service_name="a")
s2 = create_service(service_name="b")
tf1 = create_template_folder(s1)
tf2 = create_template_folder(s1)
create_template_folder(s2)
resp = admin_request.get(
"template_folder.get_template_folders_for_service", service_id=s1.id
)
assert set(resp.keys()) == {"template_folders"}
assert sorted(resp["template_folders"], key=lambda x: x["id"]) == sorted(
[
{
"id": str(tf1.id),
"name": "foo",
"service_id": str(s1.id),
"parent_id": None,
"users_with_permission": [],
},
{
"id": str(tf2.id),
"name": "foo",
"service_id": str(s1.id),
"parent_id": None,
"users_with_permission": [],
},
],
key=lambda x: x["id"],
)
def test_get_folders_for_service_with_no_folders(sample_service, admin_request):
resp = admin_request.get(
"template_folder.get_template_folders_for_service", service_id=sample_service.id
)
assert resp == {"template_folders": []}
def test_get_folders_returns_users_with_permission(admin_request, sample_service):
user_1 = create_user(email="one@gov.uk")
user_2 = create_user(email="two@gov.uk")
user_3 = create_user(email="three@gov.uk")
template_folder = create_template_folder(sample_service)
sample_service.users = [user_1, user_2, user_3]
service_user_1 = dao_get_service_user(user_1.id, sample_service.id)
service_user_2 = dao_get_service_user(user_2.id, sample_service.id)
service_user_1.folders = [template_folder]
service_user_2.folders = [template_folder]
resp = admin_request.get(
"template_folder.get_template_folders_for_service", service_id=sample_service.id
)
users_with_permission = resp["template_folders"][0]["users_with_permission"]
assert len(users_with_permission) == 2
assert str(user_1.id) in users_with_permission
assert str(user_2.id) in users_with_permission
@pytest.mark.parametrize("has_parent", [True, False])
def test_create_template_folder(admin_request, sample_service, has_parent):
existing_folder = create_template_folder(sample_service)
parent_id = str(existing_folder.id) if has_parent else None
resp = admin_request.post(
"template_folder.create_template_folder",
service_id=sample_service.id,
_data={"name": "foo", "parent_id": parent_id},
_expected_status=201,
)
assert resp["data"]["name"] == "foo"
assert resp["data"]["service_id"] == str(sample_service.id)
assert resp["data"]["parent_id"] == parent_id
@pytest.mark.parametrize("has_parent", [True, False])
def test_create_template_folder_sets_user_permissions(
admin_request, sample_service, has_parent
):
user_1 = create_user(email="one@gov.uk")
user_2 = create_user(email="two@gov.uk")
user_3 = create_user(email="three@gov.uk", state="pending")
existing_folder = create_template_folder(sample_service)
sample_service.users = [user_1, user_2, user_3]
service_user_1 = dao_get_service_user(user_1.id, sample_service.id)
service_user_1.folders = [existing_folder]
parent_id = str(existing_folder.id) if has_parent else None
resp = admin_request.post(
"template_folder.create_template_folder",
service_id=sample_service.id,
_data={"name": "foo", "parent_id": parent_id},
_expected_status=201,
)
assert resp["data"]["name"] == "foo"
assert resp["data"]["service_id"] == str(sample_service.id)
assert resp["data"]["parent_id"] == parent_id
if has_parent:
assert resp["data"]["users_with_permission"] == [str(user_1.id)]
else:
assert resp["data"]["users_with_permission"] == []
def test_create_template_folder_with_creator_id_grants_permission_to_creator(
admin_request, sample_service
):
user_1 = create_user(email="creator@gsa.gov")
user_2 = create_user(email="other@gsa.gov")
sample_service.users = [user_1, user_2]
resp = admin_request.post(
"template_folder.create_template_folder",
service_id=sample_service.id,
_data={"name": "creator folder", "parent_id": None, "created_by_id": str(user_1.id)},
_expected_status=201,
)
assert resp["data"]["name"] == "creator folder"
assert resp["data"]["users_with_permission"] == [str(user_1.id)]
@pytest.mark.parametrize("missing_field", ["name", "parent_id"])
def test_create_template_folder_fails_if_missing_fields(
admin_request, sample_service, missing_field
):
data = {"name": "foo", "parent_id": None}
data.pop(missing_field)
resp = admin_request.post(
"template_folder.create_template_folder",
service_id=sample_service.id,
_data=data,
_expected_status=400,
)
assert resp == {
"status_code": 400,
"errors": [
{
"error": "ValidationError",
"message": "{} is a required property".format(missing_field),
}
],
}
def test_create_template_folder_fails_if_unknown_parent_id(
admin_request, sample_service
):
resp = admin_request.post(
"template_folder.create_template_folder",
service_id=sample_service.id,
_data={"name": "bar", "parent_id": str(uuid.uuid4())},
_expected_status=400,
)
assert resp["result"] == "error"
assert resp["message"] == "parent_id not found"
def test_create_template_folder_fails_if_parent_id_from_different_service(
admin_request, sample_service
):
s1 = create_service(service_name="a")
parent_folder_id = create_template_folder(s1).id
resp = admin_request.post(
"template_folder.create_template_folder",
service_id=sample_service.id,
_data={"name": "bar", "parent_id": str(parent_folder_id)},
_expected_status=400,
)
assert resp["result"] == "error"
assert resp["message"] == "parent_id not found"
def test_update_template_folder_name(admin_request, sample_service):
existing_folder = create_template_folder(sample_service)
resp = admin_request.post(
"template_folder.update_template_folder",
service_id=sample_service.id,
template_folder_id=existing_folder.id,
_data={"name": "bar"},
)
assert resp["data"]["name"] == "bar"
assert existing_folder.name == "bar"
def test_update_template_folder_users(admin_request, sample_service):
existing_folder = create_template_folder(sample_service)
user_1 = create_user(email="notify_1@digital.cabinet-office.gov.uk")
user_2 = create_user(email="notify_2@digital.cabinet-office.gov.uk")
user_3 = create_user(email="notify_3@digital.cabinet-office.gov.uk")
sample_service.users += [user_1, user_2, user_3]
assert len(existing_folder.users) == 0
response_1 = admin_request.post(
"template_folder.update_template_folder",
service_id=sample_service.id,
template_folder_id=existing_folder.id,
_data={"name": "foo", "users_with_permission": [str(user_1.id)]},
)
assert response_1["data"]["users_with_permission"] == [str(user_1.id)]
assert len(existing_folder.users) == 1
response_2 = admin_request.post(
"template_folder.update_template_folder",
service_id=sample_service.id,
template_folder_id=existing_folder.id,
_data={
"name": "foo",
"users_with_permission": [str(user_2.id), str(user_3.id)],
},
)
assert response_2["data"]["users_with_permission"] == [
str(user_2.id),
str(user_3.id),
]
assert len(existing_folder.users) == 2
@pytest.mark.parametrize(
"data, err",
[
({}, "name is a required property"),
({"name": None}, "name None is not of type string"),
({"name": ""}, "name should be non-empty"),
],
)
def test_update_template_folder_fails_if_missing_name(
admin_request, sample_service, data, err
):
existing_folder = create_template_folder(sample_service)
resp = admin_request.post(
"template_folder.update_template_folder",
service_id=sample_service.id,
template_folder_id=existing_folder.id,
_data=data,
_expected_status=400,
)
assert resp == {
"status_code": 400,
"errors": [{"error": "ValidationError", "message": err}],
}
def test_delete_template_folder(admin_request, sample_service):
existing_folder = create_template_folder(sample_service)
admin_request.delete(
"template_folder.delete_template_folder",
service_id=sample_service.id,
template_folder_id=existing_folder.id,
)
assert db.session.execute(select(TemplateFolder)).scalars().all() == []
def test_delete_template_folder_fails_if_folder_has_subfolders(
admin_request, sample_service
):
existing_folder = create_template_folder(sample_service)
create_template_folder(sample_service, parent=existing_folder) # noqa
resp = admin_request.delete(
"template_folder.delete_template_folder",
service_id=sample_service.id,
template_folder_id=existing_folder.id,
_expected_status=400,
)
assert resp == {"result": "error", "message": "Folder is not empty"}
stmt = select(func.count()).select_from(TemplateFolder)
count = db.session.execute(stmt).scalar() or 0
assert count == 2
def test_delete_template_folder_fails_if_folder_contains_templates(
admin_request, sample_service, sample_template
):
existing_folder = create_template_folder(sample_service)
sample_template.folder = existing_folder
resp = admin_request.delete(
"template_folder.delete_template_folder",
service_id=sample_service.id,
template_folder_id=existing_folder.id,
_expected_status=400,
)
assert resp == {"result": "error", "message": "Folder is not empty"}
stmt = select(func.count()).select_from(TemplateFolder)
count = db.session.execute(stmt).scalar() or 0
assert count == 1
@pytest.mark.parametrize(
"data",
[
{},
{"templates": None, "folders": []},
{"folders": []},
{"templates": [], "folders": [None]},
{"templates": [], "folders": ["not a uuid"]},
],
)
def test_move_to_folder_validates_schema(data, admin_request, notify_db_session):
admin_request.post(
"template_folder.move_to_template_folder",
service_id=uuid.uuid4(),
target_template_folder_id=uuid.uuid4(),
_data=data,
_expected_status=400,
)
def test_move_to_folder_moves_folders_and_templates(admin_request, sample_service):
target_folder = create_template_folder(sample_service, name="target")
f1 = create_template_folder(sample_service, name="f1")
f2 = create_template_folder(sample_service, name="f2")
t1 = create_template(sample_service, template_name="t1", folder=f1)
t2 = create_template(sample_service, template_name="t2", folder=f1)
t3 = create_template(sample_service, template_name="t3", folder=f2)
t4 = create_template(sample_service, template_name="t4", folder=target_folder)
admin_request.post(
"template_folder.move_to_template_folder",
service_id=sample_service.id,
target_template_folder_id=target_folder.id,
_data={"templates": [str(t1.id)], "folders": [str(f1.id)]},
_expected_status=204,
)
assert target_folder.parent is None
assert f1.parent == target_folder
assert f2.parent is None # unchanged
assert (
t1.folder == target_folder
) # moved out of f1, even though f1 is also being moved
assert t2.folder == f1 # stays in f1, though f1 has moved
assert t3.folder == f2 # unchanged
assert t4.folder == target_folder # unchanged
# versions are all unchanged
assert t1.version == 1
assert t2.version == 1
assert t3.version == 1
assert t4.version == 1
def test_move_to_folder_moves_folders_and_templates_to_top_level_if_no_target(
admin_request, sample_service
):
f1 = create_template_folder(sample_service, name="f1")
f2 = create_template_folder(sample_service, name="f2", parent=f1)
t1 = create_template(sample_service, template_name="t1", folder=f1)
t2 = create_template(sample_service, template_name="t2", folder=f1)
t3 = create_template(sample_service, template_name="t3", folder=f2)
admin_request.post(
"template_folder.move_to_template_folder",
service_id=sample_service.id,
target_template_folder_id=None,
_data={"templates": [str(t1.id)], "folders": [str(f2.id)]},
_expected_status=204,
)
assert f1.parent is None # unchanged
assert f2.parent is None
assert t1.folder is None # moved out of f1
assert t2.folder == f1 # unchanged
assert t3.folder == f2 # stayed in f2 even though the parent changed
def test_move_to_folder_rejects_folder_from_other_service(
admin_request, notify_db_session
):
s1 = create_service(service_name="s1")
s2 = create_service(service_name="s2")
f2 = create_template_folder(s2)
response = admin_request.post(
"template_folder.move_to_template_folder",
service_id=s1.id,
target_template_folder_id=None,
_data={"templates": [], "folders": [str(f2.id)]},
_expected_status=400,
)
assert response["message"] == "No folder found with id {} for service {}".format(
f2.id, s1.id
)
def test_move_to_folder_rejects_template_from_other_service(
admin_request, notify_db_session
):
s1 = create_service(service_name="s1")
s2 = create_service(service_name="s2")
t2 = create_template(s2)
response = admin_request.post(
"template_folder.move_to_template_folder",
service_id=s1.id,
target_template_folder_id=None,
_data={"templates": [str(t2.id)], "folders": []},
_expected_status=400,
)
assert response[
"message"
] == "Could not move to folder: No template found with id {} for service {}".format(
t2.id, s1.id
)
def test_move_to_folder_rejects_if_it_would_cause_folder_loop(
admin_request, sample_service
):
f1 = create_template_folder(sample_service, name="f1")
target_folder = create_template_folder(sample_service, name="target", parent=f1)
response = admin_request.post(
"template_folder.move_to_template_folder",
service_id=sample_service.id,
target_template_folder_id=target_folder.id,
_data={"templates": [], "folders": [str(f1.id)]},
_expected_status=400,
)
assert response["message"] == "You cannot move a folder to one of its subfolders"
def test_move_to_folder_itself_is_rejected(admin_request, sample_service):
target_folder = create_template_folder(sample_service, name="target")
response = admin_request.post(
"template_folder.move_to_template_folder",
service_id=sample_service.id,
target_template_folder_id=target_folder.id,
_data={"templates": [], "folders": [str(target_folder.id)]},
_expected_status=400,
)
assert response["message"] == "You cannot move a folder to itself"
def test_move_to_folder_skips_archived_templates(admin_request, sample_service):
target_folder = create_template_folder(sample_service)
other_folder = create_template_folder(sample_service)
archived_template = create_template(sample_service, archived=True, folder=None)
unarchived_template = create_template(
sample_service, archived=False, folder=other_folder
)
archived_timestamp = archived_template.updated_at
admin_request.post(
"template_folder.move_to_template_folder",
service_id=sample_service.id,
target_template_folder_id=target_folder.id,
_data={
"templates": [str(archived_template.id), str(unarchived_template.id)],
"folders": [],
},
_expected_status=204,
)
assert archived_template.updated_at == archived_timestamp
assert archived_template.folder is None
assert unarchived_template.folder == target_folder