Code

Ticket #8138: 8138alternate.diff

File 8138alternate.diff, 19.3 KB (added by kmtracey, 5 years ago)
Line 
1Index: django/test/client.py
2===================================================================
3--- django/test/client.py       (revision 9646)
4+++ django/test/client.py       (working copy)
5@@ -19,6 +19,7 @@
6 from django.utils.encoding import smart_str
7 from django.utils.http import urlencode
8 from django.utils.itercompat import is_iterable
9+from django.db import transaction, close_connection
10 
11 BOUNDARY = 'BoUnDaRyStRiNg'
12 MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
13@@ -69,7 +70,9 @@
14                 response = middleware_method(request, response)
15             response = self.apply_response_fixes(request, response)
16         finally:
17+            signals.request_finished.disconnect(close_connection)           
18             signals.request_finished.send(sender=self.__class__)
19+            signals.request_finished.connect(close_connection)
20 
21         return response
22 
23Index: django/test/testcases.py
24===================================================================
25--- django/test/testcases.py    (revision 9646)
26+++ django/test/testcases.py    (working copy)
27@@ -26,7 +26,32 @@
28         value = [value]
29     return value
30 
31+real_commit = transaction.commit
32+real_rollback = transaction.rollback
33+real_enter_transaction_management = transaction.enter_transaction_management
34+real_leave_transaction_management = transaction.leave_transaction_management
35+real_savepoint_commit = transaction.savepoint_commit
36+real_savepoint_rollback = transaction.savepoint_rollback
37 
38+def nop(x=None):
39+    return
40+
41+def disable_transaction_methods():
42+    transaction.commit = nop
43+    transaction.rollback = nop
44+    transaction.savepoint_commit = nop
45+    transaction.savepoint_rollback = nop
46+    transaction.enter_transaction_management = nop
47+    transaction.leave_transaction_management = nop       
48+
49+def restore_transaction_methods():
50+    transaction.commit = real_commit
51+    transaction.rollback = real_rollback
52+    transaction.savepoint_commit = real_savepoint_commit
53+    transaction.savepoint_rollback = real_savepoint_rollback
54+    transaction.enter_transaction_management = real_enter_transaction_management
55+    transaction.leave_transaction_management = real_leave_transaction_management
56+
57 class OutputChecker(doctest.OutputChecker):
58     def check_output(self, want, got, optionflags):
59         "The entry method for doctest output checking. Defers to a sequence of child checkers"
60@@ -168,8 +193,19 @@
61         # Rollback, in case of database errors. Otherwise they'd have
62         # side effects on other tests.
63         transaction.rollback_unless_managed()
64+       
65+    def run(self, test, compileflags=None, out=None, clear_globs=True):
66+        """
67+        Wraps the parent run() and encloses it in a transaction.
68+        """
69+        transaction.enter_transaction_management()
70+        transaction.managed(True)
71+        result = doctest.DocTestRunner.run(self, test, compileflags, out, clear_globs)
72+        transaction.rollback()
73+        transaction.leave_transaction_management()
74+        return result
75 
76-class TestCase(unittest.TestCase):
77+class TransactionTestCase(unittest.TestCase):
78     def _pre_setup(self):
79         """Performs any pre-test setup. This includes:
80 
81@@ -180,16 +216,22 @@
82               ROOT_URLCONF with it.
83             * Clearing the mail test outbox.
84         """
85+        self._fixture_setup()
86+        self._urlconf_setup()
87+        mail.outbox = []
88+
89+    def _fixture_setup(self):
90         call_command('flush', verbosity=0, interactive=False)
91         if hasattr(self, 'fixtures'):
92             # We have to use this slightly awkward syntax due to the fact
93             # that we're using *args and **kwargs together.
94             call_command('loaddata', *self.fixtures, **{'verbosity': 0})
95+
96+    def _urlconf_setup(self):
97         if hasattr(self, 'urls'):
98             self._old_root_urlconf = settings.ROOT_URLCONF
99             settings.ROOT_URLCONF = self.urls
100             clear_url_caches()
101-        mail.outbox = []
102 
103     def __call__(self, result=None):
104         """
105@@ -206,7 +248,7 @@
106             import sys
107             result.addError(self, sys.exc_info())
108             return
109-        super(TestCase, self).__call__(result)
110+        super(TransactionTestCase, self).__call__(result)       
111         try:
112             self._post_teardown()
113         except (KeyboardInterrupt, SystemExit):
114@@ -221,6 +263,13 @@
115 
116             * Putting back the original ROOT_URLCONF if it was changed.
117         """
118+        self._fixture_teardown()
119+        self._urlconf_teardown()
120+
121+    def _fixture_teardown(self):
122+        pass
123+
124+    def _urlconf_teardown(self):       
125         if hasattr(self, '_old_root_urlconf'):
126             settings.ROOT_URLCONF = self._old_root_urlconf
127             clear_url_caches()
128@@ -354,3 +403,36 @@
129         self.failIf(template_name in template_names,
130             (u"Template '%s' was used unexpectedly in rendering the"
131              u" response") % template_name)
132+
133+class TestCase(TransactionTestCase):
134+    """
135+    Does basically the same as TransactionTestCase, but surrounds every test
136+    with a transaction, monkey-patches the real transaction management routines to
137+    do nothing, and rollsback the test transaction at the end of the test. You have
138+    to use TransactionTestCase, if you need transaction management inside a test.
139+    """
140+
141+    def _fixture_setup(self):
142+        if not settings.DATABASE_SUPPORTS_TRANSACTIONS:
143+            return super(TestCase, self)._fixture_setup()
144+       
145+        transaction.enter_transaction_management()
146+        transaction.managed(True)
147+        disable_transaction_methods()
148+
149+        from django.contrib.sites.models import Site
150+        Site.objects.clear_cache()
151+
152+        if hasattr(self, 'fixtures'):
153+            call_command('loaddata', *self.fixtures, **{
154+                                                        'verbosity': 0,
155+                                                        'commit': False
156+                                                        })
157+
158+    def _fixture_teardown(self):
159+        if not settings.DATABASE_SUPPORTS_TRANSACTIONS:
160+            return super(TestCase, self)._fixture_teardown()
161+               
162+        restore_transaction_methods()
163+        transaction.rollback()
164+        transaction.leave_transaction_management()
165Index: django/test/__init__.py
166===================================================================
167--- django/test/__init__.py     (revision 9646)
168+++ django/test/__init__.py     (working copy)
169@@ -3,4 +3,4 @@
170 """
171 
172 from django.test.client import Client
173-from django.test.testcases import TestCase
174+from django.test.testcases import TestCase, TransactionTestCase
175Index: django/db/backends/creation.py
176===================================================================
177--- django/db/backends/creation.py      (revision 9646)
178+++ django/db/backends/creation.py      (working copy)
179@@ -311,7 +311,8 @@
180 
181         self.connection.close()
182         settings.DATABASE_NAME = test_database_name
183-
184+        settings.DATABASE_SUPPORTS_TRANSACTIONS = self._rollback_works()
185+       
186         call_command('syncdb', verbosity=verbosity, interactive=False)
187 
188         if settings.CACHE_BACKEND.startswith('db://'):
189@@ -362,7 +363,19 @@
190                 sys.exit(1)
191 
192         return test_database_name
193-
194+   
195+    def _rollback_works(self):
196+        cursor = self.connection.cursor()
197+        cursor.execute('CREATE TABLE ROLLBACK_TEST (X INT)')
198+        self.connection._commit()
199+        cursor.execute('INSERT INTO ROLLBACK_TEST (X) VALUES (8)')
200+        self.connection._rollback()
201+        cursor.execute('SELECT COUNT(X) FROM ROLLBACK_TEST')
202+        count, = cursor.fetchone()
203+        cursor.execute('DROP TABLE ROLLBACK_TEST')
204+        self.connection._commit()
205+        return count == 0
206+       
207     def destroy_test_db(self, old_database_name, verbosity=1):
208         """
209         Destroy a test database, prompting the user for confirmation if the
210Index: tests/regressiontests/generic_inline_admin/tests.py
211===================================================================
212--- tests/regressiontests/generic_inline_admin/tests.py (revision 9646)
213+++ tests/regressiontests/generic_inline_admin/tests.py (working copy)
214@@ -21,8 +21,10 @@
215         # relies on content type IDs, which will vary depending on what
216         # other tests have been run), thus we do it here.
217         e = Episode.objects.create(name='This Week in Django')
218+        self.episode_pk = e.pk
219         m = Media(content_object=e, url='http://example.com/podcast.mp3')
220         m.save()
221+        self.media_pk = m.pk
222     
223     def tearDown(self):
224         self.client.logout()
225@@ -39,7 +41,7 @@
226         """
227         A smoke test to ensure GET on the change_view works.
228         """
229-        response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/1/')
230+        response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk)
231         self.failUnlessEqual(response.status_code, 200)
232     
233     def testBasicAddPost(self):
234@@ -64,10 +66,11 @@
235             # inline data
236             "generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"2",
237             "generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"1",
238-            "generic_inline_admin-media-content_type-object_id-0-id": u"1",
239+            "generic_inline_admin-media-content_type-object_id-0-id": u"%d" % self.media_pk,
240             "generic_inline_admin-media-content_type-object_id-0-url": u"http://example.com/podcast.mp3",
241             "generic_inline_admin-media-content_type-object_id-1-id": u"",
242             "generic_inline_admin-media-content_type-object_id-1-url": u"",
243         }
244-        response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/episode/1/', post_data)
245+        url = '/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk
246+        response = self.client.post(url, post_data)
247         self.failUnlessEqual(response.status_code, 302) # redirect somewhere
248Index: tests/regressiontests/comment_tests/tests/moderation_view_tests.py
249===================================================================
250--- tests/regressiontests/comment_tests/tests/moderation_view_tests.py  (revision 9646)
251+++ tests/regressiontests/comment_tests/tests/moderation_view_tests.py  (working copy)
252@@ -8,39 +8,43 @@
253 
254     def testFlagGet(self):
255         """GET the flag view: render a confirmation page."""
256-        self.createSomeComments()
257+        comments = self.createSomeComments()
258+        pk = comments[0].pk
259         self.client.login(username="normaluser", password="normaluser")
260-        response = self.client.get("/flag/1/")
261+        response = self.client.get("/flag/%d/" % pk)
262         self.assertTemplateUsed(response, "comments/flag.html")
263 
264     def testFlagPost(self):
265         """POST the flag view: actually flag the view (nice for XHR)"""
266-        self.createSomeComments()
267+        comments = self.createSomeComments()
268+        pk = comments[0].pk
269         self.client.login(username="normaluser", password="normaluser")
270-        response = self.client.post("/flag/1/")
271-        self.assertEqual(response["Location"], "http://testserver/flagged/?c=1")
272-        c = Comment.objects.get(pk=1)
273+        response = self.client.post("/flag/%d/" % pk)
274+        self.assertEqual(response["Location"], "http://testserver/flagged/?c=%d" % pk)
275+        c = Comment.objects.get(pk=pk)
276         self.assertEqual(c.flags.filter(flag=CommentFlag.SUGGEST_REMOVAL).count(), 1)
277         return c
278 
279     def testFlagPostTwice(self):
280         """Users don't get to flag comments more than once."""
281         c = self.testFlagPost()
282-        self.client.post("/flag/1/")
283-        self.client.post("/flag/1/")
284+        self.client.post("/flag/%d/" % c.pk)
285+        self.client.post("/flag/%d/" % c.pk)
286         self.assertEqual(c.flags.filter(flag=CommentFlag.SUGGEST_REMOVAL).count(), 1)
287 
288     def testFlagAnon(self):
289         """GET/POST the flag view while not logged in: redirect to log in."""
290-        self.createSomeComments()
291-        response = self.client.get("/flag/1/")
292-        self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/flag/1/")
293-        response = self.client.post("/flag/1/")
294-        self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/flag/1/")
295+        comments = self.createSomeComments()
296+        pk = comments[0].pk       
297+        response = self.client.get("/flag/%d/" % pk)
298+        self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/flag/%d/" % pk)
299+        response = self.client.post("/flag/%d/" % pk)
300+        self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/flag/%d/" % pk)
301 
302     def testFlaggedView(self):
303-        self.createSomeComments()
304-        response = self.client.get("/flagged/", data={"c":1})
305+        comments = self.createSomeComments()
306+        pk = comments[0].pk       
307+        response = self.client.get("/flagged/", data={"c":pk})
308         self.assertTemplateUsed(response, "comments/flagged.html")
309 
310     def testFlagSignals(self):
311@@ -70,23 +74,25 @@
312 
313     def testDeletePermissions(self):
314         """The delete view should only be accessible to 'moderators'"""
315-        self.createSomeComments()
316+        comments = self.createSomeComments()
317+        pk = comments[0].pk       
318         self.client.login(username="normaluser", password="normaluser")
319-        response = self.client.get("/delete/1/")
320-        self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/delete/1/")
321+        response = self.client.get("/delete/%d/" % pk)
322+        self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/delete/%d/" % pk)
323 
324         makeModerator("normaluser")
325-        response = self.client.get("/delete/1/")
326+        response = self.client.get("/delete/%d/" % pk)
327         self.assertEqual(response.status_code, 200)
328 
329     def testDeletePost(self):
330         """POSTing the delete view should mark the comment as removed"""
331-        self.createSomeComments()
332+        comments = self.createSomeComments()
333+        pk = comments[0].pk
334         makeModerator("normaluser")
335         self.client.login(username="normaluser", password="normaluser")
336-        response = self.client.post("/delete/1/")
337-        self.assertEqual(response["Location"], "http://testserver/deleted/?c=1")
338-        c = Comment.objects.get(pk=1)
339+        response = self.client.post("/delete/%d/" % pk)
340+        self.assertEqual(response["Location"], "http://testserver/deleted/?c=%d" % pk)
341+        c = Comment.objects.get(pk=pk)
342         self.failUnless(c.is_removed)
343         self.assertEqual(c.flags.filter(flag=CommentFlag.MODERATOR_DELETION, user__username="normaluser").count(), 1)
344 
345@@ -103,21 +109,23 @@
346         self.assertEqual(received_signals, [signals.comment_was_flagged])
347 
348     def testDeletedView(self):
349-        self.createSomeComments()
350-        response = self.client.get("/deleted/", data={"c":1})
351+        comments = self.createSomeComments()
352+        pk = comments[0].pk       
353+        response = self.client.get("/deleted/", data={"c":pk})
354         self.assertTemplateUsed(response, "comments/deleted.html")
355 
356 class ApproveViewTests(CommentTestCase):
357 
358     def testApprovePermissions(self):
359         """The delete view should only be accessible to 'moderators'"""
360-        self.createSomeComments()
361+        comments = self.createSomeComments()
362+        pk = comments[0].pk       
363         self.client.login(username="normaluser", password="normaluser")
364-        response = self.client.get("/approve/1/")
365-        self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/approve/1/")
366+        response = self.client.get("/approve/%d/" % pk)
367+        self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/approve/%d/" % pk)
368 
369         makeModerator("normaluser")
370-        response = self.client.get("/approve/1/")
371+        response = self.client.get("/approve/%d/" % pk)
372         self.assertEqual(response.status_code, 200)
373 
374     def testApprovePost(self):
375@@ -127,9 +135,9 @@
376 
377         makeModerator("normaluser")
378         self.client.login(username="normaluser", password="normaluser")
379-        response = self.client.post("/approve/1/")
380-        self.assertEqual(response["Location"], "http://testserver/approved/?c=1")
381-        c = Comment.objects.get(pk=1)
382+        response = self.client.post("/approve/%d/" % c1.pk)
383+        self.assertEqual(response["Location"], "http://testserver/approved/?c=%d" % c1.pk)
384+        c = Comment.objects.get(pk=c1.pk)
385         self.failUnless(c.is_public)
386         self.assertEqual(c.flags.filter(flag=CommentFlag.MODERATOR_APPROVAL, user__username="normaluser").count(), 1)
387 
388@@ -146,8 +154,9 @@
389         self.assertEqual(received_signals, [signals.comment_was_flagged])
390 
391     def testApprovedView(self):
392-        self.createSomeComments()
393-        response = self.client.get("/approved/", data={"c":1})
394+        comments = self.createSomeComments()
395+        pk = comments[0].pk       
396+        response = self.client.get("/approved/", data={"c":pk})
397         self.assertTemplateUsed(response, "comments/approved.html")
398 
399 
400Index: tests/regressiontests/comment_tests/tests/comment_view_tests.py
401===================================================================
402--- tests/regressiontests/comment_tests/tests/comment_view_tests.py     (revision 9646)
403+++ tests/regressiontests/comment_tests/tests/comment_view_tests.py     (working copy)
404@@ -1,3 +1,4 @@
405+import re
406 from django.conf import settings
407 from django.contrib.auth.models import User
408 from django.contrib.comments import signals
409@@ -5,6 +6,8 @@
410 from regressiontests.comment_tests.models import Article
411 from regressiontests.comment_tests.tests import CommentTestCase
412 
413+post_redirect_re = re.compile(r'^http://testserver/posted/\?c=(?P<pk>\d+$)')
414+
415 class CommentViewTests(CommentTestCase):
416 
417     def testPostCommentHTTPMethods(self):
418@@ -181,18 +184,26 @@
419         a = Article.objects.get(pk=1)
420         data = self.getValidData(a)
421         response = self.client.post("/post/", data)
422-        self.assertEqual(response["Location"], "http://testserver/posted/?c=1")
423-
424+        location = response["Location"]
425+        match = post_redirect_re.match(location)
426+        self.failUnless(match != None, "Unexpected redirect location: %s" % location)
427+       
428         data["next"] = "/somewhere/else/"
429         data["comment"] = "This is another comment"
430         response = self.client.post("/post/", data)
431-        self.assertEqual(response["Location"], "http://testserver/somewhere/else/?c=2")
432+        location = response["Location"]       
433+        match = re.search(r"^http://testserver/somewhere/else/\?c=\d+$", location)
434+        self.failUnless(match != None, "Unexpected redirect location: %s" % location)
435 
436     def testCommentDoneView(self):
437         a = Article.objects.get(pk=1)
438         data = self.getValidData(a)
439         response = self.client.post("/post/", data)
440-        response = self.client.get("/posted/", {'c':1})
441+        location = response["Location"]       
442+        match = post_redirect_re.match(location)
443+        self.failUnless(match != None, "Unexpected redirect location: %s" % location)
444+        pk = int(match.group('pk'))
445+        response = self.client.get(location)
446         self.assertTemplateUsed(response, "comments/posted.html")
447-        self.assertEqual(response.context[0]["comment"], Comment.objects.get(pk=1))
448+        self.assertEqual(response.context[0]["comment"], Comment.objects.get(pk=pk))
449