From 39198ed67edb5e37192e429baa9f88c457b9af5e Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Fri, 2 Nov 2018 16:00:22 +0000 Subject: [PATCH 1/6] Using jsonschema for create_template. Updated jsonschema to Draft7, this allowed a conditional validation on subject, if template_type == 'email' or 'letter' then subject is required. This version is backward compatible with Draft4. When creating TempalteRedacted, I've built the dict depending on if the created_by or created_by_id exists. --- app/dao/templates_dao.py | 15 ++++++++++----- app/models.py | 14 ++++++++++++++ app/schema_validation/__init__.py | 4 ++-- app/template/rest.py | 9 ++++++--- app/template/template_schemas.py | 28 ++++++++++++++++++++++++++++ requirements-app.txt | 2 +- requirements.txt | 4 +++- tests/app/template/test_rest.py | 10 +++++----- 8 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 app/template/template_schemas.py diff --git a/app/dao/templates_dao.py b/app/dao/templates_dao.py index 3d3dd9bc1..2a9164429 100644 --- a/app/dao/templates_dao.py +++ b/app/dao/templates_dao.py @@ -21,11 +21,16 @@ def dao_create_template(template): template.id = uuid.uuid4() # must be set now so version history model can use same id template.archived = False - template.template_redacted = TemplateRedacted( - template=template, - redact_personalisation=False, - updated_by=template.created_by - ) + redacted_dict = { + "template": template, + "redact_personalisation": False, + } + if template.created_by: + redacted_dict.update({"updated_by": template.created_by}) + else: + redacted_dict.update({"updated_by_id": template.created_by_id}) + + template.template_redacted = TemplateRedacted(**redacted_dict) db.session.add(template) diff --git a/app/models.py b/app/models.py index 3f2877dff..bf548bd32 100644 --- a/app/models.py +++ b/app/models.py @@ -884,6 +884,20 @@ class Template(TemplateBase): _external=True ) + @classmethod + def from_json(cls, data): + """ + Assumption: data has been validated appropriately. + + Returns a Template object based on the provided data. + """ + fields = data.copy() + + fields['created_by_id'] = fields.pop('created_by') + fields['service_id'] = fields.pop('service') + + return cls(**fields) + class TemplateRedacted(db.Model): __tablename__ = 'template_redacted' diff --git a/app/schema_validation/__init__.py b/app/schema_validation/__init__.py index 44cb0f9ce..382d9229c 100644 --- a/app/schema_validation/__init__.py +++ b/app/schema_validation/__init__.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from uuid import UUID from iso8601 import iso8601, ParseError -from jsonschema import (Draft4Validator, ValidationError, FormatChecker) +from jsonschema import (Draft7Validator, ValidationError, FormatChecker) from notifications_utils.recipients import (validate_phone_number, validate_email_address, InvalidPhoneError, InvalidEmailError) @@ -43,7 +43,7 @@ def validate(json_to_validate, schema): "https://en.wikipedia.org/wiki/ISO_8601") return True - validator = Draft4Validator(schema, format_checker=format_checker) + validator = Draft7Validator(schema, format_checker=format_checker) errors = list(validator.iter_errors(json_to_validate)) if errors.__len__() > 0: raise ValidationError(build_error_message(errors)) diff --git a/app/template/rest.py b/app/template/rest.py index 64a353c0a..52a3497fb 100644 --- a/app/template/rest.py +++ b/app/template/rest.py @@ -29,9 +29,11 @@ from app.errors import ( InvalidRequest ) from app.letters.utils import get_letter_pdf -from app.models import SMS_TYPE +from app.models import SMS_TYPE, Template from app.notifications.validators import service_has_permission, check_reply_to +from app.schema_validation import validate from app.schemas import (template_schema, template_history_schema) +from app.template.template_schemas import post_create_template_schema from app.utils import get_template_instance, get_public_notify_type_text template_blueprint = Blueprint('template', __name__, url_prefix='/service//template') @@ -49,9 +51,9 @@ def _content_count_greater_than_limit(content, template_type): @template_blueprint.route('', methods=['POST']) def create_template(service_id): fetched_service = dao_fetch_service_by_id(service_id=service_id) - # permissions needs to be placed here otherwise marshmallow will intefere with versioning + # permissions needs to be placed here otherwise marshmallow will interfere with versioning permissions = fetched_service.permissions - new_template = template_schema.load(request.get_json()).data + new_template = Template.from_json(validate(request.get_json(), post_create_template_schema)) if not service_has_permission(new_template.template_type, permissions): message = "Creating {} templates is not allowed".format( @@ -60,6 +62,7 @@ def create_template(service_id): raise InvalidRequest(errors, 403) new_template.service = fetched_service + over_limit = _content_count_greater_than_limit(new_template.content, new_template.template_type) if over_limit: message = 'Content has a character count greater than the limit of {}'.format(SMS_CHAR_COUNT_LIMIT) diff --git a/app/template/template_schemas.py b/app/template/template_schemas.py new file mode 100644 index 000000000..4f13c8ced --- /dev/null +++ b/app/template/template_schemas.py @@ -0,0 +1,28 @@ +from app.models import ( + TEMPLATE_PROCESS_TYPE, + TEMPLATE_TYPES, +) +from app.schema_validation.definitions import uuid + +post_create_template_schema = { + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "POST create new template", + "type": "object", + "title": "payload for POST /service//template", + "properties": { + "name": {"type": "string"}, + "template_type": {"enum": TEMPLATE_TYPES}, + "service": uuid, + "process_type": {"emun": TEMPLATE_PROCESS_TYPE}, + "content": {"type": "string"}, + "subject": {"type": "string"}, + "created_by": uuid + }, + "if": { + "properties": { + "template_type": {"enum": ["email", "letter"]} + } + }, + "then": {"required": ["subject"]}, + "required": ["name", "template_type", "content", "service", "created_by"] +} diff --git a/requirements-app.txt b/requirements-app.txt index e75740910..99535815d 100644 --- a/requirements-app.txt +++ b/requirements-app.txt @@ -13,7 +13,7 @@ click-datetime==0.2 eventlet==0.23.0 gunicorn==19.7.1 iso8601==0.1.12 -jsonschema==2.6.0 +jsonschema==3.0.0a3 marshmallow-sqlalchemy==0.14.1 marshmallow==2.16.0 psycopg2-binary==2.7.5 diff --git a/requirements.txt b/requirements.txt index 37613bb91..c68365b0a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ click-datetime==0.2 eventlet==0.23.0 gunicorn==19.7.1 iso8601==0.1.12 -jsonschema==2.6.0 +jsonschema==3.0.0a3 marshmallow-sqlalchemy==0.14.1 marshmallow==2.16.0 psycopg2-binary==2.7.5 @@ -39,6 +39,7 @@ git+https://github.com/alphagov/boto.git@2.43.0-patch3#egg=boto==2.43.0-patch3 alembic==1.0.2 amqp==1.4.9 anyjson==0.3.3 +attrs==18.2.0 awscli==1.16.49 bcrypt==3.1.4 billiard==3.3.0.23 @@ -67,6 +68,7 @@ phonenumbers==8.9.4 pyasn1==0.4.4 pycparser==2.19 PyPDF2==1.26.0 +pyrsistent==0.14.5 python-dateutil==2.7.5 python-editor==1.0.3 python-json-logger==0.1.8 diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index 50c5d7f37..55e265f8b 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -172,7 +172,8 @@ def test_should_error_if_created_by_missing(client, sample_user, sample_service) ) json_resp = json.loads(response.get_data(as_text=True)) assert response.status_code == 400 - assert json_resp['result'] == 'error' + assert json_resp["errors"][0]["error"] == 'ValidationError' + assert json_resp["errors"][0]["message"] == 'created_by is a required property' def test_should_be_error_if_service_does_not_exist_on_update(client, fake_uuid): @@ -211,15 +212,14 @@ def test_must_have_a_subject_on_an_email_or_letter_template(client, sample_user, data=data ) json_resp = json.loads(response.get_data(as_text=True)) - assert response.status_code == 400 - assert json_resp['result'] == 'error' - assert json_resp['message'] == {'subject': ['Invalid template subject']} + assert json_resp['errors'][0]['error'] == "ValidationError" + assert json_resp['errors'][0]["message"] == 'subject is a required property' def test_update_should_update_a_template(client, sample_user, sample_template): data = { 'content': 'my template has new content ', - 'created_by': str(sample_user.id) + 'created_by_id': str(sample_user.id) } data = json.dumps(data) auth_header = create_authorization_header() From 4849ecdf63d64755dfcc3c70eca29d7001bff78e Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Mon, 5 Nov 2018 10:54:42 +0000 Subject: [PATCH 2/6] Update the template_schema to include a parent_folder_id. When creating the Tempalte from_json, the folder is passed in. Since some validation should done, as in the folder exists and is for the same service, the folder is passed through to the Tempalte.from_json method. When the template is persisted so is the relationship to folders. TODO: If the folder is invalid a specific message should be returned. --- app/models.py | 5 +-- app/template/rest.py | 12 +++++-- app/template/template_schemas.py | 3 +- tests/app/template/test_rest.py | 58 ++++++++++++++++++++++++++++++-- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/app/models.py b/app/models.py index bf548bd32..f5bd68eae 100644 --- a/app/models.py +++ b/app/models.py @@ -885,7 +885,7 @@ class Template(TemplateBase): ) @classmethod - def from_json(cls, data): + def from_json(cls, data, folder): """ Assumption: data has been validated appropriately. @@ -895,7 +895,8 @@ class Template(TemplateBase): fields['created_by_id'] = fields.pop('created_by') fields['service_id'] = fields.pop('service') - + if fields.pop("parent_folder_id"): + fields['folder'] = folder return cls(**fields) diff --git a/app/template/rest.py b/app/template/rest.py index 52a3497fb..2084bb674 100644 --- a/app/template/rest.py +++ b/app/template/rest.py @@ -29,7 +29,7 @@ from app.errors import ( InvalidRequest ) from app.letters.utils import get_letter_pdf -from app.models import SMS_TYPE, Template +from app.models import SMS_TYPE, Template, TemplateFolder from app.notifications.validators import service_has_permission, check_reply_to from app.schema_validation import validate from app.schemas import (template_schema, template_history_schema) @@ -48,12 +48,19 @@ def _content_count_greater_than_limit(content, template_type): return template.content_count > SMS_CHAR_COUNT_LIMIT +def validate_parent_folder(parent_folder_id, service_id): + return TemplateFolder.query.filter_by(service_id=service_id, id=parent_folder_id).one() + + @template_blueprint.route('', methods=['POST']) def create_template(service_id): fetched_service = dao_fetch_service_by_id(service_id=service_id) # permissions needs to be placed here otherwise marshmallow will interfere with versioning permissions = fetched_service.permissions - new_template = Template.from_json(validate(request.get_json(), post_create_template_schema)) + template_json = validate(request.get_json(), post_create_template_schema) + folder = validate_parent_folder(parent_folder_id=template_json['parent_folder_id'], + service_id=template_json['service']) + new_template = Template.from_json(template_json, folder) if not service_has_permission(new_template.template_type, permissions): message = "Creating {} templates is not allowed".format( @@ -72,6 +79,7 @@ def create_template(service_id): check_reply_to(service_id, new_template.reply_to, new_template.template_type) dao_create_template(new_template) + return jsonify(data=template_schema.dump(new_template).data), 201 diff --git a/app/template/template_schemas.py b/app/template/template_schemas.py index 4f13c8ced..f63ad4575 100644 --- a/app/template/template_schemas.py +++ b/app/template/template_schemas.py @@ -16,7 +16,8 @@ post_create_template_schema = { "process_type": {"emun": TEMPLATE_PROCESS_TYPE}, "content": {"type": "string"}, "subject": {"type": "string"}, - "created_by": uuid + "created_by": uuid, + "parent_folder_id": uuid }, "if": { "properties": { diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index 55e265f8b..c92545af9 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -12,7 +12,7 @@ from freezegun import freeze_time from notifications_utils import SMS_CHAR_COUNT_LIMIT -from app.models import Template, SMS_TYPE, EMAIL_TYPE, LETTER_TYPE, TemplateHistory +from app.models import Template, SMS_TYPE, EMAIL_TYPE, LETTER_TYPE, TemplateHistory, TemplateFolder from app.dao.templates_dao import dao_get_template_by_id, dao_redact_template from tests import create_authorization_header @@ -21,7 +21,10 @@ from tests.app.conftest import ( sample_template_without_email_permission, sample_template_without_letter_permission, sample_template_without_sms_permission) -from tests.app.db import create_service, create_letter_contact, create_template, create_notification +from tests.app.db import ( + create_service, create_letter_contact, create_template, create_notification, + create_template_folder, +) from tests.conftest import set_config_values @@ -71,6 +74,57 @@ def test_should_create_a_new_template_for_a_service( assert sorted(json_resp['data']) == sorted(template_schema.dump(template).data) +def test_should_create_a_new_template_for_a_service_adds_folder_relationship( + client, sample_service +): + parent_folder = create_template_folder(service=sample_service, name='parent folder') + + data = { + 'name': 'my template', + 'template_type': 'sms', + 'content': 'template content', + 'service': str(sample_service.id), + 'created_by': str(sample_service.users[0].id), + 'parent_folder_id': str(parent_folder.id) + } + data = json.dumps(data) + auth_header = create_authorization_header() + + response = client.post( + '/service/{}/template'.format(sample_service.id), + headers=[('Content-Type', 'application/json'), auth_header], + data=data + ) + assert response.status_code == 201 + template = Template.query.filter(Template.name == 'my template').first() + assert template.folder == parent_folder + + +def test_create_template_should_return_404_if_folder_is_for_a_different_service( + client, sample_service +): + service2 = create_service(service_name='second service') + parent_folder = create_template_folder(service=service2) + + data = { + 'name': 'my template', + 'template_type': 'sms', + 'content': 'template content', + 'service': str(sample_service.id), + 'created_by': str(sample_service.users[0].id), + 'parent_folder_id': str(parent_folder.id) + } + data = json.dumps(data) + auth_header = create_authorization_header() + + response = client.post( + '/service/{}/template'.format(sample_service.id), + headers=[('Content-Type', 'application/json'), auth_header], + data=data + ) + assert response.status_code == 404 + + def test_should_raise_error_if_service_does_not_exist_on_create(client, sample_user, fake_uuid): data = { 'name': 'my template', From 1b0b16fa74a9de519dda908682e1b421f5721851 Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Wed, 7 Nov 2018 12:48:56 +0000 Subject: [PATCH 3/6] Update model and controller to handle parent_folder_id when creating a template. If the parent_folder_id then check if the folder exists and is for the same service. If it is add the folder to the template model object, the relationship will be persisted when the template is saved. If the folder does not exist or is for a different service, then return a ResultNotFound error. --- app/models.py | 4 +--- app/template/rest.py | 13 +++++++++---- tests/app/template/test_rest.py | 31 ++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/app/models.py b/app/models.py index f5bd68eae..b9b460bfd 100644 --- a/app/models.py +++ b/app/models.py @@ -888,15 +888,13 @@ class Template(TemplateBase): def from_json(cls, data, folder): """ Assumption: data has been validated appropriately. - Returns a Template object based on the provided data. """ fields = data.copy() fields['created_by_id'] = fields.pop('created_by') fields['service_id'] = fields.pop('service') - if fields.pop("parent_folder_id"): - fields['folder'] = folder + fields['folder'] = folder return cls(**fields) diff --git a/app/template/rest.py b/app/template/rest.py index 2084bb674..7a8606b45 100644 --- a/app/template/rest.py +++ b/app/template/rest.py @@ -48,8 +48,14 @@ def _content_count_greater_than_limit(content, template_type): return template.content_count > SMS_CHAR_COUNT_LIMIT -def validate_parent_folder(parent_folder_id, service_id): - return TemplateFolder.query.filter_by(service_id=service_id, id=parent_folder_id).one() +def validate_parent_folder(template_json): + if template_json.get("parent_folder_id"): + return TemplateFolder.query.filter_by( + service_id=template_json['service'], + id=template_json.pop("parent_folder_id") + ).one() + else: + return None @template_blueprint.route('', methods=['POST']) @@ -58,8 +64,7 @@ def create_template(service_id): # permissions needs to be placed here otherwise marshmallow will interfere with versioning permissions = fetched_service.permissions template_json = validate(request.get_json(), post_create_template_schema) - folder = validate_parent_folder(parent_folder_id=template_json['parent_folder_id'], - service_id=template_json['service']) + folder = validate_parent_folder(template_json=template_json) new_template = Template.from_json(template_json, folder) if not service_has_permission(new_template.template_type, permissions): diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index c92545af9..6cd9950b0 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -2,6 +2,7 @@ import base64 import json import random import string +import uuid from datetime import datetime, timedelta import botocore @@ -12,7 +13,13 @@ from freezegun import freeze_time from notifications_utils import SMS_CHAR_COUNT_LIMIT -from app.models import Template, SMS_TYPE, EMAIL_TYPE, LETTER_TYPE, TemplateHistory, TemplateFolder +from app.models import ( + EMAIL_TYPE, + LETTER_TYPE, + SMS_TYPE, + Template, + TemplateHistory +) from app.dao.templates_dao import dao_get_template_by_id, dao_redact_template from tests import create_authorization_header @@ -125,6 +132,28 @@ def test_create_template_should_return_404_if_folder_is_for_a_different_service( assert response.status_code == 404 +def test_create_template_should_return_404_if_folder_does_not_exist( + client, sample_service +): + data = { + 'name': 'my template', + 'template_type': 'sms', + 'content': 'template content', + 'service': str(sample_service.id), + 'created_by': str(sample_service.users[0].id), + 'parent_folder_id': str(uuid.uuid4()) + } + data = json.dumps(data) + auth_header = create_authorization_header() + + response = client.post( + '/service/{}/template'.format(sample_service.id), + headers=[('Content-Type', 'application/json'), auth_header], + data=data + ) + assert response.status_code == 404 + + def test_should_raise_error_if_service_does_not_exist_on_create(client, sample_user, fake_uuid): data = { 'name': 'my template', From 1e5b990069bdfa4c6200c7d56fc324044d826f50 Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Wed, 7 Nov 2018 16:34:51 +0000 Subject: [PATCH 4/6] Use query to get TemplateFolder by id and service_id --- app/template/rest.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/template/rest.py b/app/template/rest.py index 7a8606b45..56416dc30 100644 --- a/app/template/rest.py +++ b/app/template/rest.py @@ -15,6 +15,7 @@ from requests import post as requests_post from app.dao.notifications_dao import get_notification_by_id from app.dao.services_dao import dao_fetch_service_by_id +from app.dao.template_folder_dao import dao_get_template_folder_by_id_and_service_id from app.dao.templates_dao import ( dao_update_template, dao_create_template, @@ -29,7 +30,7 @@ from app.errors import ( InvalidRequest ) from app.letters.utils import get_letter_pdf -from app.models import SMS_TYPE, Template, TemplateFolder +from app.models import SMS_TYPE, Template from app.notifications.validators import service_has_permission, check_reply_to from app.schema_validation import validate from app.schemas import (template_schema, template_history_schema) @@ -50,10 +51,8 @@ def _content_count_greater_than_limit(content, template_type): def validate_parent_folder(template_json): if template_json.get("parent_folder_id"): - return TemplateFolder.query.filter_by( - service_id=template_json['service'], - id=template_json.pop("parent_folder_id") - ).one() + return dao_get_template_folder_by_id_and_service_id(template_folder_id=template_json.pop("parent_folder_id"), + service_id=template_json['service']) else: return None From f5de80f1956c9c55bc5cb83785a4434b95c5ee4e Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Wed, 7 Nov 2018 16:39:04 +0000 Subject: [PATCH 5/6] Fix conflict --- requirements.txt | 2 -- tests/app/template/test_rest.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index c68365b0a..7388d5cee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -39,7 +39,6 @@ git+https://github.com/alphagov/boto.git@2.43.0-patch3#egg=boto==2.43.0-patch3 alembic==1.0.2 amqp==1.4.9 anyjson==0.3.3 -attrs==18.2.0 awscli==1.16.49 bcrypt==3.1.4 billiard==3.3.0.23 @@ -68,7 +67,6 @@ phonenumbers==8.9.4 pyasn1==0.4.4 pycparser==2.19 PyPDF2==1.26.0 -pyrsistent==0.14.5 python-dateutil==2.7.5 python-editor==1.0.3 python-json-logger==0.1.8 diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index 6cd9950b0..d3de8e3d5 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -302,7 +302,7 @@ def test_must_have_a_subject_on_an_email_or_letter_template(client, sample_user, def test_update_should_update_a_template(client, sample_user, sample_template): data = { 'content': 'my template has new content ', - 'created_by_id': str(sample_user.id) + 'created_by': str(sample_user.id) } data = json.dumps(data) auth_header = create_authorization_header() From 4120525568b03e790a75247df6b83dadebe5f3ef Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Wed, 7 Nov 2018 17:07:04 +0000 Subject: [PATCH 6/6] Update error from ResultNotFound to InvalidRequest --- app/template/rest.py | 10 ++++++++-- tests/app/template/test_rest.py | 10 ++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/template/rest.py b/app/template/rest.py index 56416dc30..8cbb4125f 100644 --- a/app/template/rest.py +++ b/app/template/rest.py @@ -12,6 +12,7 @@ from notifications_utils import SMS_CHAR_COUNT_LIMIT from notifications_utils.pdf import extract_page_from_pdf from notifications_utils.template import SMSMessageTemplate from requests import post as requests_post +from sqlalchemy.orm.exc import NoResultFound from app.dao.notifications_dao import get_notification_by_id from app.dao.services_dao import dao_fetch_service_by_id @@ -51,8 +52,13 @@ def _content_count_greater_than_limit(content, template_type): def validate_parent_folder(template_json): if template_json.get("parent_folder_id"): - return dao_get_template_folder_by_id_and_service_id(template_folder_id=template_json.pop("parent_folder_id"), - service_id=template_json['service']) + try: + return dao_get_template_folder_by_id_and_service_id( + template_folder_id=template_json.pop("parent_folder_id"), + service_id=template_json['service'] + ) + except NoResultFound: + raise InvalidRequest("parent_folder_id not found", status_code=400) else: return None diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index d3de8e3d5..7cf46aa80 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -107,7 +107,7 @@ def test_should_create_a_new_template_for_a_service_adds_folder_relationship( assert template.folder == parent_folder -def test_create_template_should_return_404_if_folder_is_for_a_different_service( +def test_create_template_should_return_400_if_folder_is_for_a_different_service( client, sample_service ): service2 = create_service(service_name='second service') @@ -129,10 +129,11 @@ def test_create_template_should_return_404_if_folder_is_for_a_different_service( headers=[('Content-Type', 'application/json'), auth_header], data=data ) - assert response.status_code == 404 + assert response.status_code == 400 + assert json.loads(response.get_data(as_text=True))['message'] == 'parent_folder_id not found' -def test_create_template_should_return_404_if_folder_does_not_exist( +def test_create_template_should_return_400_if_folder_does_not_exist( client, sample_service ): data = { @@ -151,7 +152,8 @@ def test_create_template_should_return_404_if_folder_does_not_exist( headers=[('Content-Type', 'application/json'), auth_header], data=data ) - assert response.status_code == 404 + assert response.status_code == 400 + assert json.loads(response.get_data(as_text=True))['message'] == 'parent_folder_id not found' def test_should_raise_error_if_service_does_not_exist_on_create(client, sample_user, fake_uuid):