This is an automated email from the ASF dual-hosted git repository. gjm pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git
The following commit(s) were added to refs/heads/main by this push: new 4228632 Relax api regex for some models allowng . in names 4228632 is described below commit 4228632ce8c124e2bec1f76d83e1252ab5af064a Author: Gary Martin <g...@apache.org> AuthorDate: Tue Sep 14 13:36:50 2021 +0100 Relax api regex for some models allowng . in names Default api urls do not allow '.' characters when matching the names, presumably to ensure '.<format>' specifications are definitely matched properly. This patch relaxes this as we are definitely going to want to allow for '.' for things like version names. - change the lookup_value_regex for Component, Milestone and Version models - add hypothesis tests for testing the values that the api is allowed to use for names when creating objects from each list view --- pyproject.toml | 2 + trackers/api/serializers.py | 2 - trackers/api/tests/test_ticket_api.py | 10 +--- trackers/api/tests/test_ticket_api_hyp.py | 84 +++++++++++++++++++++++++++++++ trackers/api/urls.py | 1 - trackers/api/views.py | 3 ++ 6 files changed, 91 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1eec374..025e0d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,8 @@ psycopg2-binary = { version = "^2.9", optional = true } [tool.poetry.dev-dependencies] selenium = "^3.141.0" +django-extensions = "^3.1.3" +hypothesis = "^6.21.0" [tool.poetry.extras] postgres = ["psycopg2"] diff --git a/trackers/api/serializers.py b/trackers/api/serializers.py index 58d9248..c8e58cd 100644 --- a/trackers/api/serializers.py +++ b/trackers/api/serializers.py @@ -3,8 +3,6 @@ from django.shortcuts import get_object_or_404 from rest_framework import serializers from rest_framework.reverse import reverse from ..models import Component, Milestone, Product, Ticket, Version -from rest_framework_nested.serializers import NestedHyperlinkedModelSerializer -from rest_framework_nested.relations import NestedHyperlinkedIdentityField from functools import partial diff --git a/trackers/api/tests/test_ticket_api.py b/trackers/api/tests/test_ticket_api.py index b8afb0a..b4e30c0 100644 --- a/trackers/api/tests/test_ticket_api.py +++ b/trackers/api/tests/test_ticket_api.py @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. -from django.contrib.auth.models import User from django.urls import reverse from rest_framework.test import APITestCase from rest_framework import status @@ -27,12 +26,7 @@ from ...models import ( Ticket, Version, ) -from ..serializers import ( - ComponentSerializer, - MilestoneSerializer, - TicketSerializer, - VersionSerializer, -) + class TicketApiTest(APITestCase): """Tests for ticket API""" @@ -313,7 +307,7 @@ class ComponentApiTest(APITestCase): class MilestoneApiTest(APITestCase): - """Tests for component API""" + """Tests for milestone API""" def setUp(self): self.product = Product.objects.create(prefix="BH", name="Bloodhound") diff --git a/trackers/api/tests/test_ticket_api_hyp.py b/trackers/api/tests/test_ticket_api_hyp.py new file mode 100644 index 0000000..f54fc50 --- /dev/null +++ b/trackers/api/tests/test_ticket_api_hyp.py @@ -0,0 +1,84 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from django.urls import reverse +from hypothesis import example, given, strategies as st +from hypothesis.extra.django import TestCase +from rest_framework.test import APIClient, APIRequestFactory +from rest_framework import status + +from ...models import Product + +name_st = st.text( + st.characters( + blacklist_characters="/", + max_codepoint=1000, + blacklist_categories=("Cc", "Cs") + ), + min_size=1 +).map(lambda x: x.strip()).filter(lambda s: len(s) > 0) + + +class CommonAPIPropertiesTestCase(TestCase): + """Common tests for API""" + + def setUp(self): + self.client = APIClient() + self.factory = APIRequestFactory() + self.product = Product.objects.create(prefix="BH", name="Bloodhound") + self.list_uri = reverse( + self.list_view_name, + kwargs={"product_prefix": "BH"} + ) + + +class NameTestsMixin: + @given(name=name_st) + @example("next 1.x") + def test_create(self, name): + data = { + "name": name, + "description": "Example Description", + } + + response = self.client.post(self.list_uri, data) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + +class ComponentAPIPropertiesTest(CommonAPIPropertiesTestCase, NameTestsMixin): + """Hypothesis tests for component API""" + + def setUp(self): + self.list_view_name = "product-components-list" + super().setUp() + + +class MilestoneAPIPropertiesTest(CommonAPIPropertiesTestCase, NameTestsMixin): + """Hypothesis tests for milestone API""" + + def setUp(self): + self.list_view_name = "product-milestones-list" + super().setUp() + + +class VersionAPIPropertiesTest(CommonAPIPropertiesTestCase, NameTestsMixin): + """Hypothesis tests for version API""" + + def setUp(self): + self.list_view_name = "product-versions-list" + super().setUp() diff --git a/trackers/api/urls.py b/trackers/api/urls.py index 7dad774..403ea05 100644 --- a/trackers/api/urls.py +++ b/trackers/api/urls.py @@ -18,7 +18,6 @@ from django.urls import path from django.conf.urls import include from rest_framework.schemas import get_schema_view -from rest_framework.renderers import JSONOpenAPIRenderer from rest_framework_nested import routers from . import views diff --git a/trackers/api/views.py b/trackers/api/views.py index 9bacff7..91ceabd 100644 --- a/trackers/api/views.py +++ b/trackers/api/views.py @@ -63,6 +63,7 @@ class ComponentViewSet(viewsets.ModelViewSet): queryset = models.Component.objects.all() serializer_class = serializers.ComponentSerializer lookup_field = 'name' + lookup_value_regex = '[^/]+' def get_queryset(self, *args, **kwargs): prefix = self.kwargs['product_prefix'] @@ -73,6 +74,7 @@ class MilestoneViewSet(viewsets.ModelViewSet): queryset = models.Milestone.objects.all() serializer_class = serializers.MilestoneSerializer lookup_field = 'name' + lookup_value_regex = '[^/]+' def get_queryset(self, *args, **kwargs): prefix = self.kwargs['product_prefix'] @@ -83,6 +85,7 @@ class VersionViewSet(viewsets.ModelViewSet): queryset = models.Version.objects.all() serializer_class = serializers.VersionSerializer lookup_field = 'name' + lookup_value_regex = '[^/]+' def get_queryset(self, *args, **kwargs): prefix = self.kwargs['product_prefix']