Merge lp:~mwhudson/launchpad/test_traverse-set-participation-bug-611570 into lp:launchpad

Proposed by Michael Hudson-Doyle
Status: Merged
Approved by: Gary Poster
Approved revision: no longer in the source branch.
Merged at revision: 11300
Proposed branch: lp:~mwhudson/launchpad/test_traverse-set-participation-bug-611570
Merge into: lp:launchpad
Diff against target: 142 lines (+106/-2)
2 files modified
lib/lp/testing/publication.py (+14/-2)
lib/lp/testing/tests/test_publication.py (+92/-0)
To merge this branch: bzr merge lp:~mwhudson/launchpad/test_traverse-set-participation-bug-611570
Reviewer Review Type Date Requested Status
Gary Poster (community) Approve
Review via email: mp+31731@code.launchpad.net

Commit message

Add tests for the test_traverse helper and make it set up its own interaction properly

Description of the change

Hi,

This branch started out by making test_traverse set up a new interaction for the traversal it performs, so as to make the request that is being traversed 'current'. Then I wrote some more tests and discovered that, at least in some sense, the docstring's claim that it 'uses the current user' was inaccurate, so I fixed that too.

The key test helper I use is bonkers, but I think that's zope's fault (you can read the implementation of the browser:page in zope.browserpage.metaconfigure to see where I cribbed the insanity from).

Cheers,
mwh

To post a comment you must log in.
Revision history for this message
Gary Poster (gary) wrote :

Very nice, thank you.

FWIW, yes, browser:page was disparaged and eliminated from use at my last job (ZC), because of the dynamic class creation. (The newer approach is to subclass from zope.publisher.browser.BrowserPage, define your own __call__, and simply register the resulting class as an adapter.) That's what I/we didn't like about it, anyway.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/testing/publication.py'
--- lib/lp/testing/publication.py 2010-04-28 10:13:00 +0000
+++ lib/lp/testing/publication.py 2010-08-05 03:21:07 +0000
@@ -15,13 +15,17 @@
15# Z3 doesn't make this available as a utility.15# Z3 doesn't make this available as a utility.
16from zope.app import zapi16from zope.app import zapi
17from zope.app.publication.requestpublicationregistry import factoryRegistry17from zope.app.publication.requestpublicationregistry import factoryRegistry
18from zope.app.security.interfaces import IUnauthenticatedPrincipal
18from zope.component import getUtility19from zope.component import getUtility
19from zope.interface import providedBy20from zope.interface import providedBy
20from zope.publisher.interfaces.browser import IDefaultSkin21from zope.publisher.interfaces.browser import IDefaultSkin
22from zope.security.management import restoreInteraction
2123
22from canonical.launchpad.interfaces.launchpad import IOpenLaunchBag24from canonical.launchpad.interfaces.launchpad import IOpenLaunchBag
23import canonical.launchpad.layers as layers25import canonical.launchpad.layers as layers
24from canonical.launchpad.webapp import urlsplit26from canonical.launchpad.webapp import urlsplit
27from canonical.launchpad.webapp.interaction import (
28 get_current_principal, setupInteraction)
25from canonical.launchpad.webapp.servers import ProtocolErrorPublication29from canonical.launchpad.webapp.servers import ProtocolErrorPublication
2630
2731
@@ -103,8 +107,13 @@
103 if layer is not None:107 if layer is not None:
104 layers.setAdditionalLayer(request, layer)108 layers.setAdditionalLayer(request, layer)
105109
106 principal = publication.getPrincipal(request)110 principal = get_current_principal()
107 request.setPrincipal(principal)111
112 if IUnauthenticatedPrincipal.providedBy(principal):
113 login = None
114 else:
115 login = principal.person
116 setupInteraction(principal, login, request)
108117
109 getUtility(IOpenLaunchBag).clear()118 getUtility(IOpenLaunchBag).clear()
110 app = publication.getApplication(request)119 app = publication.getApplication(request)
@@ -112,4 +121,7 @@
112 # Since the last traversed object is the view, the second last should be121 # Since the last traversed object is the view, the second last should be
113 # the object that the view is on.122 # the object that the view is on.
114 obj = request.traversed_objects[-2]123 obj = request.traversed_objects[-2]
124
125 restoreInteraction()
126
115 return obj, view, request127 return obj, view, request
116128
=== added file 'lib/lp/testing/tests/test_publication.py'
--- lib/lp/testing/tests/test_publication.py 1970-01-01 00:00:00 +0000
+++ lib/lp/testing/tests/test_publication.py 2010-08-05 03:21:07 +0000
@@ -0,0 +1,92 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for the helpers in `lp.testing.publication`."""
5
6__metaclass__ = type
7
8from zope.app.pagetemplate.simpleviewclass import simple
9from zope.component import getSiteManager, getUtility
10from zope.interface import Interface
11from zope.publisher.interfaces.browser import IDefaultBrowserLayer
12from zope.security.checker import CheckerPublic, Checker, defineChecker
13
14from canonical.launchpad.interfaces.launchpad import ILaunchpadRoot
15from canonical.launchpad.webapp.interfaces import ILaunchBag
16from canonical.launchpad.webapp.publisher import get_current_browser_request
17from canonical.launchpad.webapp.servers import LaunchpadTestRequest
18from canonical.testing import DatabaseFunctionalLayer
19from lp.testing import ANONYMOUS, login, login_person, TestCaseWithFactory
20from lp.testing.publication import test_traverse
21
22
23class TestTestTraverse(TestCaseWithFactory):
24 # Tests for `test_traverse`
25
26 layer = DatabaseFunctionalLayer
27
28 def registerViewCallable(self, view_callable):
29 """Return a URL traversing to which will call `view_callable`.
30
31 :param view_callable: Will be called with no arguments during
32 traversal.
33 """
34 # This method is completely out of control. Thanks, Zope.
35 name = '+' + self.factory.getUniqueString()
36 class new_class(simple):
37 def __init__(self, context, request):
38 view_callable()
39 required = {}
40 for n in ('browserDefault', '__call__', 'publishTraverse'):
41 required[n] = CheckerPublic
42 defineChecker(new_class, Checker(required))
43 getSiteManager().registerAdapter(
44 new_class, (ILaunchpadRoot, IDefaultBrowserLayer), Interface,
45 name)
46 self.addCleanup(
47 getSiteManager().unregisterAdapter, new_class,
48 (ILaunchpadRoot, IDefaultBrowserLayer), Interface, name)
49 return 'https://launchpad.dev/' + name
50
51 def test_traverse_simple(self):
52 # test_traverse called with a product URL returns the product
53 # as the traversed object.
54 login(ANONYMOUS)
55 product = self.factory.makeProduct()
56 context, view, request = test_traverse(
57 'https://launchpad.dev/' + product.name)
58 self.assertEqual(product, context)
59
60 def test_request_is_current_during_traversal(self):
61 # The request that test_traverse creates is current during
62 # traversal in the sense of get_current_browser_request.
63 login(ANONYMOUS)
64 requests = []
65 def record_current_request():
66 requests.append(get_current_browser_request())
67 context, view, request = test_traverse(
68 self.registerViewCallable(record_current_request))
69 self.assertEqual(1, len(requests))
70 self.assertIs(request, requests[0])
71
72 def test_participation_restored(self):
73 # test_traverse restores the interaction (and hence
74 # participation) that was present before it was called.
75 request = LaunchpadTestRequest()
76 login(ANONYMOUS, request)
77 product = self.factory.makeProduct()
78 test_traverse('https://launchpad.dev/' + product.name)
79 self.assertIs(request, get_current_browser_request())
80
81 def test_uses_current_user(self):
82 # test_traverse performs the traversal as the currently logged
83 # in user.
84 person = self.factory.makePerson()
85 login_person(person)
86 users = []
87 def record_user():
88 users.append(getUtility(ILaunchBag).user)
89 context, view, request = test_traverse(
90 self.registerViewCallable(record_user))
91 self.assertEqual(1, len(users))
92 self.assertEqual(person, users[0])