Merge lp:~alecu/ubuntu-sso-client/post-restful-cleanups into lp:ubuntu-sso-client
- post-restful-cleanups
- Merge into trunk
Status: | Merged | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Natalia Bidart | ||||||||||||||||
Approved revision: | 872 | ||||||||||||||||
Merged at revision: | 845 | ||||||||||||||||
Proposed branch: | lp:~alecu/ubuntu-sso-client/post-restful-cleanups | ||||||||||||||||
Merge into: | lp:ubuntu-sso-client | ||||||||||||||||
Diff against target: |
910 lines (+250/-187) 12 files modified
run-tests (+8/-4) ubuntu_sso/account.py (+36/-16) ubuntu_sso/credentials.py (+9/-12) ubuntu_sso/tests/test_account.py (+22/-1) ubuntu_sso/tests/test_credentials.py (+59/-53) ubuntu_sso/utils/webclient/common.py (+1/-1) ubuntu_sso/utils/webclient/restful.py (+4/-0) ubuntu_sso/utils/webclient/tests/__init__.py (+60/-0) ubuntu_sso/utils/webclient/tests/test_restful.py (+12/-0) ubuntu_sso/utils/webclient/tests/test_timestamp.py (+16/-32) ubuntu_sso/utils/webclient/tests/test_webclient.py (+14/-63) ubuntu_sso/utils/webclient/timestamp.py (+9/-5) |
||||||||||||||||
To merge this branch: | bzr merge lp:~alecu/ubuntu-sso-client/post-restful-cleanups | ||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Manuel de la Peña (community) | Approve | ||
Natalia Bidart (community) | Approve | ||
Review via email: mp+91157@code.launchpad.net |
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~alecu/ubuntu-sso-client/post-restful-cleanups into lp:ubuntu-sso-client failed. Below is the output from the failed tests.
*** Running test suite for ubuntu_sso ***
ubuntu_
AccountTestCase
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
EnvironOverri
test_
test_
test_
twisted.
TestCase
runTest ... [OK]
ubuntu_
TimestampedAu
test_
ubuntu_
BasicTestCase
runTest ... [OK]
ClearCredenti
test_
test_
CredentialsAu
test_
CredentialsCa
test_
test_
test_
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~alecu/ubuntu-sso-client/post-restful-cleanups into lp:ubuntu-sso-client failed. Below is the output from the failed tests.
*** Running test suite for ubuntu_sso ***
ubuntu_
AccountTestCase
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
EnvironOverri
test_
test_
test_
twisted.
TestCase
runTest ... [OK]
ubuntu_
TimestampedAu
test_
ubuntu_
BasicTestCase
runTest ... [OK]
ClearCredenti
test_
test_
CredentialsAu
test_
CredentialsCa
test_
test_
test_
- 872. By Alejandro J. Cura
-
fix xvfb-run command line
Preview Diff
1 | === modified file 'run-tests' |
2 | --- run-tests 2012-02-02 18:09:21 +0000 |
3 | +++ run-tests 2012-02-03 17:25:21 +0000 |
4 | @@ -38,20 +38,24 @@ |
5 | echo "Please install the 'pep8' package." |
6 | fi |
7 | } |
8 | - |
9 | unset GTK_MODULES |
10 | |
11 | +XVFB=$(which xvfb-run) |
12 | +if [ $XVFB ]; then |
13 | + XVFB_CMDLINE="$XVFB -a" |
14 | +fi |
15 | + |
16 | echo "*** Running test suite for ""$MODULE"" ***" |
17 | -`which xvfb-run` u1trial --reactor=gi -p "$QT_TESTS_PATH, $GTK_TESTS_PATH" -i "test_windows.py" "$MODULE" |
18 | +$XVFB_CMDLINE u1trial --reactor=gi -p "$QT_TESTS_PATH, $GTK_TESTS_PATH" -i "test_windows.py" "$MODULE" |
19 | rm -rf _trial_temp |
20 | |
21 | echo "*** Running GTK test suite ***" |
22 | -`which xvfb-run` u1trial --reactor=gi --gui "$GTK_TESTS_PATH" |
23 | +$XVFB_CMDLINE u1trial --reactor=gi --gui "$GTK_TESTS_PATH" |
24 | rm -rf _trial_temp |
25 | |
26 | echo "*** Running QT test suite ***" |
27 | ./setup.py build |
28 | -USE_QT_MAINLOOP=True `which xvfb-run` u1trial --reactor=qt4 --gui "$QT_TESTS_PATH" |
29 | +USE_QT_MAINLOOP=True $XVFB_CMDLINE u1trial --reactor=qt4 --gui "$QT_TESTS_PATH" |
30 | rm -rf _trial_temp |
31 | rm -rf build |
32 | |
33 | |
34 | === modified file 'ubuntu_sso/account.py' |
35 | --- ubuntu_sso/account.py 2012-01-26 18:52:23 +0000 |
36 | +++ ubuntu_sso/account.py 2012-02-03 17:25:21 +0000 |
37 | @@ -134,7 +134,10 @@ |
38 | logger.debug('generate_captcha: requesting captcha, filename: %r', |
39 | filename) |
40 | restful_client = restful.RestfulClient(self.service_url) |
41 | - captcha = yield restful_client.restcall(u"captchas.new") |
42 | + try: |
43 | + captcha = yield restful_client.restcall(u"captchas.new") |
44 | + finally: |
45 | + restful_client.shutdown() |
46 | |
47 | # download captcha and save to 'filename' |
48 | logger.debug('generate_captcha: server answered: %r', captcha) |
49 | @@ -147,6 +150,8 @@ |
50 | msg = 'generate_captcha crashed while downloading the image.' |
51 | logger.exception(msg) |
52 | raise |
53 | + finally: |
54 | + wc.shutdown() |
55 | |
56 | defer.returnValue(captcha['captcha_id']) |
57 | |
58 | @@ -158,19 +163,22 @@ |
59 | 'displayname: %r, captcha_id: %r, captcha_solution: %r', |
60 | email, displayname, captcha_id, captcha_solution) |
61 | restful_client = restful.RestfulClient(self.service_url) |
62 | - if not self._valid_email(email): |
63 | - logger.error('register_user: InvalidEmailError for email: %r', |
64 | - email) |
65 | - raise InvalidEmailError() |
66 | - if not self._valid_password(password): |
67 | - logger.error('register_user: InvalidPasswordError') |
68 | - raise InvalidPasswordError() |
69 | + try: |
70 | + if not self._valid_email(email): |
71 | + logger.error('register_user: InvalidEmailError for email: %r', |
72 | + email) |
73 | + raise InvalidEmailError() |
74 | + if not self._valid_password(password): |
75 | + logger.error('register_user: InvalidPasswordError') |
76 | + raise InvalidPasswordError() |
77 | |
78 | - result = yield restful_client.restcall(u"registration.register", |
79 | - email=email, password=password, |
80 | - displayname=displayname, |
81 | - captcha_id=captcha_id, |
82 | - captcha_solution=captcha_solution) |
83 | + result = yield restful_client.restcall(u"registration.register", |
84 | + email=email, password=password, |
85 | + displayname=displayname, |
86 | + captcha_id=captcha_id, |
87 | + captcha_solution=captcha_solution) |
88 | + finally: |
89 | + restful_client.shutdown() |
90 | logger.info('register_user: email: %r result: %r', email, result) |
91 | |
92 | if result['status'].lower() == SSO_STATUS_ERROR: |
93 | @@ -196,6 +204,8 @@ |
94 | except WebClientError: |
95 | logger.exception('login failed with:') |
96 | raise AuthenticationError() |
97 | + finally: |
98 | + restful_client.shutdown() |
99 | |
100 | logger.debug('login: authentication successful! consumer_key: %r, ' \ |
101 | 'token_name: %r', credentials['consumer_key'], token_name) |
102 | @@ -207,7 +217,10 @@ |
103 | logger.debug('is_validated: requesting accounts.me() info.') |
104 | restful_client = restful.RestfulClient(self.service_url, |
105 | oauth_credentials=token) |
106 | - me_info = yield restful_client.restcall(u"accounts.me") |
107 | + try: |
108 | + me_info = yield restful_client.restcall(u"accounts.me") |
109 | + finally: |
110 | + restful_client.shutdown() |
111 | key = 'preferred_email' |
112 | result = key in me_info and me_info[key] != None |
113 | |
114 | @@ -225,8 +238,11 @@ |
115 | token_name=token_name) |
116 | restful_client = restful.RestfulClient(self.service_url, |
117 | oauth_credentials=credentials) |
118 | - result = yield restful_client.restcall(u"accounts.validate_email", |
119 | - email_token=email_token) |
120 | + try: |
121 | + result = yield restful_client.restcall(u"accounts.validate_email", |
122 | + email_token=email_token) |
123 | + finally: |
124 | + restful_client.shutdown() |
125 | logger.info('validate_email: email: %r result: %r', email, result) |
126 | if 'errors' in result: |
127 | errorsdict = self._format_webservice_errors(result['errors']) |
128 | @@ -246,6 +262,8 @@ |
129 | except WebClientError, e: |
130 | logger.exception('request_password_reset_token failed with:') |
131 | raise ResetPasswordTokenError(e[1].split('\n')[0]) |
132 | + finally: |
133 | + restful_client.shutdown() |
134 | |
135 | if result['status'].lower() == SSO_STATUS_OK: |
136 | defer.returnValue(email) |
137 | @@ -270,6 +288,8 @@ |
138 | except WebClientError, e: |
139 | logger.exception('set_new_password failed with:') |
140 | raise NewPasswordError(e[1].split('\n')[0]) |
141 | + finally: |
142 | + restful_client.shutdown() |
143 | |
144 | if result['status'].lower() == SSO_STATUS_OK: |
145 | defer.returnValue(email) |
146 | |
147 | === modified file 'ubuntu_sso/credentials.py' |
148 | --- ubuntu_sso/credentials.py 2012-01-10 17:28:08 +0000 |
149 | +++ ubuntu_sso/credentials.py 2012-02-03 17:25:21 +0000 |
150 | @@ -39,15 +39,15 @@ |
151 | |
152 | import sys |
153 | import traceback |
154 | -import urllib2 |
155 | |
156 | from functools import wraps |
157 | |
158 | from twisted.internet import defer |
159 | |
160 | -from ubuntu_sso import NO_OP, utils |
161 | +from ubuntu_sso import NO_OP |
162 | from ubuntu_sso.keyring import Keyring |
163 | from ubuntu_sso.logger import setup_logging |
164 | +from ubuntu_sso.utils import webclient |
165 | |
166 | logger = setup_logging('ubuntu_sso.credentials') |
167 | |
168 | @@ -216,6 +216,7 @@ |
169 | self.denial_cb(app_name) |
170 | |
171 | @handle_failures(msg='Problem opening the ping_url') |
172 | + @defer.inlineCallbacks |
173 | def _ping_url(self, app_name, email, credentials): |
174 | """Ping the self.ping_url with the email attached. |
175 | |
176 | @@ -235,16 +236,12 @@ |
177 | 'appending email (%r).', self.ping_url, email) |
178 | url = self.ping_url + email |
179 | |
180 | - headers = utils.oauth_headers(url, credentials) |
181 | - request = urllib2.Request(url, headers=headers) |
182 | - logger.debug('Opening the url %r with urllib2.urlopen.', |
183 | - request.get_full_url()) |
184 | - # This code is blocking, we should change this. |
185 | - # I've tried with deferToThread an twisted.web.client.getPage |
186 | - # but the returned deferred will never be fired (nataliabidart). |
187 | - response = urllib2.urlopen(request) |
188 | - logger.debug('Url opened. Response: %s.', response.code) |
189 | - return defer.succeed(response) |
190 | + wc = webclient.webclient_factory() |
191 | + try: |
192 | + response = yield wc.request(url, oauth_credentials=credentials) |
193 | + defer.returnValue(response) |
194 | + finally: |
195 | + wc.shutdown() |
196 | |
197 | @handle_exceptions(msg='Problem opening the Ubuntu SSO user interface') |
198 | def _show_ui(self, login_only): |
199 | |
200 | === modified file 'ubuntu_sso/tests/test_account.py' |
201 | --- ubuntu_sso/tests/test_account.py 2012-01-26 18:52:23 +0000 |
202 | +++ ubuntu_sso/tests/test_account.py 2012-02-03 17:25:21 +0000 |
203 | @@ -82,16 +82,30 @@ |
204 | class FakeWebClient(object): |
205 | """A fake webclient.""" |
206 | |
207 | + def __init__(self): |
208 | + self.started = True |
209 | + |
210 | def request(self, url): |
211 | """Do a fake request, return a fake Response.""" |
212 | return FakeWebClientResponse() |
213 | |
214 | + def shutdown(self): |
215 | + """Turn off this webclient.""" |
216 | + self.started = False |
217 | + |
218 | |
219 | class FakeRestfulClient(object): |
220 | """A fake restfulclient.""" |
221 | |
222 | preferred_email = EMAIL |
223 | |
224 | + def __init__(self): |
225 | + self.started = True |
226 | + |
227 | + def shutdown(self): |
228 | + """Stop this restfulclient.""" |
229 | + self.started = False |
230 | + |
231 | def fake_captchas_new(self): |
232 | """Return a local fake captcha.""" |
233 | return FAKE_NEW_CAPTCHA |
234 | @@ -231,6 +245,11 @@ |
235 | self.frc = FakeRestfulClient() |
236 | self.patch(account.restful, "RestfulClient", |
237 | lambda *args, **kwargs: self.frc) |
238 | + self.addCleanup(self.verify_frc_shutdown) |
239 | + |
240 | + def verify_frc_shutdown(self): |
241 | + """Verify that the FakeRestfulClient was stopped.""" |
242 | + assert self.frc.started == False, "Restfulclient must be shut down." |
243 | |
244 | @defer.inlineCallbacks |
245 | def tearDown(self): |
246 | @@ -244,7 +263,8 @@ |
247 | filename = self.mktemp() |
248 | self.addCleanup(lambda: os.remove(filename) |
249 | if os.path.exists(filename) else None) |
250 | - self.patch(account.webclient, "webclient_factory", FakeWebClient) |
251 | + wc = FakeWebClient() |
252 | + self.patch(account.webclient, "webclient_factory", lambda: wc) |
253 | captcha_id = yield self.processor.generate_captcha(filename) |
254 | self.assertEqual(CAPTCHA_ID, captcha_id, 'captcha id must be correct.') |
255 | self.assertTrue(os.path.isfile(filename), '%s must exist.' % filename) |
256 | @@ -254,6 +274,7 @@ |
257 | with open(filename) as f: |
258 | actual = f.read() |
259 | self.assertEqual(expected, actual, 'captcha image must be correct.') |
260 | + self.assertFalse(wc.started, "Webclient must be shut down.") |
261 | |
262 | @defer.inlineCallbacks |
263 | def test_register_user_checks_valid_email(self): |
264 | |
265 | === modified file 'ubuntu_sso/tests/test_credentials.py' |
266 | --- ubuntu_sso/tests/test_credentials.py 2011-12-21 21:38:15 +0000 |
267 | +++ ubuntu_sso/tests/test_credentials.py 2012-02-03 17:25:21 +0000 |
268 | @@ -16,12 +16,9 @@ |
269 | """Tests for the Credentials module.""" |
270 | |
271 | import logging |
272 | -import os |
273 | -import time |
274 | -import urllib2 |
275 | |
276 | from twisted.internet import defer |
277 | -from twisted.trial.unittest import TestCase, FailTest |
278 | +from twisted.trial.unittest import TestCase |
279 | from ubuntuone.devtools.handlers import MementoHandler |
280 | |
281 | from ubuntu_sso import credentials |
282 | @@ -293,8 +290,11 @@ |
283 | self.patch(credentials.Keyring, 'get_credentials', |
284 | lambda kr, app: defer.succeed(TOKEN)) |
285 | error = 'Bla' |
286 | - self.patch(credentials.urllib2, 'urlopen', |
287 | - lambda *a, **kw: self.fail(error)) |
288 | + exception = SampleMiscException(error) |
289 | + failing_request = lambda *args, **kwargs: defer.fail(exception) |
290 | + self.patch(credentials.webclient.webclient_module().WebClient, |
291 | + "request", failing_request) |
292 | + |
293 | self._cred_cleared = False |
294 | self.patch(self.obj, 'clear_credentials', |
295 | lambda: setattr(self, '_cred_cleared', True)) |
296 | @@ -303,14 +303,15 @@ |
297 | |
298 | # error cb called correctly |
299 | msg = 'Problem opening the ping_url' |
300 | - self.assert_error_cb_called(msg=msg, detailed_error=FailTest(error)) |
301 | + self.assert_error_cb_called(msg=msg, detailed_error=exception) |
302 | self.assertEqual(result, None) |
303 | |
304 | # credentials cleared |
305 | self.assertTrue(self._cred_cleared) |
306 | |
307 | # exception logged |
308 | - self.assertTrue(self.memento.check_exception(FailTest, error)) |
309 | + self.assertTrue(self.memento.check_exception(SampleMiscException, |
310 | + error)) |
311 | |
312 | @defer.inlineCallbacks |
313 | def test_pings_url(self): |
314 | @@ -356,31 +357,35 @@ |
315 | self.assertEqual(self._called, (('denial', APP_NAME), {})) |
316 | |
317 | |
318 | +class FakeWebclient(object): |
319 | + """A fake webclient.""" |
320 | + |
321 | + def __init__(self): |
322 | + self.called = [] |
323 | + |
324 | + def request(self, *args, **kwargs): |
325 | + """Save a webclient request.""" |
326 | + self.called.append((args, kwargs)) |
327 | + return "response" |
328 | + |
329 | + def shutdown(self): |
330 | + """Shutdown this fake webclient.""" |
331 | + |
332 | + |
333 | class PingUrlTestCase(CredentialsTestCase): |
334 | """Test suite for the URL pinging.""" |
335 | |
336 | @defer.inlineCallbacks |
337 | def setUp(self): |
338 | yield super(PingUrlTestCase, self).setUp() |
339 | - self._request = None |
340 | - |
341 | - def faked_urlopen(request): |
342 | - """Fake urlopener.""" |
343 | - self._request = request |
344 | - response = urllib2.addinfourl(fp=open(os.path.devnull), |
345 | - headers=request.headers, |
346 | - url=request.get_full_url(), |
347 | - code=200) |
348 | - return response |
349 | - |
350 | - self.patch(credentials.urllib2, 'urlopen', faked_urlopen) |
351 | - self.patch(credentials.utils.timestamp_checker, "get_faithful_time", |
352 | - time.time) |
353 | + self.wc = FakeWebclient() |
354 | + self.patch(credentials.webclient, "webclient_factory", |
355 | + lambda *args: self.wc) |
356 | |
357 | @defer.inlineCallbacks |
358 | def test_ping_url_if_url_is_none(self): |
359 | """self.ping_url is opened.""" |
360 | - self.patch(credentials.urllib2, 'urlopen', self.fail) |
361 | + self.patch(self.wc, 'request', lambda *args, **kwargs: defer.fail()) |
362 | self.obj.ping_url = None |
363 | yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL, |
364 | credentials=TOKEN) |
365 | @@ -392,77 +397,78 @@ |
366 | yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL, |
367 | credentials=TOKEN) |
368 | |
369 | - self.assertIsInstance(self._request, credentials.urllib2.Request) |
370 | - self.assertEqual(self._request.get_full_url(), |
371 | - self.obj.ping_url + EMAIL) |
372 | + args, _kwargs = self.wc.called[0] |
373 | + self.assertEqual(args[0], self.obj.ping_url + EMAIL) |
374 | |
375 | @defer.inlineCallbacks |
376 | def test_request_is_signed_with_credentials(self): |
377 | """The http request to self.ping_url is signed with the credentials.""" |
378 | - |
379 | - def fake_it(*a, **kw): |
380 | - """Fake oauth_headers.""" |
381 | - self._set_called(*a, **kw) |
382 | - return {} |
383 | - |
384 | - self.patch(credentials.utils, 'oauth_headers', fake_it) |
385 | - result = yield self.obj._ping_url(APP_NAME, EMAIL, TOKEN) |
386 | - |
387 | - self.assertEqual(self._called, |
388 | - ((self.obj.ping_url + EMAIL, TOKEN), {})) |
389 | - self.assertEqual(result.code, 200) |
390 | + yield self.obj._ping_url(APP_NAME, EMAIL, TOKEN) |
391 | + |
392 | + _args, kwargs = self.wc.called[0] |
393 | + self.assertEqual(kwargs["oauth_credentials"], TOKEN) |
394 | |
395 | @defer.inlineCallbacks |
396 | def test_ping_url_error(self): |
397 | """Exception is handled if ping fails.""" |
398 | error = 'Blu' |
399 | - self.patch(credentials.urllib2, 'urlopen', lambda r: self.fail(error)) |
400 | - |
401 | + exception = SampleMiscException(error) |
402 | + self.patch(self.wc, "request", |
403 | + lambda *args, **kwargs: defer.fail(exception)) |
404 | yield self.obj._ping_url(APP_NAME, EMAIL, TOKEN) |
405 | |
406 | msg = 'Problem opening the ping_url' |
407 | - self.assert_error_cb_called(msg=msg, detailed_error=FailTest(error)) |
408 | - self.assertTrue(self.memento.check_exception(FailTest, error)) |
409 | + self.assert_error_cb_called(msg=msg, detailed_error=exception) |
410 | + self.assertTrue(self.memento.check_exception(SampleMiscException, |
411 | + error)) |
412 | |
413 | @defer.inlineCallbacks |
414 | def test_ping_url_formatting(self): |
415 | """The email is added as the first formatting argument.""" |
416 | self.obj.ping_url = u'http://example.com/{email}/something/else' |
417 | - result = yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL, |
418 | + yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL, |
419 | credentials=TOKEN) |
420 | |
421 | expected = self.obj.ping_url.format(email=EMAIL) |
422 | - self.assertEqual(expected, result.url) |
423 | + assert len(self.wc.called) > 0 |
424 | + args, _kwargs = self.wc.called[0] |
425 | + self.assertEqual(expected, args[0]) |
426 | |
427 | @defer.inlineCallbacks |
428 | def test_ping_url_formatting_with_query_params(self): |
429 | """The email is added as the first formatting argument.""" |
430 | self.obj.ping_url = u'http://example.com/{email}?something=else' |
431 | - result = yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL, |
432 | - credentials=TOKEN) |
433 | + yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL, |
434 | + credentials=TOKEN) |
435 | |
436 | expected = self.obj.ping_url.format(email=EMAIL) |
437 | - self.assertEqual(expected, result.url) |
438 | + assert len(self.wc.called) > 0 |
439 | + args, _kwargs = self.wc.called[0] |
440 | + self.assertEqual(expected, args[0]) |
441 | |
442 | @defer.inlineCallbacks |
443 | def test_ping_url_formatting_no_email_kwarg(self): |
444 | """The email is added as the first formatting argument.""" |
445 | self.obj.ping_url = u'http://example.com/{0}/yadda/?something=else' |
446 | - result = yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL, |
447 | - credentials=TOKEN) |
448 | + yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL, |
449 | + credentials=TOKEN) |
450 | |
451 | expected = self.obj.ping_url.format(EMAIL) |
452 | - self.assertEqual(expected, result.url) |
453 | + assert len(self.wc.called) > 0 |
454 | + args, _kwargs = self.wc.called[0] |
455 | + self.assertEqual(expected, args[0]) |
456 | |
457 | @defer.inlineCallbacks |
458 | def test_ping_url_formatting_no_format(self): |
459 | """The email is appended if formatting could not be accomplished.""" |
460 | self.obj.ping_url = u'http://example.com/yadda/' |
461 | - result = yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL, |
462 | - credentials=TOKEN) |
463 | + yield self.obj._ping_url(app_name=APP_NAME, email=EMAIL, |
464 | + credentials=TOKEN) |
465 | |
466 | expected = self.obj.ping_url + EMAIL |
467 | - self.assertEqual(expected, result.url) |
468 | + assert len(self.wc.called) > 0 |
469 | + args, _kwargs = self.wc.called[0] |
470 | + self.assertEqual(expected, args[0]) |
471 | |
472 | |
473 | class FindCredentialsTestCase(CredentialsTestCase): |
474 | |
475 | === modified file 'ubuntu_sso/utils/webclient/common.py' |
476 | --- ubuntu_sso/utils/webclient/common.py 2012-01-27 20:05:41 +0000 |
477 | +++ ubuntu_sso/utils/webclient/common.py 2012-02-03 17:25:21 +0000 |
478 | @@ -90,7 +90,7 @@ |
479 | def get_timestamp_checker(cls): |
480 | """Get the timestamp checker for this class of webclient.""" |
481 | if cls.timestamp_checker is None: |
482 | - cls.timestamp_checker = TimestampChecker(cls()) |
483 | + cls.timestamp_checker = TimestampChecker(cls) |
484 | return cls.timestamp_checker |
485 | |
486 | def get_timestamp(self): |
487 | |
488 | === modified file 'ubuntu_sso/utils/webclient/restful.py' |
489 | --- ubuntu_sso/utils/webclient/restful.py 2012-01-25 15:02:50 +0000 |
490 | +++ ubuntu_sso/utils/webclient/restful.py 2012-02-03 17:25:21 +0000 |
491 | @@ -59,3 +59,7 @@ |
492 | post_content=encoded_args, |
493 | extra_headers=POST_HEADERS) |
494 | defer.returnValue(json.loads(result.content)) |
495 | + |
496 | + def shutdown(self): |
497 | + """Stop the webclient used by this class.""" |
498 | + self.webclient.shutdown() |
499 | |
500 | === modified file 'ubuntu_sso/utils/webclient/tests/__init__.py' |
501 | --- ubuntu_sso/utils/webclient/tests/__init__.py 2011-12-15 16:16:37 +0000 |
502 | +++ ubuntu_sso/utils/webclient/tests/__init__.py 2012-02-03 17:25:21 +0000 |
503 | @@ -15,3 +15,63 @@ |
504 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
505 | |
506 | """Tests for the proxy-aware webclient.""" |
507 | + |
508 | +from twisted.application import internet, service |
509 | +from twisted.web import http, server |
510 | + |
511 | + |
512 | +class SaveHTTPChannel(http.HTTPChannel): |
513 | + """A save protocol to be used in tests.""" |
514 | + |
515 | + protocolInstance = None |
516 | + |
517 | + # pylint: disable=C0103 |
518 | + def connectionMade(self): |
519 | + """Keep track of the given protocol.""" |
520 | + SaveHTTPChannel.protocolInstance = self |
521 | + http.HTTPChannel.connectionMade(self) |
522 | + |
523 | + |
524 | +class SaveSite(server.Site): |
525 | + """A site that let us know when it closed.""" |
526 | + |
527 | + protocol = SaveHTTPChannel |
528 | + |
529 | + def __init__(self, *args, **kwargs): |
530 | + """Create a new instance.""" |
531 | + server.Site.__init__(self, *args, **kwargs) |
532 | + # we disable the timeout in the tests, we will deal with it manually. |
533 | + # pylint: disable=C0103 |
534 | + self.timeOut = None |
535 | + |
536 | + |
537 | +class BaseMockWebServer(object): |
538 | + """A mock webserver for testing""" |
539 | + |
540 | + def __init__(self): |
541 | + """Start up this instance.""" |
542 | + self.root = self.get_root_resource() |
543 | + self.site = SaveSite(self.root) |
544 | + application = service.Application('web') |
545 | + self.service_collection = service.IServiceCollection(application) |
546 | + #pylint: disable=E1101 |
547 | + self.tcpserver = internet.TCPServer(0, self.site) |
548 | + self.tcpserver.setServiceParent(self.service_collection) |
549 | + self.service_collection.startService() |
550 | + |
551 | + def get_root_resource(self): |
552 | + """Get the root resource with all the children.""" |
553 | + raise NotImplementedError |
554 | + |
555 | + def get_iri(self): |
556 | + """Build the iri for this mock server.""" |
557 | + #pylint: disable=W0212 |
558 | + port_num = self.tcpserver._port.getHost().port |
559 | + return u"http://127.0.0.1:%d/" % port_num |
560 | + |
561 | + def stop(self): |
562 | + """Shut it down.""" |
563 | + #pylint: disable=E1101 |
564 | + if self.site.protocol.protocolInstance: |
565 | + self.site.protocol.protocolInstance.timeoutConnection() |
566 | + return self.service_collection.stopService() |
567 | |
568 | === modified file 'ubuntu_sso/utils/webclient/tests/test_restful.py' |
569 | --- ubuntu_sso/utils/webclient/tests/test_restful.py 2012-01-25 15:02:50 +0000 |
570 | +++ ubuntu_sso/utils/webclient/tests/test_restful.py 2012-02-03 17:25:21 +0000 |
571 | @@ -43,12 +43,17 @@ |
572 | self.return_value = SAMPLE_RESPONSE |
573 | self.called = [] |
574 | self.init_kwargs = kwargs |
575 | + self.running = True |
576 | |
577 | def request(self, iri, *args, **kwargs): |
578 | """Return a deferred that will be fired with a Response object.""" |
579 | self.called.append((iri, args, kwargs)) |
580 | return defer.succeed(Response(self.return_value)) |
581 | |
582 | + def shutdown(self): |
583 | + """Stop this fake webclient.""" |
584 | + self.running = False |
585 | + |
586 | |
587 | class BaseTestCase(TestCase): |
588 | """The base for the Restful Client testcases.""" |
589 | @@ -75,11 +80,18 @@ |
590 | """Initialize this testcase.""" |
591 | yield super(RestfulClientTestCase, self).setUp() |
592 | self.rc = restful.RestfulClient(SAMPLE_SERVICE_IRI) |
593 | + self.addCleanup(self.rc.shutdown) |
594 | |
595 | def test_has_a_webclient(self): |
596 | """The RC has a webclient.""" |
597 | self.assertEqual(self.rc.webclient, self.wc) |
598 | |
599 | + def test_shutsdown_the_webclient(self): |
600 | + """Calling shutdown on the restful shuts down the webclient too.""" |
601 | + self.rc.shutdown() |
602 | + # pylint: disable=E1101 |
603 | + self.assertFalse(self.rc.webclient.running, "The webclient is stopped") |
604 | + |
605 | @defer.inlineCallbacks |
606 | def test_can_make_calls(self): |
607 | """The RC can make webcalls.""" |
608 | |
609 | === modified file 'ubuntu_sso/utils/webclient/tests/test_timestamp.py' |
610 | --- ubuntu_sso/utils/webclient/tests/test_timestamp.py 2012-01-19 00:39:05 +0000 |
611 | +++ ubuntu_sso/utils/webclient/tests/test_timestamp.py 2012-02-03 17:25:21 +0000 |
612 | @@ -15,12 +15,12 @@ |
613 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
614 | """Tests for the timestamp sync classes.""" |
615 | |
616 | -from twisted.application import internet, service |
617 | from twisted.internet import defer |
618 | from twisted.trial.unittest import TestCase |
619 | -from twisted.web import server, resource |
620 | +from twisted.web import resource |
621 | |
622 | -from ubuntu_sso.utils.webclient import timestamp, webclient_factory |
623 | +from ubuntu_sso.utils.webclient import timestamp, webclient_module |
624 | +from ubuntu_sso.utils.webclient.tests import BaseMockWebServer |
625 | |
626 | |
627 | class FakedError(Exception): |
628 | @@ -46,61 +46,45 @@ |
629 | return "" |
630 | |
631 | |
632 | -class MockWebServer(object): |
633 | +class MockWebServer(BaseMockWebServer): |
634 | """A mock webserver for testing.""" |
635 | |
636 | - # pylint: disable=E1101 |
637 | - def __init__(self): |
638 | - """Start up this instance.""" |
639 | - self.root = RootResource() |
640 | - site = server.Site(self.root) |
641 | - application = service.Application('web') |
642 | - self.service_collection = service.IServiceCollection(application) |
643 | - self.tcpserver = internet.TCPServer(0, site) |
644 | - self.tcpserver.setServiceParent(self.service_collection) |
645 | - self.service_collection.startService() |
646 | - |
647 | - def get_iri(self): |
648 | - """Build the url for this mock server.""" |
649 | - # pylint: disable=W0212 |
650 | - port_num = self.tcpserver._port.getHost().port |
651 | - return u"http://localhost:%d/" % port_num |
652 | - |
653 | - def stop(self): |
654 | - """Shut it down.""" |
655 | - self.service_collection.stopService() |
656 | + def get_root_resource(self): |
657 | + """Get the root resource with all the children.""" |
658 | + return RootResource() |
659 | |
660 | |
661 | class TimestampCheckerTestCase(TestCase): |
662 | """Tests for the timestamp checker.""" |
663 | |
664 | + timeout = 1 |
665 | + |
666 | @defer.inlineCallbacks |
667 | def setUp(self): |
668 | yield super(TimestampCheckerTestCase, self).setUp() |
669 | self.ws = MockWebServer() |
670 | self.addCleanup(self.ws.stop) |
671 | - self.wc = webclient_factory() |
672 | - self.addCleanup(self.wc.shutdown) |
673 | + self.webclient_class = webclient_module().WebClient |
674 | self.patch(timestamp.TimestampChecker, "SERVER_IRI", self.ws.get_iri()) |
675 | |
676 | @defer.inlineCallbacks |
677 | def test_returned_value_is_int(self): |
678 | """The returned value is an integer.""" |
679 | - checker = timestamp.TimestampChecker(self.wc) |
680 | + checker = timestamp.TimestampChecker(self.webclient_class) |
681 | result = yield checker.get_faithful_time() |
682 | self.assertEqual(type(result), int) |
683 | |
684 | @defer.inlineCallbacks |
685 | def test_first_call_does_head(self): |
686 | """The first call gets the clock from our web.""" |
687 | - checker = timestamp.TimestampChecker(self.wc) |
688 | + checker = timestamp.TimestampChecker(self.webclient_class) |
689 | yield checker.get_faithful_time() |
690 | self.assertEqual(self.ws.root.count, 1) |
691 | |
692 | @defer.inlineCallbacks |
693 | def test_second_call_is_cached(self): |
694 | """For the second call, the time is cached.""" |
695 | - checker = timestamp.TimestampChecker(self.wc) |
696 | + checker = timestamp.TimestampChecker(self.webclient_class) |
697 | yield checker.get_faithful_time() |
698 | yield checker.get_faithful_time() |
699 | self.assertEqual(self.ws.root.count, 1) |
700 | @@ -110,7 +94,7 @@ |
701 | """After some time, the cache expires.""" |
702 | fake_timestamp = 1 |
703 | self.patch(timestamp.time, "time", lambda: fake_timestamp) |
704 | - checker = timestamp.TimestampChecker(self.wc) |
705 | + checker = timestamp.TimestampChecker(self.webclient_class) |
706 | yield checker.get_faithful_time() |
707 | fake_timestamp += timestamp.TimestampChecker.CHECKING_INTERVAL |
708 | yield checker.get_faithful_time() |
709 | @@ -121,7 +105,7 @@ |
710 | """When server can't be reached, the skew is not updated.""" |
711 | fake_timestamp = 1 |
712 | self.patch(timestamp.time, "time", lambda: fake_timestamp) |
713 | - checker = timestamp.TimestampChecker(self.wc) |
714 | + checker = timestamp.TimestampChecker(self.webclient_class) |
715 | failing_get_server_time = lambda _: defer.fail(FakedError()) |
716 | self.patch(checker, "get_server_time", failing_get_server_time) |
717 | yield checker.get_faithful_time() |
718 | @@ -132,7 +116,7 @@ |
719 | @defer.inlineCallbacks |
720 | def test_server_date_sends_nocache_headers(self): |
721 | """Getting the server date sends the no-cache headers.""" |
722 | - checker = timestamp.TimestampChecker(self.wc) |
723 | + checker = timestamp.TimestampChecker(self.webclient_class) |
724 | yield checker.get_server_date_header(self.ws.get_iri()) |
725 | self.assertEqual(len(self.ws.root.request_headers), 1) |
726 | headers = self.ws.root.request_headers[0] |
727 | |
728 | === modified file 'ubuntu_sso/utils/webclient/tests/test_webclient.py' |
729 | --- ubuntu_sso/utils/webclient/tests/test_webclient.py 2012-02-01 19:21:28 +0000 |
730 | +++ ubuntu_sso/utils/webclient/tests/test_webclient.py 2012-02-03 17:25:21 +0000 |
731 | @@ -19,10 +19,9 @@ |
732 | import sys |
733 | import urllib |
734 | |
735 | -from twisted.application import internet, service |
736 | from twisted.cred import checkers, portal |
737 | from twisted.internet import defer |
738 | -from twisted.web import http, resource, server, guard |
739 | +from twisted.web import guard, http, resource |
740 | from urllib2 import urlparse |
741 | |
742 | from ubuntuone.devtools.testcases import TestCase |
743 | @@ -30,6 +29,7 @@ |
744 | |
745 | from ubuntu_sso.utils import webclient |
746 | from ubuntu_sso.utils.webclient.common import BaseWebClient, HeaderDict |
747 | +from ubuntu_sso.utils.webclient.tests import BaseMockWebServer |
748 | |
749 | ANY_VALUE = object() |
750 | SAMPLE_KEY = "result" |
751 | @@ -148,34 +148,11 @@ |
752 | return "ERROR: Expected OAuth header not present." |
753 | |
754 | |
755 | -class SaveHTTPChannel(http.HTTPChannel): |
756 | - """A save protocol to be used in tests.""" |
757 | - |
758 | - protocolInstance = None |
759 | - |
760 | - def connectionMade(self): |
761 | - """Keep track of the given protocol.""" |
762 | - SaveHTTPChannel.protocolInstance = self |
763 | - http.HTTPChannel.connectionMade(self) |
764 | - |
765 | - |
766 | -class SaveSite(server.Site): |
767 | - """A site that let us know when it closed.""" |
768 | - |
769 | - protocol = SaveHTTPChannel |
770 | - |
771 | - def __init__(self, *args, **kwargs): |
772 | - """Create a new instance.""" |
773 | - server.Site.__init__(self, *args, **kwargs) |
774 | - # we disable the timeout in the tests, we will deal with it manually. |
775 | - self.timeOut = None |
776 | - |
777 | - |
778 | -class MockWebServer(object): |
779 | - """A mock webserver for testing""" |
780 | - |
781 | - def __init__(self): |
782 | - """Start up this instance.""" |
783 | +class MockWebServer(BaseMockWebServer): |
784 | + """A mock webserver for the webclient tests.""" |
785 | + |
786 | + def get_root_resource(self): |
787 | + """Get the root resource with all the children.""" |
788 | root = resource.Resource() |
789 | root.putChild(SIMPLERESOURCE, SimpleResource()) |
790 | root.putChild(POSTABLERESOURECE, PostableResource()) |
791 | @@ -197,27 +174,7 @@ |
792 | guarded_resource = guard.HTTPAuthSessionWrapper(test_portal, |
793 | [cred_factory]) |
794 | root.putChild(GUARDED, guarded_resource) |
795 | - |
796 | - self.site = SaveSite(root) |
797 | - application = service.Application('web') |
798 | - self.service_collection = service.IServiceCollection(application) |
799 | - #pylint: disable=E1101 |
800 | - self.tcpserver = internet.TCPServer(0, self.site) |
801 | - self.tcpserver.setServiceParent(self.service_collection) |
802 | - self.service_collection.startService() |
803 | - |
804 | - def get_iri(self): |
805 | - """Build the iri for this mock server.""" |
806 | - #pylint: disable=W0212 |
807 | - port_num = self.tcpserver._port.getHost().port |
808 | - return u"http://127.0.0.1:%d/" % port_num |
809 | - |
810 | - def stop(self): |
811 | - """Shut it down.""" |
812 | - #pylint: disable=E1101 |
813 | - if self.site.protocol.protocolInstance: |
814 | - self.site.protocol.protocolInstance.timeoutConnection() |
815 | - return self.service_collection.stopService() |
816 | + return root |
817 | |
818 | |
819 | class FakeReactor(object): |
820 | @@ -261,7 +218,7 @@ |
821 | class WebClientTestCase(TestCase): |
822 | """Test for the webclient.""" |
823 | |
824 | - timeout = 8 |
825 | + timeout = 1 |
826 | |
827 | @defer.inlineCallbacks |
828 | def setUp(self): |
829 | @@ -271,8 +228,6 @@ |
830 | self.base_iri = self.ws.get_iri() |
831 | self.wc = webclient.webclient_factory() |
832 | self.addCleanup(self.wc.shutdown) |
833 | - # pylint: disable=W0511 |
834 | - # TODO: skewed timestamp correction |
835 | |
836 | @defer.inlineCallbacks |
837 | def test_request_takes_an_iri(self): |
838 | @@ -343,6 +298,8 @@ |
839 | @defer.inlineCallbacks |
840 | def test_request_is_oauth_signed(self): |
841 | """The request is oauth signed.""" |
842 | + tsc = self.wc.get_timestamp_checker() |
843 | + self.patch(tsc, "get_faithful_time", lambda: defer.succeed(1)) |
844 | result = yield self.wc.request(self.base_iri + OAUTHRESOURCE, |
845 | oauth_credentials=SAMPLE_CREDENTIALS) |
846 | self.assertEqual(SAMPLE_RESOURCE, result.content) |
847 | @@ -366,15 +323,14 @@ |
848 | @defer.inlineCallbacks |
849 | def test_returned_content_are_bytes(self): |
850 | """The returned content are bytes.""" |
851 | + tsc = self.wc.get_timestamp_checker() |
852 | + self.patch(tsc, "get_faithful_time", lambda: defer.succeed(1)) |
853 | result = yield self.wc.request(self.base_iri + OAUTHRESOURCE, |
854 | oauth_credentials=SAMPLE_CREDENTIALS) |
855 | self.assertTrue(isinstance(result.content, bytes), |
856 | "The type of %r must be bytes" % result.content) |
857 | |
858 | |
859 | -WebClientTestCase.skip = 'Tests failing, see bug #920591.' |
860 | - |
861 | - |
862 | class TimestampCheckerTestCase(TestCase): |
863 | """Tests for the timestampchecker classmethod.""" |
864 | |
865 | @@ -388,12 +344,7 @@ |
866 | def test_timestamp_checker_has_the_same_class_as_the_creator(self): |
867 | """The TimestampChecker has the same class.""" |
868 | tsc = self.wc.get_timestamp_checker() |
869 | - self.assertEqual(tsc.webclient.__class__, self.wc.__class__) |
870 | - |
871 | - def test_timestamp_checker_uses_different_webclient_than_the_creator(self): |
872 | - """The TimestampChecker uses a different webclient than the creator.""" |
873 | - tsc = self.wc.get_timestamp_checker() |
874 | - self.assertNotEqual(tsc.webclient, self.wc) |
875 | + self.assertEqual(tsc.webclient_class, self.wc.__class__) |
876 | |
877 | def test_timestamp_checker_is_the_same_for_all_webclients(self): |
878 | """The TimestampChecker is the same for all webclients.""" |
879 | |
880 | === modified file 'ubuntu_sso/utils/webclient/timestamp.py' |
881 | --- ubuntu_sso/utils/webclient/timestamp.py 2012-01-25 15:02:50 +0000 |
882 | +++ ubuntu_sso/utils/webclient/timestamp.py 2012-02-03 17:25:21 +0000 |
883 | @@ -33,18 +33,22 @@ |
884 | ERROR_INTERVAL = 30 # in seconds |
885 | SERVER_IRI = u"http://one.ubuntu.com/api/time" |
886 | |
887 | - def __init__(self, webclient): |
888 | + def __init__(self, webclient_class): |
889 | """Initialize this instance.""" |
890 | self.next_check = time.time() |
891 | self.skew = 0 |
892 | - self.webclient = webclient |
893 | + self.webclient_class = webclient_class |
894 | |
895 | @defer.inlineCallbacks |
896 | def get_server_date_header(self, server_iri): |
897 | """Get the server date using twisted webclient.""" |
898 | - response = yield self.webclient.request(server_iri, method="HEAD", |
899 | - extra_headers=NOCACHE_HEADERS) |
900 | - defer.returnValue(response.headers["Date"][0]) |
901 | + webclient = self.webclient_class() |
902 | + try: |
903 | + response = yield webclient.request(server_iri, method="HEAD", |
904 | + extra_headers=NOCACHE_HEADERS) |
905 | + defer.returnValue(response.headers["Date"][0]) |
906 | + finally: |
907 | + webclient.shutdown() |
908 | |
909 | @defer.inlineCallbacks |
910 | def get_server_time(self): |
Looks good!