Merge lp:~jml/launchpad/login-helper-love into lp:launchpad

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
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+30185@code.launchpad.net

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.
Revision history for this message
Robert Collins (lifeless) wrote :

Thanks

review: Approve

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__)