Merge ~cjwatson/launchpad:remove-most-wsgi-intercept into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 8c7f5d03481e282c3b66ae62b9b2d1ed66a738d4
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:remove-most-wsgi-intercept
Merge into: launchpad:master
Prerequisite: ~cjwatson/launchpad:simplify-appserverlayer-browser
Diff against target: 450 lines (+47/-113)
8 files modified
lib/launchpad_loggerhead/tests.py (+14/-45)
lib/lp/registry/stories/webservice/xx-distribution-mirror.txt (+6/-7)
lib/lp/services/webapp/tests/test_error.py (+10/-9)
lib/lp/services/webservice/stories/xx-service.txt (+8/-8)
lib/lp/soyuz/stories/webservice/xx-archive.txt (+8/-8)
lib/lp/soyuz/stories/webservice/xx-person-createppa.txt (+0/-7)
lib/lp/testing/fixture.py (+0/-25)
lib/lp/testing/layers.py (+1/-4)
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+374881@code.launchpad.net

Commit message

Avoid most uses of wsgi_intercept

zope.testbrowser 4.0.0 uses WebTest instead of wsgi_intercept, so we
also want to stop using wsgi_intercept for our own purposes. We can
point the WSGI test browser directly at an appropriate WSGI application
instead.

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/launchpad_loggerhead/tests.py b/lib/launchpad_loggerhead/tests.py
index f332518..31cb5a7 100644
--- a/lib/launchpad_loggerhead/tests.py
+++ b/lib/launchpad_loggerhead/tests.py
@@ -1,7 +1,6 @@
1# Copyright 2010-2018 Canonical Ltd. This software is licensed under the1# Copyright 2010-2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4import lazr.uri
5from paste.httpexceptions import HTTPExceptionHandler4from paste.httpexceptions import HTTPExceptionHandler
6import requests5import requests
7from six.moves.urllib_parse import (6from six.moves.urllib_parse import (
@@ -11,12 +10,7 @@ from six.moves.urllib_parse import (
11import soupmatchers10import soupmatchers
12from testtools.content import Content11from testtools.content import Content
13from testtools.content_type import UTF8_TEXT12from testtools.content_type import UTF8_TEXT
14import wsgi_intercept13from zope.testbrowser.wsgi import Browser
15from wsgi_intercept.urllib2_intercept import (
16 install_opener,
17 uninstall_opener,
18 )
19import wsgi_intercept.zope_testbrowser
20from zope.security.proxy import removeSecurityProxy14from zope.security.proxy import removeSecurityProxy
2115
22from launchpad_loggerhead.app import RootApp16from launchpad_loggerhead.app import RootApp
@@ -50,12 +44,6 @@ def session_scribbler(app, test):
50 return scribble44 return scribble
5145
5246
53def dummy_destination(environ, start_response):
54 """Return a fake response."""
55 start_response('200 OK', [('Content-type', 'text/plain')])
56 return ['This is a dummy destination.\n']
57
58
59class SimpleLogInRootApp(RootApp):47class SimpleLogInRootApp(RootApp):
60 """A mock root app that doesn't require open id."""48 """A mock root app that doesn't require open id."""
61 def _complete_login(self, environ, start_response):49 def _complete_login(self, environ, start_response):
@@ -63,53 +51,35 @@ class SimpleLogInRootApp(RootApp):
63 start_response('200 OK', [('Content-type', 'text/plain')])51 start_response('200 OK', [('Content-type', 'text/plain')])
64 return ['\n']52 return ['\n']
6553
54 def __call__(self, environ, start_response):
55 codebrowse_netloc = urlsplit(
56 config.codehosting.secure_codebrowse_root).netloc
57 if environ['HTTP_HOST'] == codebrowse_netloc:
58 return RootApp.__call__(self, environ, start_response)
59 else:
60 # Return a fake response.
61 start_response('200 OK', [('Content-type', 'text/plain')])
62 return ['This is a dummy destination.\n']
63
6664
67class TestLogout(TestCase):65class TestLogout(TestCase):
68 layer = DatabaseFunctionalLayer66 layer = DatabaseFunctionalLayer
6967
70 def intercept(self, uri, app):
71 """Install wsgi interceptors for the uri, app tuple."""
72 if isinstance(uri, basestring):
73 uri = lazr.uri.URI(uri)
74 port = uri.port
75 if port is None:
76 if uri.scheme == 'http':
77 port = 80
78 elif uri.scheme == 'https':
79 port = 443
80 else:
81 raise NotImplementedError(uri.scheme)
82 else:
83 port = int(port)
84 wsgi_intercept.add_wsgi_intercept(uri.host, port, lambda: app)
85 self.intercepted.append((uri.host, port))
86
87 def setUp(self):68 def setUp(self):
88 TestCase.setUp(self)69 TestCase.setUp(self)
89 self.intercepted = []
90 self.session = None70 self.session = None
91 self.root = app = SimpleLogInRootApp(SESSION_VAR)71 app = SimpleLogInRootApp(SESSION_VAR)
92 app = session_scribbler(app, self)72 app = session_scribbler(app, self)
93 app = HTTPExceptionHandler(app)73 app = HTTPExceptionHandler(app)
94 app = SessionHandler(app, SESSION_VAR, SECRET)74 app = SessionHandler(app, SESSION_VAR, SECRET)
95 self.cookie_name = app.cookie_handler.cookie_name75 self.cookie_name = app.cookie_handler.cookie_name
96 self.intercept(config.codehosting.secure_codebrowse_root, app)76 self.browser = Browser(wsgi_app=app)
97 self.intercept(allvhosts.configs['mainsite'].rooturl,
98 dummy_destination)
99 install_opener()
100 self.browser = wsgi_intercept.zope_testbrowser.WSGI_Browser()
101 # We want to pretend we are not a robot, or else mechanize will honor77 # We want to pretend we are not a robot, or else mechanize will honor
102 # robots.txt.78 # robots.txt.
103 self.browser.mech_browser.set_handle_robots(False)79 self.browser.mech_browser.set_handle_robots(False)
104 self.browser.open(80 self.browser.open(
105 config.codehosting.secure_codebrowse_root + '+login')81 config.codehosting.secure_codebrowse_root + '+login')
10682
107 def tearDown(self):
108 uninstall_opener()
109 for host, port in self.intercepted:
110 wsgi_intercept.remove_wsgi_intercept(host, port)
111 TestCase.tearDown(self)
112
113 def testLoggerheadLogout(self):83 def testLoggerheadLogout(self):
114 # We start logged in as 'bob'.84 # We start logged in as 'bob'.
115 self.assertEqual(self.session['user'], 'bob')85 self.assertEqual(self.session['user'], 'bob')
@@ -143,8 +113,7 @@ class TestLogout(TestCase):
143 # TestLoginAndLogout.test_CookieLogoutPage).113 # TestLoginAndLogout.test_CookieLogoutPage).
144114
145 # Here, we will have a more useless example of the basic machinery.115 # Here, we will have a more useless example of the basic machinery.
146 dummy_root = 'http://dummy.dev/'116 dummy_root = 'http://dummy.test/'
147 self.intercept(dummy_root, dummy_destination)
148 self.browser.open(117 self.browser.open(
149 config.codehosting.secure_codebrowse_root +118 config.codehosting.secure_codebrowse_root +
150 '+logout?' + urlencode(dict(next_to=dummy_root + '+logout')))119 '+logout?' + urlencode(dict(next_to=dummy_root + '+logout')))
diff --git a/lib/lp/registry/stories/webservice/xx-distribution-mirror.txt b/lib/lp/registry/stories/webservice/xx-distribution-mirror.txt
index edcef0e..9e4c932 100644
--- a/lib/lp/registry/stories/webservice/xx-distribution-mirror.txt
+++ b/lib/lp/registry/stories/webservice/xx-distribution-mirror.txt
@@ -165,15 +165,15 @@ Now trying to set the owner using Sample Person's webservice is not authorized.
165 ... }165 ... }
166 >>> response = test_webservice.patch(166 >>> response = test_webservice.patch(
167 ... canonical_archive['self_link'], 'application/json', dumps(patch))167 ... canonical_archive['self_link'], 'application/json', dumps(patch))
168 >>> print response.getheader('status')168 >>> response.status
169 401 Unauthorized169 401
170170
171But if we use Karl, the mirror listing admin's, webservice, we can update the owner.171But if we use Karl, the mirror listing admin's, webservice, we can update the owner.
172172
173 >>> response = karl_webservice.patch(173 >>> response = karl_webservice.patch(
174 ... canonical_archive['self_link'], 'application/json', dumps(patch))174 ... canonical_archive['self_link'], 'application/json', dumps(patch))
175 >>> print response.getheader('status')175 >>> response.status
176 209 Content Returned176 209
177177
178 >>> patched_canonical_archive = response.jsonBody()178 >>> patched_canonical_archive = response.jsonBody()
179 >>> print patched_canonical_archive['owner_link']179 >>> print patched_canonical_archive['owner_link']
@@ -193,7 +193,6 @@ Some attributes are read-only via the API:
193 ... canonical_releases['self_link'], 'application/json', dumps(patch))193 ... canonical_releases['self_link'], 'application/json', dumps(patch))
194 >>> print response194 >>> print response
195 HTTP/1.1 400 Bad Request195 HTTP/1.1 400 Bad Request
196 Status: 400 Bad Request
197 ...196 ...
198 enabled: You tried to modify a read-only attribute.197 enabled: You tried to modify a read-only attribute.
199 date_reviewed: You tried to modify a read-only attribute.198 date_reviewed: You tried to modify a read-only attribute.
@@ -211,8 +210,8 @@ While others can be set with the appropriate authorization:
211 ... }210 ... }
212 >>> response = test_webservice.patch(211 >>> response = test_webservice.patch(
213 ... canonical_releases['self_link'], 'application/json', dumps(patch))212 ... canonical_releases['self_link'], 'application/json', dumps(patch))
214 >>> print response.getheader('status')213 >>> response.status
215 401 Unauthorized214 401
216215
217 >>> response = karl_webservice.patch(216 >>> response = karl_webservice.patch(
218 ... canonical_releases['self_link'], 'application/json', dumps(patch)).jsonBody()217 ... canonical_releases['self_link'], 'application/json', dumps(patch)).jsonBody()
diff --git a/lib/lp/services/webapp/tests/test_error.py b/lib/lp/services/webapp/tests/test_error.py
index 7ad4e83..3170ae3 100644
--- a/lib/lp/services/webapp/tests/test_error.py
+++ b/lib/lp/services/webapp/tests/test_error.py
@@ -12,6 +12,7 @@ import urllib2
1212
13from fixtures import FakeLogger13from fixtures import FakeLogger
14import psycopg214import psycopg2
15from six.moves.urllib.error import HTTPError
15from storm.exceptions import (16from storm.exceptions import (
16 DisconnectionError,17 DisconnectionError,
17 OperationalError,18 OperationalError,
@@ -26,6 +27,7 @@ from testtools.matchers import (
26import transaction27import transaction
27from zope.interface import Interface28from zope.interface import Interface
28from zope.publisher.interfaces.browser import IDefaultBrowserLayer29from zope.publisher.interfaces.browser import IDefaultBrowserLayer
30from zope.testbrowser.wsgi import Browser
2931
30from lp.services.webapp.error import (32from lp.services.webapp.error import (
31 DisconnectionErrorView,33 DisconnectionErrorView,
@@ -37,12 +39,12 @@ from lp.testing import TestCase
37from lp.testing.fixture import (39from lp.testing.fixture import (
38 CaptureOops,40 CaptureOops,
39 PGBouncerFixture,41 PGBouncerFixture,
40 Urllib2Fixture,
41 ZopeAdapterFixture,42 ZopeAdapterFixture,
42 )43 )
43from lp.testing.layers import (44from lp.testing.layers import (
44 DatabaseLayer,45 DatabaseLayer,
45 LaunchpadFunctionalLayer,46 LaunchpadFunctionalLayer,
47 wsgi_application,
46 )48 )
47from lp.testing.matchers import Contains49from lp.testing.matchers import Contains
4850
@@ -78,8 +80,8 @@ class TestDatabaseErrorViews(TestCase):
7880
79 def getHTTPError(self, url):81 def getHTTPError(self, url):
80 try:82 try:
81 urllib2.urlopen(url)83 Browser(wsgi_app=wsgi_application).open(url)
82 except urllib2.HTTPError as error:84 except HTTPError as error:
83 return error85 return error
84 else:86 else:
85 self.fail("We should have gotten an HTTP error")87 self.fail("We should have gotten an HTTP error")
@@ -103,13 +105,14 @@ class TestDatabaseErrorViews(TestCase):
103 def retryConnection(self, url, bouncer, retries=60):105 def retryConnection(self, url, bouncer, retries=60):
104 """Retry to connect to *url* for *retries* times.106 """Retry to connect to *url* for *retries* times.
105107
106 Return the file-like object returned by *urllib2.urlopen(url)*.108 Raise a TimeoutException if the connection cannot be established.
107 Raise a TimeoutException if the connection can not be established.
108 """109 """
110 browser = Browser(wsgi_app=wsgi_application)
109 for i in range(retries):111 for i in range(retries):
110 try:112 try:
111 return urllib2.urlopen(url)113 browser.open(url)
112 except urllib2.HTTPError as e:114 return
115 except HTTPError as e:
113 if e.code != httplib.SERVICE_UNAVAILABLE:116 if e.code != httplib.SERVICE_UNAVAILABLE:
114 raise117 raise
115 time.sleep(1)118 time.sleep(1)
@@ -122,7 +125,6 @@ class TestDatabaseErrorViews(TestCase):
122 def test_disconnectionerror_view_integration(self):125 def test_disconnectionerror_view_integration(self):
123 # Test setup.126 # Test setup.
124 self.useFixture(FakeLogger('SiteError', level=logging.CRITICAL))127 self.useFixture(FakeLogger('SiteError', level=logging.CRITICAL))
125 self.useFixture(Urllib2Fixture())
126 bouncer = PGBouncerFixture()128 bouncer = PGBouncerFixture()
127 # XXX gary bug=974617, bug=1011847, bug=504291 2011-07-03:129 # XXX gary bug=974617, bug=1011847, bug=504291 2011-07-03:
128 # In parallel tests, we are rarely encountering instances of130 # In parallel tests, we are rarely encountering instances of
@@ -230,7 +232,6 @@ class TestDatabaseErrorViews(TestCase):
230 def test_operationalerror_view_integration(self):232 def test_operationalerror_view_integration(self):
231 # Test setup.233 # Test setup.
232 self.useFixture(FakeLogger('SiteError', level=logging.CRITICAL))234 self.useFixture(FakeLogger('SiteError', level=logging.CRITICAL))
233 self.useFixture(Urllib2Fixture())
234235
235 class BrokenView(object):236 class BrokenView(object):
236 """A view that raises an OperationalError"""237 """A view that raises an OperationalError"""
diff --git a/lib/lp/services/webservice/stories/xx-service.txt b/lib/lp/services/webservice/stories/xx-service.txt
index cfe2c32..ee15cd4 100644
--- a/lib/lp/services/webservice/stories/xx-service.txt
+++ b/lib/lp/services/webservice/stories/xx-service.txt
@@ -54,8 +54,8 @@ consumer keys.
5454
55 >>> caller = LaunchpadWebServiceCaller(u'new-consumer', u'access-key')55 >>> caller = LaunchpadWebServiceCaller(u'new-consumer', u'access-key')
56 >>> response = caller.get(root)56 >>> response = caller.get(root)
57 >>> print response.getheader('status')57 >>> response.status
58 401 Unauthorized58 401
59 >>> print response.body59 >>> print response.body
60 Unknown consumer (new-consumer).60 Unknown consumer (new-consumer).
6161
@@ -73,14 +73,14 @@ doesn't recognize the client.
7373
74 >>> caller = LaunchpadWebServiceCaller(u'another-new-consumer', u'')74 >>> caller = LaunchpadWebServiceCaller(u'another-new-consumer', u'')
75 >>> response = caller.get(root)75 >>> response = caller.get(root)
76 >>> print response.getheader('status')76 >>> response.status
77 200 Ok77 200
7878
79Anonymous requests can't access certain data.79Anonymous requests can't access certain data.
8080
81 >>> response = anon_webservice.get(body['me_link'])81 >>> response = anon_webservice.get(body['me_link'])
82 >>> print response.getheader('status')82 >>> response.status
83 401 Unauthorized83 401
84 >>> print response.body84 >>> print response.body
85 You need to be logged in to view this URL.85 You need to be logged in to view this URL.
8686
@@ -90,8 +90,8 @@ Anonymous requests can't change the dataset.
90 >>> data = simplejson.dumps({'display_name' : "This won't work"})90 >>> data = simplejson.dumps({'display_name' : "This won't work"})
91 >>> response = anon_webservice.patch(root + "/~salgado",91 >>> response = anon_webservice.patch(root + "/~salgado",
92 ... 'application/json', data)92 ... 'application/json', data)
93 >>> print response.getheader('status')93 >>> response.status
94 401 Unauthorized94 401
95 >>> print response.body95 >>> print response.body
96 (<Person at...>, 'display_name', 'launchpad.Edit')96 (<Person at...>, 'display_name', 'launchpad.Edit')
9797
diff --git a/lib/lp/soyuz/stories/webservice/xx-archive.txt b/lib/lp/soyuz/stories/webservice/xx-archive.txt
index 57bb1f5..7acd47c 100644
--- a/lib/lp/soyuz/stories/webservice/xx-archive.txt
+++ b/lib/lp/soyuz/stories/webservice/xx-archive.txt
@@ -874,12 +874,12 @@ methods.
874874
875 >>> response = webservice.named_get(875 >>> response = webservice.named_get(
876 ... cprov_archive['self_link'], 'getPublishedSources')876 ... cprov_archive['self_link'], 'getPublishedSources')
877 >>> print(response.getheader('status'))877 >>> response.status
878 200 Ok878 200
879 >>> response = webservice.named_get(879 >>> response = webservice.named_get(
880 ... cprov_archive['self_link'], 'getPublishedBinaries')880 ... cprov_archive['self_link'], 'getPublishedBinaries')
881 >>> print(response.getheader('status'))881 >>> response.status
882 200 Ok882 200
883883
884If either method is called with the version parameter, the name must884If either method is called with the version parameter, the name must
885be specified too, otherwise it is considered a bad webservice885be specified too, otherwise it is considered a bad webservice
@@ -887,13 +887,13 @@ request.
887887
888 >>> response = webservice.named_get(888 >>> response = webservice.named_get(
889 ... cprov_archive['self_link'], 'getPublishedSources', version='1.0')889 ... cprov_archive['self_link'], 'getPublishedSources', version='1.0')
890 >>> print(response.getheader('status'))890 >>> response.status
891 400 Bad Request891 400
892 >>> response = webservice.named_get(892 >>> response = webservice.named_get(
893 ... cprov_archive['self_link'], 'getPublishedBinaries',893 ... cprov_archive['self_link'], 'getPublishedBinaries',
894 ... version='1.0')894 ... version='1.0')
895 >>> print(response.getheader('status'))895 >>> response.status
896 400 Bad Request896 400
897897
898We don't have to specify any filters when getting published sources:898We don't have to specify any filters when getting published sources:
899899
diff --git a/lib/lp/soyuz/stories/webservice/xx-person-createppa.txt b/lib/lp/soyuz/stories/webservice/xx-person-createppa.txt
index 3f4f9a1..085d8c8 100644
--- a/lib/lp/soyuz/stories/webservice/xx-person-createppa.txt
+++ b/lib/lp/soyuz/stories/webservice/xx-person-createppa.txt
@@ -22,7 +22,6 @@
22 ... name='yay', displayname='My shiny new PPA',22 ... name='yay', displayname='My shiny new PPA',
23 ... description='Shinyness!'))23 ... description='Shinyness!'))
24 HTTP/1.1 201 Created24 HTTP/1.1 201 Created
25 Status: 201
26 ...25 ...
27 Location: http://api.launchpad.test/.../+archive/ubuntu/yay26 Location: http://api.launchpad.test/.../+archive/ubuntu/yay
28 ...27 ...
@@ -32,7 +31,6 @@
32 ... displayname='My shiny new PPA', description='Shinyness!',31 ... displayname='My shiny new PPA', description='Shinyness!',
33 ... ))32 ... ))
34 HTTP/1.1 400 Bad Request33 HTTP/1.1 400 Bad Request
35 Status: 400 Bad Request
36 ...34 ...
37 A PPA cannot have the same name as its distribution.35 A PPA cannot have the same name as its distribution.
3836
@@ -52,7 +50,6 @@ They aren't a commercial admin, so they cannot create private PPAs.
52 ... private=True,50 ... private=True,
53 ... ))51 ... ))
54 HTTP/1.1 400 Bad Request52 HTTP/1.1 400 Bad Request
55 Status: 400
56 ...53 ...
57 ... is not allowed to make private PPAs54 ... is not allowed to make private PPAs
5855
@@ -78,7 +75,6 @@ Once they have commercial access, they can create private PPAs:
78 ... private=True,75 ... private=True,
79 ... ))76 ... ))
80 HTTP/1.1 201 Created77 HTTP/1.1 201 Created
81 Status: 201
82 ...78 ...
83 Location: http://api.launchpad.test/.../+archive/ubuntu/secret79 Location: http://api.launchpad.test/.../+archive/ubuntu/secret
84 ...80 ...
@@ -103,7 +99,6 @@ It's possible to create PPAs for all sorts of distributions.
103 ... ppa_owner['self_link'], 'createPPA', {}, distribution='/ubuntutest',99 ... ppa_owner['self_link'], 'createPPA', {}, distribution='/ubuntutest',
104 ... name='ppa'))100 ... name='ppa'))
105 HTTP/1.1 201 Created101 HTTP/1.1 201 Created
106 Status: 201
107 ...102 ...
108 Location: http://api.launchpad.test/.../+archive/ubuntutest/ppa103 Location: http://api.launchpad.test/.../+archive/ubuntutest/ppa
109 ...104 ...
@@ -114,7 +109,6 @@ But not for distributions that don't have PPAs enabled.
114 ... ppa_owner['self_link'], 'createPPA', {}, distribution='/redhat',109 ... ppa_owner['self_link'], 'createPPA', {}, distribution='/redhat',
115 ... name='ppa'))110 ... name='ppa'))
116 HTTP/1.1 400 Bad Request111 HTTP/1.1 400 Bad Request
117 Status: 400
118 ...112 ...
119 Red Hat does not support PPAs.113 Red Hat does not support PPAs.
120114
@@ -128,7 +122,6 @@ respectively.
128 >>> print(ppa_owner_webservice.named_post(122 >>> print(ppa_owner_webservice.named_post(
129 ... ppa_owner['self_link'], 'createPPA', {}))123 ... ppa_owner['self_link'], 'createPPA', {}))
130 HTTP/1.1 201 Created124 HTTP/1.1 201 Created
131 Status: 201
132 ...125 ...
133 Location: http://api.launchpad.test/.../+archive/ubuntu/ppa126 Location: http://api.launchpad.test/.../+archive/ubuntu/ppa
134 ...127 ...
diff --git a/lib/lp/testing/fixture.py b/lib/lp/testing/fixture.py
index 557a385..45d3b50 100644
--- a/lib/lp/testing/fixture.py
+++ b/lib/lp/testing/fixture.py
@@ -10,7 +10,6 @@ __all__ = [
10 'DisableTriggerFixture',10 'DisableTriggerFixture',
11 'PGBouncerFixture',11 'PGBouncerFixture',
12 'PGNotReadyError',12 'PGNotReadyError',
13 'Urllib2Fixture',
14 'ZopeAdapterFixture',13 'ZopeAdapterFixture',
15 'ZopeEventHandlerFixture',14 'ZopeEventHandlerFixture',
16 'ZopeUtilityFixture',15 'ZopeUtilityFixture',
@@ -31,14 +30,6 @@ from lazr.restful.utils import get_current_browser_request
31import oops30import oops
32import oops_amqp31import oops_amqp
33import pgbouncer.fixture32import pgbouncer.fixture
34from wsgi_intercept import (
35 add_wsgi_intercept,
36 remove_wsgi_intercept,
37 )
38from wsgi_intercept.urllib2_intercept import (
39 install_opener,
40 uninstall_opener,
41 )
42from zope.component import (33from zope.component import (
43 adapter,34 adapter,
44 getGlobalSiteManager,35 getGlobalSiteManager,
@@ -254,22 +245,6 @@ class ZopeUtilityFixture(Fixture):
254 gsm.registerUtility, original, self.intf, self.name)245 gsm.registerUtility, original, self.intf, self.name)
255246
256247
257class Urllib2Fixture(Fixture):
258 """Let tests use urllib to connect to an in-process Launchpad.
259
260 Initially this only supports connecting to launchpad.test because
261 that is all that is needed. Later work could connect all
262 sub-hosts (e.g. bugs.launchpad.test)."""
263
264 def _setUp(self):
265 # Work around circular import.
266 from lp.testing.layers import wsgi_application
267 add_wsgi_intercept('launchpad.test', 80, lambda: wsgi_application)
268 self.addCleanup(remove_wsgi_intercept, 'launchpad.test', 80)
269 install_opener()
270 self.addCleanup(uninstall_opener)
271
272
273class CaptureOops(Fixture):248class CaptureOops(Fixture):
274 """Capture OOPSes notified via zope event notification.249 """Capture OOPSes notified via zope event notification.
275250
diff --git a/lib/lp/testing/layers.py b/lib/lp/testing/layers.py
index 22a510f..984e87b 100644
--- a/lib/lp/testing/layers.py
+++ b/lib/lp/testing/layers.py
@@ -1020,12 +1020,9 @@ def wsgi_application(environ, start_response):
1020 request = zope.publisher.publish.publish(1020 request = zope.publisher.publish.publish(
1021 request, handle_errors=handle_errors)1021 request, handle_errors=handle_errors)
1022 response = request.response1022 response = request.response
1023 # We sort these, and then put the status first, because1023 # We sort these because it makes it easier to write reliable tests.
1024 # zope.app.testing.testbrowser does--and because it makes it easier to
1025 # write reliable tests.
1026 headers = sorted(response.getHeaders())1024 headers = sorted(response.getHeaders())
1027 status = response.getStatusString()1025 status = response.getStatusString()
1028 headers.insert(0, ('Status', status))
1029 # Start the WSGI server response.1026 # Start the WSGI server response.
1030 start_response(status, headers)1027 start_response(status, headers)
1031 # Return the result body iterable.1028 # Return the result body iterable.

Subscribers

People subscribed via source and target branches

to status/vote changes: