From 5f706d923616421ae6600501bca155cb56bc7444 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Mon, 15 Jun 2020 19:17:33 +0300 Subject: [PATCH 01/11] Install jsonschema --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e46e89a..9abc3cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,7 +29,7 @@ django-auditlog==0.4.5 phonenumbers==8.11.4 django-phonenumber-field[phonenumbers]==4.0.0 django-autocomplete-light==3.4.1 -six==1.10.0 +six==1.11.0 django-suit==0.2.26 telepot==12.3 pyexcel==0.5.14 @@ -39,3 +39,4 @@ openpyxl==2.6.4 django-app-namespace-template-loader==0.4.1 django-filter==2.0.0 whitenoise==4.1.4 +jsonschema==3.2.0 From 2e11621e5b0fc578f93041e5c7cbb2b417bf2917 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Mon, 15 Jun 2020 19:18:24 +0300 Subject: [PATCH 02/11] Set Allow any for testing purposes --- webapp/views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/webapp/views.py b/webapp/views.py index b07bd1b..9b60b43 100644 --- a/webapp/views.py +++ b/webapp/views.py @@ -1,6 +1,7 @@ """Webapp views.""" import jwt +import json from django.utils import timezone from dealer.git import git from django.conf import settings @@ -11,9 +12,11 @@ from django.views.decorators.http import require_http_methods from django_filters import rest_framework as filters from rest_framework import permissions, routers, viewsets from rest_framework.filters import OrderingFilter, SearchFilter -from rest_framework.permissions import IsAuthenticatedOrReadOnly +from rest_framework.permissions import IsAuthenticatedOrReadOnly, AllowAny from rest_framework.response import Response from rest_framework.reverse import reverse +from jsonschema import validate +import logging from webapp.models import Event, SignupForm, Signup, TemplateQuestion, Feed, Committee, Occupation, Tag from webapp.serializers import (EventSerializer, SignupFormSerializer, SignupSerializer, @@ -71,7 +74,8 @@ class SignupFormViewSet(viewsets.ModelViewSet): class SignupViewSet(viewsets.ModelViewSet): queryset = Signup.objects.all() serializer_class = SignupSerializer - permission_classes = [IsPostOrIsAuthenticated] + permission_classes = [AllowAny] + # permission_classes = [IsPostOrIsAuthenticated] # filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter) # filter_fields = '__all__' # search_fields = '__all__' From 0edde936cc7319827509de8aab7e8bfd5eb0bd53 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Mon, 15 Jun 2020 19:18:57 +0300 Subject: [PATCH 03/11] Get jsonschema from questions JSON --- webapp/models.py | 46 +++++++++++++++++++++++++++++++++++++ webapp/tests/test_signup.py | 8 +++---- webapp/views.py | 7 +++++- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/webapp/models.py b/webapp/models.py index c84bdd4..f81f8f7 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -100,6 +100,52 @@ class SignupForm(models.Model): def __str__(self): return _('#{} {}').format(self.id, self.title) + @property + def schema(self): + questions = self.questions + ids = list(map(lambda x: x["id"], questions)) + properties = { + "required": ids, + "minProperties": len(ids), + "maxProperties": len(ids) + } + + for q in questions: + id = q["id"] + question_type = q["type"] + if question_type == "text": + properties[id] = { + "type": "string" + } + elif question_type == "radiobutton": + options = q["options"] + pattern = "" + map(lambda x: pattern.join(f"^{x}$|"), options) + properties[id] = { + "type": "string", + # Remove last regex or + "pattern": pattern[:-1], + + } + elif question_type == "checkbox": + options = q["options"] + pattern = "" + map(lambda x: pattern.join(f"^{x}$|"), options) + + properties[id] = { + "type": "array", + "uniqueItems": True, + "maxItems": len(options), + "items": { + "type": "string", + "pattern": pattern[:-1] + } + } + else: raise Exception("invalid question type!") + + + return properties + class Meta: verbose_name = _('Signup form') verbose_name_plural = _('Signup forms') diff --git a/webapp/tests/test_signup.py b/webapp/tests/test_signup.py index 66b0427..845aa99 100644 --- a/webapp/tests/test_signup.py +++ b/webapp/tests/test_signup.py @@ -20,7 +20,7 @@ class SignupTestCase(APITestCase): self.hiddenForm = createSignupForm(visible=False) self.signup1 = createSignupObject(self.signupForm, ALL_QUESTION_TYPES) - self.signup2 = createSignupObject(self.signupForm, []) + self.signup2 = createSignupObject(self.signupForm, ALL_QUESTION_TYPES) username, password = "test_admin", "password123" self.authClient = User.objects.create_superuser(username, "myemail@test.com", password) @@ -58,7 +58,6 @@ class SignupTestCase(APITestCase): def test_create_signup(self): new = createSignupJSON(self.signupForm.id, ALL_QUESTION_TYPES_ANSWER) response = self.client.post(URL, new, format="json") - print(response.data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(Signup.objects.count(), 3) @@ -77,10 +76,11 @@ class SignupTestCase(APITestCase): def test_get_hidden_forms_admin(self): pass - @skip("NotImplemented") def test_create_malformed_answer(self): - response = self.client.post(URL, createSignupJSON(self.signupForm.id, []), format="json") + malformed = createSignupJSON(self.signupForm.id, []) + response = self.client.post(URL, malformed, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Signup.objects.count(), 2) # Update and Delete are available for super admin (Django Admin) # and to the user that signed up (uid token) diff --git a/webapp/views.py b/webapp/views.py index 9b60b43..136bc82 100644 --- a/webapp/views.py +++ b/webapp/views.py @@ -84,8 +84,13 @@ class SignupViewSet(viewsets.ModelViewSet): try: form = SignupForm.objects.get(id=request.data["signupForm_id"]) if (form.visible): + signup = json.loads(request.data["answer"]) + # Throws error if not valid + print(form.schema) + validate(instance=signup, schema=form.schema) return super().create(request, *args, **kwargs) - except: + except Exception as inst: + print(inst) return HttpResponseBadRequest() else: return HttpResponseBadRequest() From 2433c7828dfb3a0d761f9d518b5c8d7e8995d0a4 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Mon, 15 Jun 2020 20:02:01 +0300 Subject: [PATCH 04/11] Fix pattern generation --- webapp/models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/webapp/models.py b/webapp/models.py index f81f8f7..f60e026 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -119,18 +119,18 @@ class SignupForm(models.Model): } elif question_type == "radiobutton": options = q["options"] - pattern = "" - map(lambda x: pattern.join(f"^{x}$|"), options) + regexes = map(lambda x: f"^{x}$", options) + pattern = "|".join(regexes) properties[id] = { "type": "string", # Remove last regex or - "pattern": pattern[:-1], + "pattern": pattern, } elif question_type == "checkbox": options = q["options"] - pattern = "" - map(lambda x: pattern.join(f"^{x}$|"), options) + regexes = map(lambda x: f"^{x}$", options) + pattern = "|".join(regexes) properties[id] = { "type": "array", @@ -138,7 +138,7 @@ class SignupForm(models.Model): "maxItems": len(options), "items": { "type": "string", - "pattern": pattern[:-1] + "pattern": pattern } } else: raise Exception("invalid question type!") From f505fae3e60d7292b6bc8b21ac62c46cd7fb6bb0 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Mon, 15 Jun 2020 20:25:24 +0300 Subject: [PATCH 05/11] Test signup POST --- webapp/models.py | 17 ++++++------ webapp/tests/signup_fixture.py | 2 +- webapp/tests/test_signup.py | 51 ++++++++++++++++++++++++++++++++-- webapp/views.py | 2 -- 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/webapp/models.py b/webapp/models.py index f60e026..3715bb8 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -103,12 +103,7 @@ class SignupForm(models.Model): @property def schema(self): questions = self.questions - ids = list(map(lambda x: x["id"], questions)) - properties = { - "required": ids, - "minProperties": len(ids), - "maxProperties": len(ids) - } + properties = {} for q in questions: id = q["id"] @@ -143,8 +138,14 @@ class SignupForm(models.Model): } else: raise Exception("invalid question type!") - - return properties + ids = list(map(lambda x: x["id"], questions)) + return { + "type": "object", + "required": ids, + "minProperties": len(ids), + "maxProperties": len(ids), + "properties": properties + } class Meta: verbose_name = _('Signup form') diff --git a/webapp/tests/signup_fixture.py b/webapp/tests/signup_fixture.py index a036274..5a71a9a 100644 --- a/webapp/tests/signup_fixture.py +++ b/webapp/tests/signup_fixture.py @@ -10,7 +10,7 @@ ALL_QUESTION_TYPES = [ {"id": "i10d426d5", "name": "Asd3", "type": "checkbox", "options": ["A", "B", "C"]} ] -ALL_QUESTION_TYPES_ANSWER = {"j5CeRZDvl": "Testi", "RHJhSoaLD": "maybe", "i10d426d5": ["B"]} +ALL_QUESTION_TYPES_ANSWER = {"j5CeRZDvl": "Testi", "RHJhSoaLD": "maybe", "i10d426d5": ["B","C"]} def createSignupForm(name="Form1", start_time=timezone.now(), end_time=month_from_now(), questions=ALL_QUESTION_TYPES, visible=True): diff --git a/webapp/tests/test_signup.py b/webapp/tests/test_signup.py index 845aa99..025284d 100644 --- a/webapp/tests/test_signup.py +++ b/webapp/tests/test_signup.py @@ -17,6 +17,9 @@ class SignupTestCase(APITestCase): def setUp(self): self.signupForm = createSignupForm() + self.signupFormText = createSignupForm(name="Form2", questions=[ALL_QUESTION_TYPES[0]]) + self.signupFormRadio = createSignupForm(name="Form3", questions=[ALL_QUESTION_TYPES[1]]) + self.signupFormCheck = createSignupForm(name="Form4", questions=[ALL_QUESTION_TYPES[2]]) self.hiddenForm = createSignupForm(visible=False) self.signup1 = createSignupObject(self.signupForm, ALL_QUESTION_TYPES) @@ -77,11 +80,55 @@ class SignupTestCase(APITestCase): pass def test_create_malformed_answer(self): - malformed = createSignupJSON(self.signupForm.id, []) - response = self.client.post(URL, malformed, format="json") + # Empty body + response = self.client.post(URL, createSignupJSON(self.signupForm.id, {}), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + # Array + response = self.client.post(URL, createSignupJSON(self.signupForm.id, []), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + # Extra ids + testInput = ALL_QUESTION_TYPES_ANSWER.copy() + testInput["newId"] = "Oon extraa" + response = self.client.post(URL, createSignupJSON(self.signupForm.id, testInput), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + # Bad id + response = self.client.post(URL, createSignupJSON(self.signupFormText.id, { "malformed": "Tekstiä" }), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + # Wrong data type for text + response = self.client.post(URL, createSignupJSON(self.signupFormText.id, {"j5CeRZDvl": 123}), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + # Wrong data for checkbox + response = self.client.post(URL, createSignupJSON(self.signupFormCheck.id, { + "i10d426d5": ["D"] + }), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + # Wrong data type for checkbox + response = self.client.post(URL, createSignupJSON(self.signupFormCheck.id, { + "i10d426d5": {"j5CeRZDvl": { "asd": "123" }} + }), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + # Wrong data for radiobutton + response = self.client.post(URL, createSignupJSON(self.signupFormRadio.id, { + "RHJhSoaLD": [] + }), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + # Wrong data type for radiobutton + response = self.client.post(URL, createSignupJSON(self.signupFormRadio.id, { + "RHJhSoaLD": { "asd": "123" } + }), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Signup.objects.count(), 2) + # Update and Delete are available for super admin (Django Admin) # and to the user that signed up (uid token) @skip("NotImplemented") diff --git a/webapp/views.py b/webapp/views.py index 136bc82..72fcef4 100644 --- a/webapp/views.py +++ b/webapp/views.py @@ -86,11 +86,9 @@ class SignupViewSet(viewsets.ModelViewSet): if (form.visible): signup = json.loads(request.data["answer"]) # Throws error if not valid - print(form.schema) validate(instance=signup, schema=form.schema) return super().create(request, *args, **kwargs) except Exception as inst: - print(inst) return HttpResponseBadRequest() else: return HttpResponseBadRequest() From 90d8ee2ea9ae09da7d5ea0e4a9014b9c13e53137 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Mon, 15 Jun 2020 22:01:05 +0300 Subject: [PATCH 06/11] HTTP responses --- webapp/serializers.py | 6 +++--- webapp/tests/signup_fixture.py | 4 ++-- webapp/tests/test_signup.py | 30 +++++++++++++++--------------- webapp/views.py | 26 +++++++++++++++----------- 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/webapp/serializers.py b/webapp/serializers.py index 25517ae..765f40a 100644 --- a/webapp/serializers.py +++ b/webapp/serializers.py @@ -3,7 +3,7 @@ from webapp.models import * class SignupFormSerializer(serializers.HyperlinkedModelSerializer): - questions = serializers.JSONField(binary=True) + questions = serializers.JSONField() class Meta: model = SignupForm @@ -56,7 +56,7 @@ class SignupSerializer(serializers.ModelSerializer): source="signupForm", queryset=SignupForm.objects.all() ) - answer = serializers.JSONField(binary=True) + answer = serializers.JSONField() class Meta: model = Signup @@ -69,7 +69,7 @@ class SignupSerializer(serializers.ModelSerializer): class SavedQuestionsSerializer(serializers.ModelSerializer): - question = serializers.JSONField(binary=True) + question = serializers.JSONField() class Meta: model = TemplateQuestion diff --git a/webapp/tests/signup_fixture.py b/webapp/tests/signup_fixture.py index 5a71a9a..39856e3 100644 --- a/webapp/tests/signup_fixture.py +++ b/webapp/tests/signup_fixture.py @@ -30,8 +30,8 @@ def createSignupObject(form, answer): ) -def createSignupJSON(form_id, answer): +def createSignupRequest(form_id, answer): return { "signupForm_id": form_id, - "answer": json.dumps(answer) + "answer": answer } diff --git a/webapp/tests/test_signup.py b/webapp/tests/test_signup.py index 025284d..ea4bfa9 100644 --- a/webapp/tests/test_signup.py +++ b/webapp/tests/test_signup.py @@ -7,7 +7,7 @@ from rest_framework.test import APITestCase, force_authenticate from webapp.serializers import SignupSerializer, SignupFormSerializer from webapp.models import Signup from webapp.tests.event_fixture import createEventObject -from webapp.tests.signup_fixture import createSignupForm, createSignupObject, createSignupJSON, ALL_QUESTION_TYPES, ALL_QUESTION_TYPES_ANSWER +from webapp.tests.signup_fixture import createSignupForm, createSignupObject, createSignupRequest, ALL_QUESTION_TYPES, ALL_QUESTION_TYPES_ANSWER URL = "/api/signup/" @@ -59,20 +59,20 @@ class SignupTestCase(APITestCase): self.assertEqual(response.data, expected.data) def test_create_signup(self): - new = createSignupJSON(self.signupForm.id, ALL_QUESTION_TYPES_ANSWER) + new = createSignupRequest(self.signupForm.id, ALL_QUESTION_TYPES_ANSWER) response = self.client.post(URL, new, format="json") self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(Signup.objects.count(), 3) def test_create_signup_404_or_hidden(self): - new = createSignupJSON(3001, ALL_QUESTION_TYPES_ANSWER) + new = createSignupRequest(3001, ALL_QUESTION_TYPES_ANSWER) response = self.client.post(URL, new, format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(Signup.objects.count(), 2) - new = createSignupJSON(self.hiddenForm.id, ALL_QUESTION_TYPES_ANSWER) + new = createSignupRequest(self.hiddenForm.id, ALL_QUESTION_TYPES_ANSWER) response = self.client.post(URL, new, format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(Signup.objects.count(), 2) @skip("NotImplemented") @@ -81,47 +81,47 @@ class SignupTestCase(APITestCase): def test_create_malformed_answer(self): # Empty body - response = self.client.post(URL, createSignupJSON(self.signupForm.id, {}), format="json") + response = self.client.post(URL, createSignupRequest(self.signupForm.id, {}), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Array - response = self.client.post(URL, createSignupJSON(self.signupForm.id, []), format="json") + response = self.client.post(URL, createSignupRequest(self.signupForm.id, []), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Extra ids testInput = ALL_QUESTION_TYPES_ANSWER.copy() testInput["newId"] = "Oon extraa" - response = self.client.post(URL, createSignupJSON(self.signupForm.id, testInput), format="json") + response = self.client.post(URL, createSignupRequest(self.signupForm.id, testInput), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Bad id - response = self.client.post(URL, createSignupJSON(self.signupFormText.id, { "malformed": "Tekstiä" }), format="json") + response = self.client.post(URL, createSignupRequest(self.signupFormText.id, { "malformed": "Tekstiä" }), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Wrong data type for text - response = self.client.post(URL, createSignupJSON(self.signupFormText.id, {"j5CeRZDvl": 123}), format="json") + response = self.client.post(URL, createSignupRequest(self.signupFormText.id, {"j5CeRZDvl": 123}), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Wrong data for checkbox - response = self.client.post(URL, createSignupJSON(self.signupFormCheck.id, { + response = self.client.post(URL, createSignupRequest(self.signupFormCheck.id, { "i10d426d5": ["D"] }), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Wrong data type for checkbox - response = self.client.post(URL, createSignupJSON(self.signupFormCheck.id, { + response = self.client.post(URL, createSignupRequest(self.signupFormCheck.id, { "i10d426d5": {"j5CeRZDvl": { "asd": "123" }} }), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Wrong data for radiobutton - response = self.client.post(URL, createSignupJSON(self.signupFormRadio.id, { + response = self.client.post(URL, createSignupRequest(self.signupFormRadio.id, { "RHJhSoaLD": [] }), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Wrong data type for radiobutton - response = self.client.post(URL, createSignupJSON(self.signupFormRadio.id, { + response = self.client.post(URL, createSignupRequest(self.signupFormRadio.id, { "RHJhSoaLD": { "asd": "123" } }), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) diff --git a/webapp/views.py b/webapp/views.py index 72fcef4..0890501 100644 --- a/webapp/views.py +++ b/webapp/views.py @@ -6,16 +6,18 @@ from django.utils import timezone from dealer.git import git from django.conf import settings from django.contrib.auth import authenticate -from django.http import HttpResponseBadRequest, HttpResponse +from django.http import HttpResponseBadRequest, HttpResponse, JsonResponse from django.shortcuts import redirect, render from django.views.decorators.http import require_http_methods from django_filters import rest_framework as filters +from django.core.exceptions import ObjectDoesNotExist from rest_framework import permissions, routers, viewsets from rest_framework.filters import OrderingFilter, SearchFilter -from rest_framework.permissions import IsAuthenticatedOrReadOnly, AllowAny +from rest_framework.permissions import IsAuthenticatedOrReadOnly from rest_framework.response import Response from rest_framework.reverse import reverse from jsonschema import validate +from jsonschema.exceptions import ValidationError import logging from webapp.models import Event, SignupForm, Signup, TemplateQuestion, Feed, Committee, Occupation, Tag @@ -74,24 +76,26 @@ class SignupFormViewSet(viewsets.ModelViewSet): class SignupViewSet(viewsets.ModelViewSet): queryset = Signup.objects.all() serializer_class = SignupSerializer - permission_classes = [AllowAny] - # permission_classes = [IsPostOrIsAuthenticated] + permission_classes = [IsPostOrIsAuthenticated] # filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter) # filter_fields = '__all__' # search_fields = '__all__' def create(self, request, *args, **kwargs): try: - form = SignupForm.objects.get(id=request.data["signupForm_id"]) + id = request.data["signupForm_id"] + answer = request.data["answer"] + form = SignupForm.objects.get(id=id) if (form.visible): - signup = json.loads(request.data["answer"]) - # Throws error if not valid - validate(instance=signup, schema=form.schema) + # Throws ValidationError if not valid + validate(instance=answer, schema=form.schema) return super().create(request, *args, **kwargs) - except Exception as inst: - return HttpResponseBadRequest() + except ValidationError as inst: + return JsonResponse(status=400, data={"error": inst.message}) + except ObjectDoesNotExist: + return JsonResponse(status=404, data={"error": f"SignupForm {id} not found"}) else: - return HttpResponseBadRequest() + return JsonResponse(status=404, data={"error": f"SignupForm {id} not found"}) def update(self, request, *args, **kwargs): return super().update(request, *args, **kwargs) From b302681bbfd60b501cc726b71ed3e7f7857a1008 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Tue, 16 Jun 2020 17:32:25 +0300 Subject: [PATCH 07/11] Style fixes --- webapp/models.py | 3 ++- webapp/tests/signup_fixture.py | 2 +- webapp/tests/test_signup.py | 7 +++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/webapp/models.py b/webapp/models.py index 3715bb8..0184260 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -136,7 +136,8 @@ class SignupForm(models.Model): "pattern": pattern } } - else: raise Exception("invalid question type!") + else: + raise Exception("invalid question type!") ids = list(map(lambda x: x["id"], questions)) return { diff --git a/webapp/tests/signup_fixture.py b/webapp/tests/signup_fixture.py index 39856e3..1ba09f1 100644 --- a/webapp/tests/signup_fixture.py +++ b/webapp/tests/signup_fixture.py @@ -10,7 +10,7 @@ ALL_QUESTION_TYPES = [ {"id": "i10d426d5", "name": "Asd3", "type": "checkbox", "options": ["A", "B", "C"]} ] -ALL_QUESTION_TYPES_ANSWER = {"j5CeRZDvl": "Testi", "RHJhSoaLD": "maybe", "i10d426d5": ["B","C"]} +ALL_QUESTION_TYPES_ANSWER = {"j5CeRZDvl": "Testi", "RHJhSoaLD": "maybe", "i10d426d5": ["B", "C"]} def createSignupForm(name="Form1", start_time=timezone.now(), end_time=month_from_now(), questions=ALL_QUESTION_TYPES, visible=True): diff --git a/webapp/tests/test_signup.py b/webapp/tests/test_signup.py index ea4bfa9..99f55b2 100644 --- a/webapp/tests/test_signup.py +++ b/webapp/tests/test_signup.py @@ -95,7 +95,7 @@ class SignupTestCase(APITestCase): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Bad id - response = self.client.post(URL, createSignupRequest(self.signupFormText.id, { "malformed": "Tekstiä" }), format="json") + response = self.client.post(URL, createSignupRequest(self.signupFormText.id, {"malformed": "Tekstiä"}), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Wrong data type for text @@ -110,7 +110,7 @@ class SignupTestCase(APITestCase): # Wrong data type for checkbox response = self.client.post(URL, createSignupRequest(self.signupFormCheck.id, { - "i10d426d5": {"j5CeRZDvl": { "asd": "123" }} + "i10d426d5": {"j5CeRZDvl": {"asd": "123"}} }), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -122,13 +122,12 @@ class SignupTestCase(APITestCase): # Wrong data type for radiobutton response = self.client.post(URL, createSignupRequest(self.signupFormRadio.id, { - "RHJhSoaLD": { "asd": "123" } + "RHJhSoaLD": {"asd": "123"} }), format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(Signup.objects.count(), 2) - # Update and Delete are available for super admin (Django Admin) # and to the user that signed up (uid token) @skip("NotImplemented") From 2d6c474baabaf374773c032efa97c1e929b67517 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Tue, 16 Jun 2020 17:37:19 +0300 Subject: [PATCH 08/11] CI updates --- .gitlab-ci.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6ea320b..4a0c7d4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,12 +1,24 @@ stages: + - install - lint - test - publish - deploy +install: + image: node:12 + stage: setup + script: + - npm ci + artifacts: + paths: + - node_modules + expire_in: 1 week + test: image: python:3.7 stage: test + needs: [] services: - postgres:12 variables: @@ -25,6 +37,7 @@ test: lint:pycodestyle: image: python:3.7 stage: lint + needs: [] script: - pip install pycodestyle - pycodestyle --config=setup.cfg --count . @@ -32,22 +45,21 @@ lint:pycodestyle: lint:eslint: image: node:alpine stage: lint - before_script: - - npm install + needs: ["install"] script: - npm run eslint lint:remark: image: node:alpine stage: lint - before_script: - - npm install + needs: ["install"] script: - npm run remark publish: stage: publish image: docker:stable + needs: ["test", "lint:pycodestyle", "lint:eslint", "lint:remark"] services: - docker:stable-dind only: From 683f50b199a5c46695b341c51226f29ad5e5801b Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Tue, 16 Jun 2020 17:39:03 +0300 Subject: [PATCH 09/11] CI updates --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4a0c7d4..897cd87 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ stages: - - install + - setup - lint - test - publish From 319324503f5a192410e771a732d1cc6a00b28c89 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Tue, 16 Jun 2020 17:57:48 +0300 Subject: [PATCH 10/11] Split signup tests to 2 files --- webapp/tests/test_signup.py | 70 --------------------- webapp/tests/test_signup_errors.py | 98 ++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 70 deletions(-) create mode 100644 webapp/tests/test_signup_errors.py diff --git a/webapp/tests/test_signup.py b/webapp/tests/test_signup.py index 99f55b2..446313f 100644 --- a/webapp/tests/test_signup.py +++ b/webapp/tests/test_signup.py @@ -3,13 +3,11 @@ from unittest import skip from django.contrib.auth.models import User from rest_framework import status from rest_framework.test import APITestCase, force_authenticate - from webapp.serializers import SignupSerializer, SignupFormSerializer from webapp.models import Signup from webapp.tests.event_fixture import createEventObject from webapp.tests.signup_fixture import createSignupForm, createSignupObject, createSignupRequest, ALL_QUESTION_TYPES, ALL_QUESTION_TYPES_ANSWER - URL = "/api/signup/" @@ -33,10 +31,6 @@ class SignupTestCase(APITestCase): self.signupForm.signup_set.all(), many=True ) - - # Unauthorized - response = self.client.get(URL, format="json") - self.assertTrue(response.status_code, status.HTTP_401_UNAUTHORIZED) # Authenticate self.client.force_authenticate(user=self.authClient) response = self.client.get(URL, format="json") @@ -48,10 +42,6 @@ class SignupTestCase(APITestCase): expected = SignupSerializer( Signup.objects.get(id=id) ) - - # Unauthorized - response = self.client.get(f"{URL}{id}/", format="json") - self.assertTrue(response.status_code, status.HTTP_401_UNAUTHORIZED) # Authenticate self.client.force_authenticate(user=self.authClient) response = self.client.get(f"{URL}{id}/", format="json") @@ -64,70 +54,10 @@ class SignupTestCase(APITestCase): self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(Signup.objects.count(), 3) - def test_create_signup_404_or_hidden(self): - new = createSignupRequest(3001, ALL_QUESTION_TYPES_ANSWER) - response = self.client.post(URL, new, format="json") - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(Signup.objects.count(), 2) - - new = createSignupRequest(self.hiddenForm.id, ALL_QUESTION_TYPES_ANSWER) - response = self.client.post(URL, new, format="json") - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(Signup.objects.count(), 2) - @skip("NotImplemented") def test_get_hidden_forms_admin(self): pass - def test_create_malformed_answer(self): - # Empty body - response = self.client.post(URL, createSignupRequest(self.signupForm.id, {}), format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - # Array - response = self.client.post(URL, createSignupRequest(self.signupForm.id, []), format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - # Extra ids - testInput = ALL_QUESTION_TYPES_ANSWER.copy() - testInput["newId"] = "Oon extraa" - response = self.client.post(URL, createSignupRequest(self.signupForm.id, testInput), format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - # Bad id - response = self.client.post(URL, createSignupRequest(self.signupFormText.id, {"malformed": "Tekstiä"}), format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - # Wrong data type for text - response = self.client.post(URL, createSignupRequest(self.signupFormText.id, {"j5CeRZDvl": 123}), format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - # Wrong data for checkbox - response = self.client.post(URL, createSignupRequest(self.signupFormCheck.id, { - "i10d426d5": ["D"] - }), format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - # Wrong data type for checkbox - response = self.client.post(URL, createSignupRequest(self.signupFormCheck.id, { - "i10d426d5": {"j5CeRZDvl": {"asd": "123"}} - }), format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - # Wrong data for radiobutton - response = self.client.post(URL, createSignupRequest(self.signupFormRadio.id, { - "RHJhSoaLD": [] - }), format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - # Wrong data type for radiobutton - response = self.client.post(URL, createSignupRequest(self.signupFormRadio.id, { - "RHJhSoaLD": {"asd": "123"} - }), format="json") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - self.assertEqual(Signup.objects.count(), 2) - # Update and Delete are available for super admin (Django Admin) # and to the user that signed up (uid token) @skip("NotImplemented") diff --git a/webapp/tests/test_signup_errors.py b/webapp/tests/test_signup_errors.py new file mode 100644 index 0000000..9d30392 --- /dev/null +++ b/webapp/tests/test_signup_errors.py @@ -0,0 +1,98 @@ +from django.test import TestCase +from unittest import skip +from django.contrib.auth.models import User +from rest_framework import status +from rest_framework.test import APITestCase, force_authenticate +from webapp.serializers import SignupSerializer, SignupFormSerializer +from webapp.models import Signup +from webapp.tests.event_fixture import createEventObject +from webapp.tests.signup_fixture import createSignupForm, createSignupObject, createSignupRequest, ALL_QUESTION_TYPES, ALL_QUESTION_TYPES_ANSWER + +URL = "/api/signup/" + + +class SignupErrorTestCase(APITestCase): + def setUp(self): + self.signupForm = createSignupForm() + self.signupFormText = createSignupForm(name="Form2", questions=[ALL_QUESTION_TYPES[0]]) + self.signupFormRadio = createSignupForm(name="Form3", questions=[ALL_QUESTION_TYPES[1]]) + self.signupFormCheck = createSignupForm(name="Form4", questions=[ALL_QUESTION_TYPES[2]]) + self.hiddenForm = createSignupForm(visible=False) + + self.signup1 = createSignupObject(self.signupForm, ALL_QUESTION_TYPES) + self.signup2 = createSignupObject(self.signupForm, ALL_QUESTION_TYPES) + + username, password = "test_admin", "password123" + self.authClient = User.objects.create_superuser(username, "myemail@test.com", password) + + def test_get_all_unauthorized(self): + response = self.client.get(URL, format="json") + self.assertTrue(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_get_single_unauthorized(self): + id = self.signup1.id + expected = SignupSerializer( + Signup.objects.get(id=id) + ) + response = self.client.get(f"{URL}{id}/", format="json") + self.assertTrue(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_create_signup_404(self): + new = createSignupRequest(3001, ALL_QUESTION_TYPES_ANSWER) + response = self.client.post(URL, new, format="json") + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(Signup.objects.count(), 2) + + def test_create_signup_hidden(self): + new = createSignupRequest(self.hiddenForm.id, ALL_QUESTION_TYPES_ANSWER) + response = self.client.post(URL, new, format="json") + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(Signup.objects.count(), 2) + + def test_create_empty_body(self): + response = self.client.post(URL, createSignupRequest(self.signupForm.id, {}), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_array_body(self): + response = self.client.post(URL, createSignupRequest(self.signupForm.id, []), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_extra_body(self): + testInput = ALL_QUESTION_TYPES_ANSWER.copy() + testInput["newId"] = "Oon extraa" + response = self.client.post(URL, createSignupRequest(self.signupForm.id, testInput), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_bad_id(self): + response = self.client.post(URL, createSignupRequest(self.signupFormText.id, {"malformed": "Tekstiä"}), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_bad_type_text(self): + response = self.client.post(URL, createSignupRequest(self.signupFormText.id, {"j5CeRZDvl": 123}), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_bad_data_checkbox(self): + response = self.client.post(URL, createSignupRequest(self.signupFormCheck.id, { + "i10d426d5": ["D"] + }), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_bad_type_checkbox(self): + response = self.client.post(URL, createSignupRequest(self.signupFormCheck.id, { + "i10d426d5": {"j5CeRZDvl": {"asd": "123"}} + }), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_bad_radio(self): + response = self.client.post(URL, createSignupRequest(self.signupFormRadio.id, { + "RHJhSoaLD": [] + }), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_bad_type_radio(self): + response = self.client.post(URL, createSignupRequest(self.signupFormRadio.id, { + "RHJhSoaLD": {"asd": "123"} + }), format="json") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + self.assertEqual(Signup.objects.count(), 2) From 34984d4a5eb7a6173619062adfde9886dc4dc132 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Tue, 16 Jun 2020 18:42:55 +0300 Subject: [PATCH 11/11] Clean test imports --- webapp/tests/test_signup.py | 4 +--- webapp/tests/test_signup_errors.py | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/webapp/tests/test_signup.py b/webapp/tests/test_signup.py index 446313f..729f9a4 100644 --- a/webapp/tests/test_signup.py +++ b/webapp/tests/test_signup.py @@ -1,11 +1,9 @@ -from django.test import TestCase from unittest import skip from django.contrib.auth.models import User from rest_framework import status from rest_framework.test import APITestCase, force_authenticate -from webapp.serializers import SignupSerializer, SignupFormSerializer +from webapp.serializers import SignupSerializer from webapp.models import Signup -from webapp.tests.event_fixture import createEventObject from webapp.tests.signup_fixture import createSignupForm, createSignupObject, createSignupRequest, ALL_QUESTION_TYPES, ALL_QUESTION_TYPES_ANSWER URL = "/api/signup/" diff --git a/webapp/tests/test_signup_errors.py b/webapp/tests/test_signup_errors.py index 9d30392..7cb4867 100644 --- a/webapp/tests/test_signup_errors.py +++ b/webapp/tests/test_signup_errors.py @@ -1,11 +1,8 @@ -from django.test import TestCase -from unittest import skip from django.contrib.auth.models import User from rest_framework import status from rest_framework.test import APITestCase, force_authenticate -from webapp.serializers import SignupSerializer, SignupFormSerializer +from webapp.serializers import SignupSerializer from webapp.models import Signup -from webapp.tests.event_fixture import createEventObject from webapp.tests.signup_fixture import createSignupForm, createSignupObject, createSignupRequest, ALL_QUESTION_TYPES, ALL_QUESTION_TYPES_ANSWER URL = "/api/signup/"