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
This commit is contained in:
mariodev 2016-07-18 07:06:28 +02:00 committed by GitHub
parent 976b3bbe4d
commit c5e0382d25
7 changed files with 208 additions and 13 deletions

View File

@ -7,6 +7,7 @@ from rest_framework.permissions import AllowAny
from rest_framework.generics import CreateAPIView from rest_framework.generics import CreateAPIView
from rest_framework import status from rest_framework import status
from allauth.account.adapter import get_adapter
from allauth.account.views import ConfirmEmailView from allauth.account.views import ConfirmEmailView
from allauth.account.utils import complete_signup from allauth.account.utils import complete_signup
from allauth.account import app_settings as allauth_settings from allauth.account import app_settings as allauth_settings
@ -101,3 +102,6 @@ class SocialLoginView(LoginView):
""" """
serializer_class = SocialLoginSerializer serializer_class = SocialLoginSerializer
def process_login(self):
get_adapter(self.request).login(self.request, self.user)

View File

@ -46,14 +46,11 @@ class TwitterLoginSerializer(serializers.Serializer):
if not adapter_class: if not adapter_class:
raise serializers.ValidationError('Define adapter_class in view') raise serializers.ValidationError('Define adapter_class in view')
adapter = adapter_class() adapter = adapter_class(request)
app = adapter.get_provider().get_app(request) app = adapter.get_provider().get_app(request)
if('access_token' in attrs) and ('token_secret' in attrs):
access_token = attrs.get('access_token') access_token = attrs.get('access_token')
token_secret = attrs.get('token_secret') token_secret = attrs.get('token_secret')
else:
raise serializers.ValidationError('Incorrect input. access_token and token_secret are required.')
request.session['oauth_api.twitter.com_access_token'] = { request.session['oauth_api.twitter.com_access_token'] = {
'oauth_token': access_token, 'oauth_token': access_token,

View File

@ -80,6 +80,7 @@ INSTALLED_APPS = [
'allauth.account', 'allauth.account',
'allauth.socialaccount', 'allauth.socialaccount',
'allauth.socialaccount.providers.facebook', 'allauth.socialaccount.providers.facebook',
'allauth.socialaccount.providers.twitter',
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',

View File

@ -99,6 +99,9 @@ class BaseAPITestCase(object):
self.user_url = reverse('rest_user_details') self.user_url = reverse('rest_user_details')
self.veirfy_email_url = reverse('rest_verify_email') self.veirfy_email_url = reverse('rest_verify_email')
self.fb_login_url = reverse('fb_login') 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): def _login(self):
payload = { payload = {

View File

@ -1,3 +1,5 @@
import json
from django.test import TestCase from django.test import TestCase
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.test.utils import override_settings from django.test.utils import override_settings
@ -34,9 +36,19 @@ class TestSocialAuth(TestCase, BaseAPITestCase):
client_id='123123123', client_id='123123123',
secret='321321321', secret='321321321',
) )
twitter_social_app = SocialApp.objects.create(
provider='twitter',
name='Twitter',
client_id='11223344',
secret='55667788',
)
site = Site.objects.get_current() site = Site.objects.get_current()
social_app.sites.add(site) social_app.sites.add(site)
twitter_social_app.sites.add(site)
self.graph_api_url = GRAPH_API_URL + '/me' self.graph_api_url = GRAPH_API_URL + '/me'
self.twitter_url = 'http://twitter.com/foobarme'
@responses.activate @responses.activate
def test_failed_social_auth(self): def test_failed_social_auth(self):
@ -57,11 +69,24 @@ class TestSocialAuth(TestCase, BaseAPITestCase):
@responses.activate @responses.activate
def test_social_auth(self): def test_social_auth(self):
# fake response for facebook call # 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.add(
responses.GET, responses.GET,
self.graph_api_url, self.graph_api_url,
body=resp_body, body=json.dumps(resp_body),
status=200, status=200,
content_type='application/json' content_type='application/json'
) )
@ -80,6 +105,119 @@ class TestSocialAuth(TestCase, BaseAPITestCase):
self.assertIn('key', self.response.json.keys()) self.assertIn('key', self.response.json.keys())
self.assertEqual(get_user_model().objects.all().count(), users_count + 1) 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 @responses.activate
@override_settings( @override_settings(
ACCOUNT_EMAIL_VERIFICATION='mandatory', ACCOUNT_EMAIL_VERIFICATION='mandatory',
@ -87,11 +225,25 @@ class TestSocialAuth(TestCase, BaseAPITestCase):
REST_SESSION_LOGIN=False REST_SESSION_LOGIN=False
) )
def test_edge_case(self): 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.add(
responses.GET, responses.GET,
self.graph_api_url, self.graph_api_url,
body=resp_body % self.EMAIL, body=json.dumps(resp_body),
status=200, status=200,
content_type='application/json' content_type='application/json'
) )

View File

@ -3,14 +3,41 @@ from django.views.generic import TemplateView
from . import django_urls from . import django_urls
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter 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.urls import urlpatterns
from rest_auth.registration.views import SocialLoginView from rest_auth.registration.views import SocialLoginView
from rest_auth.social_serializers import TwitterLoginSerializer
class FacebookLogin(SocialLoginView): class FacebookLogin(SocialLoginView):
adapter_class = FacebookOAuth2Adapter 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 += [ urlpatterns += [
url(r'^rest-registration/', include('rest_auth.registration.urls')), url(r'^rest-registration/', include('rest_auth.registration.urls')),
url(r'^test-admin/', include(django_urls)), url(r'^test-admin/', include(django_urls)),
@ -19,5 +46,8 @@ urlpatterns += [
url(r'^account-confirm-email/(?P<key>\w+)/$', TemplateView.as_view(), url(r'^account-confirm-email/(?P<key>\w+)/$', TemplateView.as_view(),
name='account_confirm_email'), name='account_confirm_email'),
url(r'^social-login/facebook/$', FacebookLogin.as_view(), name='fb_login'), 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')) url(r'^accounts/', include('allauth.socialaccount.urls'))
] ]

View File

@ -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.conf import settings
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -37,6 +40,9 @@ class LoginView(GenericAPIView):
serializer_class = LoginSerializer serializer_class = LoginSerializer
token_model = TokenModel token_model = TokenModel
def process_login(self):
django_login(self.request, self.user)
def get_response_serializer(self): def get_response_serializer(self):
if getattr(settings, 'REST_USE_JWT', False): if getattr(settings, 'REST_USE_JWT', False):
response_serializer = JWTSerializer response_serializer = JWTSerializer
@ -53,7 +59,7 @@ class LoginView(GenericAPIView):
self.token = create_token(self.token_model, self.user, self.serializer) self.token = create_token(self.token_model, self.user, self.serializer)
if getattr(settings, 'REST_SESSION_LOGIN', True): if getattr(settings, 'REST_SESSION_LOGIN', True):
login(self.request, self.user) self.process_login()
def get_response(self): def get_response(self):
serializer_class = self.get_response_serializer() serializer_class = self.get_response_serializer()
@ -70,8 +76,10 @@ class LoginView(GenericAPIView):
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.request = request
self.serializer = self.get_serializer(data=self.request.data) self.serializer = self.get_serializer(data=self.request.data)
self.serializer.is_valid(raise_exception=True) self.serializer.is_valid(raise_exception=True)
self.login() self.login()
return self.get_response() return self.get_response()
@ -106,7 +114,7 @@ class LogoutView(APIView):
except (AttributeError, ObjectDoesNotExist): except (AttributeError, ObjectDoesNotExist):
pass pass
logout(request) django_logout(request)
return Response({"success": _("Successfully logged out.")}, return Response({"success": _("Successfully logged out.")},
status=status.HTTP_200_OK) status=status.HTTP_200_OK)