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