diff --git a/django/test/client.py b/django/test/client.py index 46f55d7cdc1..2ed0df8feaa 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -13,7 +13,7 @@ except ImportError: # Python 2 from urlparse import urlparse, urlsplit from django.conf import settings -from django.contrib.auth import authenticate, login +from django.contrib.auth import authenticate, login, logout, get_user_model from django.core.handlers.base import BaseHandler from django.core.handlers.wsgi import WSGIRequest from django.core.signals import (request_started, request_finished, @@ -571,11 +571,17 @@ class Client(RequestFactory): Causes the authenticated user to be logged out. """ - session = import_module(settings.SESSION_ENGINE).SessionStore() - session_cookie = self.cookies.get(settings.SESSION_COOKIE_NAME) - if session_cookie: - session.delete(session_key=session_cookie.value) - self.cookies = SimpleCookie() + request = HttpRequest() + engine = import_module(settings.SESSION_ENGINE) + UserModel = get_user_model() + if self.session: + request.session = self.session + uid = self.session.get("_auth_user_id") + if uid: + request.user = UserModel._default_manager.get(pk=uid) + else: + request.session = engine.SessionStore() + logout(request) def _handle_redirects(self, response, **extra): "Follows any redirects by requesting responses from the server using GET." diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index 08a574f211f..0970453b189 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -623,6 +623,10 @@ Miscellaneous raises NoReverseMatch. There is no change to {% url %} tag, it causes template rendering to fail like always when NoReverseMatch is risen. +* :meth:`django.test.client.Client.logout` now calls + :meth:`django.contrib.auth.logout` which will send the + :func:`~django.contrib.auth.signals.user_logged_out` signal. + Features deprecated in 1.6 ========================== diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt index 470051e78e7..d81b171a494 100644 --- a/docs/topics/testing/overview.txt +++ b/docs/topics/testing/overview.txt @@ -696,7 +696,7 @@ Use the ``django.test.client.Client`` class to make requests. After you call this method, the test client will have all the cookies and session data cleared to defaults. Subsequent requests will appear - to come from an AnonymousUser. + to come from an :class:`~django.contrib.auth.models.AnonymousUser`. Testing responses ~~~~~~~~~~~~~~~~~ diff --git a/tests/test_client_regress/models.py b/tests/test_client_regress/models.py index e69de29bb2d..b72d4480bfe 100644 --- a/tests/test_client_regress/models.py +++ b/tests/test_client_regress/models.py @@ -0,0 +1,12 @@ +from django.db import models +from django.contrib.auth.models import AbstractBaseUser, BaseUserManager + + +class CustomUser(AbstractBaseUser): + email = models.EmailField(verbose_name='email address', max_length=255, unique=True) + custom_objects = BaseUserManager() + + USERNAME_FIELD = 'email' + + class Meta: + app_label = 'test_client_regress' diff --git a/tests/test_client_regress/tests.py b/tests/test_client_regress/tests.py index dc656c127dd..67e66fa52d5 100644 --- a/tests/test_client_regress/tests.py +++ b/tests/test_client_regress/tests.py @@ -17,7 +17,10 @@ from django.template.response import SimpleTemplateResponse from django.utils._os import upath from django.utils.translation import ugettext_lazy from django.http import HttpResponse +from django.contrib.auth.signals import user_logged_out, user_logged_in +from django.contrib.auth.models import User +from .models import CustomUser from .views import CustomTestException @override_settings( @@ -961,6 +964,76 @@ class SessionTests(TestCase): self.client.logout() self.client.logout() + def test_logout_with_user(self): + """Logout should send user_logged_out signal if user was logged in.""" + def listener(*args, **kwargs): + listener.executed = True + self.assertEqual(kwargs['sender'], User) + listener.executed = False + + user_logged_out.connect(listener) + self.client.login(username='testclient', password='password') + self.client.logout() + user_logged_out.disconnect(listener) + self.assertTrue(listener.executed) + + @override_settings(AUTH_USER_MODEL='test_client_regress.CustomUser') + def test_logout_with_custom_user(self): + """Logout should send user_logged_out signal if custom user was logged in.""" + def listener(*args, **kwargs): + self.assertEqual(kwargs['sender'], CustomUser) + listener.executed = True + listener.executed = False + u = CustomUser.custom_objects.create(email='test@test.com') + u.set_password('password') + u.save() + + user_logged_out.connect(listener) + self.client.login(username='test@test.com', password='password') + self.client.logout() + user_logged_out.disconnect(listener) + self.assertTrue(listener.executed) + + def test_logout_without_user(self): + """Logout should send signal even if user not authenticated.""" + def listener(user, *args, **kwargs): + listener.user = user + listener.executed = True + listener.executed = False + + user_logged_out.connect(listener) + self.client.login(username='incorrect', password='password') + self.client.logout() + user_logged_out.disconnect(listener) + + self.assertTrue(listener.executed) + self.assertIsNone(listener.user) + + def test_login_with_user(self): + """Login should send user_logged_in signal on successful login.""" + def listener(*args, **kwargs): + listener.executed = True + listener.executed = False + + user_logged_in.connect(listener) + self.client.login(username='testclient', password='password') + user_logged_out.disconnect(listener) + + self.assertTrue(listener.executed) + + def test_login_without_signal(self): + """Login shouldn't send signal if user wasn't logged in""" + def listener(*args, **kwargs): + listener.executed = True + listener.executed = False + + user_logged_in.connect(listener) + self.client.login(username='incorrect', password='password') + user_logged_in.disconnect(listener) + + self.assertFalse(listener.executed) + + class RequestMethodTests(TestCase): def test_get(self): "Request a view via request method GET"