From 6e51e4f5cdec4f4580360a487d7bf5ebdef08709 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 16 Dec 2014 15:34:19 +0000 Subject: Versioning first pass --- tests/test_versioning.py | 104 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 tests/test_versioning.py (limited to 'tests/test_versioning.py') diff --git a/tests/test_versioning.py b/tests/test_versioning.py new file mode 100644 index 00000000..d90b29a1 --- /dev/null +++ b/tests/test_versioning.py @@ -0,0 +1,104 @@ +from django.conf.urls import url +from rest_framework import versioning +from rest_framework.decorators import APIView +from rest_framework.response import Response +from rest_framework.reverse import reverse +from rest_framework.test import APIRequestFactory, APITestCase + + +class RequestVersionView(APIView): + def get(self, request, *args, **kwargs): + return Response({'version': request.version}) + +class ReverseView(APIView): + def get(self, request, *args, **kwargs): + return Response({'url': reverse('another', request=request)}) + + +factory = APIRequestFactory() + +mock_view = lambda request: None + +urlpatterns = [ + url(r'^another/$', mock_view, name='another') +] + + +class TestRequestVersion: + def test_unversioned(self): + view = RequestVersionView.as_view() + + request = factory.get('/endpoint/') + response = view(request) + assert response.data == {'version': None} + + def test_query_param_versioning(self): + scheme = versioning.QueryParameterVersioning + view = RequestVersionView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/?version=1.2.3') + response = view(request) + assert response.data == {'version': '1.2.3'} + + request = factory.get('/endpoint/') + response = view(request) + assert response.data == {'version': None} + + def test_host_name_versioning(self): + scheme = versioning.HostNameVersioning + view = RequestVersionView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/', HTTP_HOST='v1.example.org') + response = view(request) + assert response.data == {'version': 'v1'} + + request = factory.get('/endpoint/') + response = view(request) + assert response.data == {'version': None} + + def test_accept_header_versioning(self): + scheme = versioning.AcceptHeaderVersioning + view = RequestVersionView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/', HTTP_ACCEPT='application/json; version=1.2.3') + response = view(request) + assert response.data == {'version': '1.2.3'} + + request = factory.get('/endpoint/', HTTP_ACCEPT='application/json') + response = view(request) + assert response.data == {'version': None} + + def test_url_path_versioning(self): + scheme = versioning.URLPathVersioning + view = RequestVersionView.as_view(versioning_class=scheme) + + request = factory.get('/1.2.3/endpoint/') + response = view(request, version='1.2.3') + assert response.data == {'version': '1.2.3'} + + request = factory.get('/endpoint/') + response = view(request) + assert response.data == {'version': None} + + +class TestURLReversing(APITestCase): + urls = 'tests.test_versioning' + + def test_reverse_unversioned(self): + view = ReverseView.as_view() + + request = factory.get('/endpoint/') + response = view(request) + assert response.data == {'url': 'http://testserver/another/'} + + def test_reverse_host_name_versioning(self): + scheme = versioning.HostNameVersioning + view = ReverseView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/', HTTP_HOST='v1.example.org') + response = view(request) + assert response.data == {'url': 'http://v1.example.org/another/'} + + request = factory.get('/endpoint/') + response = view(request) + assert response.data == {'url': 'http://testserver/another/'} -- cgit v1.2.3 From 4e91ec61339838426e246e20ef062c963a78c4e1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 16 Dec 2014 16:14:08 +0000 Subject: Added NamespaceVersioning --- tests/test_versioning.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) (limited to 'tests/test_versioning.py') diff --git a/tests/test_versioning.py b/tests/test_versioning.py index d90b29a1..eaac5dfb 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -1,4 +1,4 @@ -from django.conf.urls import url +from django.conf.urls import include, url from rest_framework import versioning from rest_framework.decorators import APIView from rest_framework.response import Response @@ -10,6 +10,7 @@ class RequestVersionView(APIView): def get(self, request, *args, **kwargs): return Response({'version': request.version}) + class ReverseView(APIView): def get(self, request, *args, **kwargs): return Response({'url': reverse('another', request=request)}) @@ -19,8 +20,14 @@ factory = APIRequestFactory() mock_view = lambda request: None +included_patterns = [ + url(r'^namespaced/$', mock_view, name='another'), +] + urlpatterns = [ - url(r'^another/$', mock_view, name='another') + url(r'^v1/', include(included_patterns, namespace='v1')), + url(r'^another/$', mock_view, name='another'), + url(r'^(?P[^/]+)/another/$', mock_view, name='another') ] @@ -80,6 +87,22 @@ class TestRequestVersion: response = view(request) assert response.data == {'version': None} + def test_namespace_versioning(self): + class FakeResolverMatch: + namespace = 'v1' + + scheme = versioning.NamespaceVersioning + view = RequestVersionView.as_view(versioning_class=scheme) + + request = factory.get('/v1/endpoint/') + request.resolver_match = FakeResolverMatch + response = view(request, version='v1') + assert response.data == {'version': 'v1'} + + request = factory.get('/endpoint/') + response = view(request) + assert response.data == {'version': None} + class TestURLReversing(APITestCase): urls = 'tests.test_versioning' @@ -91,6 +114,18 @@ class TestURLReversing(APITestCase): response = view(request) assert response.data == {'url': 'http://testserver/another/'} + def test_reverse_query_param_versioning(self): + scheme = versioning.QueryParameterVersioning + view = ReverseView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/?version=v1') + response = view(request) + assert response.data == {'url': 'http://testserver/another/?version=v1'} + + request = factory.get('/endpoint/') + response = view(request) + assert response.data == {'url': 'http://testserver/another/'} + def test_reverse_host_name_versioning(self): scheme = versioning.HostNameVersioning view = ReverseView.as_view(versioning_class=scheme) @@ -102,3 +137,31 @@ class TestURLReversing(APITestCase): request = factory.get('/endpoint/') response = view(request) assert response.data == {'url': 'http://testserver/another/'} + + def test_reverse_url_path_versioning(self): + scheme = versioning.URLPathVersioning + view = ReverseView.as_view(versioning_class=scheme) + + request = factory.get('/v1/endpoint/') + response = view(request, version='v1') + assert response.data == {'url': 'http://testserver/v1/another/'} + + request = factory.get('/endpoint/') + response = view(request) + assert response.data == {'url': 'http://testserver/another/'} + + def test_namespace_versioning(self): + class FakeResolverMatch: + namespace = 'v1' + + scheme = versioning.NamespaceVersioning + view = ReverseView.as_view(versioning_class=scheme) + + request = factory.get('/v1/endpoint/') + request.resolver_match = FakeResolverMatch + response = view(request, version='v1') + assert response.data == {'url': 'http://testserver/v1/namespaced/'} + + request = factory.get('/endpoint/') + response = view(request) + assert response.data == {'url': 'http://testserver/another/'} -- cgit v1.2.3 From 05a6eaec8aebdca2248b9e1069a15769fd85a480 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 17 Dec 2014 12:41:46 +0000 Subject: More docs, plus 'ALLOWED_VERSIONS' setting. --- tests/test_versioning.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) (limited to 'tests/test_versioning.py') diff --git a/tests/test_versioning.py b/tests/test_versioning.py index eaac5dfb..c44f727d 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -1,5 +1,5 @@ from django.conf.urls import include, url -from rest_framework import versioning +from rest_framework import status, versioning from rest_framework.decorators import APIView from rest_framework.response import Response from rest_framework.reverse import reverse @@ -16,6 +16,16 @@ class ReverseView(APIView): return Response({'url': reverse('another', request=request)}) +class RequestInvalidVersionView(APIView): + def determine_version(self, request, *args, **kwargs): + scheme = self.versioning_class() + scheme.allowed_versions = ('v1', 'v2') + return (scheme.determine_version(request, *args, **kwargs), scheme) + + def get(self, request, *args, **kwargs): + return Response({'version': request.version}) + + factory = APIRequestFactory() mock_view = lambda request: None @@ -150,7 +160,7 @@ class TestURLReversing(APITestCase): response = view(request) assert response.data == {'url': 'http://testserver/another/'} - def test_namespace_versioning(self): + def test_reverse_namespace_versioning(self): class FakeResolverMatch: namespace = 'v1' @@ -165,3 +175,49 @@ class TestURLReversing(APITestCase): request = factory.get('/endpoint/') response = view(request) assert response.data == {'url': 'http://testserver/another/'} + + +class TestInvalidVersion: + def test_invalid_query_param_versioning(self): + scheme = versioning.QueryParameterVersioning + view = RequestInvalidVersionView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/?version=v3') + response = view(request) + assert response.status_code == status.HTTP_404_NOT_FOUND + + def test_invalid_host_name_versioning(self): + scheme = versioning.HostNameVersioning + view = RequestInvalidVersionView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/', HTTP_HOST='v3.example.org') + response = view(request) + assert response.status_code == status.HTTP_404_NOT_FOUND + + def test_invalid_accept_header_versioning(self): + scheme = versioning.AcceptHeaderVersioning + view = RequestInvalidVersionView.as_view(versioning_class=scheme) + + request = factory.get('/endpoint/', HTTP_ACCEPT='application/json; version=v3') + response = view(request) + assert response.status_code == status.HTTP_406_NOT_ACCEPTABLE + + def test_invalid_url_path_versioning(self): + scheme = versioning.URLPathVersioning + view = RequestInvalidVersionView.as_view(versioning_class=scheme) + + request = factory.get('/v3/endpoint/') + response = view(request, version='v3') + assert response.status_code == status.HTTP_404_NOT_FOUND + + def test_invalid_namespace_versioning(self): + class FakeResolverMatch: + namespace = 'v3' + + scheme = versioning.NamespaceVersioning + view = RequestInvalidVersionView.as_view(versioning_class=scheme) + + request = factory.get('/v3/endpoint/') + request.resolver_match = FakeResolverMatch + response = view(request, version='v3') + assert response.status_code == status.HTTP_404_NOT_FOUND -- cgit v1.2.3 From 030f01afdbcd4018a288250ef1f4c12de28e63bb Mon Sep 17 00:00:00 2001 From: Brandon Cazander Date: Tue, 3 Feb 2015 02:14:38 -0800 Subject: Reorganize tests. --- tests/test_versioning.py | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'tests/test_versioning.py') diff --git a/tests/test_versioning.py b/tests/test_versioning.py index c44f727d..e7c8485e 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -1,9 +1,13 @@ +from .utils import MockObject, MockQueryset from django.conf.urls import include, url +from django.core.exceptions import ObjectDoesNotExist +from rest_framework import serializers from rest_framework import status, versioning from rest_framework.decorators import APIView from rest_framework.response import Response from rest_framework.reverse import reverse -from rest_framework.test import APIRequestFactory, APITestCase +from rest_framework.test import APIRequestFactory, APITestCase, APISimpleTestCase +from rest_framework.versioning import NamespaceVersioning class RequestVersionView(APIView): @@ -29,15 +33,18 @@ class RequestInvalidVersionView(APIView): factory = APIRequestFactory() mock_view = lambda request: None +dummy_view = lambda request, pk: None included_patterns = [ url(r'^namespaced/$', mock_view, name='another'), + url(r'^example/(?P\d+)/$', dummy_view, name='example-detail') ] urlpatterns = [ url(r'^v1/', include(included_patterns, namespace='v1')), url(r'^another/$', mock_view, name='another'), - url(r'^(?P[^/]+)/another/$', mock_view, name='another') + url(r'^(?P[^/]+)/another/$', mock_view, name='another'), + url(r'^example/(?P\d+)/$', dummy_view, name='example-detail') ] @@ -221,3 +228,33 @@ class TestInvalidVersion: request.resolver_match = FakeResolverMatch response = view(request, version='v3') assert response.status_code == status.HTTP_404_NOT_FOUND + + +class TestHyperlinkedRelatedField(APISimpleTestCase): + urls = 'tests.test_versioning' + + def setUp(self): + + class HyperlinkedMockQueryset(MockQueryset): + def get(self, **lookup): + for item in self.items: + if item.pk == int(lookup.get('pk', -1)): + return item + raise ObjectDoesNotExist() + + self.queryset = HyperlinkedMockQueryset([ + MockObject(pk=1, name='foo'), + MockObject(pk=2, name='bar'), + MockObject(pk=3, name='baz') + ]) + self.field = serializers.HyperlinkedRelatedField( + view_name='example-detail', + queryset=self.queryset + ) + request = factory.post('/', urlconf='tests.test_versioning') + request.versioning_scheme = NamespaceVersioning() + self.field._context = {'request': request} + + def test_bug_2489(self): + self.field.to_internal_value('/example/3/') + self.field.to_internal_value('/v1/example/3/') -- cgit v1.2.3 From e1c45133126e0c47b8470b4cf7a43c6a7f4fca43 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 5 Feb 2015 00:58:09 +0000 Subject: Fix NamespaceVersioning with hyperlinked serializer fields --- tests/test_versioning.py | 50 ++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 21 deletions(-) (limited to 'tests/test_versioning.py') diff --git a/tests/test_versioning.py b/tests/test_versioning.py index e7c8485e..cdd10065 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -1,4 +1,4 @@ -from .utils import MockObject, MockQueryset +from .utils import MockObject, MockQueryset, UsingURLPatterns from django.conf.urls import include, url from django.core.exceptions import ObjectDoesNotExist from rest_framework import serializers @@ -6,8 +6,9 @@ from rest_framework import status, versioning from rest_framework.decorators import APIView from rest_framework.response import Response from rest_framework.reverse import reverse -from rest_framework.test import APIRequestFactory, APITestCase, APISimpleTestCase +from rest_framework.test import APIRequestFactory, APITestCase from rest_framework.versioning import NamespaceVersioning +import pytest class RequestVersionView(APIView): @@ -35,18 +36,6 @@ factory = APIRequestFactory() mock_view = lambda request: None dummy_view = lambda request, pk: None -included_patterns = [ - url(r'^namespaced/$', mock_view, name='another'), - url(r'^example/(?P\d+)/$', dummy_view, name='example-detail') -] - -urlpatterns = [ - url(r'^v1/', include(included_patterns, namespace='v1')), - url(r'^another/$', mock_view, name='another'), - url(r'^(?P[^/]+)/another/$', mock_view, name='another'), - url(r'^example/(?P\d+)/$', dummy_view, name='example-detail') -] - class TestRequestVersion: def test_unversioned(self): @@ -121,8 +110,17 @@ class TestRequestVersion: assert response.data == {'version': None} -class TestURLReversing(APITestCase): - urls = 'tests.test_versioning' +class TestURLReversing(UsingURLPatterns, APITestCase): + included = [ + url(r'^namespaced/$', mock_view, name='another'), + url(r'^example/(?P\d+)/$', dummy_view, name='example-detail') + ] + + urlpatterns = [ + url(r'^v1/', include(included, namespace='v1')), + url(r'^another/$', mock_view, name='another'), + url(r'^(?P[^/]+)/another/$', mock_view, name='another'), + ] def test_reverse_unversioned(self): view = ReverseView.as_view() @@ -230,10 +228,18 @@ class TestInvalidVersion: assert response.status_code == status.HTTP_404_NOT_FOUND -class TestHyperlinkedRelatedField(APISimpleTestCase): - urls = 'tests.test_versioning' +class TestHyperlinkedRelatedField(UsingURLPatterns, APITestCase): + included = [ + url(r'^namespaced/(?P\d+)/$', mock_view, name='namespaced'), + ] + + urlpatterns = [ + url(r'^v1/', include(included, namespace='v1')), + url(r'^v2/', include(included, namespace='v2')) + ] def setUp(self): + super(TestHyperlinkedRelatedField, self).setUp() class HyperlinkedMockQueryset(MockQueryset): def get(self, **lookup): @@ -248,13 +254,15 @@ class TestHyperlinkedRelatedField(APISimpleTestCase): MockObject(pk=3, name='baz') ]) self.field = serializers.HyperlinkedRelatedField( - view_name='example-detail', + view_name='namespaced', queryset=self.queryset ) request = factory.post('/', urlconf='tests.test_versioning') request.versioning_scheme = NamespaceVersioning() + request.version = 'v1' self.field._context = {'request': request} def test_bug_2489(self): - self.field.to_internal_value('/example/3/') - self.field.to_internal_value('/v1/example/3/') + self.field.to_internal_value('/v1/namespaced/3/') + with pytest.raises(serializers.ValidationError): + self.field.to_internal_value('/v2/namespaced/3/') -- cgit v1.2.3 From f98f842827c6e79bbaa196482e3c3c549e8999c8 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 5 Feb 2015 01:24:55 +0000 Subject: Minor bits of test cleanup --- tests/test_versioning.py | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) (limited to 'tests/test_versioning.py') diff --git a/tests/test_versioning.py b/tests/test_versioning.py index cdd10065..553463d1 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -1,6 +1,5 @@ -from .utils import MockObject, MockQueryset, UsingURLPatterns +from .utils import UsingURLPatterns from django.conf.urls import include, url -from django.core.exceptions import ObjectDoesNotExist from rest_framework import serializers from rest_framework import status, versioning from rest_framework.decorators import APIView @@ -33,8 +32,8 @@ class RequestInvalidVersionView(APIView): factory = APIRequestFactory() -mock_view = lambda request: None -dummy_view = lambda request, pk: None +dummy_view = lambda request: None +dummy_pk_view = lambda request, pk: None class TestRequestVersion: @@ -112,14 +111,14 @@ class TestRequestVersion: class TestURLReversing(UsingURLPatterns, APITestCase): included = [ - url(r'^namespaced/$', mock_view, name='another'), - url(r'^example/(?P\d+)/$', dummy_view, name='example-detail') + url(r'^namespaced/$', dummy_view, name='another'), + url(r'^example/(?P\d+)/$', dummy_pk_view, name='example-detail') ] urlpatterns = [ url(r'^v1/', include(included, namespace='v1')), - url(r'^another/$', mock_view, name='another'), - url(r'^(?P[^/]+)/another/$', mock_view, name='another'), + url(r'^another/$', dummy_view, name='another'), + url(r'^(?P[^/]+)/another/$', dummy_view, name='another'), ] def test_reverse_unversioned(self): @@ -230,7 +229,7 @@ class TestInvalidVersion: class TestHyperlinkedRelatedField(UsingURLPatterns, APITestCase): included = [ - url(r'^namespaced/(?P\d+)/$', mock_view, name='namespaced'), + url(r'^namespaced/(?P\d+)/$', dummy_view, name='namespaced'), ] urlpatterns = [ @@ -241,28 +240,20 @@ class TestHyperlinkedRelatedField(UsingURLPatterns, APITestCase): def setUp(self): super(TestHyperlinkedRelatedField, self).setUp() - class HyperlinkedMockQueryset(MockQueryset): - def get(self, **lookup): - for item in self.items: - if item.pk == int(lookup.get('pk', -1)): - return item - raise ObjectDoesNotExist() - - self.queryset = HyperlinkedMockQueryset([ - MockObject(pk=1, name='foo'), - MockObject(pk=2, name='bar'), - MockObject(pk=3, name='baz') - ]) + class MockQueryset(object): + def get(self, pk): + return 'object %s' % pk + self.field = serializers.HyperlinkedRelatedField( view_name='namespaced', - queryset=self.queryset + queryset=MockQueryset() ) - request = factory.post('/', urlconf='tests.test_versioning') + request = factory.get('/') request.versioning_scheme = NamespaceVersioning() request.version = 'v1' self.field._context = {'request': request} def test_bug_2489(self): - self.field.to_internal_value('/v1/namespaced/3/') + assert self.field.to_internal_value('/v1/namespaced/3/') == 'object 3' with pytest.raises(serializers.ValidationError): self.field.to_internal_value('/v2/namespaced/3/') -- cgit v1.2.3