Commit 4fdd51b7 authored by Unai Zalakain's avatar Unai Zalakain Committed by Anssi Kääriäinen
Browse files

Fixed #15179 -- middlewares not applied for test client login()

Requests made with django.test.Client.login() and logout() respect
defaults defined in django.test.Client instantiation and are processed
through middleware.

Thanks to Loic for the reviews.
parent 9348fc56
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@ from django.core.handlers.wsgi import WSGIRequest
from django.core.signals import (request_started, request_finished,
    got_request_exception)
from django.db import close_old_connections
from django.http import SimpleCookie, HttpRequest, QueryDict
from django.http import SimpleCookie, QueryDict
from django.template import TemplateDoesNotExist
from django.test import signals
from django.utils.functional import curry
@@ -83,8 +83,9 @@ def closing_iterator_wrapper(iterable, close):
class ClientHandler(BaseHandler):
    """
    A HTTP Handler that can be used for testing purposes.
    Uses the WSGI interface to compose requests, but returns
    the raw HttpResponse object
    Uses the WSGI interface to compose requests, but returns the raw
    HttpResponse object with the originating WSGIRequest attached to its
    ``request_instance`` attribute.
    """
    def __init__(self, enforce_csrf_checks=True, *args, **kwargs):
        self.enforce_csrf_checks = enforce_csrf_checks
@@ -105,7 +106,13 @@ class ClientHandler(BaseHandler):
        # required for backwards compatibility with external tests against
        # admin views.
        request._dont_enforce_csrf_checks = not self.enforce_csrf_checks

        # Request goes through middleware.
        response = self.get_response(request)
        # Attach the originating request to the response so that it could be
        # later retrieved.
        response.request_instance = request

        # We're emulating a WSGI server; we must call the close method
        # on completion.
        if response.streaming:
@@ -546,8 +553,9 @@ class Client(RequestFactory):
                'django.contrib.sessions' in settings.INSTALLED_APPS):
            engine = import_module(settings.SESSION_ENGINE)

            # Create a fake request to store login details.
            request = HttpRequest()
            # Create a fake request that goes through request middleware
            request = self.request().request_instance

            if self.session:
                request.session = self.session
            else:
@@ -579,7 +587,9 @@ class Client(RequestFactory):

        Causes the authenticated user to be logged out.
        """
        request = HttpRequest()
        # Create a fake request that goes through request middleware
        request = self.request().request_instance

        engine = import_module(settings.SESSION_ENGINE)
        UserModel = get_user_model()
        if self.session:
+5 −0
Original line number Diff line number Diff line
@@ -496,6 +496,11 @@ Tests
  :class:`~django.test.Client`. If ``True``, the request will be made
  through HTTPS.

* Requests made with :meth:`Client.login() <django.test.Client.login>` and
  :meth:`Client.logout() <django.test.Client.logout>` respect defaults defined
  in :class:`~django.test.Client` instantiation and are processed through
  middleware.

Backwards incompatible changes in 1.7
=====================================

+14 −0
Original line number Diff line number Diff line
@@ -663,6 +663,13 @@ Use the ``django.test.Client`` class to make requests.
        :meth:`~django.contrib.auth.models.UserManager.create_user` helper
        method to create a new user with a correctly hashed password.

        .. versionadded:: 1.7

            Requests made with :meth:`~django.test.Client.login` go through the
            request middleware. If you need to control the environment, you can
            do so at :class:`~django.test.Client` instantiation or with the
            `Client.defaults` attribute.

    .. method:: Client.logout()

        If your site uses Django's :doc:`authentication system</topics/auth/index>`,
@@ -673,6 +680,13 @@ Use the ``django.test.Client`` class to make requests.
        and session data cleared to defaults. Subsequent requests will appear
        to come from an :class:`~django.contrib.auth.models.AnonymousUser`.

        .. versionadded:: 1.7

            Requests made with :meth:`~django.test.Client.logout` go through the
            request middleware. If you need to control the environment, you can
            do so at :class:`~django.test.Client` instantiation or with the
            `Client.defaults` attribute.

Testing responses
~~~~~~~~~~~~~~~~~

+44 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ from __future__ import unicode_literals
import os
import itertools

from django.conf import settings
from django.core.urlresolvers import reverse
from django.template import (TemplateSyntaxError,
    Context, Template, loader)
@@ -761,6 +762,11 @@ class AssertFormsetErrorTests(TestCase):
                                    **kwargs)


class ProcessedMiddleware(object):
    def process_request(self, request):
        request.has_been_processed = True


@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class LoginTests(TestCase):
    fixtures = ['testdata']
@@ -781,6 +787,24 @@ class LoginTests(TestCase):
        # default client.
        self.assertRedirects(response, "http://testserver/test_client_regress/get_view/")

    @override_settings(
        MIDDLEWARE_CLASSES=list(settings.MIDDLEWARE_CLASSES) +
        ['test_client_regress.tests.ProcessedMiddleware'])
    def test_request_middleware(self):
        "Check that the request middleware is executed on login request"

        def listener(sender, signal, **kwargs):
            request = kwargs['request']
            self.assertTrue(hasattr(request, 'has_been_processed'))

        # Unlike other Client request performing methods, login and logout don't
        # return the response, therefore we must use signals to get it
        user_logged_in.connect(listener)
        try:
            self.client.login(username='testclient', password='password')
        finally:
            user_logged_in.disconnect(listener)


@override_settings(
    PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
@@ -1260,12 +1284,32 @@ class UploadedFileEncodingTest(TestCase):


class RequestHeadersTest(TestCase):
    fixtures = ['testdata']

    def test_client_headers(self):
        "A test client can receive custom headers"
        response = self.client.get("/test_client_regress/check_headers/", HTTP_X_ARG_CHECK='Testing 123')
        self.assertEqual(response.content, b"HTTP_X_ARG_CHECK: Testing 123")
        self.assertEqual(response.status_code, 200)

    @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
    def test_client_login_headers(self):
        "Test client headers are used in login"

        client = Client(HTTP_HOST='different')

        def listener(sender, signal, **kwargs):
            request = kwargs['request']
            self.assertEqual(request.get_host(), 'different')

        # Unlike other Client request performing methods, login and logout don't
        # return the response, therefore we must use signals to get it
        user_logged_in.connect(listener)
        try:
            client.login(username='testclient', password='password')
        finally:
            user_logged_in.disconnect(listener)

    def test_client_headers_redirect(self):
        "Test client headers are preserved through redirects"
        response = self.client.get("/test_client_regress/check_headers_redirect/", follow=True, HTTP_X_ARG_CHECK='Testing 123')