Merge lp:~deryck/launchpad/better-testing-for-status-changes into lp:launchpad

Proposed by Deryck Hodge
Status: Merged
Approved by: Deryck Hodge
Approved revision: no longer in the source branch.
Merged at revision: 11754
Proposed branch: lp:~deryck/launchpad/better-testing-for-status-changes
Merge into: lp:launchpad
Diff against target: 534 lines (+366/-146)
2 files modified
lib/lp/bugs/doc/bugtask-status-changes.txt (+51/-131)
lib/lp/bugs/tests/test_bugtask_status.py (+315/-15)
To merge this branch: bzr merge lp:~deryck/launchpad/better-testing-for-status-changes
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+38552@code.launchpad.net

Commit message

Refactor doctest for bugtask status changes into separate unit test and documentation.

Description of the change

This branch is the first toward a fix for Bug #126516, or more specifically for a fix to limit transitioning away from Fix Released status. Before this branch the tests for bugtask status changes was a mix of a unit test and doctest, both living under lp.bugs.tests.

The branch refactors that mess, opting to do a complete unit test of BugTask.transitionToStatus and BugTask.canTransitionToStatus. The doctest is then made to be documentation and moved to lp.bugs.doc. Some example code is left in the doctest, though refactored to use factory generated data.

To test, run:

./bin/test -cvvt bugtask-status-changes
./bin/test -cvvt test_bugtask_status

Thanks for the review!

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

(17:52:07) adeuring: deryck: I think it might make sense to change the test setup so that the product owner is not a member of the bug supervisor team; right now, the term "or user.inTeam(pillar.owner)" in canTransitionToStatus() is not executed
(17:52:53) deryck: adeuring, ok, that's a good idea. I can do that.
(17:53:07) adeuring: deryck: thanks! another suggestion, before you start ;)
(17:53:16) deryck: sure
(17:55:28) salgado heißt jetzt salgado-lunch
(17:55:30) adeuring: deryck: you don't test at all for celebrities.bug_watch_updater, celebrities.bug_importer, celebrities.janitor. You could add these tests quickly by definig a base class for tests of "extended permissions", where you do not define the user who is tested, then derive the "real" test classes for supervisor, owner and the celebrities.
(17:55:45) adeuring: That should make the tests a bit shorter and more comprehensive
(17:57:44) deryck: sure, I like that idea. I can do that, too.
(17:57:59) adeuring: deryck: cool, thanks!

Revision history for this message
Abel Deuring (adeuring) wrote :

Hi Deryck,

thanks for the refactoring. r=me.

Just a few nitpicks:

could you add TestCases for the celebrities bug_watch_updater,
bug_importer, janitor?

> +class TestBugTaskStatusTransitionForPrivilegedUserBase:
> + """Base class used to test privileged users and status transitions."""
> +
> + layer = LaunchpadFunctionalLayer
> +
> + def setUp(self):
> + super(TestBugTaskStatusTransitionForPrivilegedUserBase, self).setUp()
> + # Creation of task and target are deferred to subclasses.
> + self.task = None
> + self.person = None
> + self.makePersonAndTask()
> +
> + def makePersonAndTask(self):
> + """Create a bug task and privileged person for this task.
> +
> + This method is user by subclasses to correctly setup
> + each test.
> + """

I think this should be something like "is (overloaded|implemented) by
subclasses"

cheers
Abel

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== renamed file 'lib/lp/bugs/tests/test_bugtask_status.txt' => 'lib/lp/bugs/doc/bugtask-status-changes.txt'
2--- lib/lp/bugs/tests/test_bugtask_status.txt 2010-10-09 16:36:22 +0000
3+++ lib/lp/bugs/doc/bugtask-status-changes.txt 2010-10-19 13:36:54 +0000
4@@ -1,137 +1,57 @@
5-= Setting Bug Statuses =
6-
7- >>> from canonical.launchpad.interfaces import (
8- ... CreateBugParams, IPersonSet, IProductSet)
9+Changing Bug Task Status
10+========================
11+
12+Restrictions
13+------------
14+
15+There are a few simple rules around who can change the status of a
16+bug task. There are three statuses that can only be set by either
17+the project registrant or the bug supervisor:
18+
19+ * Won't Fix
20+ * Expired
21+ * Triaged
22+
23+ >>> owner = factory.makePerson()
24+ >>> product = factory.makeProduct(owner=owner)
25+ >>> bugtask = factory.makeBugTask(target=product)
26+ >>> user = factory.makePerson()
27+
28 >>> from lp.bugs.interfaces.bugtask import BugTaskStatus
29-
30- >>> firefox = getUtility(IProductSet).getByName('firefox')
31- >>> nopriv_user = getUtility(IPersonSet).getByName('no-priv')
32- >>> login('foo.bar@canonical.com')
33- >>> firefox.setBugSupervisor(nopriv_user, nopriv_user)
34- >>> login('no-priv@canonical.com')
35- >>> bug_params = CreateBugParams(
36- ... nopriv_user, 'Test bug', 'Something')
37- >>> firefox_bug = firefox.createBug(bug_params)
38-
39-Bug Supervisors can transition bugs to the Won't Fix, Expired and
40-Triaged statuses.
41-
42- >>> [firefox_bugtask] = firefox_bug.bugtasks
43- >>> firefox_bugtask.transitionToStatus(
44- ... BugTaskStatus.WONTFIX, nopriv_user)
45- >>> print firefox_bugtask.status.title
46- Won't Fix
47-
48- >>> firefox_bugtask.transitionToStatus(
49- ... BugTaskStatus.EXPIRED, nopriv_user)
50- >>> print firefox_bugtask.status.title
51- Expired
52-
53- >>> firefox_bugtask.transitionToStatus(
54- ... BugTaskStatus.TRIAGED, nopriv_user)
55- >>> print firefox_bugtask.status.title
56- Triaged
57-
58-The product registrant can transition to the Won't Fix, Expired and
59-Triaged statuses too.
60-
61- >>> firefox_bugtask.transitionToStatus(
62- ... BugTaskStatus.CONFIRMED, nopriv_user)
63- >>> print firefox_bugtask.status.title
64- Confirmed
65-
66- >>> firefox.owner.inTeam(firefox.bug_supervisor)
67- False
68-
69- >>> firefox_bugtask.transitionToStatus(
70- ... BugTaskStatus.WONTFIX, firefox.owner)
71- >>> print firefox_bugtask.status.title
72- Won't Fix
73-
74- >>> firefox_bugtask.transitionToStatus(
75- ... BugTaskStatus.EXPIRED, firefox.owner)
76- >>> print firefox_bugtask.status.title
77- Expired
78-
79- >>> firefox_bugtask.transitionToStatus(
80- ... BugTaskStatus.TRIAGED, firefox.owner)
81- >>> print firefox_bugtask.status.title
82- Triaged
83-
84-Users who are not bug supervisors or product registrants cannot
85-transition the status to Won't Fix or Triaged. The option is not
86-exposed in the UI, but we also enforce this rule at the database level
87-to ensure that all call sites adhere to this.
88-
89- >>> login('foo.bar@canonical.com')
90- >>> firefox.setBugSupervisor(None, None)
91-
92- >>> login('no-priv@canonical.com')
93-
94- >>> firefox_bugtask.transitionToStatus(
95- ... BugTaskStatus.WONTFIX, nopriv_user)
96- Traceback (most recent call last):
97- ...
98- UserCannotEditBugTaskStatus: Only Bug Supervisors may change status to Won't Fix.
99-
100- >>> firefox_bugtask.transitionToStatus(
101- ... BugTaskStatus.EXPIRED, nopriv_user)
102- Traceback (most recent call last):
103- ...
104- UserCannotEditBugTaskStatus: Only Bug Supervisors may change status to Expired.
105-
106- >>> firefox_bugtask.transitionToStatus(
107- ... BugTaskStatus.TRIAGED, nopriv_user)
108- Traceback (most recent call last):
109- ...
110- UserCannotEditBugTaskStatus: Only Bug Supervisors may change status to Triaged.
111-
112-Users who are not bug supervisors or product registrants cannot
113-transition the status from Won't Fix to anything else.
114-
115- >>> login('foo.bar@canonical.com')
116- >>> foo_bar = getUtility(ILaunchBag).user
117- >>> firefox.setBugSupervisor(foo_bar, foo_bar)
118- >>> firefox_bugtask.transitionToStatus(
119- ... BugTaskStatus.WONTFIX, foo_bar)
120-
121- >>> login('no-priv@canonical.com')
122- >>> firefox_bugtask.transitionToStatus(
123- ... BugTaskStatus.NEW, nopriv_user)
124- Traceback (most recent call last):
125- ...
126- UserCannotEditBugTaskStatus: Only Bug Supervisors may change status to New.
127+ >>> login_person(owner)
128+ >>> bugtask.transitionToStatus(BugTaskStatus.WONTFIX, owner)
129+ >>> print bugtask.status.title
130+ Won't Fix
131+
132+Regular users of Launchpad cannot transition a bug task to any of
133+these statuses.
134+
135+An additional restraint is added to Won't Fix. Only the product
136+registrant or bug supervisor can change from this status to any
137+other status.
138+
139+ >>> login_person(user)
140+ >>> bugtask.transitionToStatus(BugTaskStatus.CONFIRMED, user)
141+ Traceback (most recent call last):
142+ ...
143+ UserCannotEditBugTaskStatus...
144+
145+This is fully tested in
146+lp.bugs.tests.test_bugtask_status.TestBugTaskStatusSetting.
147+
148+Testing for Permission
149+----------------------
150
151 The method IBugTask.canTransitionToStatus comes in handy here. It
152 tells us if a transition to a status is permitted. It is *not* a
153-dry-run of transitionToStatus, but is good enough and fast enough to
154-be used by UI code, e.g. to display only those statuses to which a
155-user can transition a particular bugtask.
156-
157- >>> login('foo.bar@canonical.com')
158- >>> firefox_bugtask.transitionToStatus(
159- ... BugTaskStatus.TRIAGED, foo_bar)
160-
161- >>> login('no-priv@canonical.com')
162-
163- >>> firefox_bugtask.canTransitionToStatus(
164- ... BugTaskStatus.WONTFIX, nopriv_user)
165- False
166-
167- >>> firefox_bugtask.canTransitionToStatus(
168- ... BugTaskStatus.TRIAGED, nopriv_user)
169- False
170-
171- >>> firefox_bugtask.canTransitionToStatus(
172- ... BugTaskStatus.INCOMPLETE, nopriv_user)
173+dry-run of IBugTask.transitionToStatus, but is good enough and fast
174+enough to be used by UI code, e.g. to display only those statuses to
175+which a user can transition a particular bugtask.
176+
177+ >>> bugtask.canTransitionToStatus(BugTaskStatus.TRIAGED, owner)
178 True
179-
180- >>> login('foo.bar@canonical.com')
181- >>> firefox_bugtask.transitionToStatus(
182- ... BugTaskStatus.WONTFIX, getUtility(ILaunchBag).user)
183-
184- >>> login('no-priv@canonical.com')
185-
186- >>> firefox_bugtask.canTransitionToStatus(
187- ... BugTaskStatus.NEW, nopriv_user)
188+ >>> bugtask.canTransitionToStatus(BugTaskStatus.TRIAGED, user)
189 False
190+
191+This method is fully tested in
192+lp.bugs.tests.test_bugtask_status.TestCanTransitionToStatus.
193
194=== modified file 'lib/lp/bugs/tests/test_bugtask_status.py'
195--- lib/lp/bugs/tests/test_bugtask_status.py 2010-10-04 19:50:45 +0000
196+++ lib/lp/bugs/tests/test_bugtask_status.py 2010-10-19 13:36:54 +0000
197@@ -1,22 +1,322 @@
198 # Copyright 2009 Canonical Ltd. This software is licensed under the
199 # GNU Affero General Public License version 3 (see the file LICENSE).
200
201-"""Test for choosing the request and publication."""
202+"""Tests for bug task status transitions."""
203
204 __metaclass__ = type
205
206-from canonical.launchpad.testing.systemdocs import (
207- LayeredDocFileSuite,
208- setUp,
209- tearDown,
210+from zope.component import getUtility
211+from zope.security.proxy import removeSecurityProxy
212+
213+from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
214+from canonical.testing.layers import LaunchpadFunctionalLayer
215+from lp.bugs.interfaces.bugtask import UserCannotEditBugTaskStatus
216+from lp.bugs.model.bugtask import BugTaskStatus
217+from lp.testing import (
218+ person_logged_in,
219+ TestCaseWithFactory,
220 )
221-from canonical.testing.layers import LaunchpadFunctionalLayer
222-
223-
224-def test_suite():
225- suite = LayeredDocFileSuite(
226- 'test_bugtask_status.txt',
227- layer=LaunchpadFunctionalLayer, setUp=setUp, tearDown=tearDown,
228- )
229- return suite
230-
231+
232+
233+class TestBugTaskStatusTransitionForUser(TestCaseWithFactory):
234+ """Test bugtask status transitions for a regular logged in user."""
235+
236+ layer = LaunchpadFunctionalLayer
237+
238+ def setUp(self):
239+ super(TestBugTaskStatusTransitionForUser, self).setUp()
240+ self.user = self.factory.makePerson()
241+ self.task = self.factory.makeBugTask()
242+
243+ def test_user_transition_all_statuses(self):
244+ # A regular user should not be able to set statuses in
245+ # BUG_SUPERVISOR_BUGTASK_STATUSES, but can set any
246+ # other status.
247+ self.assertEqual(self.task.status, BugTaskStatus.NEW)
248+ with person_logged_in(self.user):
249+ self.assertRaises(
250+ UserCannotEditBugTaskStatus, self.task.transitionToStatus,
251+ BugTaskStatus.WONTFIX, self.user)
252+ self.assertRaises(
253+ UserCannotEditBugTaskStatus, self.task.transitionToStatus,
254+ BugTaskStatus.EXPIRED, self.user)
255+ self.assertRaises(
256+ UserCannotEditBugTaskStatus, self.task.transitionToStatus,
257+ BugTaskStatus.TRIAGED, self.user)
258+ self.task.transitionToStatus(BugTaskStatus.NEW, self.user)
259+ self.assertEqual(self.task.status, BugTaskStatus.NEW)
260+ self.task.transitionToStatus(
261+ BugTaskStatus.INCOMPLETE, self.user)
262+ self.assertEqual(self.task.status, BugTaskStatus.INCOMPLETE)
263+ self.task.transitionToStatus(BugTaskStatus.OPINION, self.user)
264+ self.assertEqual(self.task.status, BugTaskStatus.OPINION)
265+ self.task.transitionToStatus(BugTaskStatus.INVALID, self.user)
266+ self.assertEqual(self.task.status, BugTaskStatus.INVALID)
267+ self.task.transitionToStatus(BugTaskStatus.CONFIRMED, self.user)
268+ self.assertEqual(self.task.status, BugTaskStatus.CONFIRMED)
269+ self.task.transitionToStatus(
270+ BugTaskStatus.INPROGRESS, self.user)
271+ self.assertEqual(self.task.status, BugTaskStatus.INPROGRESS)
272+ self.task.transitionToStatus(
273+ BugTaskStatus.FIXCOMMITTED, self.user)
274+ self.assertEqual(self.task.status, BugTaskStatus.FIXCOMMITTED)
275+ self.task.transitionToStatus(
276+ BugTaskStatus.FIXRELEASED, self.user)
277+ self.assertEqual(self.task.status, BugTaskStatus.FIXRELEASED)
278+
279+ def test_user_cannot_unset_wont_fix_status(self):
280+ # A regular user should not be able to transition a bug away
281+ # from Won't Fix.
282+ removeSecurityProxy(self.task).status = BugTaskStatus.WONTFIX
283+ with person_logged_in(self.user):
284+ self.assertRaises(
285+ UserCannotEditBugTaskStatus, self.task.transitionToStatus,
286+ BugTaskStatus.CONFIRMED, self.user)
287+
288+ def test_user_canTransitionToStatus(self):
289+ # Regular user cannot transition to BUG_SUPERVISOR_BUGTASK_STATUSES,
290+ # but can transition to any other status.
291+ self.assertEqual(
292+ self.task.canTransitionToStatus(
293+ BugTaskStatus.WONTFIX, self.user),
294+ False)
295+ self.assertEqual(
296+ self.task.canTransitionToStatus(
297+ BugTaskStatus.EXPIRED, self.user),
298+ False)
299+ self.assertEqual(
300+ self.task.canTransitionToStatus(
301+ BugTaskStatus.TRIAGED, self.user),
302+ False)
303+ self.assertEqual(
304+ self.task.canTransitionToStatus(
305+ BugTaskStatus.NEW, self.user),
306+ True)
307+ self.assertEqual(
308+ self.task.canTransitionToStatus(
309+ BugTaskStatus.INCOMPLETE, self.user), True)
310+ self.assertEqual(
311+ self.task.canTransitionToStatus(
312+ BugTaskStatus.OPINION, self.user),
313+ True)
314+ self.assertEqual(
315+ self.task.canTransitionToStatus(
316+ BugTaskStatus.INVALID, self.user),
317+ True)
318+ self.assertEqual(
319+ self.task.canTransitionToStatus(
320+ BugTaskStatus.CONFIRMED, self.user),
321+ True)
322+ self.assertEqual(
323+ self.task.canTransitionToStatus(
324+ BugTaskStatus.INPROGRESS, self.user),
325+ True)
326+ self.assertEqual(
327+ self.task.canTransitionToStatus(
328+ BugTaskStatus.FIXCOMMITTED, self.user),
329+ True)
330+ self.assertEqual(
331+ self.task.canTransitionToStatus(
332+ BugTaskStatus.FIXRELEASED, self.user),
333+ True)
334+
335+ def test_user_canTransitionToStatus_from_wontfix(self):
336+ # A regular user cannot transition away from Won't Fix,
337+ # so canTransitionToStatus should return False.
338+ removeSecurityProxy(self.task).status = BugTaskStatus.WONTFIX
339+ self.assertEqual(
340+ self.task.canTransitionToStatus(
341+ BugTaskStatus.NEW, self.user),
342+ False)
343+
344+
345+class TestBugTaskStatusTransitionForPrivilegedUserBase:
346+ """Base class used to test privileged users and status transitions."""
347+
348+ layer = LaunchpadFunctionalLayer
349+
350+ def setUp(self):
351+ super(TestBugTaskStatusTransitionForPrivilegedUserBase, self).setUp()
352+ # Creation of task and target are deferred to subclasses.
353+ self.task = None
354+ self.person = None
355+ self.makePersonAndTask()
356+
357+ def makePersonAndTask(self):
358+ """Create a bug task and privileged person for this task.
359+
360+ This method is implemented by subclasses to correctly setup
361+ each test.
362+ """
363+ raise NotImplementedError(self.makePersonAndTask)
364+
365+ def test_privileged_user_transition_any_status(self):
366+ # Privileged users (like owner or bug supervisor) should
367+ # be able to set any status.
368+ with person_logged_in(self.person):
369+ self.task.transitionToStatus(BugTaskStatus.WONTFIX, self.person)
370+ self.assertEqual(self.task.status, BugTaskStatus.WONTFIX)
371+ self.task.transitionToStatus(BugTaskStatus.EXPIRED, self.person)
372+ self.assertEqual(self.task.status, BugTaskStatus.EXPIRED)
373+ self.task.transitionToStatus(BugTaskStatus.TRIAGED, self.person)
374+ self.assertEqual(self.task.status, BugTaskStatus.TRIAGED)
375+ self.task.transitionToStatus(BugTaskStatus.NEW, self.person)
376+ self.assertEqual(self.task.status, BugTaskStatus.NEW)
377+ self.task.transitionToStatus(
378+ BugTaskStatus.INCOMPLETE, self.person)
379+ self.assertEqual(self.task.status, BugTaskStatus.INCOMPLETE)
380+ self.task.transitionToStatus(BugTaskStatus.OPINION, self.person)
381+ self.assertEqual(self.task.status, BugTaskStatus.OPINION)
382+ self.task.transitionToStatus(BugTaskStatus.INVALID, self.person)
383+ self.assertEqual(self.task.status, BugTaskStatus.INVALID)
384+ self.task.transitionToStatus(BugTaskStatus.CONFIRMED, self.person)
385+ self.assertEqual(self.task.status, BugTaskStatus.CONFIRMED)
386+ self.task.transitionToStatus(
387+ BugTaskStatus.INPROGRESS, self.person)
388+ self.assertEqual(self.task.status, BugTaskStatus.INPROGRESS)
389+ self.task.transitionToStatus(
390+ BugTaskStatus.FIXCOMMITTED, self.person)
391+ self.assertEqual(self.task.status, BugTaskStatus.FIXCOMMITTED)
392+ self.task.transitionToStatus(
393+ BugTaskStatus.FIXRELEASED, self.person)
394+ self.assertEqual(self.task.status, BugTaskStatus.FIXRELEASED)
395+
396+ def test_privileged_user_can_unset_wont_fix_status(self):
397+ # Privileged users can transition away from Won't Fix.
398+ removeSecurityProxy(self.task).status = BugTaskStatus.WONTFIX
399+ with person_logged_in(self.person):
400+ self.task.transitionToStatus(BugTaskStatus.CONFIRMED, self.person)
401+ self.assertEqual(self.task.status, BugTaskStatus.CONFIRMED)
402+
403+ def test_privileged_user_canTransitionToStatus(self):
404+ # Privileged users (like owner or bug supervisor) should
405+ # be able to set any status, so canTransitionToStatus should
406+ # always return True.
407+ self.assertEqual(
408+ self.task.canTransitionToStatus(
409+ BugTaskStatus.WONTFIX, self.person),
410+ True)
411+ self.assertEqual(
412+ self.task.canTransitionToStatus(
413+ BugTaskStatus.EXPIRED, self.person),
414+ True)
415+ self.assertEqual(
416+ self.task.canTransitionToStatus(
417+ BugTaskStatus.TRIAGED, self.person),
418+ True)
419+ self.assertEqual(
420+ self.task.canTransitionToStatus(
421+ BugTaskStatus.NEW, self.person),
422+ True)
423+ self.assertEqual(
424+ self.task.canTransitionToStatus(
425+ BugTaskStatus.INCOMPLETE, self.person),
426+ True)
427+ self.assertEqual(
428+ self.task.canTransitionToStatus(
429+ BugTaskStatus.OPINION, self.person),
430+ True)
431+ self.assertEqual(
432+ self.task.canTransitionToStatus(
433+ BugTaskStatus.INVALID, self.person),
434+ True)
435+ self.assertEqual(
436+ self.task.canTransitionToStatus(
437+ BugTaskStatus.CONFIRMED, self.person),
438+ True)
439+ self.assertEqual(
440+ self.task.canTransitionToStatus(
441+ BugTaskStatus.INPROGRESS, self.person),
442+ True)
443+ self.assertEqual(
444+ self.task.canTransitionToStatus(
445+ BugTaskStatus.FIXCOMMITTED, self.person),
446+ True)
447+ self.assertEqual(
448+ self.task.canTransitionToStatus(
449+ BugTaskStatus.FIXRELEASED, self.person),
450+ True)
451+
452+ def test_privileged_user_canTransitionToStatus_from_wontfix(self):
453+ # A privileged user can transition away from Won't Fix, so
454+ # canTransitionToStatus should return True.
455+ removeSecurityProxy(self.task).status = BugTaskStatus.WONTFIX
456+ self.assertEqual(
457+ self.task.canTransitionToStatus(
458+ BugTaskStatus.NEW, self.person),
459+ True)
460+
461+
462+class TestBugTaskStatusTransitionOwnerPerson(
463+ TestBugTaskStatusTransitionForPrivilegedUserBase, TestCaseWithFactory):
464+ """Tests to ensure owner person can transition to any status.."""
465+
466+ def makePersonAndTask(self):
467+ self.person = self.factory.makePerson()
468+ self.product = self.factory.makeProduct(owner=self.person)
469+ self.task = self.factory.makeBugTask(target=self.product)
470+
471+
472+class TestBugTaskStatusTransitionOwnerTeam(
473+ TestBugTaskStatusTransitionForPrivilegedUserBase, TestCaseWithFactory):
474+ """Tests to ensure owner team can transition to any status.."""
475+
476+ def makePersonAndTask(self):
477+ self.person = self.factory.makePerson()
478+ self.team = self.factory.makeTeam(members=[self.person])
479+ self.product = self.factory.makeProduct(owner=self.team)
480+ self.task = self.factory.makeBugTask(target=self.product)
481+
482+
483+class TestBugTaskStatusTransitionBugSupervisorPerson(
484+ TestBugTaskStatusTransitionForPrivilegedUserBase, TestCaseWithFactory):
485+ """Tests to ensure bug supervisor person can transition to any status."""
486+
487+ def makePersonAndTask(self):
488+ self.owner = self.factory.makePerson()
489+ self.person = self.factory.makePerson()
490+ self.product = self.factory.makeProduct(owner=self.owner)
491+ self.task = self.factory.makeBugTask(target=self.product)
492+ with person_logged_in(self.owner):
493+ self.product.setBugSupervisor(self.person, self.person)
494+
495+
496+class TestBugTaskStatusTransitionBugSupervisorTeamMember(
497+ TestBugTaskStatusTransitionForPrivilegedUserBase, TestCaseWithFactory):
498+ """Tests to ensure bug supervisor team can transition to any status."""
499+
500+ def makePersonAndTask(self):
501+ self.owner = self.factory.makePerson()
502+ self.person = self.factory.makePerson()
503+ self.team = self.factory.makeTeam(members=[self.person])
504+ self.product = self.factory.makeProduct(owner=self.owner)
505+ self.task = self.factory.makeBugTask(target=self.product)
506+ with person_logged_in(self.owner):
507+ self.product.setBugSupervisor(self.team, self.team)
508+
509+
510+class TestBugTaskStatusTransitionBugWatchUpdater(
511+ TestBugTaskStatusTransitionForPrivilegedUserBase, TestCaseWithFactory):
512+ """Tests to ensure bug_watch_updater can transition to any status."""
513+
514+ def makePersonAndTask(self):
515+ self.person = getUtility(ILaunchpadCelebrities).bug_watch_updater
516+ self.task = self.factory.makeBugTask()
517+
518+
519+class TestBugTaskStatusTransitionBugImporter(
520+ TestBugTaskStatusTransitionForPrivilegedUserBase, TestCaseWithFactory):
521+ """Tests to ensure bug_importer can transition to any status."""
522+
523+ def makePersonAndTask(self):
524+ self.person = getUtility(ILaunchpadCelebrities).bug_importer
525+ self.task = self.factory.makeBugTask()
526+
527+
528+class TestBugTaskStatusTransitionJanitor(
529+ TestBugTaskStatusTransitionForPrivilegedUserBase, TestCaseWithFactory):
530+ """Tests to ensure lp janitor can transition to any status."""
531+
532+ def makePersonAndTask(self):
533+ self.person = getUtility(ILaunchpadCelebrities).janitor
534+ self.task = self.factory.makeBugTask()