From 6a400339cc04c6ed876a6332ce1a2ff4edf44a8b Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 12 Apr 2021 11:34:17 +0200 Subject: [PATCH 01/17] :sparkles: Add endpoints to viewsets --- src/openforms/forms/api/viewsets.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/openforms/forms/api/viewsets.py b/src/openforms/forms/api/viewsets.py index 0298011550..78ea2db7df 100644 --- a/src/openforms/forms/api/viewsets.py +++ b/src/openforms/forms/api/viewsets.py @@ -2,7 +2,7 @@ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view -from rest_framework import permissions, status, viewsets +from rest_framework import mixins, permissions, status, viewsets from rest_framework.decorators import action from rest_framework.request import Request from rest_framework.response import Response @@ -34,9 +34,16 @@ class BaseFormsViewSet(viewsets.ReadOnlyModelViewSet): list=extend_schema(summary=_("List form steps")), retrieve=extend_schema(summary=_("Retrieve form step details")), ) -class FormStepViewSet(NestedViewSetMixin, BaseFormsViewSet): +class FormStepViewSet( + NestedViewSetMixin, + mixins.CreateModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, + BaseFormsViewSet, +): serializer_class = FormStepSerializer queryset = FormStep.objects.all() + permission_classes = [permissions.IsAuthenticatedOrReadOnly] @extend_schema_view( @@ -80,6 +87,7 @@ def configuration(self, request: Request, *args, **kwargs): list=extend_schema(summary=_("List forms")), retrieve=extend_schema(summary=_("Retrieve form details")), ) -class FormViewSet(BaseFormsViewSet): +class FormViewSet(mixins.CreateModelMixin, mixins.UpdateModelMixin, BaseFormsViewSet): queryset = Form.objects.filter(active=True) serializer_class = FormSerializer + permission_classes = [permissions.IsAuthenticatedOrReadOnly] From dc3a85d5ed9a7da9589adcfeb3c6fadf17226ef3 Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 12 Apr 2021 11:41:33 +0200 Subject: [PATCH 02/17] :sparkles: Create and use IsStaffOrReadOnly permission --- src/openforms/forms/api/permissions.py | 14 ++++++++++++++ src/openforms/forms/api/viewsets.py | 5 +++-- 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/openforms/forms/api/permissions.py diff --git a/src/openforms/forms/api/permissions.py b/src/openforms/forms/api/permissions.py new file mode 100644 index 0000000000..46096e579c --- /dev/null +++ b/src/openforms/forms/api/permissions.py @@ -0,0 +1,14 @@ +from rest_framework.permissions import BasePermission, SAFE_METHODS + + +class IsStaffOrReadOnly(BasePermission): + """ + The request is authenticated as a user, or is a read-only request. + """ + + def has_permission(self, request, view): + return bool( + request.method in SAFE_METHODS or + request.user and + request.user.is_staff + ) diff --git a/src/openforms/forms/api/viewsets.py b/src/openforms/forms/api/viewsets.py index 78ea2db7df..4335a1cab5 100644 --- a/src/openforms/forms/api/viewsets.py +++ b/src/openforms/forms/api/viewsets.py @@ -16,6 +16,7 @@ FormStepSerializer, ) from ..models import Form, FormDefinition, FormStep +from ..api.permissions import IsStaffOrReadOnly class BaseFormsViewSet(viewsets.ReadOnlyModelViewSet): @@ -43,7 +44,7 @@ class FormStepViewSet( ): serializer_class = FormStepSerializer queryset = FormStep.objects.all() - permission_classes = [permissions.IsAuthenticatedOrReadOnly] + permission_classes = [IsStaffOrReadOnly] @extend_schema_view( @@ -90,4 +91,4 @@ def configuration(self, request: Request, *args, **kwargs): class FormViewSet(mixins.CreateModelMixin, mixins.UpdateModelMixin, BaseFormsViewSet): queryset = Form.objects.filter(active=True) serializer_class = FormSerializer - permission_classes = [permissions.IsAuthenticatedOrReadOnly] + permission_classes = [IsStaffOrReadOnly] From 10ba31d3303c29e097966f040733f9818c6d9680 Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 12 Apr 2021 11:47:37 +0200 Subject: [PATCH 03/17] :green_heart: Run isort and black --- src/openforms/forms/api/permissions.py | 6 ++---- src/openforms/forms/api/viewsets.py | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/openforms/forms/api/permissions.py b/src/openforms/forms/api/permissions.py index 46096e579c..d5e7b8fda0 100644 --- a/src/openforms/forms/api/permissions.py +++ b/src/openforms/forms/api/permissions.py @@ -1,4 +1,4 @@ -from rest_framework.permissions import BasePermission, SAFE_METHODS +from rest_framework.permissions import SAFE_METHODS, BasePermission class IsStaffOrReadOnly(BasePermission): @@ -8,7 +8,5 @@ class IsStaffOrReadOnly(BasePermission): def has_permission(self, request, view): return bool( - request.method in SAFE_METHODS or - request.user and - request.user.is_staff + request.method in SAFE_METHODS or request.user and request.user.is_staff ) diff --git a/src/openforms/forms/api/viewsets.py b/src/openforms/forms/api/viewsets.py index 4335a1cab5..9be30bb144 100644 --- a/src/openforms/forms/api/viewsets.py +++ b/src/openforms/forms/api/viewsets.py @@ -10,13 +10,13 @@ from openforms.api.pagination import PageNumberPagination +from ..api.permissions import IsStaffOrReadOnly from ..api.serializers import ( FormDefinitionSerializer, FormSerializer, FormStepSerializer, ) from ..models import Form, FormDefinition, FormStep -from ..api.permissions import IsStaffOrReadOnly class BaseFormsViewSet(viewsets.ReadOnlyModelViewSet): From 87c6847d71e202b9d629a391a5d8b87174709d4d Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 12 Apr 2021 11:48:06 +0200 Subject: [PATCH 04/17] :bulb: Fix comment --- src/openforms/forms/api/permissions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openforms/forms/api/permissions.py b/src/openforms/forms/api/permissions.py index d5e7b8fda0..cb62b16143 100644 --- a/src/openforms/forms/api/permissions.py +++ b/src/openforms/forms/api/permissions.py @@ -3,7 +3,7 @@ class IsStaffOrReadOnly(BasePermission): """ - The request is authenticated as a user, or is a read-only request. + The request is a staff user, or is a read-only request. """ def has_permission(self, request, view): From 7ba511267c9927d6f2bf7f0b0882aaa0cf8060f7 Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 12 Apr 2021 13:31:22 +0200 Subject: [PATCH 05/17] :recycle: Update inheritance --- src/openforms/forms/api/viewsets.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/openforms/forms/api/viewsets.py b/src/openforms/forms/api/viewsets.py index 9be30bb144..154fa64a81 100644 --- a/src/openforms/forms/api/viewsets.py +++ b/src/openforms/forms/api/viewsets.py @@ -37,14 +37,12 @@ class BaseFormsViewSet(viewsets.ReadOnlyModelViewSet): ) class FormStepViewSet( NestedViewSetMixin, - mixins.CreateModelMixin, - mixins.UpdateModelMixin, - mixins.DestroyModelMixin, - BaseFormsViewSet, + viewsets.ModelViewSet, ): serializer_class = FormStepSerializer queryset = FormStep.objects.all() permission_classes = [IsStaffOrReadOnly] + lookup_field = "uuid" @extend_schema_view( From c5b8bed703da1576caf636baacd6a29f7ff0f8da Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 12 Apr 2021 13:33:19 +0200 Subject: [PATCH 06/17] :white_check_mark: Add unit tests for creating a form --- src/openforms/forms/tests/test_api.py | 45 +++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/openforms/forms/tests/test_api.py b/src/openforms/forms/tests/test_api.py index 6e7d874757..23d996066b 100644 --- a/src/openforms/forms/tests/test_api.py +++ b/src/openforms/forms/tests/test_api.py @@ -4,23 +4,25 @@ from django.http import HttpRequest from django.urls import reverse +from django_webtest import WebTest from rest_framework import status from rest_framework.test import APITestCase +from ..models import Form from .factories import FormDefinitionFactory, FormFactory, FormStepFactory -class FormsAPITests(APITestCase): +class FormsAPITests(WebTest): def setUp(self): # TODO: Replace with API-token User = get_user_model() - user = User.objects.create_user( + self.user = User.objects.create_user( username="john", password="secret", email="john@example.com" ) # TODO: Axes requires HttpRequest, should we have that in the API at all? assert self.client.login( - request=HttpRequest(), username=user.username, password="secret" + request=HttpRequest(), username=self.user.username, password="secret" ) @expectedFailure @@ -42,6 +44,43 @@ def test_list(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 2) + def test_post_successful(self): + self.user.is_staff = True + self.user.save() + url = reverse("api:form-list") + data = { + "name": "Test Post Form", + "slug": "test-post-form", + } + response = self.client.post(url, data=data, format="json") + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(Form.objects.filter(**data).count(), 1) + + def test_post_with_bad_data(self): + self.user.is_staff = True + self.user.save() + url = reverse("api:form-list") + data = { + "bad": "data", + } + response = self.client.post(url, data=data, format="json") + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Form.objects.count(), 0) + + def test_post_without_authentication(self): + + url = reverse("api:form-list") + data = { + "name": "Test Post Form", + "slug": "test-post-form", + } + response = self.client.post(url, data=data, format="json") + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(Form.objects.count(), 0) + def test_steps_list(self): step = FormStepFactory.create() From a99096af02b30987f360d88cc5ec46d0acaad34d Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 12 Apr 2021 14:12:08 +0200 Subject: [PATCH 07/17] :white_check_mark: Add unit tests for putting and patching a form --- src/openforms/forms/tests/test_api.py | 131 +++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 3 deletions(-) diff --git a/src/openforms/forms/tests/test_api.py b/src/openforms/forms/tests/test_api.py index 23d996066b..282a233c39 100644 --- a/src/openforms/forms/tests/test_api.py +++ b/src/openforms/forms/tests/test_api.py @@ -1,7 +1,9 @@ +import uuid from unittest import expectedFailure from django.contrib.auth import get_user_model from django.http import HttpRequest +from django.test.client import MULTIPART_CONTENT from django.urls import reverse from django_webtest import WebTest @@ -52,7 +54,7 @@ def test_post_successful(self): "name": "Test Post Form", "slug": "test-post-form", } - response = self.client.post(url, data=data, format="json") + response = self.client.post(url, data=data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(Form.objects.filter(**data).count(), 1) @@ -64,7 +66,7 @@ def test_post_with_bad_data(self): data = { "bad": "data", } - response = self.client.post(url, data=data, format="json") + response = self.client.post(url, data=data) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(Form.objects.count(), 0) @@ -76,11 +78,134 @@ def test_post_without_authentication(self): "name": "Test Post Form", "slug": "test-post-form", } - response = self.client.post(url, data=data, format="json") + response = self.client.post(url, data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(Form.objects.count(), 0) + def test_patch_form(self): + form = FormFactory.create() + self.user.is_staff = True + self.user.save() + + url = f'{reverse("api:form-list")}/{form.uuid}' + data = { + "name": "Test Patch Form", + } + response = self.client.patch(url, data=data, content_type='application/json') + + self.assertEqual(response.status_code, status.HTTP_200_OK) + form.refresh_from_db() + self.assertEqual(form.name, "Test Patch Form") + + def test_patch_form_without_authentication(self): + form = FormFactory.create() + url = f'{reverse("api:form-list")}/{form.uuid}' + data = { + "name": "Test Patch Form", + } + response = self.client.patch(url, data=data, content_type='application/json') + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + form.refresh_from_db() + self.assertNotEqual(form.name, "Test Patch Form") + + def test_patch_form_with_bad_data(self): + form = FormFactory.create() + url = f'{reverse("api:form-list")}/{form.uuid}' + data = { + "bad": "data", + } + response = self.client.patch(url, data=data, content_type='application/json') + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + form.refresh_from_db() + self.assertNotEqual(form.name, "Test Patch Form") + + def test_patch_form_404(self): + form = FormFactory.create() + self.user.is_staff = True + self.user.save() + + url = f'{reverse("api:form-list")}/{uuid.uuid4()}' + data = { + "bad": "data", + } + response = self.client.patch(url, data=data, content_type='application/json') + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + form.refresh_from_db() + self.assertNotEqual(form.name, "Test Patch Form") + + def test_put_form(self): + form = FormFactory.create() + self.user.is_staff = True + self.user.save() + + url = f'{reverse("api:form-list")}/{form.uuid}' + data = { + "name": "Test Put Form", + "slug": "test-put-form", + } + response = self.client.put(url, data=data, content_type='application/json') + + self.assertEqual(response.status_code, status.HTTP_200_OK) + form.refresh_from_db() + self.assertEqual(form.name, "Test Put Form") + + def test_put_form_with_incomplete_data(self): + form = FormFactory.create() + self.user.is_staff = True + self.user.save() + + url = f'{reverse("api:form-list")}/{form.uuid}' + data = { + "name": "Test Put Form", + } + response = self.client.put(url, data=data, content_type='application/json') + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.json(), {'slug': ['Dit veld is vereist.']}) + + def test_put_form_without_authentication(self): + form = FormFactory.create() + url = f'{reverse("api:form-list")}/{form.uuid}' + data = { + "name": "Test Put Form", + } + response = self.client.put(url, data=data, content_type='application/json') + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + form.refresh_from_db() + self.assertNotEqual(form.name, "Test Put Form") + + def test_put_form_with_bad_data(self): + form = FormFactory.create() + url = f'{reverse("api:form-list")}/{form.uuid}' + data = { + "bad": "data", + } + response = self.client.put(url, data=data, content_type='application/json') + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + form.refresh_from_db() + self.assertNotEqual(form.name, "Test Put Form") + + def test_put_form_404(self): + form = FormFactory.create() + self.user.is_staff = True + self.user.save() + + url = f'{reverse("api:form-list")}/{uuid.uuid4()}' + data = { + "bad": "data", + } + response = self.client.put(url, data=data, content_type='application/json') + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + form.refresh_from_db() + self.assertNotEqual(form.name, "Test Put Form") + def test_steps_list(self): step = FormStepFactory.create() From f170aad018271ab47ae9923b91a94bda09ec535c Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 12 Apr 2021 14:26:46 +0200 Subject: [PATCH 08/17] :white_check_mark: Update unit tests --- src/openforms/forms/tests/test_api.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/openforms/forms/tests/test_api.py b/src/openforms/forms/tests/test_api.py index 282a233c39..5d1adc14a4 100644 --- a/src/openforms/forms/tests/test_api.py +++ b/src/openforms/forms/tests/test_api.py @@ -3,10 +3,8 @@ from django.contrib.auth import get_user_model from django.http import HttpRequest -from django.test.client import MULTIPART_CONTENT from django.urls import reverse -from django_webtest import WebTest from rest_framework import status from rest_framework.test import APITestCase @@ -14,7 +12,7 @@ from .factories import FormDefinitionFactory, FormFactory, FormStepFactory -class FormsAPITests(WebTest): +class FormsAPITests(APITestCase): def setUp(self): # TODO: Replace with API-token User = get_user_model() @@ -92,7 +90,7 @@ def test_patch_form(self): data = { "name": "Test Patch Form", } - response = self.client.patch(url, data=data, content_type='application/json') + response = self.client.patch(url, data=data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) form.refresh_from_db() @@ -104,7 +102,7 @@ def test_patch_form_without_authentication(self): data = { "name": "Test Patch Form", } - response = self.client.patch(url, data=data, content_type='application/json') + response = self.client.patch(url, data=data, content_type="application/json") self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) form.refresh_from_db() @@ -116,7 +114,7 @@ def test_patch_form_with_bad_data(self): data = { "bad": "data", } - response = self.client.patch(url, data=data, content_type='application/json') + response = self.client.patch(url, data=data, content_type="application/json") self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) form.refresh_from_db() @@ -131,7 +129,7 @@ def test_patch_form_404(self): data = { "bad": "data", } - response = self.client.patch(url, data=data, content_type='application/json') + response = self.client.patch(url, data=data, content_type="application/json") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) form.refresh_from_db() @@ -147,7 +145,7 @@ def test_put_form(self): "name": "Test Put Form", "slug": "test-put-form", } - response = self.client.put(url, data=data, content_type='application/json') + response = self.client.put(url, data=data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) form.refresh_from_db() @@ -162,10 +160,10 @@ def test_put_form_with_incomplete_data(self): data = { "name": "Test Put Form", } - response = self.client.put(url, data=data, content_type='application/json') + response = self.client.put(url, data=data, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(response.json(), {'slug': ['Dit veld is vereist.']}) + self.assertEqual(response.json(), {"slug": ["Dit veld is vereist."]}) def test_put_form_without_authentication(self): form = FormFactory.create() @@ -173,7 +171,7 @@ def test_put_form_without_authentication(self): data = { "name": "Test Put Form", } - response = self.client.put(url, data=data, content_type='application/json') + response = self.client.put(url, data=data, content_type="application/json") self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) form.refresh_from_db() @@ -185,7 +183,7 @@ def test_put_form_with_bad_data(self): data = { "bad": "data", } - response = self.client.put(url, data=data, content_type='application/json') + response = self.client.put(url, data=data, content_type="application/json") self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) form.refresh_from_db() @@ -200,7 +198,7 @@ def test_put_form_404(self): data = { "bad": "data", } - response = self.client.put(url, data=data, content_type='application/json') + response = self.client.put(url, data=data, content_type="application/json") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) form.refresh_from_db() From 121122a91b786cd6a6b6db6e0efb93dd75461573 Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 12 Apr 2021 16:30:45 +0200 Subject: [PATCH 09/17] :sparkles: Add creating and updating of form steps --- src/openforms/forms/api/serializers.py | 38 +++- src/openforms/forms/api/validators.py | 23 +++ src/openforms/forms/tests/test_api.py | 240 ++++++++++++++++++++++++- 3 files changed, 292 insertions(+), 9 deletions(-) create mode 100644 src/openforms/forms/api/validators.py diff --git a/src/openforms/forms/api/serializers.py b/src/openforms/forms/api/serializers.py index 5e590197a9..55b5021214 100644 --- a/src/openforms/forms/api/serializers.py +++ b/src/openforms/forms/api/serializers.py @@ -1,4 +1,5 @@ -import json +from django.core.exceptions import ObjectDoesNotExist +from django.http import Http404 from rest_framework import serializers from rest_framework_nested.relations import NestedHyperlinkedRelatedField @@ -7,6 +8,7 @@ from ..custom_field_types import handle_custom_types from ..models import Form, FormDefinition, FormStep +from .validators import FormDefinitionValidator class MinimalFormStepSerializer(serializers.ModelSerializer): @@ -84,13 +86,41 @@ class Meta: class FormStepSerializer(serializers.ModelSerializer): - index = serializers.IntegerField(source="order") - configuration = serializers.JSONField(source="form_definition.configuration") + index = serializers.IntegerField(source="order", read_only=True) + configuration = serializers.JSONField( + source="form_definition.configuration", read_only=True + ) + form_definition = serializers.UUIDField(write_only=True) parent_lookup_kwargs = { "form_uuid": "form__uuid", } + validators = [ + FormDefinitionValidator(), + ] + class Meta: model = FormStep - fields = ("index", "configuration") + fields = ("index", "configuration", "form_definition") + + def update(self, instance, validated_data): + instance.form_definition = FormDefinition.objects.get( + uuid=validated_data["form_definition"] + ) + instance.save() + + return instance + + def create(self, validated_data): + + try: + form = Form.objects.get(uuid=self.context["view"].kwargs["form_uuid"]) + except ObjectDoesNotExist: + raise Http404() + + form_definition = FormDefinition.objects.get( + uuid=validated_data["form_definition"] + ) + + return FormStep.objects.create(form=form, form_definition=form_definition) diff --git a/src/openforms/forms/api/validators.py b/src/openforms/forms/api/validators.py new file mode 100644 index 0000000000..85960662b9 --- /dev/null +++ b/src/openforms/forms/api/validators.py @@ -0,0 +1,23 @@ +from django.core.exceptions import ValidationError +from django.utils.translation import ugettext_lazy as _ + +from openforms.forms.models import FormDefinition + + +class FormDefinitionValidator: + code = "non-existant-form-definition" + message = _("A form definition does not exist with this uuid") + + def set_context(self, serializer): + """ + This hook is called by the serializer instance, + prior to the validation call being made. + """ + self.instance = getattr(serializer, "instance", None) + + def __call__(self, attrs: dict): + if ( + not attrs.get("form_definition") + or not FormDefinition.objects.filter(uuid=attrs["form_definition"]).exists() + ): + raise ValidationError(self.message, code=self.code) diff --git a/src/openforms/forms/tests/test_api.py b/src/openforms/forms/tests/test_api.py index 5d1adc14a4..cbac67ad79 100644 --- a/src/openforms/forms/tests/test_api.py +++ b/src/openforms/forms/tests/test_api.py @@ -8,7 +8,7 @@ from rest_framework import status from rest_framework.test import APITestCase -from ..models import Form +from ..models import Form, FormStep from .factories import FormDefinitionFactory, FormFactory, FormStepFactory @@ -110,13 +110,15 @@ def test_patch_form_without_authentication(self): def test_patch_form_with_bad_data(self): form = FormFactory.create() + self.user.is_staff = True + self.user.save() url = f'{reverse("api:form-list")}/{form.uuid}' data = { "bad": "data", } response = self.client.patch(url, data=data, content_type="application/json") - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) form.refresh_from_db() self.assertNotEqual(form.name, "Test Patch Form") @@ -179,13 +181,15 @@ def test_put_form_without_authentication(self): def test_put_form_with_bad_data(self): form = FormFactory.create() + self.user.is_staff = True + self.user.save() url = f'{reverse("api:form-list")}/{form.uuid}' data = { "bad": "data", } response = self.client.put(url, data=data, content_type="application/json") - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) form.refresh_from_db() self.assertNotEqual(form.name, "Test Put Form") @@ -204,15 +208,241 @@ def test_put_form_404(self): form.refresh_from_db() self.assertNotEqual(form.name, "Test Put Form") + +class FormsStepsAPITests(APITestCase): + def setUp(self): + # TODO: Replace with API-token + User = get_user_model() + self.user = User.objects.create_user( + username="john", password="secret", email="john@example.com" + ) + self.step = FormStepFactory.create() + self.other_form_definition = FormDefinitionFactory.create() + + # TODO: Axes requires HttpRequest, should we have that in the API at all? + assert self.client.login( + request=HttpRequest(), username=self.user.username, password="secret" + ) + def test_steps_list(self): - step = FormStepFactory.create() - url = reverse("api:form-steps-list", args=(step.form.uuid,)) + url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 1) + def test_post_successful(self): + self.user.is_staff = True + self.user.save() + url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) + data = { + "formDefinition": self.step.form_definition.uuid, + } + response = self.client.post(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(FormStep.objects.count(), 2) + + def test_post_with_bad_data(self): + self.user.is_staff = True + self.user.save() + url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) + data = { + "bad": "data", + } + response = self.client.post(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(FormStep.objects.count(), 1) + + def test_post_404(self): + self.user.is_staff = True + self.user.save() + url = reverse("api:form-steps-list", args=(uuid.uuid4(),)) + data = { + "formDefinition": self.step.form_definition.uuid, + } + response = self.client.post(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(FormStep.objects.count(), 1) + + def test_post_without_authentication(self): + url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) + data = { + "formDefinition": self.step.form_definition.uuid, + } + response = self.client.post(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(FormStep.objects.count(), 1) + + def test_put_successful(self): + self.user.is_staff = True + self.user.save() + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + data = { + "formDefinition": self.other_form_definition.uuid, + } + response = self.client.put(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + FormStep.objects.filter(form_definition=self.other_form_definition).count(), + 1, + ) + + def test_put_404(self): + self.user.is_staff = True + self.user.save() + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{uuid.uuid4()}' + data = { + "formDefinition": self.other_form_definition.uuid, + } + response = self.client.put(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual( + FormStep.objects.filter(form_definition=self.other_form_definition).count(), + 0, + ) + + def test_put_with_non_existant_form_definition(self): + self.user.is_staff = True + self.user.save() + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + data = { + "formDefinition": uuid.uuid4(), + } + response = self.client.put(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + FormStep.objects.filter(form_definition=self.other_form_definition).count(), + 0, + ) + + def test_put_with_bad_data(self): + self.user.is_staff = True + self.user.save() + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + data = { + "bad": "data", + } + response = self.client.put(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(FormStep.objects.count(), 1) + + def test_put_without_authentication(self): + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + data = { + "formDefinition": self.other_form_definition.uuid, + } + response = self.client.put(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual( + FormStep.objects.filter(form_definition=self.other_form_definition).count(), + 0, + ) + + def test_patch_successful(self): + self.user.is_staff = True + self.user.save() + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + data = { + "formDefinition": self.other_form_definition.uuid, + } + response = self.client.patch(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + FormStep.objects.filter(form_definition=self.other_form_definition).count(), + 1, + ) + + def test_patch_404(self): + self.user.is_staff = True + self.user.save() + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{uuid.uuid4()}' + data = { + "formDefinition": self.other_form_definition.uuid, + } + response = self.client.patch(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual( + FormStep.objects.filter(form_definition=self.other_form_definition).count(), + 0, + ) + + def test_patch_with_bad_uuid(self): + self.user.is_staff = True + self.user.save() + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + data = { + "formDefinition": uuid.uuid4(), + } + response = self.client.patch(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + FormStep.objects.filter(form_definition=self.other_form_definition).count(), + 0, + ) + + def test_patch_with_bad_data(self): + self.user.is_staff = True + self.user.save() + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + data = { + "bad": "data", + } + response = self.client.patch(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(FormStep.objects.count(), 1) + + def test_patch_without_authentication(self): + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + data = { + "formDefinition": self.other_form_definition.uuid, + } + response = self.client.patch(url, data=data) + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual( + FormStep.objects.filter(form_definition=self.other_form_definition).count(), + 0, + ) + + def test_delete_successful(self): + self.user.is_staff = True + self.user.save() + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + response = self.client.delete(url) + + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(FormStep.objects.count(), 0) + + def test_delete_404(self): + self.user.is_staff = True + self.user.save() + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{uuid.uuid4()}' + response = self.client.delete(url) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(FormStep.objects.count(), 1) + + def test_delete_not_authenticated(self): + url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + response = self.client.delete(url) + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(FormStep.objects.count(), 1) + class FormDefinitionsAPITests(APITestCase): def setUp(self): From e6fe64c974475d9a75295f0292185dc71169d7f5 Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Tue, 13 Apr 2021 14:49:05 +0200 Subject: [PATCH 10/17] :recycle: Clean up unused code --- src/openforms/forms/api/serializers.py | 12 +++--------- src/openforms/forms/api/validators.py | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/openforms/forms/api/serializers.py b/src/openforms/forms/api/serializers.py index 55b5021214..5bc0bed828 100644 --- a/src/openforms/forms/api/serializers.py +++ b/src/openforms/forms/api/serializers.py @@ -1,6 +1,3 @@ -from django.core.exceptions import ObjectDoesNotExist -from django.http import Http404 - from rest_framework import serializers from rest_framework_nested.relations import NestedHyperlinkedRelatedField @@ -8,7 +5,7 @@ from ..custom_field_types import handle_custom_types from ..models import Form, FormDefinition, FormStep -from .validators import FormDefinitionValidator +from .validators import FormDefinitionValidator, FormValidator class MinimalFormStepSerializer(serializers.ModelSerializer): @@ -97,6 +94,7 @@ class FormStepSerializer(serializers.ModelSerializer): } validators = [ + FormValidator(), FormDefinitionValidator(), ] @@ -114,11 +112,7 @@ def update(self, instance, validated_data): def create(self, validated_data): - try: - form = Form.objects.get(uuid=self.context["view"].kwargs["form_uuid"]) - except ObjectDoesNotExist: - raise Http404() - + form = Form.objects.get(uuid=self.context["view"].kwargs["form_uuid"]) form_definition = FormDefinition.objects.get( uuid=validated_data["form_definition"] ) diff --git a/src/openforms/forms/api/validators.py b/src/openforms/forms/api/validators.py index 85960662b9..5c7892ae4a 100644 --- a/src/openforms/forms/api/validators.py +++ b/src/openforms/forms/api/validators.py @@ -1,19 +1,28 @@ from django.core.exceptions import ValidationError +from django.http import Http404 from django.utils.translation import ugettext_lazy as _ -from openforms.forms.models import FormDefinition +from openforms.forms.models import Form, FormDefinition -class FormDefinitionValidator: - code = "non-existant-form-definition" - message = _("A form definition does not exist with this uuid") +class FormValidator: + message = _("A form does not exist with this uuid") def set_context(self, serializer): """ This hook is called by the serializer instance, prior to the validation call being made. """ - self.instance = getattr(serializer, "instance", None) + self.form_uuid = serializer.context["view"].kwargs["form_uuid"] + + def __call__(self, attrs: dict): + if not Form.objects.filter(uuid=self.form_uuid).exists(): + raise Http404(self.message) + + +class FormDefinitionValidator: + code = "non-existant-form-definition" + message = _("A form definition does not exist with this uuid") def __call__(self, attrs: dict): if ( From fd499b670e08ce90d523852fc30587d903569788 Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Tue, 13 Apr 2021 14:52:42 +0200 Subject: [PATCH 11/17] :recycle: Remove unused code --- src/openforms/forms/api/validators.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/openforms/forms/api/validators.py b/src/openforms/forms/api/validators.py index 5c7892ae4a..354a8eb2d9 100644 --- a/src/openforms/forms/api/validators.py +++ b/src/openforms/forms/api/validators.py @@ -6,7 +6,6 @@ class FormValidator: - message = _("A form does not exist with this uuid") def set_context(self, serializer): """ @@ -17,7 +16,7 @@ def set_context(self, serializer): def __call__(self, attrs: dict): if not Form.objects.filter(uuid=self.form_uuid).exists(): - raise Http404(self.message) + raise Http404() class FormDefinitionValidator: From 7b7e9d9fb51b6c761207e0d3c1b3f790359dc263 Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Tue, 13 Apr 2021 15:40:40 +0200 Subject: [PATCH 12/17] :lipstick: style code with black --- src/openforms/forms/api/validators.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/openforms/forms/api/validators.py b/src/openforms/forms/api/validators.py index 354a8eb2d9..1fdc1facae 100644 --- a/src/openforms/forms/api/validators.py +++ b/src/openforms/forms/api/validators.py @@ -6,7 +6,6 @@ class FormValidator: - def set_context(self, serializer): """ This hook is called by the serializer instance, From 647a5242af2de2fe0fa95ae659ceda5d436c44ad Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Wed, 14 Apr 2021 09:38:07 +0200 Subject: [PATCH 13/17] :memo: Update schema documentation --- src/openforms/forms/api/viewsets.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/openforms/forms/api/viewsets.py b/src/openforms/forms/api/viewsets.py index 154fa64a81..a8bf3c0bc5 100644 --- a/src/openforms/forms/api/viewsets.py +++ b/src/openforms/forms/api/viewsets.py @@ -34,6 +34,10 @@ class BaseFormsViewSet(viewsets.ReadOnlyModelViewSet): @extend_schema_view( list=extend_schema(summary=_("List form steps")), retrieve=extend_schema(summary=_("Retrieve form step details")), + create=extend_schema(summary=_("Create a form step")), + update=extend_schema(summary=_("Update all details of a form step")), + partial_update=extend_schema(summary=_("Update some details of a form step")), + destroy=extend_schema(summary=_("Delete a form step")), ) class FormStepViewSet( NestedViewSetMixin, @@ -85,6 +89,9 @@ def configuration(self, request: Request, *args, **kwargs): @extend_schema_view( list=extend_schema(summary=_("List forms")), retrieve=extend_schema(summary=_("Retrieve form details")), + create=extend_schema(summary=_("Create form")), + update=extend_schema(summary=_("Update all details of a form")), + partial_update=extend_schema(summary=_("Update given details of a form")), ) class FormViewSet(mixins.CreateModelMixin, mixins.UpdateModelMixin, BaseFormsViewSet): queryset = Form.objects.filter(active=True) From 7865c4470b584c30838cd85229b99f6d84dd623b Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 19 Apr 2021 10:22:45 +0200 Subject: [PATCH 14/17] :recycle: Update code based on PR feedback --- src/openforms/forms/api/permissions.py | 4 +- src/openforms/forms/api/serializers.py | 35 ++++++-------- src/openforms/forms/api/validators.py | 30 ------------ src/openforms/forms/api/viewsets.py | 18 +++---- src/openforms/forms/tests/test_api.py | 67 +++++++++++++++----------- 5 files changed, 64 insertions(+), 90 deletions(-) delete mode 100644 src/openforms/forms/api/validators.py diff --git a/src/openforms/forms/api/permissions.py b/src/openforms/forms/api/permissions.py index cb62b16143..523e876768 100644 --- a/src/openforms/forms/api/permissions.py +++ b/src/openforms/forms/api/permissions.py @@ -7,6 +7,6 @@ class IsStaffOrReadOnly(BasePermission): """ def has_permission(self, request, view): - return bool( - request.method in SAFE_METHODS or request.user and request.user.is_staff + return request.method in SAFE_METHODS or ( + request.user and request.user.is_staff ) diff --git a/src/openforms/forms/api/serializers.py b/src/openforms/forms/api/serializers.py index 5bc0bed828..f8c593f399 100644 --- a/src/openforms/forms/api/serializers.py +++ b/src/openforms/forms/api/serializers.py @@ -1,3 +1,5 @@ +from django.shortcuts import get_object_or_404 + from rest_framework import serializers from rest_framework_nested.relations import NestedHyperlinkedRelatedField @@ -5,7 +7,8 @@ from ..custom_field_types import handle_custom_types from ..models import Form, FormDefinition, FormStep -from .validators import FormDefinitionValidator, FormValidator + +# from .validators import FormDefinitionValidator, FormValidator class MinimalFormStepSerializer(serializers.ModelSerializer): @@ -82,39 +85,29 @@ class Meta: } -class FormStepSerializer(serializers.ModelSerializer): +class FormStepSerializer(serializers.HyperlinkedModelSerializer): index = serializers.IntegerField(source="order", read_only=True) configuration = serializers.JSONField( source="form_definition.configuration", read_only=True ) - form_definition = serializers.UUIDField(write_only=True) parent_lookup_kwargs = { "form_uuid": "form__uuid", } - validators = [ - FormValidator(), - FormDefinitionValidator(), - ] - class Meta: model = FormStep fields = ("index", "configuration", "form_definition") - def update(self, instance, validated_data): - instance.form_definition = FormDefinition.objects.get( - uuid=validated_data["form_definition"] - ) - instance.save() - - return instance + extra_kwargs = { + "form_definition": { + "view_name": "api:formdefinition-detail", + "lookup_field": "uuid", + }, + } def create(self, validated_data): - - form = Form.objects.get(uuid=self.context["view"].kwargs["form_uuid"]) - form_definition = FormDefinition.objects.get( - uuid=validated_data["form_definition"] + validated_data["form"] = get_object_or_404( + Form, uuid=self.context["view"].kwargs["form_uuid"] ) - - return FormStep.objects.create(form=form, form_definition=form_definition) + return super().create(validated_data) diff --git a/src/openforms/forms/api/validators.py b/src/openforms/forms/api/validators.py deleted file mode 100644 index 1fdc1facae..0000000000 --- a/src/openforms/forms/api/validators.py +++ /dev/null @@ -1,30 +0,0 @@ -from django.core.exceptions import ValidationError -from django.http import Http404 -from django.utils.translation import ugettext_lazy as _ - -from openforms.forms.models import Form, FormDefinition - - -class FormValidator: - def set_context(self, serializer): - """ - This hook is called by the serializer instance, - prior to the validation call being made. - """ - self.form_uuid = serializer.context["view"].kwargs["form_uuid"] - - def __call__(self, attrs: dict): - if not Form.objects.filter(uuid=self.form_uuid).exists(): - raise Http404() - - -class FormDefinitionValidator: - code = "non-existant-form-definition" - message = _("A form definition does not exist with this uuid") - - def __call__(self, attrs: dict): - if ( - not attrs.get("form_definition") - or not FormDefinition.objects.filter(uuid=attrs["form_definition"]).exists() - ): - raise ValidationError(self.message, code=self.code) diff --git a/src/openforms/forms/api/viewsets.py b/src/openforms/forms/api/viewsets.py index a8bf3c0bc5..84863373ac 100644 --- a/src/openforms/forms/api/viewsets.py +++ b/src/openforms/forms/api/viewsets.py @@ -19,13 +19,6 @@ from ..models import Form, FormDefinition, FormStep -class BaseFormsViewSet(viewsets.ReadOnlyModelViewSet): - lookup_field = "uuid" - # anonymous clients must be able to get the form definitions in the browser - # The DRF settings apply some default throttling to mitigate abuse - permission_classes = [permissions.AllowAny] - - @extend_schema( parameters=[ OpenApiParameter("form_uuid", OpenApiTypes.UUID, location=OpenApiParameter.PATH) @@ -59,10 +52,14 @@ class FormStepViewSet( tags=["forms"], ), ) -class FormDefinitionViewSet(BaseFormsViewSet): +class FormDefinitionViewSet(viewsets.ReadOnlyModelViewSet): queryset = FormDefinition.objects.order_by("slug") serializer_class = FormDefinitionSerializer pagination_class = PageNumberPagination + lookup_field = "uuid" + # anonymous clients must be able to get the form definitions in the browser + # The DRF settings apply some default throttling to mitigate abuse + permission_classes = [permissions.AllowAny] def get_serializer_context(self) -> dict: context = super().get_serializer_context() @@ -93,7 +90,10 @@ def configuration(self, request: Request, *args, **kwargs): update=extend_schema(summary=_("Update all details of a form")), partial_update=extend_schema(summary=_("Update given details of a form")), ) -class FormViewSet(mixins.CreateModelMixin, mixins.UpdateModelMixin, BaseFormsViewSet): +class FormViewSet( + mixins.CreateModelMixin, mixins.UpdateModelMixin, viewsets.ReadOnlyModelViewSet +): queryset = Form.objects.filter(active=True) + lookup_field = "uuid" serializer_class = FormSerializer permission_classes = [IsStaffOrReadOnly] diff --git a/src/openforms/forms/tests/test_api.py b/src/openforms/forms/tests/test_api.py index cbac67ad79..1a4784da4b 100644 --- a/src/openforms/forms/tests/test_api.py +++ b/src/openforms/forms/tests/test_api.py @@ -236,9 +236,10 @@ def test_post_successful(self): self.user.is_staff = True self.user.save() url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) - data = { - "formDefinition": self.step.form_definition.uuid, - } + form_detail_url = reverse( + "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.post(url, data=data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) @@ -260,9 +261,10 @@ def test_post_404(self): self.user.is_staff = True self.user.save() url = reverse("api:form-steps-list", args=(uuid.uuid4(),)) - data = { - "formDefinition": self.step.form_definition.uuid, - } + form_detail_url = reverse( + "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.post(url, data=data) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -270,9 +272,10 @@ def test_post_404(self): def test_post_without_authentication(self): url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) - data = { - "formDefinition": self.step.form_definition.uuid, - } + form_detail_url = reverse( + "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.post(url, data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) @@ -282,9 +285,11 @@ def test_put_successful(self): self.user.is_staff = True self.user.save() url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' - data = { - "formDefinition": self.other_form_definition.uuid, - } + form_detail_url = reverse( + "api:formdefinition-detail", + kwargs={"uuid": self.other_form_definition.uuid}, + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -337,9 +342,10 @@ def test_put_with_bad_data(self): def test_put_without_authentication(self): url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' - data = { - "formDefinition": self.other_form_definition.uuid, - } + form_detail_url = reverse( + "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) @@ -352,9 +358,11 @@ def test_patch_successful(self): self.user.is_staff = True self.user.save() url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' - data = { - "formDefinition": self.other_form_definition.uuid, - } + form_detail_url = reverse( + "api:formdefinition-detail", + kwargs={"uuid": self.other_form_definition.uuid}, + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.patch(url, data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -367,9 +375,10 @@ def test_patch_404(self): self.user.is_staff = True self.user.save() url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{uuid.uuid4()}' - data = { - "formDefinition": self.other_form_definition.uuid, - } + form_detail_url = reverse( + "api:formdefinition-detail", kwargs={"uuid": uuid.uuid4()} + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.patch(url, data=data) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -382,9 +391,10 @@ def test_patch_with_bad_uuid(self): self.user.is_staff = True self.user.save() url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' - data = { - "formDefinition": uuid.uuid4(), - } + form_detail_url = reverse( + "api:formdefinition-detail", kwargs={"uuid": uuid.uuid4()} + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.patch(url, data=data) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -402,14 +412,15 @@ def test_patch_with_bad_data(self): } response = self.client.patch(url, data=data) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(FormStep.objects.count(), 1) def test_patch_without_authentication(self): url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' - data = { - "formDefinition": self.other_form_definition.uuid, - } + form_detail_url = reverse( + "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.patch(url, data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) From 4d9a997b9ee126412118df73c86f49c3a79e3298 Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 19 Apr 2021 11:34:09 +0200 Subject: [PATCH 15/17] :white_check_mark: Update unit tests based on PR feedback --- src/openforms/forms/tests/test_api.py | 222 +++++++++++++++----------- 1 file changed, 130 insertions(+), 92 deletions(-) diff --git a/src/openforms/forms/tests/test_api.py b/src/openforms/forms/tests/test_api.py index 1a4784da4b..4c29a69722 100644 --- a/src/openforms/forms/tests/test_api.py +++ b/src/openforms/forms/tests/test_api.py @@ -44,7 +44,7 @@ def test_list(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 2) - def test_post_successful(self): + def test_create_form_successful(self): self.user.is_staff = True self.user.save() url = reverse("api:form-list") @@ -55,9 +55,12 @@ def test_post_successful(self): response = self.client.post(url, data=data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(Form.objects.filter(**data).count(), 1) + self.assertEqual(Form.objects.count(), 1) + form = Form.objects.get() + self.assertEqual(form.name, "Test Post Form") + self.assertEqual(form.slug, "test-post-form") - def test_post_with_bad_data(self): + def test_create_form_unsuccessful_with_bad_data(self): self.user.is_staff = True self.user.save() url = reverse("api:form-list") @@ -67,9 +70,13 @@ def test_post_with_bad_data(self): response = self.client.post(url, data=data) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual(Form.objects.count(), 0) + self.assertFalse(Form.objects.exists()) + self.assertEqual( + response.json(), + {"name": ["Dit veld is vereist."], "slug": ["Dit veld is vereist."]}, + ) - def test_post_without_authentication(self): + def test_create_form_unsuccessful_without_authorization(self): url = reverse("api:form-list") data = { @@ -79,130 +86,131 @@ def test_post_without_authentication(self): response = self.client.post(url, data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(Form.objects.count(), 0) + self.assertFalse(Form.objects.exists()) - def test_patch_form(self): + def test_partial_update_of_form(self): form = FormFactory.create() self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-list")}/{form.uuid}' + url = reverse("api:form-detail", kwargs={"uuid": form.uuid}) data = { "name": "Test Patch Form", } - response = self.client.patch(url, data=data, format="json") + response = self.client.patch(url, data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) form.refresh_from_db() self.assertEqual(form.name, "Test Patch Form") - def test_patch_form_without_authentication(self): + def test_partial_update_of_form_unsuccessful_without_authorization(self): form = FormFactory.create() - url = f'{reverse("api:form-list")}/{form.uuid}' + url = reverse("api:form-detail", kwargs={"uuid": form.uuid}) data = { "name": "Test Patch Form", } - response = self.client.patch(url, data=data, content_type="application/json") + response = self.client.patch(url, data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) form.refresh_from_db() self.assertNotEqual(form.name, "Test Patch Form") - def test_patch_form_with_bad_data(self): + def test_partial_update_of_form_with_bad_data_does_no_update(self): form = FormFactory.create() self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-list")}/{form.uuid}' + url = reverse("api:form-detail", kwargs={"uuid": form.uuid}) data = { "bad": "data", } - response = self.client.patch(url, data=data, content_type="application/json") + response = self.client.patch(url, data=data) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - form.refresh_from_db() - self.assertNotEqual(form.name, "Test Patch Form") + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Check form is unchanged + self.assertEqual(form, Form.objects.get(id=form.id)) - def test_patch_form_404(self): - form = FormFactory.create() + def test_partial_update_of_form_unsuccessful_when_form_cannot_be_found(self): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-list")}/{uuid.uuid4()}' + url = reverse("api:form-detail", kwargs={"uuid": uuid.uuid4()}) data = { "bad": "data", } - response = self.client.patch(url, data=data, content_type="application/json") + response = self.client.patch(url, data=data) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - form.refresh_from_db() - self.assertNotEqual(form.name, "Test Patch Form") - def test_put_form(self): + def test_complete_update_of_form_successful(self): form = FormFactory.create() self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-list")}/{form.uuid}' + url = reverse("api:form-detail", kwargs={"uuid": form.uuid}) data = { "name": "Test Put Form", "slug": "test-put-form", } - response = self.client.put(url, data=data, format="json") + response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) form.refresh_from_db() self.assertEqual(form.name, "Test Put Form") - def test_put_form_with_incomplete_data(self): + def test_complete_update_of_form_with_incomplete_data_unsuccessful(self): form = FormFactory.create() self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-list")}/{form.uuid}' + url = reverse("api:form-detail", kwargs={"uuid": form.uuid}) data = { "name": "Test Put Form", } - response = self.client.put(url, data=data, format="json") + response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.json(), {"slug": ["Dit veld is vereist."]}) - def test_put_form_without_authentication(self): + def test_complete_update_of_form_unsuccessful_without_authorization(self): form = FormFactory.create() - url = f'{reverse("api:form-list")}/{form.uuid}' + url = reverse("api:form-detail", kwargs={"uuid": form.uuid}) data = { "name": "Test Put Form", } - response = self.client.put(url, data=data, content_type="application/json") + response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) form.refresh_from_db() self.assertNotEqual(form.name, "Test Put Form") - def test_put_form_with_bad_data(self): + def test_complete_update_of_form_unsuccessful_with_bad_data(self): form = FormFactory.create() self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-list")}/{form.uuid}' + url = reverse("api:form-detail", kwargs={"uuid": form.uuid}) data = { "bad": "data", } - response = self.client.put(url, data=data, content_type="application/json") + response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) form.refresh_from_db() self.assertNotEqual(form.name, "Test Put Form") + self.assertEqual( + response.json(), + {"name": ["Dit veld is vereist."], "slug": ["Dit veld is vereist."]}, + ) - def test_put_form_404(self): + def test_complete_update_of_form_when_form_cannot_be_found(self): form = FormFactory.create() self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-list")}/{uuid.uuid4()}' + url = reverse("api:form-detail", kwargs={"uuid": uuid.uuid4()}) data = { "bad": "data", } - response = self.client.put(url, data=data, content_type="application/json") + response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) form.refresh_from_db() @@ -232,7 +240,7 @@ def test_steps_list(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.json()), 1) - def test_post_successful(self): + def test_create_form_step_successful(self): self.user.is_staff = True self.user.save() url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) @@ -245,7 +253,7 @@ def test_post_successful(self): self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(FormStep.objects.count(), 2) - def test_post_with_bad_data(self): + def test_create_form_step_unsuccessful_with_bad_data(self): self.user.is_staff = True self.user.save() url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) @@ -256,8 +264,9 @@ def test_post_with_bad_data(self): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(FormStep.objects.count(), 1) + self.assertEqual(response.json(), {"formDefinition": ["Dit veld is vereist."]}) - def test_post_404(self): + def test_create_form_step_unsuccessful_when_form_is_not_found(self): self.user.is_staff = True self.user.save() url = reverse("api:form-steps-list", args=(uuid.uuid4(),)) @@ -270,7 +279,7 @@ def test_post_404(self): self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(FormStep.objects.count(), 1) - def test_post_without_authentication(self): + def test_create_form_step_unsuccessful_without_authorization(self): url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} @@ -281,10 +290,12 @@ def test_post_without_authentication(self): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(FormStep.objects.count(), 1) - def test_put_successful(self): + def test_complete_form_step_update_successful(self): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + url = reverse( + "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + ) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": self.other_form_definition.uuid}, @@ -298,40 +309,52 @@ def test_put_successful(self): 1, ) - def test_put_404(self): + def test_complete_form_step_update_unsuccessful_when_form_step_not_found(self): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{uuid.uuid4()}' - data = { - "formDefinition": self.other_form_definition.uuid, - } + url = reverse("api:form-steps-detail", args=(self.step.form.uuid, uuid.uuid4())) + form_detail_url = reverse( + "api:formdefinition-detail", + kwargs={"uuid": self.other_form_definition.uuid}, + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual( - FormStep.objects.filter(form_definition=self.other_form_definition).count(), - 0, + self.assertFalse( + FormStep.objects.filter(form_definition=self.other_form_definition).exists() ) - def test_put_with_non_existant_form_definition(self): + def test_complete_form_step_update_unsuccessful_with_non_existant_form_definition( + self, + ): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' - data = { - "formDefinition": uuid.uuid4(), - } + url = reverse( + "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + ) + form_detail_url = reverse( + "api:formdefinition-detail", + kwargs={"uuid": uuid.uuid4()}, + ) + data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertFalse( + FormStep.objects.filter(form_definition=self.other_form_definition).exists() + ) self.assertEqual( - FormStep.objects.filter(form_definition=self.other_form_definition).count(), - 0, + response.json(), + {"formDefinition": ["Ongeldige hyperlink - Object bestaat niet."]}, ) - def test_put_with_bad_data(self): + def test_complete_form_step_update_unsuccessful_with_bad_data(self): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + url = reverse( + "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + ) data = { "bad": "data", } @@ -339,9 +362,12 @@ def test_put_with_bad_data(self): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(FormStep.objects.count(), 1) + self.assertEqual(response.json(), {"formDefinition": ["Dit veld is vereist."]}) - def test_put_without_authentication(self): - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + def test_complete_form_step_update_unsuccessful_without_authorization(self): + url = reverse( + "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + ) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} ) @@ -349,15 +375,16 @@ def test_put_without_authentication(self): response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual( - FormStep.objects.filter(form_definition=self.other_form_definition).count(), - 0, + self.assertFalse( + FormStep.objects.filter(form_definition=self.other_form_definition).exists() ) - def test_patch_successful(self): + def test_partial_form_step_update_successful(self): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + url = reverse( + "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + ) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": self.other_form_definition.uuid}, @@ -371,10 +398,10 @@ def test_patch_successful(self): 1, ) - def test_patch_404(self): + def test_partial_form_step_update_unsuccessful_when_form_step_not_found(self): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{uuid.uuid4()}' + url = reverse("api:form-steps-detail", args=(self.step.form.uuid, uuid.uuid4())) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": uuid.uuid4()} ) @@ -382,15 +409,16 @@ def test_patch_404(self): response = self.client.patch(url, data=data) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual( - FormStep.objects.filter(form_definition=self.other_form_definition).count(), - 0, + self.assertFalse( + FormStep.objects.filter(form_definition=self.other_form_definition).exists() ) - def test_patch_with_bad_uuid(self): + def test_partial_form_step_update_unsuccessful_when_form_definition_not_found(self): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + url = reverse( + "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + ) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": uuid.uuid4()} ) @@ -398,15 +426,20 @@ def test_patch_with_bad_uuid(self): response = self.client.patch(url, data=data) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertFalse( + FormStep.objects.filter(form_definition=self.other_form_definition).exists() + ) self.assertEqual( - FormStep.objects.filter(form_definition=self.other_form_definition).count(), - 0, + response.json(), + {"formDefinition": ["Ongeldige hyperlink - Object bestaat niet."]}, ) - def test_patch_with_bad_data(self): + def test_partial_form_step_update_unsuccessful_with_bad_data(self): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + url = reverse( + "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + ) data = { "bad": "data", } @@ -415,8 +448,10 @@ def test_patch_with_bad_data(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(FormStep.objects.count(), 1) - def test_patch_without_authentication(self): - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + def test_partial_form_step_update_unsuccessful_without_authorization(self): + url = reverse( + "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + ) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} ) @@ -424,31 +459,34 @@ def test_patch_without_authentication(self): response = self.client.patch(url, data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual( - FormStep.objects.filter(form_definition=self.other_form_definition).count(), - 0, + self.assertFalse( + FormStep.objects.filter(form_definition=self.other_form_definition).exists() ) - def test_delete_successful(self): + def test_delete_form_step_successful(self): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + url = reverse( + "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + ) response = self.client.delete(url) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertEqual(FormStep.objects.count(), 0) + self.assertFalse(FormStep.objects.exists()) - def test_delete_404(self): + def test_delete_form_step_unsuccessful_when_form_not_found(self): self.user.is_staff = True self.user.save() - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{uuid.uuid4()}' + url = reverse("api:form-steps-detail", args=(self.step.form.uuid, uuid.uuid4())) response = self.client.delete(url) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(FormStep.objects.count(), 1) - def test_delete_not_authenticated(self): - url = f'{reverse("api:form-steps-list", args=(self.step.form.uuid,))}/{self.step.uuid}' + def test_delete_form_step_unsuccessful_when_not_authenticated(self): + url = reverse( + "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + ) response = self.client.delete(url) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) From ac0a798e091df88e260fc372fe3602299f6201b8 Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 19 Apr 2021 12:30:01 +0200 Subject: [PATCH 16/17] :white_check_mark: Remove unneeded code from unit tests --- src/openforms/forms/api/serializers.py | 2 - src/openforms/forms/tests/test_api.py | 105 +++++++++++-------------- 2 files changed, 48 insertions(+), 59 deletions(-) diff --git a/src/openforms/forms/api/serializers.py b/src/openforms/forms/api/serializers.py index f8c593f399..0e2c163e05 100644 --- a/src/openforms/forms/api/serializers.py +++ b/src/openforms/forms/api/serializers.py @@ -8,8 +8,6 @@ from ..custom_field_types import handle_custom_types from ..models import Form, FormDefinition, FormStep -# from .validators import FormDefinitionValidator, FormValidator - class MinimalFormStepSerializer(serializers.ModelSerializer): form_definition = serializers.SlugRelatedField(read_only=True, slug_field="name") diff --git a/src/openforms/forms/tests/test_api.py b/src/openforms/forms/tests/test_api.py index 4c29a69722..45928982bd 100644 --- a/src/openforms/forms/tests/test_api.py +++ b/src/openforms/forms/tests/test_api.py @@ -77,7 +77,6 @@ def test_create_form_unsuccessful_with_bad_data(self): ) def test_create_form_unsuccessful_without_authorization(self): - url = reverse("api:form-list") data = { "name": "Test Post Form", @@ -115,27 +114,13 @@ def test_partial_update_of_form_unsuccessful_without_authorization(self): form.refresh_from_db() self.assertNotEqual(form.name, "Test Patch Form") - def test_partial_update_of_form_with_bad_data_does_no_update(self): - form = FormFactory.create() - self.user.is_staff = True - self.user.save() - url = reverse("api:form-detail", kwargs={"uuid": form.uuid}) - data = { - "bad": "data", - } - response = self.client.patch(url, data=data) - - self.assertEqual(response.status_code, status.HTTP_200_OK) - # Check form is unchanged - self.assertEqual(form, Form.objects.get(id=form.id)) - def test_partial_update_of_form_unsuccessful_when_form_cannot_be_found(self): self.user.is_staff = True self.user.save() url = reverse("api:form-detail", kwargs={"uuid": uuid.uuid4()}) data = { - "bad": "data", + "name": "Test Patch Form", } response = self.client.patch(url, data=data) @@ -156,6 +141,7 @@ def test_complete_update_of_form_successful(self): self.assertEqual(response.status_code, status.HTTP_200_OK) form.refresh_from_db() self.assertEqual(form.name, "Test Put Form") + self.assertEqual(form.slug, "test-put-form") def test_complete_update_of_form_with_incomplete_data_unsuccessful(self): form = FormFactory.create() @@ -175,13 +161,15 @@ def test_complete_update_of_form_unsuccessful_without_authorization(self): form = FormFactory.create() url = reverse("api:form-detail", kwargs={"uuid": form.uuid}) data = { - "name": "Test Put Form", + "name": "Test Post Form", + "slug": "test-post-form", } response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) form.refresh_from_db() self.assertNotEqual(form.name, "Test Put Form") + self.assertNotEqual(form.slug, "test-put-form") def test_complete_update_of_form_unsuccessful_with_bad_data(self): form = FormFactory.create() @@ -194,27 +182,23 @@ def test_complete_update_of_form_unsuccessful_with_bad_data(self): response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - form.refresh_from_db() - self.assertNotEqual(form.name, "Test Put Form") self.assertEqual( response.json(), {"name": ["Dit veld is vereist."], "slug": ["Dit veld is vereist."]}, ) def test_complete_update_of_form_when_form_cannot_be_found(self): - form = FormFactory.create() self.user.is_staff = True self.user.save() url = reverse("api:form-detail", kwargs={"uuid": uuid.uuid4()}) data = { - "bad": "data", + "name": "Test Post Form", + "slug": "test-post-form", } response = self.client.put(url, data=data) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - form.refresh_from_db() - self.assertNotEqual(form.name, "Test Put Form") class FormsStepsAPITests(APITestCase): @@ -233,8 +217,7 @@ def setUp(self): ) def test_steps_list(self): - - url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) + url = reverse("api:form-steps-list", kwargs={"form_uuid": self.step.form.uuid}) response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -243,20 +226,24 @@ def test_steps_list(self): def test_create_form_step_successful(self): self.user.is_staff = True self.user.save() - url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) + url = reverse("api:form-steps-list", kwargs={"form_uuid": self.step.form.uuid}) form_detail_url = reverse( - "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} + "api:formdefinition-detail", + kwargs={"uuid": self.other_form_definition.uuid}, ) data = {"formDefinition": f"http://testserver{form_detail_url}"} response = self.client.post(url, data=data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(FormStep.objects.count(), 2) + self.assertEqual( + FormStep.objects.filter(form_definition=self.other_form_definition).count(), + 1, + ) def test_create_form_step_unsuccessful_with_bad_data(self): self.user.is_staff = True self.user.save() - url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) + url = reverse("api:form-steps-list", kwargs={"form_uuid": self.step.form.uuid}) data = { "bad": "data", } @@ -269,7 +256,7 @@ def test_create_form_step_unsuccessful_with_bad_data(self): def test_create_form_step_unsuccessful_when_form_is_not_found(self): self.user.is_staff = True self.user.save() - url = reverse("api:form-steps-list", args=(uuid.uuid4(),)) + url = reverse("api:form-steps-list", kwargs={"form_uuid": uuid.uuid4()}) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} ) @@ -280,7 +267,7 @@ def test_create_form_step_unsuccessful_when_form_is_not_found(self): self.assertEqual(FormStep.objects.count(), 1) def test_create_form_step_unsuccessful_without_authorization(self): - url = reverse("api:form-steps-list", args=(self.step.form.uuid,)) + url = reverse("api:form-steps-list", kwargs={"form_uuid": self.step.form.uuid}) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} ) @@ -294,7 +281,8 @@ def test_complete_form_step_update_successful(self): self.user.is_staff = True self.user.save() url = reverse( - "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": self.step.uuid}, ) form_detail_url = reverse( "api:formdefinition-detail", @@ -312,7 +300,10 @@ def test_complete_form_step_update_successful(self): def test_complete_form_step_update_unsuccessful_when_form_step_not_found(self): self.user.is_staff = True self.user.save() - url = reverse("api:form-steps-detail", args=(self.step.form.uuid, uuid.uuid4())) + url = reverse( + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": uuid.uuid4()}, + ) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": self.other_form_definition.uuid}, @@ -331,7 +322,8 @@ def test_complete_form_step_update_unsuccessful_with_non_existant_form_definitio self.user.is_staff = True self.user.save() url = reverse( - "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": self.step.uuid}, ) form_detail_url = reverse( "api:formdefinition-detail", @@ -353,7 +345,8 @@ def test_complete_form_step_update_unsuccessful_with_bad_data(self): self.user.is_staff = True self.user.save() url = reverse( - "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": self.step.uuid}, ) data = { "bad": "data", @@ -366,7 +359,8 @@ def test_complete_form_step_update_unsuccessful_with_bad_data(self): def test_complete_form_step_update_unsuccessful_without_authorization(self): url = reverse( - "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": self.step.uuid}, ) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} @@ -383,7 +377,8 @@ def test_partial_form_step_update_successful(self): self.user.is_staff = True self.user.save() url = reverse( - "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": self.step.uuid}, ) form_detail_url = reverse( "api:formdefinition-detail", @@ -401,7 +396,10 @@ def test_partial_form_step_update_successful(self): def test_partial_form_step_update_unsuccessful_when_form_step_not_found(self): self.user.is_staff = True self.user.save() - url = reverse("api:form-steps-detail", args=(self.step.form.uuid, uuid.uuid4())) + url = reverse( + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": uuid.uuid4()}, + ) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": uuid.uuid4()} ) @@ -417,7 +415,8 @@ def test_partial_form_step_update_unsuccessful_when_form_definition_not_found(se self.user.is_staff = True self.user.save() url = reverse( - "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": self.step.uuid}, ) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": uuid.uuid4()} @@ -434,23 +433,10 @@ def test_partial_form_step_update_unsuccessful_when_form_definition_not_found(se {"formDefinition": ["Ongeldige hyperlink - Object bestaat niet."]}, ) - def test_partial_form_step_update_unsuccessful_with_bad_data(self): - self.user.is_staff = True - self.user.save() - url = reverse( - "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) - ) - data = { - "bad": "data", - } - response = self.client.patch(url, data=data) - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(FormStep.objects.count(), 1) - def test_partial_form_step_update_unsuccessful_without_authorization(self): url = reverse( - "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": self.step.uuid}, ) form_detail_url = reverse( "api:formdefinition-detail", kwargs={"uuid": self.step.form_definition.uuid} @@ -467,7 +453,8 @@ def test_delete_form_step_successful(self): self.user.is_staff = True self.user.save() url = reverse( - "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": self.step.uuid}, ) response = self.client.delete(url) @@ -477,7 +464,10 @@ def test_delete_form_step_successful(self): def test_delete_form_step_unsuccessful_when_form_not_found(self): self.user.is_staff = True self.user.save() - url = reverse("api:form-steps-detail", args=(self.step.form.uuid, uuid.uuid4())) + url = reverse( + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": uuid.uuid4()}, + ) response = self.client.delete(url) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -485,7 +475,8 @@ def test_delete_form_step_unsuccessful_when_form_not_found(self): def test_delete_form_step_unsuccessful_when_not_authenticated(self): url = reverse( - "api:form-steps-detail", args=(self.step.form.uuid, self.step.uuid) + "api:form-steps-detail", + kwargs={"form_uuid": self.step.form.uuid, "uuid": self.step.uuid}, ) response = self.client.delete(url) From 956f3c434ee6c8318f1171accf7b1c6f7e094c3c Mon Sep 17 00:00:00 2001 From: shea-maykinmedia Date: Mon, 19 Apr 2021 14:07:45 +0200 Subject: [PATCH 17/17] :white_check_mark: Fix unit test name --- src/openforms/forms/tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openforms/forms/tests/test_api.py b/src/openforms/forms/tests/test_api.py index 45928982bd..868d348bb3 100644 --- a/src/openforms/forms/tests/test_api.py +++ b/src/openforms/forms/tests/test_api.py @@ -473,7 +473,7 @@ def test_delete_form_step_unsuccessful_when_form_not_found(self): self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(FormStep.objects.count(), 1) - def test_delete_form_step_unsuccessful_when_not_authenticated(self): + def test_delete_form_step_unsuccessful_when_not_authorized(self): url = reverse( "api:form-steps-detail", kwargs={"form_uuid": self.step.form.uuid, "uuid": self.step.uuid},