Merge lp:~jml/launchpad/login-helper-love into lp:launchpad
- login-helper-love
- Merge into devel
Proposed by
Jonathan Lange
Status: | Merged |
---|---|
Approved by: | Robert Collins |
Approved revision: | no longer in the source branch. |
Merged at revision: | 11160 |
Proposed branch: | lp:~jml/launchpad/login-helper-love |
Merge into: | lp:launchpad |
Diff against target: |
692 lines (+336/-76) 9 files modified
lib/lp/code/model/tests/test_codeimportjob.py (+11/-27) lib/lp/services/tests/test_utils.py (+74/-1) lib/lp/services/utils.py (+15/-0) lib/lp/testing/__init__.py (+24/-37) lib/lp/testing/_login.py (+68/-2) lib/lp/testing/factory.py (+21/-4) lib/lp/testing/sampledata.py (+0/-2) lib/lp/testing/tests/test_factory.py (+10/-0) lib/lp/testing/tests/test_login.py (+113/-3) |
To merge this branch: | bzr merge lp:~jml/launchpad/login-helper-love |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Collins (community) | Approve | ||
Review via email: mp+30185@code.launchpad.net |
Commit message
Description of the change
This moves a bunch of login helpers to the _login module, which groups them all together nicely.
It gets rid of a bunch of custom login helpers from test_codeimportjob and makes the module completely non-reliant on sample data.
In the course of doing this, we add some generic-ish helpers for doing context manager -> decorator stuff, and a loginAsAnyone helper to the factory.
All of these changes are tested.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/code/model/tests/test_codeimportjob.py' |
2 | --- lib/lp/code/model/tests/test_codeimportjob.py 2010-07-10 14:20:23 +0000 |
3 | +++ lib/lp/code/model/tests/test_codeimportjob.py 2010-07-19 16:01:10 +0000 |
4 | @@ -18,8 +18,6 @@ |
5 | |
6 | import transaction |
7 | |
8 | -from twisted.python.util import mergeFunctionMetadata |
9 | - |
10 | from zope.component import getUtility |
11 | from zope.security.proxy import removeSecurityProxy |
12 | |
13 | @@ -36,10 +34,9 @@ |
14 | from lp.code.interfaces.codeimportjob import ( |
15 | ICodeImportJobSet, ICodeImportJobWorkflow) |
16 | from lp.code.interfaces.codeimportresult import ICodeImportResult |
17 | -from lp.registry.interfaces.person import IPersonSet |
18 | from lp.testing import ( |
19 | - ANONYMOUS, login, login_celebrity, logout, TestCaseWithFactory) |
20 | -from lp.testing.sampledata import NO_PRIVILEGE_EMAIL, VCS_IMPORTS_MEMBER_EMAIL |
21 | + ANONYMOUS, login, login_celebrity, logout, TestCaseWithFactory, |
22 | + with_anonymous_login, with_celebrity_logged_in) |
23 | from canonical.launchpad.testing.codeimporthelpers import ( |
24 | make_finished_import, make_running_import) |
25 | from canonical.launchpad.testing.pages import get_feedback_messages |
26 | @@ -960,23 +957,7 @@ |
27 | CodeImportReviewStatus.FAILING, code_import.review_status) |
28 | |
29 | |
30 | -def logged_in_as(email): |
31 | - """Return a decorator that wraps functions to runs logged in as `email`. |
32 | - """ |
33 | - def decorator(function): |
34 | - def decorated(*args, **kw): |
35 | - login(email) |
36 | - try: |
37 | - return function(*args, **kw) |
38 | - finally: |
39 | - logout() |
40 | - return mergeFunctionMetadata(function, decorated) |
41 | - return decorator |
42 | - |
43 | - |
44 | -# This is a dependence on the sample data: David Allouche is a member of the |
45 | -# ~vcs-imports celebrity team. |
46 | -logged_in_for_code_imports = logged_in_as(VCS_IMPORTS_MEMBER_EMAIL) |
47 | +logged_in_for_code_imports = with_celebrity_logged_in('vcs_imports') |
48 | |
49 | |
50 | class TestRequestJobUIRaces(TestCaseWithFactory): |
51 | @@ -998,12 +979,15 @@ |
52 | code_import_id = code_import.id |
53 | return code_import_id, branch_url |
54 | |
55 | - @logged_in_as(NO_PRIVILEGE_EMAIL) |
56 | def requestJobByUserWithDisplayName(self, code_import_id, displayname): |
57 | """Record a request for the job by a user with the given name.""" |
58 | - getUtility(ICodeImportJobWorkflow).requestJob( |
59 | - getUtility(ICodeImportSet).get(code_import_id).import_job, |
60 | - self.factory.makePerson(displayname=displayname)) |
61 | + self.factory.loginAsAnyone() |
62 | + try: |
63 | + getUtility(ICodeImportJobWorkflow).requestJob( |
64 | + getUtility(ICodeImportSet).get(code_import_id).import_job, |
65 | + self.factory.makePerson(displayname=displayname)) |
66 | + finally: |
67 | + logout() |
68 | |
69 | @logged_in_for_code_imports |
70 | def deleteJob(self, code_import_id): |
71 | @@ -1013,7 +997,7 @@ |
72 | getUtility(ICodeImportSet).get(code_import_id).updateFromData( |
73 | {'review_status': CodeImportReviewStatus.SUSPENDED}, user) |
74 | |
75 | - @logged_in_as(ANONYMOUS) |
76 | + @with_anonymous_login |
77 | def startJob(self, code_import_id): |
78 | """Mark the job as started on an arbitrary machine.""" |
79 | getUtility(ICodeImportJobWorkflow).startJob( |
80 | |
81 | === modified file 'lib/lp/services/tests/test_utils.py' |
82 | --- lib/lp/services/tests/test_utils.py 2010-04-12 05:52:41 +0000 |
83 | +++ lib/lp/services/tests/test_utils.py 2010-07-19 16:01:10 +0000 |
84 | @@ -5,10 +5,15 @@ |
85 | |
86 | __metaclass__ = type |
87 | |
88 | +from contextlib import contextmanager |
89 | import itertools |
90 | import unittest |
91 | |
92 | -from lp.services.utils import CachingIterator, iter_split |
93 | +from lp.services.utils import ( |
94 | + CachingIterator, |
95 | + decorate_with, |
96 | + iter_split, |
97 | + ) |
98 | from lp.testing import TestCase |
99 | |
100 | |
101 | @@ -67,5 +72,73 @@ |
102 | self.assertEqual([1,2,3,4], list(i1)) |
103 | |
104 | |
105 | +class TestDecorateWith(TestCase): |
106 | + """Tests for `decorate_with`.""" |
107 | + |
108 | + @contextmanager |
109 | + def trivialContextManager(self): |
110 | + """A trivial context manager, used for testing.""" |
111 | + yield |
112 | + |
113 | + def test_decorate_with_calls_context(self): |
114 | + # When run, a function decorated with decorated_with runs with the |
115 | + # context given to decorated_with. |
116 | + calls = [] |
117 | + @contextmanager |
118 | + def appending_twice(): |
119 | + calls.append('before') |
120 | + yield |
121 | + calls.append('after') |
122 | + @decorate_with(appending_twice) |
123 | + def function(): |
124 | + pass |
125 | + function() |
126 | + self.assertEquals(['before', 'after'], calls) |
127 | + |
128 | + def test_decorate_with_function(self): |
129 | + # The original function is actually called when we call the result of |
130 | + # decoration. |
131 | + calls = [] |
132 | + @decorate_with(self.trivialContextManager) |
133 | + def function(): |
134 | + calls.append('foo') |
135 | + function() |
136 | + self.assertEquals(['foo'], calls) |
137 | + |
138 | + def test_decorate_with_call_twice(self): |
139 | + # A function decorated with decorate_with can be called twice. |
140 | + calls = [] |
141 | + @decorate_with(self.trivialContextManager) |
142 | + def function(): |
143 | + calls.append('foo') |
144 | + function() |
145 | + function() |
146 | + self.assertEquals(['foo', 'foo'], calls) |
147 | + |
148 | + def test_decorate_with_arguments(self): |
149 | + # decorate_with passes through arguments. |
150 | + calls = [] |
151 | + @decorate_with(self.trivialContextManager) |
152 | + def function(*args, **kwargs): |
153 | + calls.append((args, kwargs)) |
154 | + function('foo', 'bar', qux=4) |
155 | + self.assertEquals([(('foo', 'bar'), {'qux': 4})], calls) |
156 | + |
157 | + def test_decorate_with_name_and_docstring(self): |
158 | + # decorate_with preserves function names and docstrings. |
159 | + @decorate_with(self.trivialContextManager) |
160 | + def arbitrary_name(): |
161 | + """Arbitrary docstring.""" |
162 | + self.assertEqual('arbitrary_name', arbitrary_name.__name__) |
163 | + self.assertEqual('Arbitrary docstring.', arbitrary_name.__doc__) |
164 | + |
165 | + def test_decorate_with_returns(self): |
166 | + # decorate_with returns the original function's return value. |
167 | + decorator = decorate_with(self.trivialContextManager) |
168 | + arbitrary_value = self.getUniqueString() |
169 | + result = decorator(lambda: arbitrary_value)() |
170 | + self.assertEqual(arbitrary_value, result) |
171 | + |
172 | + |
173 | def test_suite(): |
174 | return unittest.TestLoader().loadTestsFromName(__name__) |
175 | |
176 | === modified file 'lib/lp/services/utils.py' |
177 | --- lib/lp/services/utils.py 2010-04-12 10:47:13 +0000 |
178 | +++ lib/lp/services/utils.py 2010-07-19 16:01:10 +0000 |
179 | @@ -1,6 +1,8 @@ |
180 | # Copyright 2009 Canonical Ltd. This software is licensed under the |
181 | # GNU Affero General Public License version 3 (see the file LICENSE). |
182 | |
183 | +from __future__ import with_statement |
184 | + |
185 | """Generic Python utilities. |
186 | |
187 | Functions, lists and so forth. Nothing here that does system calls or network |
188 | @@ -10,6 +12,7 @@ |
189 | __metaclass__ = type |
190 | __all__ = [ |
191 | 'CachingIterator', |
192 | + 'decorate_with', |
193 | 'iter_split', |
194 | 'synchronize', |
195 | 'text_delta', |
196 | @@ -19,6 +22,8 @@ |
197 | import itertools |
198 | |
199 | from lazr.enum import BaseItem |
200 | + |
201 | +from twisted.python.util import mergeFunctionMetadata |
202 | from zope.security.proxy import isinstance as zope_isinstance |
203 | |
204 | |
205 | @@ -141,3 +146,13 @@ |
206 | break |
207 | self.data.append(item) |
208 | yield item |
209 | + |
210 | + |
211 | +def decorate_with(context_factory, *args, **kwargs): |
212 | + """Create a decorator that runs decorated functions with 'context'.""" |
213 | + def decorator(function): |
214 | + def decorated(*a, **kw): |
215 | + with context_factory(*args, **kwargs): |
216 | + return function(*a, **kw) |
217 | + return mergeFunctionMetadata(function, decorated) |
218 | + return decorator |
219 | |
220 | === modified file 'lib/lp/testing/__init__.py' |
221 | --- lib/lp/testing/__init__.py 2010-07-09 17:44:53 +0000 |
222 | +++ lib/lp/testing/__init__.py 2010-07-19 16:01:10 +0000 |
223 | @@ -10,9 +10,11 @@ |
224 | __metaclass__ = type |
225 | __all__ = [ |
226 | 'ANONYMOUS', |
227 | + 'anonymous_logged_in', |
228 | 'build_yui_unittest_suite', |
229 | 'BrowserTestCase', |
230 | 'capture_events', |
231 | + 'celebrity_logged_in', |
232 | 'FakeTime', |
233 | 'get_lsb_information', |
234 | 'is_logged_in', |
235 | @@ -27,6 +29,7 @@ |
236 | 'map_branch_contents', |
237 | 'normalize_whitespace', |
238 | 'oauth_access_token_for', |
239 | + 'person_logged_in', |
240 | 'record_statements', |
241 | 'run_with_login', |
242 | 'run_with_storm_debug', |
243 | @@ -39,6 +42,8 @@ |
244 | 'validate_mock_class', |
245 | 'WindmillTestCase', |
246 | 'with_anonymous_login', |
247 | + 'with_celebrity_logged_in', |
248 | + 'with_person_logged_in', |
249 | 'ws_object', |
250 | 'YUIUnitTestCase', |
251 | 'ZopeTestInSubProcess', |
252 | @@ -69,8 +74,6 @@ |
253 | import testtools |
254 | import transaction |
255 | |
256 | -from twisted.python.util import mergeFunctionMetadata |
257 | - |
258 | from windmill.authoring import WindmillTestClient |
259 | |
260 | from zope.component import adapter, getUtility |
261 | @@ -85,15 +88,27 @@ |
262 | from canonical.config import config |
263 | from canonical.launchpad.webapp.errorlog import ErrorReportEvent |
264 | from canonical.launchpad.webapp.interaction import ANONYMOUS |
265 | -from canonical.launchpad.webapp.interfaces import ILaunchBag |
266 | from canonical.launchpad.windmill.testing import constants |
267 | from lp.codehosting.vfs import branch_id_to_path, get_rw_server |
268 | from lp.registry.interfaces.packaging import IPackagingUtil |
269 | -# Import the login and logout functions here as it is a much better |
270 | +# Import the login helper functions here as it is a much better |
271 | # place to import them from in tests. |
272 | from lp.testing._login import ( |
273 | - is_logged_in, login, login_as, login_celebrity, login_person, login_team, |
274 | - logout) |
275 | + anonymous_logged_in, |
276 | + celebrity_logged_in, |
277 | + is_logged_in, |
278 | + login, |
279 | + login_as, |
280 | + login_celebrity, |
281 | + login_person, |
282 | + login_team, |
283 | + logout, |
284 | + person_logged_in, |
285 | + run_with_login, |
286 | + with_anonymous_login, |
287 | + with_celebrity_logged_in, |
288 | + with_person_logged_in, |
289 | + ) |
290 | # canonical.launchpad.ftests expects test_tales to be imported from here. |
291 | # XXX: JonathanLange 2010-01-01: Why?! |
292 | from lp.testing._tales import test_tales |
293 | @@ -221,11 +236,12 @@ |
294 | |
295 | class TestCase(testtools.TestCase): |
296 | """Provide Launchpad-specific test facilities.""" |
297 | + |
298 | def becomeDbUser(self, dbuser): |
299 | """Commit, then log into the database as `dbuser`. |
300 | - |
301 | + |
302 | For this to work, the test must run in a layer. |
303 | - |
304 | + |
305 | Try to test every code path at least once under a realistic db |
306 | user, or you'll hit privilege violations later on. |
307 | """ |
308 | @@ -821,35 +837,6 @@ |
309 | return distinfo |
310 | |
311 | |
312 | -def with_anonymous_login(function): |
313 | - """Decorate 'function' so that it runs in an anonymous login.""" |
314 | - def wrapped(*args, **kwargs): |
315 | - login(ANONYMOUS) |
316 | - try: |
317 | - return function(*args, **kwargs) |
318 | - finally: |
319 | - logout() |
320 | - return mergeFunctionMetadata(function, wrapped) |
321 | - |
322 | - |
323 | -@contextmanager |
324 | -def person_logged_in(person): |
325 | - current_person = getUtility(ILaunchBag).user |
326 | - logout() |
327 | - login_person(person) |
328 | - try: |
329 | - yield |
330 | - finally: |
331 | - logout() |
332 | - login_person(current_person) |
333 | - |
334 | - |
335 | -def run_with_login(person, function, *args, **kwargs): |
336 | - """Run 'function' with 'person' logged in.""" |
337 | - with person_logged_in(person): |
338 | - return function(*args, **kwargs) |
339 | - |
340 | - |
341 | def time_counter(origin=None, delta=timedelta(seconds=5)): |
342 | """A generator for yielding datetime values. |
343 | |
344 | |
345 | === modified file 'lib/lp/testing/_login.py' |
346 | --- lib/lp/testing/_login.py 2010-07-13 09:56:16 +0000 |
347 | +++ lib/lp/testing/_login.py 2010-07-19 16:01:10 +0000 |
348 | @@ -1,11 +1,15 @@ |
349 | # Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
350 | # GNU Affero General Public License version 3 (see the file LICENSE). |
351 | |
352 | +from __future__ import with_statement |
353 | + |
354 | # We like global statements! |
355 | # pylint: disable-msg=W0602,W0603 |
356 | __metaclass__ = type |
357 | |
358 | __all__ = [ |
359 | + 'anonymous_logged_in', |
360 | + 'celebrity_logged_in', |
361 | 'login', |
362 | 'login_as', |
363 | 'login_celebrity', |
364 | @@ -13,20 +17,28 @@ |
365 | 'login_team', |
366 | 'logout', |
367 | 'is_logged_in', |
368 | + 'person_logged_in', |
369 | + 'run_with_login', |
370 | + 'with_anonymous_login', |
371 | + 'with_celebrity_logged_in', |
372 | + 'with_person_logged_in', |
373 | ] |
374 | |
375 | +from contextlib import contextmanager |
376 | import random |
377 | |
378 | from zope.component import getUtility |
379 | from zope.security.management import endInteraction |
380 | -from zope.security.proxy import removeSecurityProxy |
381 | |
382 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
383 | from canonical.launchpad.webapp.interaction import ( |
384 | - ANONYMOUS, setupInteractionByEmail, setupInteractionForPerson) |
385 | + ANONYMOUS, get_current_principal, setupInteractionByEmail, |
386 | + setupInteractionForPerson) |
387 | +from canonical.launchpad.webapp.interfaces import ILaunchBag |
388 | from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
389 | from canonical.launchpad.webapp.vhosts import allvhosts |
390 | |
391 | +from lp.services.utils import decorate_with |
392 | |
393 | |
394 | _logged_in = False |
395 | @@ -141,3 +153,57 @@ |
396 | global _logged_in |
397 | _logged_in = False |
398 | endInteraction() |
399 | + |
400 | + |
401 | +def _with_login(login_method, identifier): |
402 | + """Make a context manager that runs with a particular log in.""" |
403 | + current_person = getUtility(ILaunchBag).user |
404 | + current_principal = get_current_principal() |
405 | + login_method(identifier) |
406 | + try: |
407 | + yield |
408 | + finally: |
409 | + if current_principal is None: |
410 | + logout() |
411 | + else: |
412 | + login_person(current_person) |
413 | + |
414 | + |
415 | +@contextmanager |
416 | +def person_logged_in(person): |
417 | + """Make a context manager for running logged in as 'person'. |
418 | + |
419 | + :param person: A person, an account, a team or ANONYMOUS. If a team, |
420 | + will log in as an arbitrary member of that team. |
421 | + """ |
422 | + return _with_login(login_as, person) |
423 | + |
424 | + |
425 | +@contextmanager |
426 | +def anonymous_logged_in(): |
427 | + """Make a context manager for running with the anonymous log in.""" |
428 | + return _with_login(login_as, ANONYMOUS) |
429 | + |
430 | + |
431 | +@contextmanager |
432 | +def celebrity_logged_in(celebrity_name): |
433 | + """Make a context manager for running logged in as a celebrity.""" |
434 | + return _with_login(login_celebrity, celebrity_name) |
435 | + |
436 | + |
437 | +with_anonymous_login = decorate_with(person_logged_in, None) |
438 | + |
439 | + |
440 | +def with_person_logged_in(person): |
441 | + return decorate_with(person_logged_in, person) |
442 | + |
443 | + |
444 | +def with_celebrity_logged_in(celebrity_name): |
445 | + """Decorate a function so that it's run with a celebrity logged in.""" |
446 | + return decorate_with(celebrity_logged_in, celebrity_name) |
447 | + |
448 | + |
449 | +def run_with_login(person, function, *args, **kwargs): |
450 | + """Run 'function' with 'person' logged in.""" |
451 | + with person_logged_in(person): |
452 | + return function(*args, **kwargs) |
453 | |
454 | === modified file 'lib/lp/testing/factory.py' |
455 | --- lib/lp/testing/factory.py 2010-07-16 18:15:44 +0000 |
456 | +++ lib/lp/testing/factory.py 2010-07-19 16:01:10 +0000 |
457 | @@ -121,7 +121,7 @@ |
458 | ISourcePackage, SourcePackageUrgency) |
459 | from lp.registry.interfaces.sourcepackagename import ( |
460 | ISourcePackageNameSet) |
461 | -from lp.registry.interfaces.ssh import ISSHKeySet, SSHKeyType |
462 | +from lp.registry.interfaces.ssh import ISSHKeySet |
463 | from lp.registry.interfaces.distributionmirror import ( |
464 | MirrorContent, MirrorSpeed) |
465 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
466 | @@ -148,9 +148,15 @@ |
467 | from lp.soyuz.model.processor import ProcessorFamily, ProcessorFamilySet |
468 | from lp.soyuz.model.publishing import ( |
469 | BinaryPackagePublishingHistory, SourcePackagePublishingHistory) |
470 | - |
471 | -from lp.testing import run_with_login, time_counter, login, logout, temp_dir |
472 | - |
473 | +from lp.testing import ( |
474 | + ANONYMOUS, |
475 | + login, |
476 | + login_as, |
477 | + logout, |
478 | + run_with_login, |
479 | + temp_dir, |
480 | + time_counter, |
481 | + ) |
482 | from lp.translations.interfaces.potemplate import IPOTemplateSet |
483 | from lp.translations.interfaces.translationimportqueue import ( |
484 | RosettaImportStatus) |
485 | @@ -341,6 +347,17 @@ |
486 | logout() |
487 | return result |
488 | |
489 | + def loginAsAnyone(self): |
490 | + """Log in as an arbitrary person. |
491 | + |
492 | + If you want to log in as a celebrity, including admins, see |
493 | + `lp.testing.login_celebrity`. |
494 | + """ |
495 | + login(ANONYMOUS) |
496 | + person = self.makePerson() |
497 | + login_as(person) |
498 | + return person |
499 | + |
500 | def makeCopyArchiveLocation(self, distribution=None, owner=None, |
501 | name=None, enabled=True): |
502 | """Create and return a new arbitrary location for copy packages.""" |
503 | |
504 | === modified file 'lib/lp/testing/sampledata.py' |
505 | --- lib/lp/testing/sampledata.py 2010-07-10 14:20:23 +0000 |
506 | +++ lib/lp/testing/sampledata.py 2010-07-19 16:01:10 +0000 |
507 | @@ -10,9 +10,7 @@ |
508 | __metaclass__ = type |
509 | __all__ = [ |
510 | 'NO_PRIVILEGE_EMAIL', |
511 | - 'VCS_IMPORTS_MEMBER_EMAIL', |
512 | ] |
513 | |
514 | |
515 | NO_PRIVILEGE_EMAIL = 'no-priv@canonical.com' |
516 | -VCS_IMPORTS_MEMBER_EMAIL = 'david.allouche@canonical.com' |
517 | |
518 | === modified file 'lib/lp/testing/tests/test_factory.py' |
519 | --- lib/lp/testing/tests/test_factory.py 2010-07-10 09:04:39 +0000 |
520 | +++ lib/lp/testing/tests/test_factory.py 2010-07-19 16:01:10 +0000 |
521 | @@ -7,6 +7,9 @@ |
522 | |
523 | import unittest |
524 | |
525 | +from zope.component import getUtility |
526 | + |
527 | +from canonical.launchpad.webapp.interfaces import ILaunchBag |
528 | from canonical.testing.layers import DatabaseFunctionalLayer |
529 | from lp.code.enums import CodeImportReviewStatus |
530 | from lp.testing import TestCaseWithFactory |
531 | @@ -29,6 +32,13 @@ |
532 | code_import = self.factory.makeCodeImport(review_status=status) |
533 | self.assertEqual(status, code_import.review_status) |
534 | |
535 | + def test_loginAsAnyone(self): |
536 | + # Login as anyone logs you in as any user. |
537 | + person = self.factory.loginAsAnyone() |
538 | + current_person = getUtility(ILaunchBag).user |
539 | + self.assertIsNot(None, person) |
540 | + self.assertEqual(person, current_person) |
541 | + |
542 | |
543 | def test_suite(): |
544 | return unittest.TestLoader().loadTestsFromName(__name__) |
545 | |
546 | === modified file 'lib/lp/testing/tests/test_login.py' |
547 | --- lib/lp/testing/tests/test_login.py 2010-07-13 09:56:16 +0000 |
548 | +++ lib/lp/testing/tests/test_login.py 2010-07-19 16:01:10 +0000 |
549 | @@ -1,6 +1,8 @@ |
550 | # Copyright 2010 Canonical Ltd. This software is licensed under the |
551 | # GNU Affero General Public License version 3 (see the file LICENSE). |
552 | |
553 | +from __future__ import with_statement |
554 | + |
555 | """Tests for the login helpers.""" |
556 | |
557 | __metaclass__ = type |
558 | @@ -15,8 +17,21 @@ |
559 | from canonical.launchpad.webapp.interfaces import IOpenLaunchBag |
560 | from canonical.testing.layers import DatabaseFunctionalLayer |
561 | from lp.testing import ( |
562 | - ANONYMOUS, is_logged_in, login, login_as, login_celebrity, login_person, |
563 | - login_team, logout) |
564 | + ANONYMOUS, |
565 | + anonymous_logged_in, |
566 | + celebrity_logged_in, |
567 | + is_logged_in, |
568 | + login, |
569 | + login_as, |
570 | + login_celebrity, |
571 | + login_person, |
572 | + login_team, |
573 | + logout, |
574 | + person_logged_in, |
575 | + with_anonymous_login, |
576 | + with_celebrity_logged_in, |
577 | + with_person_logged_in, |
578 | + ) |
579 | from lp.testing import TestCaseWithFactory |
580 | |
581 | |
582 | @@ -177,7 +192,7 @@ |
583 | login_celebrity('vcs_imports') |
584 | vcs_imports = getUtility(ILaunchpadCelebrities).vcs_imports |
585 | person = self.getLoggedInPerson() |
586 | - self.assertTrue(person.inTeam, vcs_imports) |
587 | + self.assertTrue(person.inTeam(vcs_imports)) |
588 | |
589 | def test_login_nonexistent_celebrity(self): |
590 | # login_celebrity raises ValueError when called with a non-existent |
591 | @@ -186,6 +201,101 @@ |
592 | e = self.assertRaises(ValueError, login_celebrity, 'nonexistent') |
593 | self.assertEqual(str(e), "No such celebrity: 'nonexistent'") |
594 | |
595 | + def test_person_logged_in(self): |
596 | + # The person_logged_in context manager runs with a person logged in. |
597 | + person = self.factory.makePerson() |
598 | + with person_logged_in(person): |
599 | + self.assertLoggedIn(person) |
600 | + |
601 | + def test_person_logged_in_restores_person(self): |
602 | + # Once outside of the person_logged_in context, the originally |
603 | + # logged-in person is re-logged in. |
604 | + a = self.factory.makePerson() |
605 | + login_as(a) |
606 | + b = self.factory.makePerson() |
607 | + with person_logged_in(b): |
608 | + self.assertLoggedIn(b) |
609 | + self.assertLoggedIn(a) |
610 | + |
611 | + def test_person_logged_in_restores_logged_out(self): |
612 | + # If we are logged out before the person_logged_in context, then we |
613 | + # are logged out afterwards. |
614 | + person = self.factory.makePerson() |
615 | + logout() |
616 | + with person_logged_in(person): |
617 | + pass |
618 | + self.assertLoggedOut() |
619 | + |
620 | + def test_person_logged_in_restores_person_even_when_raises(self): |
621 | + # Once outside of the person_logged_in context, the originially |
622 | + # logged-in person is re-logged in. |
623 | + a = self.factory.makePerson() |
624 | + login_as(a) |
625 | + b = self.factory.makePerson() |
626 | + try: |
627 | + with person_logged_in(b): |
628 | + 1/0 |
629 | + except ZeroDivisionError: |
630 | + pass |
631 | + self.assertLoggedIn(a) |
632 | + |
633 | + def test_team_logged_in(self): |
634 | + # person_logged_in also works when given teams. |
635 | + team = self.factory.makeTeam() |
636 | + with person_logged_in(team): |
637 | + person = self.getLoggedInPerson() |
638 | + self.assertTrue(person.inTeam(team)) |
639 | + |
640 | + def test_celebrity_logged_in(self): |
641 | + # celebrity_logged_in runs in a context where a celebrity is logged |
642 | + # in. |
643 | + vcs_imports = getUtility(ILaunchpadCelebrities).vcs_imports |
644 | + with celebrity_logged_in('vcs_imports'): |
645 | + person = self.getLoggedInPerson() |
646 | + self.assertTrue(person.inTeam(vcs_imports)) |
647 | + |
648 | + def test_celebrity_logged_in_restores_person(self): |
649 | + # Once outside of the celebrity_logged_in context, the originally |
650 | + # logged-in person is re-logged in. |
651 | + person = self.factory.makePerson() |
652 | + login_as(person) |
653 | + with celebrity_logged_in('vcs_imports'): |
654 | + pass |
655 | + self.assertLoggedIn(person) |
656 | + |
657 | + def test_with_celebrity_logged_in(self): |
658 | + # with_celebrity_logged_in decorates a function so that it runs with |
659 | + # the given person logged in. |
660 | + vcs_imports = getUtility(ILaunchpadCelebrities).vcs_imports |
661 | + @with_celebrity_logged_in('vcs_imports') |
662 | + def f(): |
663 | + return self.getLoggedInPerson() |
664 | + logout() |
665 | + person = f() |
666 | + self.assertTrue(person.inTeam, vcs_imports) |
667 | + |
668 | + def test_with_person_logged_in(self): |
669 | + person = self.factory.makePerson() |
670 | + @with_person_logged_in(person) |
671 | + def f(): |
672 | + return self.getLoggedInPerson() |
673 | + logout() |
674 | + logged_in = f() |
675 | + self.assertEqual(person, logged_in) |
676 | + |
677 | + def test_with_anonymous_log_in(self): |
678 | + # with_anonymous_login logs in as the anonymous user. |
679 | + @with_anonymous_login |
680 | + def f(): |
681 | + return self.getLoggedInPerson() |
682 | + person = f() |
683 | + self.assertEqual(ANONYMOUS, person) |
684 | + |
685 | + def test_anonymous_log_in(self): |
686 | + # anonymous_logged_in is a context logged in as anonymous. |
687 | + with anonymous_logged_in(): |
688 | + self.assertLoggedIn(ANONYMOUS) |
689 | + |
690 | |
691 | def test_suite(): |
692 | return unittest.TestLoader().loadTestsFromName(__name__) |
Thanks