Merge ~cjwatson/launchpad:remove-most-wsgi-intercept into launchpad:master
- Git
- lp:~cjwatson/launchpad
- remove-most-wsgi-intercept
- Merge into 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) |
Related bugs: |
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.
Description of the change
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
1 | diff --git a/lib/launchpad_loggerhead/tests.py b/lib/launchpad_loggerhead/tests.py | |||
2 | index f332518..31cb5a7 100644 | |||
3 | --- a/lib/launchpad_loggerhead/tests.py | |||
4 | +++ b/lib/launchpad_loggerhead/tests.py | |||
5 | @@ -1,7 +1,6 @@ | |||
6 | 1 | # Copyright 2010-2018 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2010-2018 Canonical Ltd. This software is licensed under the |
7 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
8 | 3 | 3 | ||
9 | 4 | import lazr.uri | ||
10 | 5 | from paste.httpexceptions import HTTPExceptionHandler | 4 | from paste.httpexceptions import HTTPExceptionHandler |
11 | 6 | import requests | 5 | import requests |
12 | 7 | from six.moves.urllib_parse import ( | 6 | from six.moves.urllib_parse import ( |
13 | @@ -11,12 +10,7 @@ from six.moves.urllib_parse import ( | |||
14 | 11 | import soupmatchers | 10 | import soupmatchers |
15 | 12 | from testtools.content import Content | 11 | from testtools.content import Content |
16 | 13 | from testtools.content_type import UTF8_TEXT | 12 | from testtools.content_type import UTF8_TEXT |
23 | 14 | import wsgi_intercept | 13 | from zope.testbrowser.wsgi import Browser |
18 | 15 | from wsgi_intercept.urllib2_intercept import ( | ||
19 | 16 | install_opener, | ||
20 | 17 | uninstall_opener, | ||
21 | 18 | ) | ||
22 | 19 | import wsgi_intercept.zope_testbrowser | ||
24 | 20 | from zope.security.proxy import removeSecurityProxy | 14 | from zope.security.proxy import removeSecurityProxy |
25 | 21 | 15 | ||
26 | 22 | from launchpad_loggerhead.app import RootApp | 16 | from launchpad_loggerhead.app import RootApp |
27 | @@ -50,12 +44,6 @@ def session_scribbler(app, test): | |||
28 | 50 | return scribble | 44 | return scribble |
29 | 51 | 45 | ||
30 | 52 | 46 | ||
31 | 53 | def dummy_destination(environ, start_response): | ||
32 | 54 | """Return a fake response.""" | ||
33 | 55 | start_response('200 OK', [('Content-type', 'text/plain')]) | ||
34 | 56 | return ['This is a dummy destination.\n'] | ||
35 | 57 | |||
36 | 58 | |||
37 | 59 | class SimpleLogInRootApp(RootApp): | 47 | class SimpleLogInRootApp(RootApp): |
38 | 60 | """A mock root app that doesn't require open id.""" | 48 | """A mock root app that doesn't require open id.""" |
39 | 61 | def _complete_login(self, environ, start_response): | 49 | def _complete_login(self, environ, start_response): |
40 | @@ -63,53 +51,35 @@ class SimpleLogInRootApp(RootApp): | |||
41 | 63 | start_response('200 OK', [('Content-type', 'text/plain')]) | 51 | start_response('200 OK', [('Content-type', 'text/plain')]) |
42 | 64 | return ['\n'] | 52 | return ['\n'] |
43 | 65 | 53 | ||
44 | 54 | def __call__(self, environ, start_response): | ||
45 | 55 | codebrowse_netloc = urlsplit( | ||
46 | 56 | config.codehosting.secure_codebrowse_root).netloc | ||
47 | 57 | if environ['HTTP_HOST'] == codebrowse_netloc: | ||
48 | 58 | return RootApp.__call__(self, environ, start_response) | ||
49 | 59 | else: | ||
50 | 60 | # Return a fake response. | ||
51 | 61 | start_response('200 OK', [('Content-type', 'text/plain')]) | ||
52 | 62 | return ['This is a dummy destination.\n'] | ||
53 | 63 | |||
54 | 66 | 64 | ||
55 | 67 | class TestLogout(TestCase): | 65 | class TestLogout(TestCase): |
56 | 68 | layer = DatabaseFunctionalLayer | 66 | layer = DatabaseFunctionalLayer |
57 | 69 | 67 | ||
58 | 70 | def intercept(self, uri, app): | ||
59 | 71 | """Install wsgi interceptors for the uri, app tuple.""" | ||
60 | 72 | if isinstance(uri, basestring): | ||
61 | 73 | uri = lazr.uri.URI(uri) | ||
62 | 74 | port = uri.port | ||
63 | 75 | if port is None: | ||
64 | 76 | if uri.scheme == 'http': | ||
65 | 77 | port = 80 | ||
66 | 78 | elif uri.scheme == 'https': | ||
67 | 79 | port = 443 | ||
68 | 80 | else: | ||
69 | 81 | raise NotImplementedError(uri.scheme) | ||
70 | 82 | else: | ||
71 | 83 | port = int(port) | ||
72 | 84 | wsgi_intercept.add_wsgi_intercept(uri.host, port, lambda: app) | ||
73 | 85 | self.intercepted.append((uri.host, port)) | ||
74 | 86 | |||
75 | 87 | def setUp(self): | 68 | def setUp(self): |
76 | 88 | TestCase.setUp(self) | 69 | TestCase.setUp(self) |
77 | 89 | self.intercepted = [] | ||
78 | 90 | self.session = None | 70 | self.session = None |
80 | 91 | self.root = app = SimpleLogInRootApp(SESSION_VAR) | 71 | app = SimpleLogInRootApp(SESSION_VAR) |
81 | 92 | app = session_scribbler(app, self) | 72 | app = session_scribbler(app, self) |
82 | 93 | app = HTTPExceptionHandler(app) | 73 | app = HTTPExceptionHandler(app) |
83 | 94 | app = SessionHandler(app, SESSION_VAR, SECRET) | 74 | app = SessionHandler(app, SESSION_VAR, SECRET) |
84 | 95 | self.cookie_name = app.cookie_handler.cookie_name | 75 | self.cookie_name = app.cookie_handler.cookie_name |
90 | 96 | self.intercept(config.codehosting.secure_codebrowse_root, app) | 76 | self.browser = Browser(wsgi_app=app) |
86 | 97 | self.intercept(allvhosts.configs['mainsite'].rooturl, | ||
87 | 98 | dummy_destination) | ||
88 | 99 | install_opener() | ||
89 | 100 | self.browser = wsgi_intercept.zope_testbrowser.WSGI_Browser() | ||
91 | 101 | # We want to pretend we are not a robot, or else mechanize will honor | 77 | # We want to pretend we are not a robot, or else mechanize will honor |
92 | 102 | # robots.txt. | 78 | # robots.txt. |
93 | 103 | self.browser.mech_browser.set_handle_robots(False) | 79 | self.browser.mech_browser.set_handle_robots(False) |
94 | 104 | self.browser.open( | 80 | self.browser.open( |
95 | 105 | config.codehosting.secure_codebrowse_root + '+login') | 81 | config.codehosting.secure_codebrowse_root + '+login') |
96 | 106 | 82 | ||
97 | 107 | def tearDown(self): | ||
98 | 108 | uninstall_opener() | ||
99 | 109 | for host, port in self.intercepted: | ||
100 | 110 | wsgi_intercept.remove_wsgi_intercept(host, port) | ||
101 | 111 | TestCase.tearDown(self) | ||
102 | 112 | |||
103 | 113 | def testLoggerheadLogout(self): | 83 | def testLoggerheadLogout(self): |
104 | 114 | # We start logged in as 'bob'. | 84 | # We start logged in as 'bob'. |
105 | 115 | self.assertEqual(self.session['user'], 'bob') | 85 | self.assertEqual(self.session['user'], 'bob') |
106 | @@ -143,8 +113,7 @@ class TestLogout(TestCase): | |||
107 | 143 | # TestLoginAndLogout.test_CookieLogoutPage). | 113 | # TestLoginAndLogout.test_CookieLogoutPage). |
108 | 144 | 114 | ||
109 | 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. |
112 | 146 | dummy_root = 'http://dummy.dev/' | 116 | dummy_root = 'http://dummy.test/' |
111 | 147 | self.intercept(dummy_root, dummy_destination) | ||
113 | 148 | self.browser.open( | 117 | self.browser.open( |
114 | 149 | config.codehosting.secure_codebrowse_root + | 118 | config.codehosting.secure_codebrowse_root + |
115 | 150 | '+logout?' + urlencode(dict(next_to=dummy_root + '+logout'))) | 119 | '+logout?' + urlencode(dict(next_to=dummy_root + '+logout'))) |
116 | diff --git a/lib/lp/registry/stories/webservice/xx-distribution-mirror.txt b/lib/lp/registry/stories/webservice/xx-distribution-mirror.txt | |||
117 | index edcef0e..9e4c932 100644 | |||
118 | --- a/lib/lp/registry/stories/webservice/xx-distribution-mirror.txt | |||
119 | +++ b/lib/lp/registry/stories/webservice/xx-distribution-mirror.txt | |||
120 | @@ -165,15 +165,15 @@ Now trying to set the owner using Sample Person's webservice is not authorized. | |||
121 | 165 | ... } | 165 | ... } |
122 | 166 | >>> response = test_webservice.patch( | 166 | >>> response = test_webservice.patch( |
123 | 167 | ... canonical_archive['self_link'], 'application/json', dumps(patch)) | 167 | ... canonical_archive['self_link'], 'application/json', dumps(patch)) |
126 | 168 | >>> print response.getheader('status') | 168 | >>> response.status |
127 | 169 | 401 Unauthorized | 169 | 401 |
128 | 170 | 170 | ||
129 | 171 | But if we use Karl, the mirror listing admin's, webservice, we can update the owner. | 171 | But if we use Karl, the mirror listing admin's, webservice, we can update the owner. |
130 | 172 | 172 | ||
131 | 173 | >>> response = karl_webservice.patch( | 173 | >>> response = karl_webservice.patch( |
132 | 174 | ... canonical_archive['self_link'], 'application/json', dumps(patch)) | 174 | ... canonical_archive['self_link'], 'application/json', dumps(patch)) |
135 | 175 | >>> print response.getheader('status') | 175 | >>> response.status |
136 | 176 | 209 Content Returned | 176 | 209 |
137 | 177 | 177 | ||
138 | 178 | >>> patched_canonical_archive = response.jsonBody() | 178 | >>> patched_canonical_archive = response.jsonBody() |
139 | 179 | >>> print patched_canonical_archive['owner_link'] | 179 | >>> print patched_canonical_archive['owner_link'] |
140 | @@ -193,7 +193,6 @@ Some attributes are read-only via the API: | |||
141 | 193 | ... canonical_releases['self_link'], 'application/json', dumps(patch)) | 193 | ... canonical_releases['self_link'], 'application/json', dumps(patch)) |
142 | 194 | >>> print response | 194 | >>> print response |
143 | 195 | HTTP/1.1 400 Bad Request | 195 | HTTP/1.1 400 Bad Request |
144 | 196 | Status: 400 Bad Request | ||
145 | 197 | ... | 196 | ... |
146 | 198 | enabled: You tried to modify a read-only attribute. | 197 | enabled: You tried to modify a read-only attribute. |
147 | 199 | date_reviewed: You tried to modify a read-only attribute. | 198 | date_reviewed: You tried to modify a read-only attribute. |
148 | @@ -211,8 +210,8 @@ While others can be set with the appropriate authorization: | |||
149 | 211 | ... } | 210 | ... } |
150 | 212 | >>> response = test_webservice.patch( | 211 | >>> response = test_webservice.patch( |
151 | 213 | ... canonical_releases['self_link'], 'application/json', dumps(patch)) | 212 | ... canonical_releases['self_link'], 'application/json', dumps(patch)) |
154 | 214 | >>> print response.getheader('status') | 213 | >>> response.status |
155 | 215 | 401 Unauthorized | 214 | 401 |
156 | 216 | 215 | ||
157 | 217 | >>> response = karl_webservice.patch( | 216 | >>> response = karl_webservice.patch( |
158 | 218 | ... canonical_releases['self_link'], 'application/json', dumps(patch)).jsonBody() | 217 | ... canonical_releases['self_link'], 'application/json', dumps(patch)).jsonBody() |
159 | diff --git a/lib/lp/services/webapp/tests/test_error.py b/lib/lp/services/webapp/tests/test_error.py | |||
160 | index 7ad4e83..3170ae3 100644 | |||
161 | --- a/lib/lp/services/webapp/tests/test_error.py | |||
162 | +++ b/lib/lp/services/webapp/tests/test_error.py | |||
163 | @@ -12,6 +12,7 @@ import urllib2 | |||
164 | 12 | 12 | ||
165 | 13 | from fixtures import FakeLogger | 13 | from fixtures import FakeLogger |
166 | 14 | import psycopg2 | 14 | import psycopg2 |
167 | 15 | from six.moves.urllib.error import HTTPError | ||
168 | 15 | from storm.exceptions import ( | 16 | from storm.exceptions import ( |
169 | 16 | DisconnectionError, | 17 | DisconnectionError, |
170 | 17 | OperationalError, | 18 | OperationalError, |
171 | @@ -26,6 +27,7 @@ from testtools.matchers import ( | |||
172 | 26 | import transaction | 27 | import transaction |
173 | 27 | from zope.interface import Interface | 28 | from zope.interface import Interface |
174 | 28 | from zope.publisher.interfaces.browser import IDefaultBrowserLayer | 29 | from zope.publisher.interfaces.browser import IDefaultBrowserLayer |
175 | 30 | from zope.testbrowser.wsgi import Browser | ||
176 | 29 | 31 | ||
177 | 30 | from lp.services.webapp.error import ( | 32 | from lp.services.webapp.error import ( |
178 | 31 | DisconnectionErrorView, | 33 | DisconnectionErrorView, |
179 | @@ -37,12 +39,12 @@ from lp.testing import TestCase | |||
180 | 37 | from lp.testing.fixture import ( | 39 | from lp.testing.fixture import ( |
181 | 38 | CaptureOops, | 40 | CaptureOops, |
182 | 39 | PGBouncerFixture, | 41 | PGBouncerFixture, |
183 | 40 | Urllib2Fixture, | ||
184 | 41 | ZopeAdapterFixture, | 42 | ZopeAdapterFixture, |
185 | 42 | ) | 43 | ) |
186 | 43 | from lp.testing.layers import ( | 44 | from lp.testing.layers import ( |
187 | 44 | DatabaseLayer, | 45 | DatabaseLayer, |
188 | 45 | LaunchpadFunctionalLayer, | 46 | LaunchpadFunctionalLayer, |
189 | 47 | wsgi_application, | ||
190 | 46 | ) | 48 | ) |
191 | 47 | from lp.testing.matchers import Contains | 49 | from lp.testing.matchers import Contains |
192 | 48 | 50 | ||
193 | @@ -78,8 +80,8 @@ class TestDatabaseErrorViews(TestCase): | |||
194 | 78 | 80 | ||
195 | 79 | def getHTTPError(self, url): | 81 | def getHTTPError(self, url): |
196 | 80 | try: | 82 | try: |
199 | 81 | urllib2.urlopen(url) | 83 | Browser(wsgi_app=wsgi_application).open(url) |
200 | 82 | except urllib2.HTTPError as error: | 84 | except HTTPError as error: |
201 | 83 | return error | 85 | return error |
202 | 84 | else: | 86 | else: |
203 | 85 | self.fail("We should have gotten an HTTP error") | 87 | self.fail("We should have gotten an HTTP error") |
204 | @@ -103,13 +105,14 @@ class TestDatabaseErrorViews(TestCase): | |||
205 | 103 | def retryConnection(self, url, bouncer, retries=60): | 105 | def retryConnection(self, url, bouncer, retries=60): |
206 | 104 | """Retry to connect to *url* for *retries* times. | 106 | """Retry to connect to *url* for *retries* times. |
207 | 105 | 107 | ||
210 | 106 | Return the file-like object returned by *urllib2.urlopen(url)*. | 108 | Raise a TimeoutException if the connection cannot be established. |
209 | 107 | Raise a TimeoutException if the connection can not be established. | ||
211 | 108 | """ | 109 | """ |
212 | 110 | browser = Browser(wsgi_app=wsgi_application) | ||
213 | 109 | for i in range(retries): | 111 | for i in range(retries): |
214 | 110 | try: | 112 | try: |
217 | 111 | return urllib2.urlopen(url) | 113 | browser.open(url) |
218 | 112 | except urllib2.HTTPError as e: | 114 | return |
219 | 115 | except HTTPError as e: | ||
220 | 113 | if e.code != httplib.SERVICE_UNAVAILABLE: | 116 | if e.code != httplib.SERVICE_UNAVAILABLE: |
221 | 114 | raise | 117 | raise |
222 | 115 | time.sleep(1) | 118 | time.sleep(1) |
223 | @@ -122,7 +125,6 @@ class TestDatabaseErrorViews(TestCase): | |||
224 | 122 | def test_disconnectionerror_view_integration(self): | 125 | def test_disconnectionerror_view_integration(self): |
225 | 123 | # Test setup. | 126 | # Test setup. |
226 | 124 | self.useFixture(FakeLogger('SiteError', level=logging.CRITICAL)) | 127 | self.useFixture(FakeLogger('SiteError', level=logging.CRITICAL)) |
227 | 125 | self.useFixture(Urllib2Fixture()) | ||
228 | 126 | bouncer = PGBouncerFixture() | 128 | bouncer = PGBouncerFixture() |
229 | 127 | # XXX gary bug=974617, bug=1011847, bug=504291 2011-07-03: | 129 | # XXX gary bug=974617, bug=1011847, bug=504291 2011-07-03: |
230 | 128 | # In parallel tests, we are rarely encountering instances of | 130 | # In parallel tests, we are rarely encountering instances of |
231 | @@ -230,7 +232,6 @@ class TestDatabaseErrorViews(TestCase): | |||
232 | 230 | def test_operationalerror_view_integration(self): | 232 | def test_operationalerror_view_integration(self): |
233 | 231 | # Test setup. | 233 | # Test setup. |
234 | 232 | self.useFixture(FakeLogger('SiteError', level=logging.CRITICAL)) | 234 | self.useFixture(FakeLogger('SiteError', level=logging.CRITICAL)) |
235 | 233 | self.useFixture(Urllib2Fixture()) | ||
236 | 234 | 235 | ||
237 | 235 | class BrokenView(object): | 236 | class BrokenView(object): |
238 | 236 | """A view that raises an OperationalError""" | 237 | """A view that raises an OperationalError""" |
239 | diff --git a/lib/lp/services/webservice/stories/xx-service.txt b/lib/lp/services/webservice/stories/xx-service.txt | |||
240 | index cfe2c32..ee15cd4 100644 | |||
241 | --- a/lib/lp/services/webservice/stories/xx-service.txt | |||
242 | +++ b/lib/lp/services/webservice/stories/xx-service.txt | |||
243 | @@ -54,8 +54,8 @@ consumer keys. | |||
244 | 54 | 54 | ||
245 | 55 | >>> caller = LaunchpadWebServiceCaller(u'new-consumer', u'access-key') | 55 | >>> caller = LaunchpadWebServiceCaller(u'new-consumer', u'access-key') |
246 | 56 | >>> response = caller.get(root) | 56 | >>> response = caller.get(root) |
249 | 57 | >>> print response.getheader('status') | 57 | >>> response.status |
250 | 58 | 401 Unauthorized | 58 | 401 |
251 | 59 | >>> print response.body | 59 | >>> print response.body |
252 | 60 | Unknown consumer (new-consumer). | 60 | Unknown consumer (new-consumer). |
253 | 61 | 61 | ||
254 | @@ -73,14 +73,14 @@ doesn't recognize the client. | |||
255 | 73 | 73 | ||
256 | 74 | >>> caller = LaunchpadWebServiceCaller(u'another-new-consumer', u'') | 74 | >>> caller = LaunchpadWebServiceCaller(u'another-new-consumer', u'') |
257 | 75 | >>> response = caller.get(root) | 75 | >>> response = caller.get(root) |
260 | 76 | >>> print response.getheader('status') | 76 | >>> response.status |
261 | 77 | 200 Ok | 77 | 200 |
262 | 78 | 78 | ||
263 | 79 | Anonymous requests can't access certain data. | 79 | Anonymous requests can't access certain data. |
264 | 80 | 80 | ||
265 | 81 | >>> response = anon_webservice.get(body['me_link']) | 81 | >>> response = anon_webservice.get(body['me_link']) |
268 | 82 | >>> print response.getheader('status') | 82 | >>> response.status |
269 | 83 | 401 Unauthorized | 83 | 401 |
270 | 84 | >>> print response.body | 84 | >>> print response.body |
271 | 85 | You need to be logged in to view this URL. | 85 | You need to be logged in to view this URL. |
272 | 86 | 86 | ||
273 | @@ -90,8 +90,8 @@ Anonymous requests can't change the dataset. | |||
274 | 90 | >>> data = simplejson.dumps({'display_name' : "This won't work"}) | 90 | >>> data = simplejson.dumps({'display_name' : "This won't work"}) |
275 | 91 | >>> response = anon_webservice.patch(root + "/~salgado", | 91 | >>> response = anon_webservice.patch(root + "/~salgado", |
276 | 92 | ... 'application/json', data) | 92 | ... 'application/json', data) |
279 | 93 | >>> print response.getheader('status') | 93 | >>> response.status |
280 | 94 | 401 Unauthorized | 94 | 401 |
281 | 95 | >>> print response.body | 95 | >>> print response.body |
282 | 96 | (<Person at...>, 'display_name', 'launchpad.Edit') | 96 | (<Person at...>, 'display_name', 'launchpad.Edit') |
283 | 97 | 97 | ||
284 | diff --git a/lib/lp/soyuz/stories/webservice/xx-archive.txt b/lib/lp/soyuz/stories/webservice/xx-archive.txt | |||
285 | index 57bb1f5..7acd47c 100644 | |||
286 | --- a/lib/lp/soyuz/stories/webservice/xx-archive.txt | |||
287 | +++ b/lib/lp/soyuz/stories/webservice/xx-archive.txt | |||
288 | @@ -874,12 +874,12 @@ methods. | |||
289 | 874 | 874 | ||
290 | 875 | >>> response = webservice.named_get( | 875 | >>> response = webservice.named_get( |
291 | 876 | ... cprov_archive['self_link'], 'getPublishedSources') | 876 | ... cprov_archive['self_link'], 'getPublishedSources') |
294 | 877 | >>> print(response.getheader('status')) | 877 | >>> response.status |
295 | 878 | 200 Ok | 878 | 200 |
296 | 879 | >>> response = webservice.named_get( | 879 | >>> response = webservice.named_get( |
297 | 880 | ... cprov_archive['self_link'], 'getPublishedBinaries') | 880 | ... cprov_archive['self_link'], 'getPublishedBinaries') |
300 | 881 | >>> print(response.getheader('status')) | 881 | >>> response.status |
301 | 882 | 200 Ok | 882 | 200 |
302 | 883 | 883 | ||
303 | 884 | If either method is called with the version parameter, the name must | 884 | If either method is called with the version parameter, the name must |
304 | 885 | be specified too, otherwise it is considered a bad webservice | 885 | be specified too, otherwise it is considered a bad webservice |
305 | @@ -887,13 +887,13 @@ request. | |||
306 | 887 | 887 | ||
307 | 888 | >>> response = webservice.named_get( | 888 | >>> response = webservice.named_get( |
308 | 889 | ... cprov_archive['self_link'], 'getPublishedSources', version='1.0') | 889 | ... cprov_archive['self_link'], 'getPublishedSources', version='1.0') |
311 | 890 | >>> print(response.getheader('status')) | 890 | >>> response.status |
312 | 891 | 400 Bad Request | 891 | 400 |
313 | 892 | >>> response = webservice.named_get( | 892 | >>> response = webservice.named_get( |
314 | 893 | ... cprov_archive['self_link'], 'getPublishedBinaries', | 893 | ... cprov_archive['self_link'], 'getPublishedBinaries', |
315 | 894 | ... version='1.0') | 894 | ... version='1.0') |
318 | 895 | >>> print(response.getheader('status')) | 895 | >>> response.status |
319 | 896 | 400 Bad Request | 896 | 400 |
320 | 897 | 897 | ||
321 | 898 | We don't have to specify any filters when getting published sources: | 898 | We don't have to specify any filters when getting published sources: |
322 | 899 | 899 | ||
323 | diff --git a/lib/lp/soyuz/stories/webservice/xx-person-createppa.txt b/lib/lp/soyuz/stories/webservice/xx-person-createppa.txt | |||
324 | index 3f4f9a1..085d8c8 100644 | |||
325 | --- a/lib/lp/soyuz/stories/webservice/xx-person-createppa.txt | |||
326 | +++ b/lib/lp/soyuz/stories/webservice/xx-person-createppa.txt | |||
327 | @@ -22,7 +22,6 @@ | |||
328 | 22 | ... name='yay', displayname='My shiny new PPA', | 22 | ... name='yay', displayname='My shiny new PPA', |
329 | 23 | ... description='Shinyness!')) | 23 | ... description='Shinyness!')) |
330 | 24 | HTTP/1.1 201 Created | 24 | HTTP/1.1 201 Created |
331 | 25 | Status: 201 | ||
332 | 26 | ... | 25 | ... |
333 | 27 | Location: http://api.launchpad.test/.../+archive/ubuntu/yay | 26 | Location: http://api.launchpad.test/.../+archive/ubuntu/yay |
334 | 28 | ... | 27 | ... |
335 | @@ -32,7 +31,6 @@ | |||
336 | 32 | ... displayname='My shiny new PPA', description='Shinyness!', | 31 | ... displayname='My shiny new PPA', description='Shinyness!', |
337 | 33 | ... )) | 32 | ... )) |
338 | 34 | HTTP/1.1 400 Bad Request | 33 | HTTP/1.1 400 Bad Request |
339 | 35 | Status: 400 Bad Request | ||
340 | 36 | ... | 34 | ... |
341 | 37 | A PPA cannot have the same name as its distribution. | 35 | A PPA cannot have the same name as its distribution. |
342 | 38 | 36 | ||
343 | @@ -52,7 +50,6 @@ They aren't a commercial admin, so they cannot create private PPAs. | |||
344 | 52 | ... private=True, | 50 | ... private=True, |
345 | 53 | ... )) | 51 | ... )) |
346 | 54 | HTTP/1.1 400 Bad Request | 52 | HTTP/1.1 400 Bad Request |
347 | 55 | Status: 400 | ||
348 | 56 | ... | 53 | ... |
349 | 57 | ... is not allowed to make private PPAs | 54 | ... is not allowed to make private PPAs |
350 | 58 | 55 | ||
351 | @@ -78,7 +75,6 @@ Once they have commercial access, they can create private PPAs: | |||
352 | 78 | ... private=True, | 75 | ... private=True, |
353 | 79 | ... )) | 76 | ... )) |
354 | 80 | HTTP/1.1 201 Created | 77 | HTTP/1.1 201 Created |
355 | 81 | Status: 201 | ||
356 | 82 | ... | 78 | ... |
357 | 83 | Location: http://api.launchpad.test/.../+archive/ubuntu/secret | 79 | Location: http://api.launchpad.test/.../+archive/ubuntu/secret |
358 | 84 | ... | 80 | ... |
359 | @@ -103,7 +99,6 @@ It's possible to create PPAs for all sorts of distributions. | |||
360 | 103 | ... ppa_owner['self_link'], 'createPPA', {}, distribution='/ubuntutest', | 99 | ... ppa_owner['self_link'], 'createPPA', {}, distribution='/ubuntutest', |
361 | 104 | ... name='ppa')) | 100 | ... name='ppa')) |
362 | 105 | HTTP/1.1 201 Created | 101 | HTTP/1.1 201 Created |
363 | 106 | Status: 201 | ||
364 | 107 | ... | 102 | ... |
365 | 108 | Location: http://api.launchpad.test/.../+archive/ubuntutest/ppa | 103 | Location: http://api.launchpad.test/.../+archive/ubuntutest/ppa |
366 | 109 | ... | 104 | ... |
367 | @@ -114,7 +109,6 @@ But not for distributions that don't have PPAs enabled. | |||
368 | 114 | ... ppa_owner['self_link'], 'createPPA', {}, distribution='/redhat', | 109 | ... ppa_owner['self_link'], 'createPPA', {}, distribution='/redhat', |
369 | 115 | ... name='ppa')) | 110 | ... name='ppa')) |
370 | 116 | HTTP/1.1 400 Bad Request | 111 | HTTP/1.1 400 Bad Request |
371 | 117 | Status: 400 | ||
372 | 118 | ... | 112 | ... |
373 | 119 | Red Hat does not support PPAs. | 113 | Red Hat does not support PPAs. |
374 | 120 | 114 | ||
375 | @@ -128,7 +122,6 @@ respectively. | |||
376 | 128 | >>> print(ppa_owner_webservice.named_post( | 122 | >>> print(ppa_owner_webservice.named_post( |
377 | 129 | ... ppa_owner['self_link'], 'createPPA', {})) | 123 | ... ppa_owner['self_link'], 'createPPA', {})) |
378 | 130 | HTTP/1.1 201 Created | 124 | HTTP/1.1 201 Created |
379 | 131 | Status: 201 | ||
380 | 132 | ... | 125 | ... |
381 | 133 | Location: http://api.launchpad.test/.../+archive/ubuntu/ppa | 126 | Location: http://api.launchpad.test/.../+archive/ubuntu/ppa |
382 | 134 | ... | 127 | ... |
383 | diff --git a/lib/lp/testing/fixture.py b/lib/lp/testing/fixture.py | |||
384 | index 557a385..45d3b50 100644 | |||
385 | --- a/lib/lp/testing/fixture.py | |||
386 | +++ b/lib/lp/testing/fixture.py | |||
387 | @@ -10,7 +10,6 @@ __all__ = [ | |||
388 | 10 | 'DisableTriggerFixture', | 10 | 'DisableTriggerFixture', |
389 | 11 | 'PGBouncerFixture', | 11 | 'PGBouncerFixture', |
390 | 12 | 'PGNotReadyError', | 12 | 'PGNotReadyError', |
391 | 13 | 'Urllib2Fixture', | ||
392 | 14 | 'ZopeAdapterFixture', | 13 | 'ZopeAdapterFixture', |
393 | 15 | 'ZopeEventHandlerFixture', | 14 | 'ZopeEventHandlerFixture', |
394 | 16 | 'ZopeUtilityFixture', | 15 | 'ZopeUtilityFixture', |
395 | @@ -31,14 +30,6 @@ from lazr.restful.utils import get_current_browser_request | |||
396 | 31 | import oops | 30 | import oops |
397 | 32 | import oops_amqp | 31 | import oops_amqp |
398 | 33 | import pgbouncer.fixture | 32 | import pgbouncer.fixture |
399 | 34 | from wsgi_intercept import ( | ||
400 | 35 | add_wsgi_intercept, | ||
401 | 36 | remove_wsgi_intercept, | ||
402 | 37 | ) | ||
403 | 38 | from wsgi_intercept.urllib2_intercept import ( | ||
404 | 39 | install_opener, | ||
405 | 40 | uninstall_opener, | ||
406 | 41 | ) | ||
407 | 42 | from zope.component import ( | 33 | from zope.component import ( |
408 | 43 | adapter, | 34 | adapter, |
409 | 44 | getGlobalSiteManager, | 35 | getGlobalSiteManager, |
410 | @@ -254,22 +245,6 @@ class ZopeUtilityFixture(Fixture): | |||
411 | 254 | gsm.registerUtility, original, self.intf, self.name) | 245 | gsm.registerUtility, original, self.intf, self.name) |
412 | 255 | 246 | ||
413 | 256 | 247 | ||
414 | 257 | class Urllib2Fixture(Fixture): | ||
415 | 258 | """Let tests use urllib to connect to an in-process Launchpad. | ||
416 | 259 | |||
417 | 260 | Initially this only supports connecting to launchpad.test because | ||
418 | 261 | that is all that is needed. Later work could connect all | ||
419 | 262 | sub-hosts (e.g. bugs.launchpad.test).""" | ||
420 | 263 | |||
421 | 264 | def _setUp(self): | ||
422 | 265 | # Work around circular import. | ||
423 | 266 | from lp.testing.layers import wsgi_application | ||
424 | 267 | add_wsgi_intercept('launchpad.test', 80, lambda: wsgi_application) | ||
425 | 268 | self.addCleanup(remove_wsgi_intercept, 'launchpad.test', 80) | ||
426 | 269 | install_opener() | ||
427 | 270 | self.addCleanup(uninstall_opener) | ||
428 | 271 | |||
429 | 272 | |||
430 | 273 | class CaptureOops(Fixture): | 248 | class CaptureOops(Fixture): |
431 | 274 | """Capture OOPSes notified via zope event notification. | 249 | """Capture OOPSes notified via zope event notification. |
432 | 275 | 250 | ||
433 | diff --git a/lib/lp/testing/layers.py b/lib/lp/testing/layers.py | |||
434 | index 22a510f..984e87b 100644 | |||
435 | --- a/lib/lp/testing/layers.py | |||
436 | +++ b/lib/lp/testing/layers.py | |||
437 | @@ -1020,12 +1020,9 @@ def wsgi_application(environ, start_response): | |||
438 | 1020 | request = zope.publisher.publish.publish( | 1020 | request = zope.publisher.publish.publish( |
439 | 1021 | request, handle_errors=handle_errors) | 1021 | request, handle_errors=handle_errors) |
440 | 1022 | response = request.response | 1022 | response = request.response |
444 | 1023 | # We sort these, and then put the status first, because | 1023 | # We sort these because it makes it easier to write reliable tests. |
442 | 1024 | # zope.app.testing.testbrowser does--and because it makes it easier to | ||
443 | 1025 | # write reliable tests. | ||
445 | 1026 | headers = sorted(response.getHeaders()) | 1024 | headers = sorted(response.getHeaders()) |
446 | 1027 | status = response.getStatusString() | 1025 | status = response.getStatusString() |
447 | 1028 | headers.insert(0, ('Status', status)) | ||
448 | 1029 | # Start the WSGI server response. | 1026 | # Start the WSGI server response. |
449 | 1030 | start_response(status, headers) | 1027 | start_response(status, headers) |
450 | 1031 | # Return the result body iterable. | 1028 | # Return the result body iterable. |