From 9860eb683ffc8dda79969f5f29917228c26ac1bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Delobelle?= Date: Wed, 26 Jul 2017 18:32:35 +0200 Subject: [PATCH] Update tests - Delete patch_cas_client on CASTestCase. It's replaced with patch_cas_response method. - Move CAS*TestCase to allauth_cas.test.testcases. --- allauth_cas/test/__init__.py | 0 allauth_cas/test/testcases.py | 104 ++++++++++++++++++++++++++++++++++ tests/test_testcases.py | 58 +++++++++++++++++++ tests/test_views.py | 8 +-- tests/testcases.py | 76 ------------------------- 5 files changed, 166 insertions(+), 80 deletions(-) create mode 100644 allauth_cas/test/__init__.py create mode 100644 allauth_cas/test/testcases.py create mode 100644 tests/test_testcases.py delete mode 100644 tests/testcases.py diff --git a/allauth_cas/test/__init__.py b/allauth_cas/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/allauth_cas/test/testcases.py b/allauth_cas/test/testcases.py new file mode 100644 index 0000000..d13500e --- /dev/null +++ b/allauth_cas/test/testcases.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +try: + from unittest.mock import patch +except ImportError: + from mock import patch + +from django.conf import settings +from django.test import TestCase + +import cas + +from allauth_cas import CAS_PROVIDER_SESSION_KEY + + +class CASTestCase(TestCase): + + def patch_cas_response( + self, + valid_ticket, + username=None, attributes={}): + """ + Patch the CASClient class used by views of CAS providers. + + Arguments determines the response of verify_ticket method: + + - If ticket given as paramater to this method is equal to valid_ticket, + its return value corresponds to a successful authentication on CAS + server for user whose login is username argument (default: + "username") and extra attributes (provided by the server) are + attributes argument (default: {}). + + - If ticket doesn't match valid_ticket, the response corresponds to a + reject from CAS server. + + Special values for valid_ticket: + + - If valid_ticket is '__all__', a success response is always returned. + - If valid_ticket is None, a failure response is always returned. + + Note that valid_ticket sould be a string (which is the type of the + ticket retrieved from GET parameter on request on the callback view). + """ + if hasattr(self, '_patch_cas_client'): + self.patch_cas_client_stop() + + class MockCASClient(cas.CASClientV2): + _username = username + + def __init__(self_client, *args, **kwargs): + kwargs.pop('version') + super(MockCASClient, self_client).__init__(*args, **kwargs) + + def verify_ticket(self_client, ticket): + if valid_ticket == '__all__' or ticket == valid_ticket: + username = self_client._username or 'username' + return username, attributes, None + return None, {}, None + + self._patch_cas_client = patch( + 'allauth_cas.views.cas.CASClient', + MockCASClient, + ) + self._patch_cas_client.start() + + def patch_cas_client_stop(self): + self._patch_cas_client.stop() + del self._patch_cas_client + + def tearDown(self): + if hasattr(self, '_patch_cas_client'): + self.patch_cas_client_stop() + + +class CASViewTestCase(CASTestCase): + + def assertLoginSuccess(self, response, redirect_to=None): + """ + Asserts response corresponds to a successful login. + + To check this, the response should redirect to redirect_to (default to + /accounts/profile/, the default redirect after a successful login). + Also CAS_PROVIDER_SESSION_KEY should be set in the client' session. By + default, self.client is used. + """ + if redirect_to is None: + redirect_to = settings.LOGIN_REDIRECT_URL + + self.assertRedirects( + response, redirect_to, + fetch_redirect_response=False, + ) + self.assertIn( + CAS_PROVIDER_SESSION_KEY, + response.wsgi_request.session, + ) + + def assertLoginFailure(self, response): + """ + Asserts response corresponds to a failed login. + """ + return self.assertInHTML( + '

Social Network Login Failure

', + str(response.content), + ) diff --git a/tests/test_testcases.py b/tests/test_testcases.py new file mode 100644 index 0000000..993d6c3 --- /dev/null +++ b/tests/test_testcases.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +from django.test import Client + +from allauth_cas.test.testcases import CASViewTestCase + + +class CASTestCaseTests(CASViewTestCase): + + def test_patch_cas_response_verify_success(self): + self.patch_cas_response(valid_ticket='123456') + r = self.client.get('/accounts/theid/login/callback/', { + 'ticket': '123456', + }) + self.assertLoginSuccess(r) + + def test_patch_cas_response_verify_failure(self): + self.patch_cas_response(valid_ticket='123456') + r = self.client.get('/accounts/theid/login/callback/', { + 'ticket': '000000', + }) + self.assertLoginFailure(r) + + def test_patch_cas_response_accept(self): + self.patch_cas_response(valid_ticket='__all__') + r = self.client.get('/accounts/theid/login/callback/', { + 'ticket': '000000', + }) + self.assertLoginSuccess(r) + + def test_patch_cas_response_reject(self): + self.patch_cas_response(valid_ticket=None) + r = self.client.get('/accounts/theid/login/callback/', { + 'ticket': '000000', + }) + self.assertLoginFailure(r) + + def test_patch_cas_reponse_multiple(self): + self.patch_cas_response(valid_ticket='__all__') + client_0 = Client() + r_0 = client_0.get('/accounts/theid/login/callback/', { + 'ticket': '000000', + }) + self.assertLoginSuccess(r_0) + + self.patch_cas_response(valid_ticket=None) + client_1 = Client() + r_1 = client_1.get('/accounts/theid/login/callback/', { + 'ticket': '111111', + }) + self.assertLoginFailure(r_1) + + def test_assertLoginSuccess(self): + self.patch_cas_response(valid_ticket='__all__') + r = self.client.get('/accounts/theid/login/callback/', { + 'ticket': '000000', + 'next': '/path/', + }) + self.assertLoginSuccess(r, redirect_to='/path/') diff --git a/tests/test_views.py b/tests/test_views.py index 18a985a..ae5a83a 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -8,10 +8,10 @@ import django from django.test import RequestFactory, TestCase, override_settings from allauth_cas.exceptions import CASAuthenticationError +from allauth_cas.test.testcases import CASViewTestCase from allauth_cas.views import CASView from .example.views import ExampleCASAdapter -from .testcases import CASViewTestCase if django.VERSION >= (1, 10): from django.urls import reverse @@ -179,7 +179,7 @@ class CASCallbackViewTests(CASViewTestCase): """ If ticket is valid, the user is logged in. """ - self.patch_cas_client('verify') + self.patch_cas_response(username='username', valid_ticket='123456') r = self.client.get('/accounts/theid/login/callback/', { 'ticket': '123456', }) @@ -189,7 +189,7 @@ class CASCallbackViewTests(CASViewTestCase): """ Login failure page is returned if the ticket is invalid. """ - self.patch_cas_client('verify') + self.patch_cas_response(username='username', valid_ticket='123456') r = self.client.get('/accounts/theid/login/callback/', { 'ticket': '000000', }) @@ -199,7 +199,7 @@ class CASCallbackViewTests(CASViewTestCase): """ Login failure page is returned if request lacks a ticket. """ - self.patch_cas_client('verify') + self.patch_cas_response(username='username', valid_ticket='123456') r = self.client.get('/accounts/theid/login/callback/') self.assertLoginFailure(r) diff --git a/tests/testcases.py b/tests/testcases.py deleted file mode 100644 index 6b039ad..0000000 --- a/tests/testcases.py +++ /dev/null @@ -1,76 +0,0 @@ -# -*- coding: utf-8 -*- -try: - from unittest.mock import patch -except ImportError: - from mock import patch - -from django.test import TestCase as DjangoTestCase - -from allauth_cas import CAS_PROVIDER_SESSION_KEY - -from . import cas_clients - - -class TestCase(DjangoTestCase): - - def patch_cas_client(self, label): - """ - Patch cas.CASClient in allauth_cs.views module with another CAS client - selectable with label argument. - - Patch is stopped at the end of the current test. - """ - if hasattr(self, '_patch_cas_client'): - self.patch_cas_client_stop() - - if label == 'verify': - new = cas_clients.VerifyCASClient - elif label == 'accept': - new = cas_clients.AcceptCASClient - elif label == 'reject': - new = cas_clients.RejectCASClient - - self._patch_cas_client = patch('allauth_cas.views.cas.CASClient', new) - self._patch_cas_client.start() - - def patch_cas_client_stop(self): - self._patch_cas_client.stop() - - def tearDown(self): - if hasattr(self, '_patch_cas_client'): - self.patch_cas_client_stop() - - -class CASViewTestCase(TestCase): - - def assertLoginSuccess(self, response, redirect_to=None, client=None): - """ - Asserts response corresponds to a successful login. - - To check this, the response should redirect to redirect_to (default to - /accounts/profile/, the default redirect after a successful login). - Also CAS_PROVIDER_SESSION_KEY should be set in the client' session. By - default, self.client is used. - """ - if client is None: - client = self.client - if redirect_to is None: - redirect_to = '/accounts/profile/' - - self.assertRedirects( - response, redirect_to, - fetch_redirect_response=False, - ) - self.assertIn( - CAS_PROVIDER_SESSION_KEY, - client.session, - ) - - def assertLoginFailure(self, response): - """ - Asserts response corresponds to a failed login. - """ - return self.assertInHTML( - '

Social Network Login Failure

', - str(response.content), - )