From c5e0382d2504193d5eac2f316dc4f9bd9d9243c4 Mon Sep 17 00:00:00 2001 From: mariodev Date: Mon, 18 Jul 2016 07:06:28 +0200 Subject: [PATCH] Increased test coverage (#229) * Added twitter login test * pep8 * Fixes missing backend attr issue * Refactored login process * pep8 * Added more tests for twitter social login --- rest_auth/registration/views.py | 4 + rest_auth/social_serializers.py | 9 +- rest_auth/tests/settings.py | 1 + rest_auth/tests/test_base.py | 3 + rest_auth/tests/test_social.py | 160 +++++++++++++++++++++++++++++++- rest_auth/tests/urls.py | 30 ++++++ rest_auth/views.py | 14 ++- 7 files changed, 208 insertions(+), 13 deletions(-) diff --git a/rest_auth/registration/views.py b/rest_auth/registration/views.py index 713c46e..40f895e 100644 --- a/rest_auth/registration/views.py +++ b/rest_auth/registration/views.py @@ -7,6 +7,7 @@ from rest_framework.permissions import AllowAny from rest_framework.generics import CreateAPIView from rest_framework import status +from allauth.account.adapter import get_adapter from allauth.account.views import ConfirmEmailView from allauth.account.utils import complete_signup from allauth.account import app_settings as allauth_settings @@ -101,3 +102,6 @@ class SocialLoginView(LoginView): """ serializer_class = SocialLoginSerializer + + def process_login(self): + get_adapter(self.request).login(self.request, self.user) diff --git a/rest_auth/social_serializers.py b/rest_auth/social_serializers.py index de6223f..6e06be5 100644 --- a/rest_auth/social_serializers.py +++ b/rest_auth/social_serializers.py @@ -46,14 +46,11 @@ class TwitterLoginSerializer(serializers.Serializer): if not adapter_class: raise serializers.ValidationError('Define adapter_class in view') - adapter = adapter_class() + adapter = adapter_class(request) app = adapter.get_provider().get_app(request) - if('access_token' in attrs) and ('token_secret' in attrs): - access_token = attrs.get('access_token') - token_secret = attrs.get('token_secret') - else: - raise serializers.ValidationError('Incorrect input. access_token and token_secret are required.') + access_token = attrs.get('access_token') + token_secret = attrs.get('token_secret') request.session['oauth_api.twitter.com_access_token'] = { 'oauth_token': access_token, diff --git a/rest_auth/tests/settings.py b/rest_auth/tests/settings.py index 743759c..060cc89 100644 --- a/rest_auth/tests/settings.py +++ b/rest_auth/tests/settings.py @@ -80,6 +80,7 @@ INSTALLED_APPS = [ 'allauth.account', 'allauth.socialaccount', 'allauth.socialaccount.providers.facebook', + 'allauth.socialaccount.providers.twitter', 'rest_framework', 'rest_framework.authtoken', diff --git a/rest_auth/tests/test_base.py b/rest_auth/tests/test_base.py index 992b158..faaf7bb 100644 --- a/rest_auth/tests/test_base.py +++ b/rest_auth/tests/test_base.py @@ -99,6 +99,9 @@ class BaseAPITestCase(object): self.user_url = reverse('rest_user_details') self.veirfy_email_url = reverse('rest_verify_email') self.fb_login_url = reverse('fb_login') + self.tw_login_url = reverse('tw_login') + self.tw_login_no_view_url = reverse('tw_login_no_view') + self.tw_login_no_adapter_url = reverse('tw_login_no_adapter') def _login(self): payload = { diff --git a/rest_auth/tests/test_social.py b/rest_auth/tests/test_social.py index ac25977..9356acd 100644 --- a/rest_auth/tests/test_social.py +++ b/rest_auth/tests/test_social.py @@ -1,3 +1,5 @@ +import json + from django.test import TestCase from django.contrib.auth import get_user_model from django.test.utils import override_settings @@ -34,9 +36,19 @@ class TestSocialAuth(TestCase, BaseAPITestCase): client_id='123123123', secret='321321321', ) + + twitter_social_app = SocialApp.objects.create( + provider='twitter', + name='Twitter', + client_id='11223344', + secret='55667788', + ) + site = Site.objects.get_current() social_app.sites.add(site) + twitter_social_app.sites.add(site) self.graph_api_url = GRAPH_API_URL + '/me' + self.twitter_url = 'http://twitter.com/foobarme' @responses.activate def test_failed_social_auth(self): @@ -57,11 +69,24 @@ class TestSocialAuth(TestCase, BaseAPITestCase): @responses.activate def test_social_auth(self): # fake response for facebook call - resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true}' # noqa + resp_body = { + "id": "123123123123", + "first_name": "John", + "gender": "male", + "last_name": "Smith", + "link": "https://www.facebook.com/john.smith", + "locale": "en_US", + "name": "John Smith", + "timezone": 2, + "updated_time": "2014-08-13T10:14:38+0000", + "username": "john.smith", + "verified": True + } + responses.add( responses.GET, self.graph_api_url, - body=resp_body, + body=json.dumps(resp_body), status=200, content_type='application/json' ) @@ -80,6 +105,119 @@ class TestSocialAuth(TestCase, BaseAPITestCase): self.assertIn('key', self.response.json.keys()) self.assertEqual(get_user_model().objects.all().count(), users_count + 1) + def _twitter_social_auth(self): + # fake response for twitter call + resp_body = { + "id": "123123123123", + } + + responses.add( + responses.GET, + 'https://api.twitter.com/1.1/account/verify_credentials.json', + body=json.dumps(resp_body), + status=200, + content_type='application/json' + ) + + users_count = get_user_model().objects.all().count() + payload = { + 'access_token': 'abc123', + 'token_secret': '1111222233334444' + } + + self.post(self.tw_login_url, data=payload) + + self.assertIn('key', self.response.json.keys()) + self.assertEqual(get_user_model().objects.all().count(), users_count + 1) + + # make sure that second request will not create a new user + self.post(self.tw_login_url, data=payload, status_code=200) + self.assertIn('key', self.response.json.keys()) + self.assertEqual(get_user_model().objects.all().count(), users_count + 1) + + @responses.activate + @override_settings(SOCIALACCOUNT_AUTO_SIGNUP=True) + def test_twitter_social_auth(self): + self._twitter_social_auth() + + @responses.activate + @override_settings(SOCIALACCOUNT_AUTO_SIGNUP=False) + def test_twitter_social_auth_without_auto_singup(self): + self._twitter_social_auth() + + @responses.activate + def test_twitter_social_auth_request_error(self): + # fake response for twitter call + resp_body = { + "id": "123123123123", + } + + responses.add( + responses.GET, + 'https://api.twitter.com/1.1/account/verify_credentials.json', + body=json.dumps(resp_body), + status=400, + content_type='application/json' + ) + + users_count = get_user_model().objects.all().count() + payload = { + 'access_token': 'abc123', + 'token_secret': '1111222233334444' + } + + self.post(self.tw_login_url, data=payload, status_code=400) + self.assertNotIn('key', self.response.json.keys()) + self.assertEqual(get_user_model().objects.all().count(), users_count) + + @responses.activate + def test_twitter_social_auth_no_view_in_context(self): + # fake response for twitter call + resp_body = { + "id": "123123123123", + } + + responses.add( + responses.GET, + 'https://api.twitter.com/1.1/account/verify_credentials.json', + body=json.dumps(resp_body), + status=400, + content_type='application/json' + ) + + users_count = get_user_model().objects.all().count() + payload = { + 'access_token': 'abc123', + 'token_secret': '1111222233334444' + } + + self.post(self.tw_login_no_view_url, data=payload, status_code=400) + self.assertEqual(get_user_model().objects.all().count(), users_count) + + @responses.activate + def test_twitter_social_auth_no_adapter(self): + # fake response for twitter call + resp_body = { + "id": "123123123123", + } + + responses.add( + responses.GET, + 'https://api.twitter.com/1.1/account/verify_credentials.json', + body=json.dumps(resp_body), + status=400, + content_type='application/json' + ) + + users_count = get_user_model().objects.all().count() + payload = { + 'access_token': 'abc123', + 'token_secret': '1111222233334444' + } + + self.post(self.tw_login_no_adapter_url, data=payload, status_code=400) + self.assertEqual(get_user_model().objects.all().count(), users_count) + @responses.activate @override_settings( ACCOUNT_EMAIL_VERIFICATION='mandatory', @@ -87,11 +225,25 @@ class TestSocialAuth(TestCase, BaseAPITestCase): REST_SESSION_LOGIN=False ) def test_edge_case(self): - resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true,"email":"%s"}' # noqa + resp_body = { + "id": "123123123123", + "first_name": "John", + "gender": "male", + "last_name": "Smith", + "link": "https://www.facebook.com/john.smith", + "locale": "en_US", + "name": "John Smith", + "timezone": 2, + "updated_time": "2014-08-13T10:14:38+0000", + "username": "john.smith", + "verified": True, + "email": self.EMAIL + } + responses.add( responses.GET, self.graph_api_url, - body=resp_body % self.EMAIL, + body=json.dumps(resp_body), status=200, content_type='application/json' ) diff --git a/rest_auth/tests/urls.py b/rest_auth/tests/urls.py index d922f7f..c33d390 100644 --- a/rest_auth/tests/urls.py +++ b/rest_auth/tests/urls.py @@ -3,14 +3,41 @@ from django.views.generic import TemplateView from . import django_urls from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter +from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter + +from rest_framework.decorators import api_view from rest_auth.urls import urlpatterns from rest_auth.registration.views import SocialLoginView +from rest_auth.social_serializers import TwitterLoginSerializer class FacebookLogin(SocialLoginView): adapter_class = FacebookOAuth2Adapter + +class TwitterLogin(SocialLoginView): + adapter_class = TwitterOAuthAdapter + serializer_class = TwitterLoginSerializer + + +class TwitterLoginSerializerFoo(TwitterLoginSerializer): + pass + + +@api_view(['POST']) +def twitter_login_view(request): + serializer = TwitterLoginSerializerFoo( + data={'access_token': '11223344', 'token_secret': '55667788'}, + context={'request': request} + ) + serializer.is_valid(raise_exception=True) + + +class TwitterLoginNoAdapter(SocialLoginView): + serializer_class = TwitterLoginSerializer + + urlpatterns += [ url(r'^rest-registration/', include('rest_auth.registration.urls')), url(r'^test-admin/', include(django_urls)), @@ -19,5 +46,8 @@ urlpatterns += [ url(r'^account-confirm-email/(?P\w+)/$', TemplateView.as_view(), name='account_confirm_email'), url(r'^social-login/facebook/$', FacebookLogin.as_view(), name='fb_login'), + url(r'^social-login/twitter/$', TwitterLogin.as_view(), name='tw_login'), + url(r'^social-login/twitter-no-view/$', twitter_login_view, name='tw_login_no_view'), + url(r'^social-login/twitter-no-adapter/$', TwitterLoginNoAdapter.as_view(), name='tw_login_no_adapter'), url(r'^accounts/', include('allauth.socialaccount.urls')) ] diff --git a/rest_auth/views.py b/rest_auth/views.py index 55f767d..0761600 100644 --- a/rest_auth/views.py +++ b/rest_auth/views.py @@ -1,4 +1,7 @@ -from django.contrib.auth import login, logout +from django.contrib.auth import ( + login as django_login, + logout as django_logout +) from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import ugettext_lazy as _ @@ -37,6 +40,9 @@ class LoginView(GenericAPIView): serializer_class = LoginSerializer token_model = TokenModel + def process_login(self): + django_login(self.request, self.user) + def get_response_serializer(self): if getattr(settings, 'REST_USE_JWT', False): response_serializer = JWTSerializer @@ -53,7 +59,7 @@ class LoginView(GenericAPIView): self.token = create_token(self.token_model, self.user, self.serializer) if getattr(settings, 'REST_SESSION_LOGIN', True): - login(self.request, self.user) + self.process_login() def get_response(self): serializer_class = self.get_response_serializer() @@ -70,8 +76,10 @@ class LoginView(GenericAPIView): return Response(serializer.data, status=status.HTTP_200_OK) def post(self, request, *args, **kwargs): + self.request = request self.serializer = self.get_serializer(data=self.request.data) self.serializer.is_valid(raise_exception=True) + self.login() return self.get_response() @@ -106,7 +114,7 @@ class LogoutView(APIView): except (AttributeError, ObjectDoesNotExist): pass - logout(request) + django_logout(request) return Response({"success": _("Successfully logged out.")}, status=status.HTTP_200_OK)