Ticket #4476: 4476_mixed.diff

File 4476_mixed.diff, 12.5 KB (added by Marc Fargas, 16 years ago)

New patch, comments below.

  • django/test/client.py

    diff --git a/django/test/client.py b/django/test/client.py
    index 96d0c89..d7230cd 100644
    a b  
    11import urllib
    2 from urlparse import urlparse, urlunparse
     2from urlparse import urlparse, urlunparse, urlsplit
    33import sys
    44import os
    55try:
    from django.contrib.auth import authenticate, login  
    1212from django.core.handlers.base import BaseHandler
    1313from django.core.handlers.wsgi import WSGIRequest
    1414from django.core.signals import got_request_exception
    15 from django.http import SimpleCookie, HttpRequest
     15from django.http import SimpleCookie, HttpRequest, QueryDict
    1616from django.template import TemplateDoesNotExist
    1717from django.test import signals
    1818from django.utils.functional import curry
    class Client(object):  
    261261
    262262        return response
    263263
    264     def get(self, path, data={}, **extra):
     264    def get(self, path, data={}, follow=False, **extra):
    265265        """
    266266        Requests a response from the server using GET.
    267267        """
    class Client(object):  
    275275        }
    276276        r.update(extra)
    277277
    278         return self.request(**r)
     278        response = self.request(**r)
     279        if follow:
     280            response = self._handle_redirects(response)
     281        return response
    279282
    280     def post(self, path, data={}, content_type=MULTIPART_CONTENT, **extra):
     283    def post(self, path, data={}, content_type=MULTIPART_CONTENT,
     284        follow=False, **extra):
    281285        """
    282286        Requests a response from the server using POST.
    283287        """
    class Client(object):  
    297301        }
    298302        r.update(extra)
    299303
    300         return self.request(**r)
     304        response = self.request(**r)
     305        if follow:
     306            response = self._handle_redirects(response)
     307        return response
    301308
    302309    def head(self, path, data={}, **extra):
    303310        """
    class Client(object):  
    352359
    353360        return self.request(**r)
    354361
    355     def delete(self, path, data={}, **extra):
     362    def delete(self, path, data={}, follow=True, **extra):
    356363        """
    357364        Send a DELETE request to the server.
    358365        """
    class Client(object):  
    365372        }
    366373        r.update(extra)
    367374
    368         return self.request(**r)
     375        response = self.request(**r)
     376        if follow:
     377            response = self._handle_redirects(response)
     378        return response
    369379
    370380    def login(self, **credentials):
    371381        """
    class Client(object):  
    416426        session = __import__(settings.SESSION_ENGINE, {}, {}, ['']).SessionStore()
    417427        session.delete(session_key=self.cookies[settings.SESSION_COOKIE_NAME].value)
    418428        self.cookies = SimpleCookie()
     429
     430    def _handle_redirects(self, response):
     431        "Follows any redirects by requesting responses from the server using GET."
     432
     433        response.redirect_chain = []
     434        while response.status_code in (301, 302, 303, 307):
     435            url = response['Location']
     436            scheme, netloc, path, query, fragment = urlsplit(url)
     437
     438            redirect_chain = response.redirect_chain
     439            redirect_chain.append((url, response.status_code))
     440
     441            # The test client doesn't handle external links,
     442            # but since the situation is simulated in test_client,
     443            # we fake things here by ignoring the netloc portion of the
     444            # redirected URL.
     445            response = self.get(path, QueryDict(query), follow=False)
     446            response.redirect_chain = redirect_chain
     447
     448            # Prevent loops
     449            if response.redirect_chain[-1] in response.redirect_chain[0:-1]:
     450                break
     451        return response
     452
  • django/test/testcases.py

    diff --git a/django/test/testcases.py b/django/test/testcases.py
    index eed252b..6c4f5bb 100644
    a b class TransactionTestCase(unittest.TestCase):  
    269269            clear_url_caches()
    270270
    271271    def assertRedirects(self, response, expected_url, status_code=302,
    272                         target_status_code=200, host=None):
     272                        target_status_code=200, host=None, redirect_level=-1):
    273273        """Asserts that a response redirected to a specific URL, and that the
    274         redirect URL can be loaded.
     274        redirect URL could be loaded.
    275275
    276276        Note that assertRedirects won't work for external links since it uses
    277277        TestClient to do a request.
     278
     279        Note that assertRedirects only works properly if follow was set to True
     280        in the request.
    278281        """
    279         self.assertEqual(response.status_code, status_code,
     282
     283        redirect_url = response.get('Location', '')
     284        redirect_status_code = response.status_code
     285        if not hasattr(response, 'redirect_chain'):
     286            # follow wasn't set to True. Be backwards compatible...
     287            import warnings
     288            warnings.warn(
     289                "For using TestCase.assertRedirects() you have to call get() and/or post() with follow=True",
     290                PendingDeprecationWarning,
     291            )
     292            scheme, netloc, path, query, fragment = urlsplit(redirect_url)
     293            final_response = response.client.get(path, QueryDict(query))
     294            response = final_response
     295        else:
     296            chain = response.redirect_chain
     297            redirect_url = chain[redirect_level][0]
     298            redirect_status_code = chain[redirect_level][1]
     299        scheme, netloc, path, query, fragment = urlsplit(redirect_url)
     300
     301        self.assertEqual(redirect_status_code, status_code,
    280302            ("Response didn't redirect as expected: Response code was %d"
    281              " (expected %d)" % (response.status_code, status_code)))
    282         url = response['Location']
    283         scheme, netloc, path, query, fragment = urlsplit(url)
     303             " (expected %d)" % (redirect_status_code, status_code)))
     304
    284305        e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit(expected_url)
    285306        if not (e_scheme or e_netloc):
    286307            expected_url = urlunsplit(('http', host or 'testserver', e_path,
    287308                    e_query, e_fragment))
    288         self.assertEqual(url, expected_url,
    289             "Response redirected to '%s', expected '%s'" % (url, expected_url))
     309        self.assertEqual(redirect_url, expected_url,
     310            "Response redirected to '%s', expected '%s'" % (redirect_url, expected_url))
    290311
    291         # Get the redirection page, using the same client that was used
    292         # to obtain the original response.
    293         redirect_response = response.client.get(path, QueryDict(query))
    294         self.assertEqual(redirect_response.status_code, target_status_code,
     312        self.assertEqual(response.status_code, target_status_code,
    295313            ("Couldn't retrieve redirection page '%s': response code was %d"
    296314             " (expected %d)") %
    297                  (path, redirect_response.status_code, target_status_code))
     315                 (path, response.status_code, target_status_code))
    298316
    299317    def assertContains(self, response, text, count=None, status_code=200):
    300318        """
    class TestCase(TransactionTestCase):  
    430448        restore_transaction_methods()
    431449        transaction.rollback()
    432450        transaction.leave_transaction_management()
    433         connection.close()
    434  No newline at end of file
     451        connection.close()
  • docs/topics/testing.txt

    diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt
    index bd68f6b..e0b7878 100644
    a b arguments at time of construction:  
    478478    Once you have a ``Client`` instance, you can call any of the following
    479479    methods:
    480480
    481     .. method:: Client.get(path, data={})
     481    .. method:: Client.get(path, data={}, follow=False)
     482
     483       .. versionchanged:: 1.1
     484          The ``follow`` argument is new in this version.
    482485
    483486        Makes a GET request on the provided ``path`` and returns a ``Response``
    484487        object, which is documented below.
    arguments at time of construction:  
    505508        If you provide URL both an encoded GET data and a data argument,
    506509        the data argument will take precedence.
    507510
    508     .. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT)
     511        If you set ``follow`` to ``True`` the client will follow any redirects
     512        and a ``redirect_chain``attribute will be set in the response object
     513        containing tuples of the intermediate urls and status codes.
     514
     515        If you had an url ``/redirect_me/`` that redirected to ``/final/``, this is
     516        what you'd see::
     517
     518            >>> response = c.get('/redirect_me/')
     519            >>> response.redirect_chain
     520            [(u'/final/', 301)]
     521
     522
     523    .. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False)
     524
     525       .. versionchanged:: 1.1
     526          The ``follow`` argument is new in this version.
    509527
    510528        Makes a POST request on the provided ``path`` and returns a
    511529        ``Response`` object, which is documented below.
    arguments at time of construction:  
    568586        to retrieve the username and password, and could interrogate request.GET
    569587        to determine if the user was a visitor.
    570588
     589        If you set ``follow`` to ``True`` the client will follow any redirects
     590        and a ``redirect_chain``attribute will be set in the response object
     591        containing tuples of the intermediate urls and status codes.
     592
    571593    .. method:: Client.head(path, data={})
    572594
    573595        .. versionadded:: development
    applications:  
    10251047    Asserts that the template with the given name was *not* used in rendering
    10261048    the response.
    10271049
    1028 .. method:: assertRedirects(response, expected_url, status_code=302, target_status_code=200)
     1050.. method:: assertRedirects(response, expected_url, status_code=302, target_status_code=200, redirect_level=-1)
    10291051
    10301052    Asserts that the response return a ``status_code`` redirect status, it
    1031     redirected to ``expected_url`` (including any GET data), and the subsequent
     1053    redirected to ``expected_url`` (including any GET data), and the final
    10321054    page was received with ``target_status_code``.
    10331055
     1056    If you are expecting multiple levels of redirects, you can specify to
     1057    which ``redirect_level`` the ``expected_url`` and ``status_code`` are
     1058    matched against. A value of -2 checks against the latest redirect in
     1059    the chain.
     1060
    10341061E-mail services
    10351062---------------
    10361063
  • tests/regressiontests/test_client_regress/models.py

    diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
    index 7015f71..af50b43 100644
    a b class AssertRedirectsTests(TestCase):  
    148148        except AssertionError, e:
    149149            self.assertEquals(str(e), "Couldn't retrieve redirection page '/test_client/permanent_redirect_view/': response code was 301 (expected 200)")
    150150
     151    def test_redirect_chain(self):
     152        # First with GET
     153        response = self.client.get('/test_client_regress/redirects/', {}, True)
     154        self.assertRedirects(response,
     155            '/test_client_regress/redirects/further/', 301, 404, redirect_level=0)
     156        self.assertRedirects(response,
     157            '/test_client_regress/redirects/further/more/', 301, 404, redirect_level=1)
     158        self.assertRedirects(response,
     159            '/test_client_regress/redirects/further/more/end/', 301, 404, redirect_level=2)
     160
     161        # Now with POST
     162        response = self.client.post('/test_client_regress/redirects/',
     163            {'nothing': 'to_send'}, follow=True)
     164        self.assertRedirects(response,
     165            '/test_client_regress/redirects/further/more/end/', 301, 404)
     166
    151167class AssertFormErrorTests(TestCase):
    152168    def test_unknown_form(self):
    153169        "An assertion is raised if the form name is unknown"
  • tests/regressiontests/test_client_regress/urls.py

    diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py
    index e7db15a..4b38699 100644
    a b  
    11from django.conf.urls.defaults import *
     2from django.views.generic.simple import redirect_to
    23import views
    34
    45urlpatterns = patterns('',
    urlpatterns = patterns('',  
    89    (r'^request_data/$', views.request_data),
    910    url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'),
    1011    (r'^login_protected_redirect_view/$', views.login_protected_redirect_view),
     12    (r'^redirects/$', redirect_to, {'url': 'further/'}),
     13    (r'^redirects/further/$', redirect_to, {'url': 'more/'}),
     14    (r'^redirects/further/more/$', redirect_to, {'url': 'end/'}),
    1115    (r'^set_session/$', views.set_session_view),
    1216    (r'^check_session/$', views.check_session_view),
    1317    (r'^request_methods/$', views.request_methods_view),
Back to Top