Merge lp:~danilo/launchpad/bug-772763-remove-unmute-dialog-part1 into lp:launchpad/db-devel

Proposed by Данило Шеган
Status: Merged
Approved by: Данило Шеган
Approved revision: no longer in the source branch.
Merged at revision: 10582
Proposed branch: lp:~danilo/launchpad/bug-772763-remove-unmute-dialog-part1
Merge into: lp:launchpad/db-devel
Diff against target: 518 lines (+204/-54)
9 files modified
database/schema/security.cfg (+4/-0)
lib/lp/bugs/browser/bug.py (+2/-4)
lib/lp/bugs/browser/bugsubscription.py (+16/-7)
lib/lp/bugs/browser/tests/test_bug_views.py (+24/-1)
lib/lp/bugs/browser/tests/test_bugsubscription_views.py (+46/-13)
lib/lp/bugs/interfaces/bug.py (+5/-3)
lib/lp/bugs/model/bug.py (+15/-5)
lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py (+76/-21)
lib/lp/bugs/tests/test_bug.py (+16/-0)
To merge this branch: bzr merge lp:~danilo/launchpad/bug-772763-remove-unmute-dialog-part1
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Данило Шеган (community) db Abstain
Review via email: mp+61775@code.launchpad.net

Commit message

[r=adeuring][bug=772763][incr] Ground-work for making "Unmute bug mail" unmute directly (and restoring any previous subscription) without popping up a dialog.

Description of the change

= Bug 772763: remove unmute dialog, part1 =

As part of solving 772763, we remove the pop-up unmute dialog (to directly unmute instead). To prepare for that, we first do some server-side cleanups and make unmute() method return the previously masked subscription (if any).

== Proposed fix ==

This branch does a few things:

 - Make "static" (i.e. non-JS) page IBugTask:+mute not redirect to +subscribe to offer unmute but instead allow direct unmuting
 - Ensures Mute/Unmute link is shown when bug is muted
 - Takes mutes into consideration for all subscribers-calculation (direct, duplicate, also-notified) (changes in model/bug.py)
 - Make unmute() method return a previous subscription (if any)

== Pre-implementation notes ==

This is mostly Gary's branch. I am only shepherding it and providing a few tests of my own.

== Implementation details ==

Not cleaning up the lint for the model/bug.py since it'd taint the diff.

== Tests ==

bin/test -cvvt TestBugSubscriptionInfo -t TestBugSubscriptionMethods -t BugMuteSelfViewTestCase -t TestBugPortletSubscribers

== Demo and Q/A ==

Check that IBugTask:+mute page behaves as expected (iow, allows unmuting as well). Rest of the QA will be possible only when the follow-up branch lands.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py
  lib/lp/bugs/browser/tests/test_bug_views.py
  lib/lp/bugs/browser/bug.py
  lib/lp/bugs/model/bug.py
  lib/lp/bugs/browser/tests/test_bugsubscription_views.py
  lib/lp/bugs/browser/bugsubscription.py
  lib/lp/bugs/tests/test_bug.py
  lib/lp/bugs/interfaces/bug.py

./lib/lp/bugs/model/bug.py
     575: E225 missing whitespace around operator
     747: E225 missing whitespace around operator
     751: E225 missing whitespace around operator
     766: E225 missing whitespace around operator
    1461: E225 missing whitespace around operator
    1674: E261 at least two spaces before inline comment
    1676: E261 at least two spaces before inline comment
    1687: E261 at least two spaces before inline comment
    1689: E261 at least two spaces before inline comment
    1707: E225 missing whitespace around operator
    2262: E225 missing whitespace around operator
    2277: E225 missing whitespace around operator
    2287: E261 at least two spaces before inline comment
    2325: E225 missing whitespace around operator
    2587: E225 missing whitespace around operator
    2628: E225 missing whitespace around operator

To post a comment you must log in.
Revision history for this message
Данило Шеган (danilo) :
review: Abstain (db)
Revision history for this message
Abel Deuring (adeuring) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2011-05-20 16:15:35 +0000
+++ database/schema/security.cfg 2011-05-23 09:01:25 +0000
@@ -546,6 +546,7 @@
546public.bugcve = SELECT, INSERT546public.bugcve = SELECT, INSERT
547public.bugjob = SELECT, INSERT547public.bugjob = SELECT, INSERT
548public.bugmessage = SELECT, INSERT, UPDATE548public.bugmessage = SELECT, INSERT, UPDATE
549public.bugmute = SELECT
549public.bugnomination = SELECT550public.bugnomination = SELECT
550public.bugnotification = SELECT, INSERT551public.bugnotification = SELECT, INSERT
551public.bugnotificationfilter = SELECT, INSERT552public.bugnotificationfilter = SELECT, INSERT
@@ -1238,6 +1239,7 @@
1238public.bugcve = SELECT, INSERT1239public.bugcve = SELECT, INSERT
1239public.bugjob = SELECT, INSERT1240public.bugjob = SELECT, INSERT
1240public.bugmessage = SELECT, INSERT1241public.bugmessage = SELECT, INSERT
1242public.bugmute = SELECT
1241public.bugnomination = SELECT1243public.bugnomination = SELECT
1242public.bugnotification = SELECT, INSERT1244public.bugnotification = SELECT, INSERT
1243public.bugnotificationfilter = SELECT, INSERT1245public.bugnotificationfilter = SELECT, INSERT
@@ -1341,6 +1343,7 @@
1341public.bugcve = SELECT, INSERT1343public.bugcve = SELECT, INSERT
1342public.bugjob = SELECT, INSERT1344public.bugjob = SELECT, INSERT
1343public.bugmessage = SELECT, INSERT1345public.bugmessage = SELECT, INSERT
1346public.bugmute = SELECT
1344public.bugnomination = SELECT1347public.bugnomination = SELECT
1345public.bugnotification = SELECT, INSERT1348public.bugnotification = SELECT, INSERT
1346public.bugnotificationfilter = SELECT, INSERT1349public.bugnotificationfilter = SELECT, INSERT
@@ -1638,6 +1641,7 @@
1638public.bugcve = SELECT, INSERT1641public.bugcve = SELECT, INSERT
1639public.bugjob = SELECT, INSERT1642public.bugjob = SELECT, INSERT
1640public.bugmessage = SELECT, INSERT1643public.bugmessage = SELECT, INSERT
1644public.bugmute = SELECT
1641public.bugnomination = SELECT, INSERT, UPDATE1645public.bugnomination = SELECT, INSERT, UPDATE
1642public.bugnotification = SELECT, INSERT1646public.bugnotification = SELECT, INSERT
1643public.bugnotificationattachment = SELECT1647public.bugnotificationattachment = SELECT
16441648
=== modified file 'lib/lp/bugs/browser/bug.py'
--- lib/lp/bugs/browser/bug.py 2011-05-17 14:54:56 +0000
+++ lib/lp/bugs/browser/bug.py 2011-05-23 09:01:25 +0000
@@ -292,7 +292,7 @@
292292
293 return Link(293 return Link(
294 link, text, icon='remove', summary=(294 link, text, icon='remove', summary=(
295 "Mute this bug so that you will never receive emails "295 "Mute this bug so that you will not receive emails "
296 "about it."))296 "about it."))
297297
298 def nominate(self):298 def nominate(self):
@@ -538,9 +538,7 @@
538 """Return True if the user should see the Mute link."""538 """Return True if the user should see the Mute link."""
539 if features.getFeatureFlag('malone.advanced-subscriptions.enabled'):539 if features.getFeatureFlag('malone.advanced-subscriptions.enabled'):
540 user_is_subscribed = (540 user_is_subscribed = (
541 # Note that we don't have to check for isMuted(), since541 self.context.isMuted(self.user) or
542 # if isMuted() is True isSubscribed() will also be
543 # True.
544 self.context.isSubscribed(self.user) or542 self.context.isSubscribed(self.user) or
545 self.context.isSubscribedToDupes(self.user) or543 self.context.isSubscribedToDupes(self.user) or
546 self.context.personIsAlsoNotifiedSubscriber(self.user))544 self.context.personIsAlsoNotifiedSubscriber(self.user))
547545
=== modified file 'lib/lp/bugs/browser/bugsubscription.py'
--- lib/lp/bugs/browser/bugsubscription.py 2011-05-18 08:17:35 +0000
+++ lib/lp/bugs/browser/bugsubscription.py 2011-05-23 09:01:25 +0000
@@ -630,7 +630,10 @@
630630
631 @property631 @property
632 def label(self):632 def label(self):
633 return "Mute bug mail for bug %s" % self.context.bug.id633 if self.context.bug.isMuted(self.user):
634 return "Unmute bug mail for bug %s" % self.context.bug.id
635 else:
636 return "Mute bug mail for bug %s" % self.context.bug.id
634637
635 page_title = label638 page_title = label
636639
@@ -641,15 +644,21 @@
641 cancel_url = next_url644 cancel_url = next_url
642645
643 def initialize(self):646 def initialize(self):
647 self.is_muted = self.context.bug.isMuted(self.user)
644 super(BugMuteSelfView, self).initialize()648 super(BugMuteSelfView, self).initialize()
645 # If the user is already muted, redirect them to the +subscribe
646 # page, since there's no point doing its work twice.
647 if self.context.bug.isMuted(self.user):
648 self.request.response.redirect(
649 canonical_url(self.context, view_name="+subscribe"))
650649
651 @action('Mute bug mail', name='mute')650 @action('Mute bug mail',
651 name='mute',
652 condition=lambda form, action: not form.is_muted)
652 def mute_action(self, action, data):653 def mute_action(self, action, data):
653 self.context.bug.mute(self.user, self.user)654 self.context.bug.mute(self.user, self.user)
654 self.request.response.addInfoNotification(655 self.request.response.addInfoNotification(
655 "Mail for bug #%s has been muted." % self.context.bug.id)656 "Mail for bug #%s has been muted." % self.context.bug.id)
657
658 @action('Unmute bug mail',
659 name='unmute',
660 condition=lambda form, action: form.is_muted)
661 def unmute_action(self, action, data):
662 self.context.bug.unmute(self.user, self.user)
663 self.request.response.addInfoNotification(
664 "Mail for bug #%s has been unmuted." % self.context.bug.id)
656665
=== modified file 'lib/lp/bugs/browser/tests/test_bug_views.py'
--- lib/lp/bugs/browser/tests/test_bug_views.py 2011-05-17 14:54:56 +0000
+++ lib/lp/bugs/browser/tests/test_bug_views.py 2011-05-23 09:01:25 +0000
@@ -173,7 +173,30 @@
173 self.assertTrue('mute_subscription' in html)173 self.assertTrue('mute_subscription' in html)
174 # The template uses user_should_see_mute_link to decide174 # The template uses user_should_see_mute_link to decide
175 # whether or not to display the mute link.175 # whether or not to display the mute link.
176 soup = BeautifulSoup(html)
177 self.assertTrue(176 self.assertTrue(
178 self._hasCSSClass(html, 'mute-link-container', 'hidden'),177 self._hasCSSClass(html, 'mute-link-container', 'hidden'),
179 'No "hidden" CSS class in mute-link-container.')178 'No "hidden" CSS class in mute-link-container.')
179
180 def test_mute_subscription_link_shown_if_muted(self):
181 # If a person is muted but not otherwise subscribed, they should still
182 # see the (un)mute link.
183 person = self.factory.makePerson()
184 with person_logged_in(person):
185 with FeatureFixture({self.feature_flag_1: 'on'}):
186 self.bug.mute(person, person)
187 # The user isn't subscribed already, but is muted.
188 self.assertFalse(self.bug.isSubscribed(person))
189 self.assertFalse(
190 self.bug.personIsAlsoNotifiedSubscriber(
191 person))
192 self.assertTrue(self.bug.isMuted(person))
193 view = create_initialized_view(
194 self.bug, name="+portlet-subscribers")
195 self.assertTrue(view.user_should_see_mute_link,
196 "User should see mute link.")
197 contents = view.render()
198 self.assertTrue('mute_subscription' in contents,
199 "'mute_subscription' not in contents.")
200 self.assertFalse(
201 self._hasCSSClass(
202 contents, 'mute-link-container', 'hidden'))
180203
=== modified file 'lib/lp/bugs/browser/tests/test_bugsubscription_views.py'
--- lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-05-17 11:44:33 +0000
+++ lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-05-23 09:01:25 +0000
@@ -8,7 +8,6 @@
8import transaction8import transaction
99
10from canonical.launchpad.ftests import LaunchpadFormHarness10from canonical.launchpad.ftests import LaunchpadFormHarness
11from canonical.launchpad.webapp import canonical_url
12from canonical.testing.layers import LaunchpadFunctionalLayer11from canonical.testing.layers import LaunchpadFunctionalLayer
1312
14from lp.bugs.browser.bugsubscription import (13from lp.bugs.browser.bugsubscription import (
@@ -409,6 +408,44 @@
409 self.bug = self.factory.makeBug()408 self.bug = self.factory.makeBug()
410 self.person = self.factory.makePerson()409 self.person = self.factory.makePerson()
411410
411 def test_is_muted_false(self):
412 # BugMuteSelfView initialization sets the is_muted property.
413 # When the person has not muted the bug, it's false.
414 with person_logged_in(self.person):
415 self.assertFalse(self.bug.isMuted(self.person))
416 view = create_initialized_view(
417 self.bug.default_bugtask, name="+mute")
418 self.assertFalse(view.is_muted)
419
420 def test_is_muted_true(self):
421 # BugMuteSelfView initialization sets the is_muted property.
422 # When the person has muted the bug, it's true.
423 with person_logged_in(self.person):
424 self.bug.mute(self.person, self.person)
425 self.assertTrue(self.bug.isMuted(self.person))
426 view = create_initialized_view(
427 self.bug.default_bugtask, name="+mute")
428 self.assertTrue(view.is_muted)
429
430 def test_label_nonmuted(self):
431 # Label to use for the button.
432 with person_logged_in(self.person):
433 self.assertFalse(self.bug.isMuted(self.person))
434 expected_label = "Mute bug mail for bug %s" % self.bug.id
435 view = create_initialized_view(
436 self.bug.default_bugtask, name="+mute")
437 self.assertEqual(expected_label, view.label)
438
439 def test_label_muted(self):
440 # Label to use for the button.
441 with person_logged_in(self.person):
442 self.bug.mute(self.person, self.person)
443 self.assertTrue(self.bug.isMuted(self.person))
444 expected_label = "Unmute bug mail for bug %s" % self.bug.id
445 view = create_initialized_view(
446 self.bug.default_bugtask, name="+mute")
447 self.assertEqual(expected_label, view.label)
448
412 def test_bug_mute_self_view_mutes_bug(self):449 def test_bug_mute_self_view_mutes_bug(self):
413 # The BugMuteSelfView mutes bug mail for the current user when450 # The BugMuteSelfView mutes bug mail for the current user when
414 # its form is submitted.451 # its form is submitted.
@@ -419,17 +456,13 @@
419 form={'field.actions.mute': 'Mute bug mail'})456 form={'field.actions.mute': 'Mute bug mail'})
420 self.assertTrue(self.bug.isMuted(self.person))457 self.assertTrue(self.bug.isMuted(self.person))
421458
422 def test_bug_mute_self_view_redirects_muted_users(self):459 def test_bug_mute_self_view_unmutes_bug(self):
423 # The BugMuteSelfView redirects muted users to the +subscribe460 # The BugMuteSelfView unmutes bug mail for the current user when
424 # page, where they can remove their muted subscription or change461 # its form is submitted and the bug was already muted.
425 # their BugNotificationLevel.
426 with person_logged_in(self.person):462 with person_logged_in(self.person):
427 self.bug.mute(self.person, self.person)463 self.bug.mute(self.person, self.person)
428 mute_view = create_initialized_view(464 self.assertTrue(self.bug.isMuted(self.person))
429 self.bug.default_bugtask, name="+mute")465 create_initialized_view(
430 response = mute_view.request.response466 self.bug.default_bugtask, name="+mute",
431 self.assertEqual(302, response.getStatus())467 form={'field.actions.unmute': 'Unmute bug mail'})
432 self.assertEqual(468 self.assertFalse(self.bug.isMuted(self.person))
433 canonical_url(self.bug.default_bugtask,
434 view_name="+subscribe"),
435 response.getHeader('Location'))
436469
=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py 2011-05-18 08:17:35 +0000
+++ lib/lp/bugs/interfaces/bug.py 2011-05-23 09:01:25 +0000
@@ -509,7 +509,9 @@
509 @export_write_operation()509 @export_write_operation()
510 @operation_for_version('devel')510 @operation_for_version('devel')
511 def unmute(person, unmuted_by):511 def unmute(person, unmuted_by):
512 """Remove a muted subscription for `person`."""512 """Remove a muted subscription for `person`.
513
514 Returns previously muted direct subscription, if any."""
513515
514 def getDirectSubscriptions():516 def getDirectSubscriptions():
515 """A sequence of IBugSubscriptions directly linked to this bug."""517 """A sequence of IBugSubscriptions directly linked to this bug."""
@@ -779,9 +781,9 @@
779 schema=Interface, title=_('Target'), required=False),781 schema=Interface, title=_('Target'), required=False),
780 nominations=List(782 nominations=List(
781 title=_("Nominations to search through."),783 title=_("Nominations to search through."),
782 value_type=Reference(schema=Interface), # IBugNomination784 value_type=Reference(schema=Interface), # IBugNomination
783 required=False))785 required=False))
784 @operation_returns_collection_of(Interface) # IBugNomination786 @operation_returns_collection_of(Interface) # IBugNomination
785 @export_read_operation()787 @export_read_operation()
786 def getNominations(target=None, nominations=None):788 def getNominations(target=None, nominations=None):
787 """Return a list of all IBugNominations for this bug.789 """Return a list of all IBugNominations for this bug.
788790
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2011-05-18 08:17:35 +0000
+++ lib/lp/bugs/model/bug.py 2011-05-23 09:01:25 +0000
@@ -54,6 +54,7 @@
54 Count,54 Count,
55 Desc,55 Desc,
56 Exists,56 Exists,
57 In,
57 Join,58 Join,
58 LeftJoin,59 LeftJoin,
59 Max,60 Max,
@@ -531,7 +532,7 @@
531 parent = message_by_id.get(parent.id, parent)532 parent = message_by_id.get(parent.id, parent)
532 else:533 else:
533 message, bugmessage = row534 message, bugmessage = row
534 parent = None # parent attribute is not going to be accessed.535 parent = None # parent attribute is not going to be accessed.
535 index = bugmessage.index536 index = bugmessage.index
536 result = IndexedMessage(message, inside, index, parent)537 result = IndexedMessage(message, inside, index, parent)
537 if include_parents:538 if include_parents:
@@ -891,6 +892,7 @@
891 person = unmuted_by892 person = unmuted_by
892 mutes = self._getMutes(person)893 mutes = self._getMutes(person)
893 store.remove(mutes.one())894 store.remove(mutes.one())
895 return self.getSubscriptionForPerson(person)
894896
895 @property897 @property
896 def subscriptions(self):898 def subscriptions(self):
@@ -2249,7 +2251,9 @@
2249 return IStore(BugSubscription).find(2251 return IStore(BugSubscription).find(
2250 BugSubscription,2252 BugSubscription,
2251 BugSubscription.bug_notification_level >= self.level,2253 BugSubscription.bug_notification_level >= self.level,
2252 BugSubscription.bug == self.bug)2254 BugSubscription.bug == self.bug,
2255 Not(In(BugSubscription.person_id,
2256 Select(BugMute.person_id, BugMute.bug_id==self.bug.id))))
22532257
2254 @cachedproperty2258 @cachedproperty
2255 @freeze(BugSubscriptionSet)2259 @freeze(BugSubscriptionSet)
@@ -2262,12 +2266,14 @@
2262 BugSubscription,2266 BugSubscription,
2263 BugSubscription.bug_notification_level >= self.level,2267 BugSubscription.bug_notification_level >= self.level,
2264 BugSubscription.bug_id == Bug.id,2268 BugSubscription.bug_id == Bug.id,
2265 Bug.duplicateof == self.bug)2269 Bug.duplicateof == self.bug,
2270 Not(In(BugSubscription.person_id,
2271 Select(BugMute.person_id, BugMute.bug_id==Bug.id))))
22662272
2267 @cachedproperty2273 @cachedproperty
2268 @freeze(BugSubscriptionSet)2274 @freeze(BugSubscriptionSet)
2269 def duplicate_only_subscriptions(self):2275 def duplicate_only_subscriptions(self):
2270 """Subscripitions to duplicates of the bug.2276 """Subscriptions to duplicates of the bug.
22712277
2272 Excludes subscriptions for people who have a direct subscription or2278 Excludes subscriptions for people who have a direct subscription or
2273 are also notified for another reason.2279 are also notified for another reason.
@@ -2308,11 +2314,15 @@
2308 if self.bug.private:2314 if self.bug.private:
2309 return BugSubscriberSet()2315 return BugSubscriberSet()
2310 else:2316 else:
2317 muted = IStore(BugMute).find(
2318 Person,
2319 BugMute.person_id==Person.id,
2320 BugMute.bug==self.bug)
2311 return BugSubscriberSet().union(2321 return BugSubscriberSet().union(
2312 self.structural_subscriptions.subscribers,2322 self.structural_subscriptions.subscribers,
2313 self.all_pillar_owners_without_bug_supervisors,2323 self.all_pillar_owners_without_bug_supervisors,
2314 self.all_assignees).difference(2324 self.all_assignees).difference(
2315 self.direct_subscriptions.subscribers)2325 self.direct_subscriptions.subscribers).difference(muted)
23162326
2317 @cachedproperty2327 @cachedproperty
2318 def indirect_subscribers(self):2328 def indirect_subscribers(self):
23192329
=== modified file 'lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py'
--- lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py 2011-05-16 16:57:55 +0000
+++ lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py 2011-05-23 09:01:25 +0000
@@ -132,21 +132,42 @@
132 return BugSubscriptionInfo(132 return BugSubscriptionInfo(
133 self.bug, BugNotificationLevel.LIFECYCLE)133 self.bug, BugNotificationLevel.LIFECYCLE)
134134
135 def _create_direct_subscriptions(self):
136 subscribers = (
137 self.factory.makePerson(),
138 self.factory.makePerson())
139 with person_logged_in(self.bug.owner):
140 subscriptions = tuple(
141 self.bug.subscribe(subscriber, subscriber)
142 for subscriber in subscribers)
143 return subscribers, subscriptions
144
135 def test_direct(self):145 def test_direct(self):
136 # The set of direct subscribers.146 # The set of direct subscribers.
137 subscribers = (147 subscribers, subscriptions = self._create_direct_subscriptions()
138 self.factory.makePerson(),
139 self.factory.makePerson())
140 with person_logged_in(self.bug.owner):
141 subscriptions = tuple(
142 self.bug.subscribe(subscriber, subscriber)
143 for subscriber in subscribers)
144 found_subscriptions = self.getInfo().direct_subscriptions148 found_subscriptions = self.getInfo().direct_subscriptions
145 self.assertEqual(set(subscriptions), found_subscriptions)149 self.assertEqual(set(subscriptions), found_subscriptions)
146 self.assertEqual(subscriptions, found_subscriptions.sorted)150 self.assertEqual(subscriptions, found_subscriptions.sorted)
147 self.assertEqual(set(subscribers), found_subscriptions.subscribers)151 self.assertEqual(set(subscribers), found_subscriptions.subscribers)
148 self.assertEqual(subscribers, found_subscriptions.subscribers.sorted)152 self.assertEqual(subscribers, found_subscriptions.subscribers.sorted)
149153
154 def test_muted_direct(self):
155 # If a direct is muted, it is not listed.
156 subscribers, subscriptions = self._create_direct_subscriptions()
157 with person_logged_in(subscribers[0]):
158 self.bug.mute(subscribers[0], subscribers[0])
159 found_subscriptions = self.getInfo().direct_subscriptions
160 self.assertEqual(set([subscriptions[1]]), found_subscriptions)
161
162 def _create_duplicate_subscription(self):
163 duplicate_bug = self.factory.makeBug(product=self.target)
164 with person_logged_in(duplicate_bug.owner):
165 duplicate_bug.markAsDuplicate(self.bug)
166 duplicate_bug_subscription = (
167 duplicate_bug.getSubscriptionForPerson(
168 duplicate_bug.owner))
169 return duplicate_bug, duplicate_bug_subscription
170
150 def test_duplicate(self):171 def test_duplicate(self):
151 # The set of subscribers from duplicate bugs.172 # The set of subscribers from duplicate bugs.
152 found_subscriptions = self.getInfo().duplicate_subscriptions173 found_subscriptions = self.getInfo().duplicate_subscriptions
@@ -154,12 +175,8 @@
154 self.assertEqual((), found_subscriptions.sorted)175 self.assertEqual((), found_subscriptions.sorted)
155 self.assertEqual(set(), found_subscriptions.subscribers)176 self.assertEqual(set(), found_subscriptions.subscribers)
156 self.assertEqual((), found_subscriptions.subscribers.sorted)177 self.assertEqual((), found_subscriptions.subscribers.sorted)
157 duplicate_bug = self.factory.makeBug(product=self.target)178 duplicate_bug, duplicate_bug_subscription = (
158 with person_logged_in(duplicate_bug.owner):179 self._create_duplicate_subscription())
159 duplicate_bug.markAsDuplicate(self.bug)
160 duplicate_bug_subscription = (
161 duplicate_bug.getSubscriptionForPerson(
162 duplicate_bug.owner))
163 found_subscriptions = self.getInfo().duplicate_subscriptions180 found_subscriptions = self.getInfo().duplicate_subscriptions
164 self.assertEqual(181 self.assertEqual(
165 set([duplicate_bug_subscription]),182 set([duplicate_bug_subscription]),
@@ -174,6 +191,15 @@
174 (duplicate_bug.owner,),191 (duplicate_bug.owner,),
175 found_subscriptions.subscribers.sorted)192 found_subscriptions.subscribers.sorted)
176193
194 def test_muted_duplicate(self):
195 # If a duplicate is muted, it is not listed.
196 duplicate_bug, duplicate_bug_subscription = (
197 self._create_duplicate_subscription())
198 with person_logged_in(duplicate_bug.owner):
199 self.bug.mute(duplicate_bug.owner, duplicate_bug.owner)
200 found_subscriptions = self.getInfo().duplicate_subscriptions
201 self.assertEqual(set(), found_subscriptions)
202
177 def test_duplicate_for_private_bug(self):203 def test_duplicate_for_private_bug(self):
178 # The set of subscribers from duplicate bugs is always empty when the204 # The set of subscribers from duplicate bugs is always empty when the
179 # master bug is private.205 # master bug is private.
@@ -274,11 +300,7 @@
274 (bugtask.pillar.owner, bugtask2.pillar.owner),300 (bugtask.pillar.owner, bugtask2.pillar.owner),
275 found_owners.sorted)301 found_owners.sorted)
276302
277 def test_also_notified_subscribers(self):303 def _create_also_notified_subscribers(self):
278 # The set of also notified subscribers.
279 found_subscribers = self.getInfo().also_notified_subscribers
280 self.assertEqual(set(), found_subscribers)
281 self.assertEqual((), found_subscribers.sorted)
282 # Add an assignee, a bug supervisor and a structural subscriber.304 # Add an assignee, a bug supervisor and a structural subscriber.
283 bugtask = self.bug.default_bugtask305 bugtask = self.bug.default_bugtask
284 assignee = self.factory.makePerson()306 assignee = self.factory.makePerson()
@@ -291,6 +313,15 @@
291 with person_logged_in(structural_subscriber):313 with person_logged_in(structural_subscriber):
292 bugtask.target.addSubscription(314 bugtask.target.addSubscription(
293 structural_subscriber, structural_subscriber)315 structural_subscriber, structural_subscriber)
316 return assignee, supervisor, structural_subscriber
317
318 def test_also_notified_subscribers(self):
319 # The set of also notified subscribers.
320 found_subscribers = self.getInfo().also_notified_subscribers
321 self.assertEqual(set(), found_subscribers)
322 self.assertEqual((), found_subscribers.sorted)
323 assignee, supervisor, structural_subscriber = (
324 self._create_also_notified_subscribers())
294 # Add a direct subscription.325 # Add a direct subscription.
295 direct_subscriber = self.factory.makePerson()326 direct_subscriber = self.factory.makePerson()
296 with person_logged_in(self.bug.owner):327 with person_logged_in(self.bug.owner):
@@ -305,6 +336,30 @@
305 (assignee, supervisor, structural_subscriber),336 (assignee, supervisor, structural_subscriber),
306 found_subscribers.sorted)337 found_subscribers.sorted)
307338
339 def test_muted_also_notified_subscribers(self):
340 # If someone is muted, they are not listed in the
341 # also_notified_subscribers.
342 assignee, supervisor, structural_subscriber = (
343 self._create_also_notified_subscribers())
344 # As a control, we first show that the
345 # the assignee, supervisor and structural subscriber do show up
346 # when they are not muted.
347 found_subscribers = self.getInfo().also_notified_subscribers
348 self.assertEqual(
349 set([assignee, supervisor, structural_subscriber]),
350 found_subscribers)
351 # Now we mute all of the subscribers.
352 with person_logged_in(assignee):
353 self.bug.mute(assignee, assignee)
354 with person_logged_in(supervisor):
355 self.bug.mute(supervisor, supervisor)
356 with person_logged_in(structural_subscriber):
357 self.bug.mute(structural_subscriber, structural_subscriber)
358 # Now we don't see them.
359 found_subscribers = self.getInfo().also_notified_subscribers
360 self.assertEqual(
361 set(), found_subscribers)
362
308 def test_also_notified_subscribers_for_private_bug(self):363 def test_also_notified_subscribers_for_private_bug(self):
309 # The set of also notified subscribers is always empty when the master364 # The set of also notified subscribers is always empty when the master
310 # bug is private.365 # bug is private.
@@ -472,7 +527,7 @@
472 self.info.all_pillar_owners_without_bug_supervisors527 self.info.all_pillar_owners_without_bug_supervisors
473528
474 def test_also_notified_subscribers(self):529 def test_also_notified_subscribers(self):
475 with self.exactly_x_queries(5):530 with self.exactly_x_queries(6):
476 self.info.also_notified_subscribers531 self.info.also_notified_subscribers
477532
478 def test_also_notified_subscribers_later(self):533 def test_also_notified_subscribers_later(self):
@@ -482,11 +537,11 @@
482 self.info.all_pillar_owners_without_bug_supervisors537 self.info.all_pillar_owners_without_bug_supervisors
483 self.info.direct_subscriptions.subscribers538 self.info.direct_subscriptions.subscribers
484 self.info.structural_subscriptions.subscribers539 self.info.structural_subscriptions.subscribers
485 with self.exactly_x_queries(0):540 with self.exactly_x_queries(1):
486 self.info.also_notified_subscribers541 self.info.also_notified_subscribers
487542
488 def test_indirect_subscribers(self):543 def test_indirect_subscribers(self):
489 with self.exactly_x_queries(6):544 with self.exactly_x_queries(7):
490 self.info.indirect_subscribers545 self.info.indirect_subscribers
491546
492 def test_indirect_subscribers_later(self):547 def test_indirect_subscribers_later(self):
493548
=== modified file 'lib/lp/bugs/tests/test_bug.py'
--- lib/lp/bugs/tests/test_bug.py 2011-05-18 08:17:35 +0000
+++ lib/lp/bugs/tests/test_bug.py 2011-05-23 09:01:25 +0000
@@ -105,6 +105,22 @@
105 self.bug.unmute(self.person, self.person)105 self.bug.unmute(self.person, self.person)
106 self.assertFalse(self.bug.isMuted(self.person))106 self.assertFalse(self.bug.isMuted(self.person))
107107
108 def test_unmute_returns_direct_subscription(self):
109 # Bug.unmute() returns the previously muted direct subscription, if
110 # any.
111 with person_logged_in(self.person):
112 self.bug.mute(self.person, self.person)
113 self.assertEqual(True, self.bug.isMuted(self.person))
114 self.assertEqual(None, self.bug.unmute(self.person, self.person))
115 self.assertEqual(False, self.bug.isMuted(self.person))
116 subscription = self.bug.subscribe(
117 self.person, self.person,
118 level=BugNotificationLevel.METADATA)
119 self.bug.mute(self.person, self.person)
120 self.assertEqual(True, self.bug.isMuted(self.person))
121 self.assertEqual(
122 subscription, self.bug.unmute(self.person, self.person))
123
108 def test_unmute_mutes_unmuter(self):124 def test_unmute_mutes_unmuter(self):
109 # When exposed in the web API, the unmute method regards the125 # When exposed in the web API, the unmute method regards the
110 # first, `person` argument as optional, and the second126 # first, `person` argument as optional, and the second

Subscribers

People subscribed via source and target branches

to status/vote changes: