Merge lp:~bac/launchpad/bugjam-579502 into lp:launchpad
- bugjam-579502
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Curtis Hovey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 12145 |
Proposed branch: | lp:~bac/launchpad/bugjam-579502 |
Merge into: | lp:launchpad |
Diff against target: |
588 lines (+159/-59) 11 files modified
lib/lp/bugs/doc/bug-heat.txt (+12/-14) lib/lp/bugs/doc/bugnotification-sending.txt (+10/-12) lib/lp/bugs/doc/bugzilla-import.txt (+0/-3) lib/lp/bugs/doc/initial-bug-contacts.txt (+0/-1) lib/lp/bugs/model/bug.py (+10/-6) lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions.txt (+0/-5) lib/lp/bugs/subscribers/bug.py (+1/-1) lib/lp/bugs/tests/test_bugchanges.py (+4/-3) lib/lp/bugs/tests/test_bugnotification.py (+114/-7) lib/lp/bugs/tests/test_bugs_webservice.py (+6/-6) lib/lp/bugs/tests/test_bugtask_search.py (+2/-1) |
To merge this branch: | bzr merge lp:~bac/launchpad/bugjam-579502 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Curtis Hovey (community) | code | Approve | |
Review via email: mp+44302@code.launchpad.net |
Commit message
[r=sinzui]
Description of the change
= Summary =
If product or distro does not use Launchpad for bugs then the owner
should not be notified of bug state changes.
== Proposed fix ==
Check official_malone before adding the registrant.
== Pre-implementation notes ==
Talk with Curtis.
== Implementation details ==
As above.
== Tests ==
bin/test -vvm lp.bugs -t test_bugnotific
== Demo and Q/A ==
Create a distribution doesn't use Launchpad for bugs. Create a bugtask
affecting the distro as a different user. Ensure the distro owner does
not get an email. (Will this work? Need to find out about qastaging and
sending email.)
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/
lib/lp/
lib/lp/
Brad Crittenden (bac) wrote : | # |
Curtis I discovered many more test that depended upon the pillar owner receiving email notification. The diff for the fixes is at http://
Preview Diff
1 | === modified file 'lib/lp/bugs/doc/bug-heat.txt' | |||
2 | --- lib/lp/bugs/doc/bug-heat.txt 2010-12-22 20:46:21 +0000 | |||
3 | +++ lib/lp/bugs/doc/bug-heat.txt 2010-12-23 19:45:24 +0000 | |||
4 | @@ -80,52 +80,52 @@ | |||
5 | 80 | 80 | ||
6 | 81 | >>> changed = bug.setPrivate(True, bug_owner) | 81 | >>> changed = bug.setPrivate(True, bug_owner) |
7 | 82 | >>> bug.heat | 82 | >>> bug.heat |
9 | 83 | 158 | 83 | 156 |
10 | 84 | 84 | ||
11 | 85 | Setting the bug as security related adds another 250 heat points. | 85 | Setting the bug as security related adds another 250 heat points. |
12 | 86 | 86 | ||
13 | 87 | >>> changed = bug.setSecurityRelated(True) | 87 | >>> changed = bug.setSecurityRelated(True) |
14 | 88 | >>> bug.heat | 88 | >>> bug.heat |
16 | 89 | 408 | 89 | 406 |
17 | 90 | 90 | ||
18 | 91 | Marking the bug public removes 150 heat points. | 91 | Marking the bug public removes 150 heat points. |
19 | 92 | 92 | ||
20 | 93 | >>> changed = bug.setPrivate(False, bug_owner) | 93 | >>> changed = bug.setPrivate(False, bug_owner) |
21 | 94 | >>> bug.heat | 94 | >>> bug.heat |
23 | 95 | 258 | 95 | 256 |
24 | 96 | 96 | ||
25 | 97 | And marking it not security-related removes 250 points. | 97 | And marking it not security-related removes 250 points. |
26 | 98 | 98 | ||
27 | 99 | >>> changed = bug.setSecurityRelated(False) | 99 | >>> changed = bug.setSecurityRelated(False) |
28 | 100 | >>> bug.heat | 100 | >>> bug.heat |
30 | 101 | 8 | 101 | 6 |
31 | 102 | 102 | ||
32 | 103 | Adding a subscriber to the bug increases its heat by 2 points. | 103 | Adding a subscriber to the bug increases its heat by 2 points. |
33 | 104 | 104 | ||
34 | 105 | >>> new_subscriber = factory.makePerson() | 105 | >>> new_subscriber = factory.makePerson() |
35 | 106 | >>> subscription = bug.subscribe(new_subscriber, new_subscriber) | 106 | >>> subscription = bug.subscribe(new_subscriber, new_subscriber) |
36 | 107 | >>> bug.heat | 107 | >>> bug.heat |
38 | 108 | 10 | 108 | 8 |
39 | 109 | 109 | ||
40 | 110 | When a user unsubscribes, the bug loses 2 points of heat. | 110 | When a user unsubscribes, the bug loses 2 points of heat. |
41 | 111 | 111 | ||
42 | 112 | >>> bug.unsubscribe(new_subscriber, new_subscriber) | 112 | >>> bug.unsubscribe(new_subscriber, new_subscriber) |
43 | 113 | >>> bug.heat | 113 | >>> bug.heat |
45 | 114 | 8 | 114 | 6 |
46 | 115 | 115 | ||
47 | 116 | Should a user mark themselves as affected by the bug, it will gain 4 | 116 | Should a user mark themselves as affected by the bug, it will gain 4 |
48 | 117 | points of heat. | 117 | points of heat. |
49 | 118 | 118 | ||
50 | 119 | >>> bug.markUserAffected(new_subscriber) | 119 | >>> bug.markUserAffected(new_subscriber) |
51 | 120 | >>> bug.heat | 120 | >>> bug.heat |
53 | 121 | 12 | 121 | 10 |
54 | 122 | 122 | ||
55 | 123 | If a user who was previously affected marks themself as not affected, | 123 | If a user who was previously affected marks themself as not affected, |
56 | 124 | the bug loses 4 points of heat. | 124 | the bug loses 4 points of heat. |
57 | 125 | 125 | ||
58 | 126 | >>> bug.markUserAffected(new_subscriber, False) | 126 | >>> bug.markUserAffected(new_subscriber, False) |
59 | 127 | >>> bug.heat | 127 | >>> bug.heat |
61 | 128 | 8 | 128 | 6 |
62 | 129 | 129 | ||
63 | 130 | If a user who wasn't affected by the bug marks themselve as explicitly | 130 | If a user who wasn't affected by the bug marks themselve as explicitly |
64 | 131 | unaffected, the bug's heat doesn't change. | 131 | unaffected, the bug's heat doesn't change. |
65 | @@ -133,7 +133,7 @@ | |||
66 | 133 | >>> unaffected_person = factory.makePerson() | 133 | >>> unaffected_person = factory.makePerson() |
67 | 134 | >>> bug.markUserAffected(unaffected_person, False) | 134 | >>> bug.markUserAffected(unaffected_person, False) |
68 | 135 | >>> bug.heat | 135 | >>> bug.heat |
70 | 136 | 8 | 136 | 6 |
71 | 137 | 137 | ||
72 | 138 | Marking the bug as a duplicate will set its heat to zero, whilst also | 138 | Marking the bug as a duplicate will set its heat to zero, whilst also |
73 | 139 | adding 10 points of heat to the bug it duplicates, 6 points for the | 139 | adding 10 points of heat to the bug it duplicates, 6 points for the |
74 | @@ -149,14 +149,14 @@ | |||
75 | 149 | 0 | 149 | 0 |
76 | 150 | 150 | ||
77 | 151 | >>> duplicated_bug.heat | 151 | >>> duplicated_bug.heat |
79 | 152 | 16 | 152 | 14 |
80 | 153 | 153 | ||
81 | 154 | Unmarking the bug as a duplicate restores its heat and updates the | 154 | Unmarking the bug as a duplicate restores its heat and updates the |
82 | 155 | duplicated bug's heat. | 155 | duplicated bug's heat. |
83 | 156 | 156 | ||
84 | 157 | >>> bug.markAsDuplicate(None) | 157 | >>> bug.markAsDuplicate(None) |
85 | 158 | >>> bug.heat | 158 | >>> bug.heat |
87 | 159 | 8 | 159 | 6 |
88 | 160 | 160 | ||
89 | 161 | >>> duplicated_bug.heat | 161 | >>> duplicated_bug.heat |
90 | 162 | 6 | 162 | 6 |
91 | @@ -184,7 +184,7 @@ | |||
92 | 184 | ... old_value=bug.description, new_value='Some text') | 184 | ... old_value=bug.description, new_value='Some text') |
93 | 185 | >>> bug.addChange(change) | 185 | >>> bug.addChange(change) |
94 | 186 | >>> bug.heat | 186 | >>> bug.heat |
96 | 187 | 8 | 187 | 6 |
97 | 188 | 188 | ||
98 | 189 | 189 | ||
99 | 190 | Getting bugs whose heat is outdated | 190 | Getting bugs whose heat is outdated |
100 | @@ -359,5 +359,3 @@ | |||
101 | 359 | 123 | 359 | 123 |
102 | 360 | >>> print distro.max_bug_heat | 360 | >>> print distro.max_bug_heat |
103 | 361 | 456 | 361 | 456 |
104 | 362 | |||
105 | 363 | |||
106 | 364 | 362 | ||
107 | === modified file 'lib/lp/bugs/doc/bugnotification-sending.txt' | |||
108 | --- lib/lp/bugs/doc/bugnotification-sending.txt 2010-12-06 17:43:53 +0000 | |||
109 | +++ lib/lp/bugs/doc/bugnotification-sending.txt 2010-12-23 19:45:24 +0000 | |||
110 | @@ -303,7 +303,7 @@ | |||
111 | 303 | <BLANKLINE> | 303 | <BLANKLINE> |
112 | 304 | ** Visibility changed to: Public. | 304 | ** Visibility changed to: Public. |
113 | 305 | <BLANKLINE> | 305 | <BLANKLINE> |
115 | 306 | -- | 306 | -- |
116 | 307 | You received this bug notification because you are a member of Ubuntu | 307 | You received this bug notification because you are a member of Ubuntu |
117 | 308 | Team, which is the registrant for Ubuntu. | 308 | Team, which is the registrant for Ubuntu. |
118 | 309 | http://bugs.launchpad.dev/bugs/1 | 309 | http://bugs.launchpad.dev/bugs/1 |
119 | @@ -582,8 +582,6 @@ | |||
120 | 582 | 0 | 582 | 0 |
121 | 583 | >>> print err | 583 | >>> print err |
122 | 584 | DEBUG ... | 584 | DEBUG ... |
123 | 585 | INFO Notifying mark@example.com about bug 2. | ||
124 | 586 | ... | ||
125 | 587 | INFO Notifying support@ubuntu.com about bug 2. | 585 | INFO Notifying support@ubuntu.com about bug 2. |
126 | 588 | ... | 586 | ... |
127 | 589 | INFO Notifying test@canonical.com about bug 2. | 587 | INFO Notifying test@canonical.com about bug 2. |
128 | @@ -727,15 +725,12 @@ | |||
129 | 727 | >>> for message in trigger_and_get_email_messages(bug_three): | 725 | >>> for message in trigger_and_get_email_messages(bug_three): |
130 | 728 | ... message.get_all('X-Launchpad-Bug-Tags') | 726 | ... message.get_all('X-Launchpad-Bug-Tags') |
131 | 729 | [u'layout-test'] | 727 | [u'layout-test'] |
132 | 730 | [u'layout-test'] | ||
133 | 731 | 728 | ||
134 | 732 | If we add a tag to bug three that will also be included in the header. | 729 | If we add a tag to bug three that will also be included in the header. |
135 | 733 | The tags will be space-separated to allow the list to be wrapped if it | 730 | The tags will be space-separated to allow the list to be wrapped if it |
136 | 734 | gets over-long. | 731 | gets over-long. |
137 | 735 | 732 | ||
138 | 736 | >>> switch_db_to_launchpad() | 733 | >>> switch_db_to_launchpad() |
139 | 737 | |||
140 | 738 | >>> bug_three.unsubscribe(sample_person, sample_person) | ||
141 | 739 | >>> bug_three.tags = [u'layout-test', u'another-tag', u'yet-another'] | 734 | >>> bug_three.tags = [u'layout-test', u'another-tag', u'yet-another'] |
142 | 740 | 735 | ||
143 | 741 | >>> switch_db_to_bugnotification() | 736 | >>> switch_db_to_bugnotification() |
144 | @@ -756,6 +751,9 @@ | |||
145 | 756 | >>> for message in trigger_and_get_email_messages(bug_three): | 751 | >>> for message in trigger_and_get_email_messages(bug_three): |
146 | 757 | ... message.get_all('X-Launchpad-Bug-Tags') | 752 | ... message.get_all('X-Launchpad-Bug-Tags') |
147 | 758 | 753 | ||
148 | 754 | >>> switch_db_to_launchpad() | ||
149 | 755 | >>> #bug_three.unsubscribe(sample_person, sample_person) | ||
150 | 756 | |||
151 | 759 | 757 | ||
152 | 760 | The X-Launchpad-Bug-Private header | 758 | The X-Launchpad-Bug-Private header |
153 | 761 | ---------------------------------- | 759 | ---------------------------------- |
154 | @@ -769,7 +767,7 @@ | |||
155 | 769 | 767 | ||
156 | 770 | >>> for message in trigger_and_get_email_messages(bug_three): | 768 | >>> for message in trigger_and_get_email_messages(bug_three): |
157 | 771 | ... print message['To'], message.get_all('X-Launchpad-Bug-Private') | 769 | ... print message['To'], message.get_all('X-Launchpad-Bug-Private') |
159 | 772 | mark@example.com ['no'] | 770 | test@canonical.com ['no'] |
160 | 773 | 771 | ||
161 | 774 | Predictably, private bugs are sent with a slightly different header: | 772 | Predictably, private bugs are sent with a slightly different header: |
162 | 775 | 773 | ||
163 | @@ -780,7 +778,7 @@ | |||
164 | 780 | 778 | ||
165 | 781 | >>> for message in trigger_and_get_email_messages(bug_three): | 779 | >>> for message in trigger_and_get_email_messages(bug_three): |
166 | 782 | ... print message['To'], message.get_all('X-Launchpad-Bug-Private') | 780 | ... print message['To'], message.get_all('X-Launchpad-Bug-Private') |
168 | 783 | mark@example.com ['yes'] | 781 | test@canonical.com ['yes'] |
169 | 784 | 782 | ||
170 | 785 | 783 | ||
171 | 786 | The X-Launchpad-Bug-Security-Vulnerability header | 784 | The X-Launchpad-Bug-Security-Vulnerability header |
172 | @@ -796,7 +794,7 @@ | |||
173 | 796 | >>> for message in trigger_and_get_email_messages(bug_three): | 794 | >>> for message in trigger_and_get_email_messages(bug_three): |
174 | 797 | ... print message['To'], ( | 795 | ... print message['To'], ( |
175 | 798 | ... message.get_all('X-Launchpad-Bug-Security-Vulnerability')) | 796 | ... message.get_all('X-Launchpad-Bug-Security-Vulnerability')) |
177 | 799 | mark@example.com ['no'] | 797 | test@canonical.com ['no'] |
178 | 800 | 798 | ||
179 | 801 | The presence of the security flag on a bug is, surprise, denoted by a | 799 | The presence of the security flag on a bug is, surprise, denoted by a |
180 | 802 | simple "yes": | 800 | simple "yes": |
181 | @@ -809,7 +807,7 @@ | |||
182 | 809 | >>> for message in trigger_and_get_email_messages(bug_three): | 807 | >>> for message in trigger_and_get_email_messages(bug_three): |
183 | 810 | ... print message['To'], ( | 808 | ... print message['To'], ( |
184 | 811 | ... message.get_all('X-Launchpad-Bug-Security-Vulnerability')) | 809 | ... message.get_all('X-Launchpad-Bug-Security-Vulnerability')) |
186 | 812 | mark@example.com ['yes'] | 810 | test@canonical.com ['yes'] |
187 | 813 | 811 | ||
188 | 814 | 812 | ||
189 | 815 | The X-Launchpad-Bug-Commenters header | 813 | The X-Launchpad-Bug-Commenters header |
190 | @@ -1238,7 +1236,7 @@ | |||
191 | 1238 | <BLANKLINE> | 1236 | <BLANKLINE> |
192 | 1239 | ** Summary changed to: whatever. | 1237 | ** Summary changed to: whatever. |
193 | 1240 | <BLANKLINE> | 1238 | <BLANKLINE> |
195 | 1241 | -- | 1239 | -- |
196 | 1242 | You received this bug notification because you are subscribed to Mozilla | 1240 | You received this bug notification because you are subscribed to Mozilla |
197 | 1243 | Firefox. | 1241 | Firefox. |
198 | 1244 | ... | 1242 | ... |
199 | @@ -1287,7 +1285,7 @@ | |||
200 | 1287 | <BLANKLINE> | 1285 | <BLANKLINE> |
201 | 1288 | ** Summary changed to: something. | 1286 | ** Summary changed to: something. |
202 | 1289 | <BLANKLINE> | 1287 | <BLANKLINE> |
204 | 1290 | -- | 1288 | -- |
205 | 1291 | You received this bug notification because you are a member of ShipIt | 1289 | You received this bug notification because you are a member of ShipIt |
206 | 1292 | Administrators, which is a direct subscriber. | 1290 | Administrators, which is a direct subscriber. |
207 | 1293 | http://bugs.launchpad.dev/bugs/1 | 1291 | http://bugs.launchpad.dev/bugs/1 |
208 | 1294 | 1292 | ||
209 | === modified file 'lib/lp/bugs/doc/bugzilla-import.txt' | |||
210 | --- lib/lp/bugs/doc/bugzilla-import.txt 2010-10-18 22:24:59 +0000 | |||
211 | +++ lib/lp/bugs/doc/bugzilla-import.txt 2010-12-23 19:45:24 +0000 | |||
212 | @@ -363,7 +363,6 @@ | |||
213 | 363 | Reporter: Sample Person | 363 | Reporter: Sample Person |
214 | 364 | Created: 2005-04-04 00:00:00+00:00 | 364 | Created: 2005-04-04 00:00:00+00:00 |
215 | 365 | Subscribers: | 365 | Subscribers: |
216 | 366 | Mark Shuttleworth | ||
217 | 367 | Sample Person | 366 | Sample Person |
218 | 368 | Ubuntu Team | 367 | Ubuntu Team |
219 | 369 | Task: Evolution | 368 | Task: Evolution |
220 | @@ -540,5 +539,3 @@ | |||
221 | 540 | True | 539 | True |
222 | 541 | >>> bug4.duplicateof == bug3 | 540 | >>> bug4.duplicateof == bug3 |
223 | 542 | True | 541 | True |
224 | 543 | |||
225 | 544 | |||
226 | 545 | 542 | ||
227 | === modified file 'lib/lp/bugs/doc/initial-bug-contacts.txt' | |||
228 | --- lib/lp/bugs/doc/initial-bug-contacts.txt 2010-10-18 22:24:59 +0000 | |||
229 | +++ lib/lp/bugs/doc/initial-bug-contacts.txt 2010-12-23 19:45:24 +0000 | |||
230 | @@ -348,7 +348,6 @@ | |||
231 | 348 | 348 | ||
232 | 349 | >>> print '\n'.join(subscriber_names(bug_two_in_ubuntu.bug)) | 349 | >>> print '\n'.join(subscriber_names(bug_two_in_ubuntu.bug)) |
233 | 350 | Foo Bar | 350 | Foo Bar |
234 | 351 | Mark Shuttleworth | ||
235 | 352 | Sample Person | 351 | Sample Person |
236 | 353 | Steve Alexander | 352 | Steve Alexander |
237 | 354 | Ubuntu Team | 353 | Ubuntu Team |
238 | 355 | 354 | ||
239 | === modified file 'lib/lp/bugs/model/bug.py' | |||
240 | --- lib/lp/bugs/model/bug.py 2010-11-05 09:16:14 +0000 | |||
241 | +++ lib/lp/bugs/model/bug.py 2010-12-23 19:45:24 +0000 | |||
242 | @@ -988,10 +988,10 @@ | |||
243 | 988 | # If the target's bug supervisor isn't set, | 988 | # If the target's bug supervisor isn't set, |
244 | 989 | # we add the owner as a subscriber. | 989 | # we add the owner as a subscriber. |
245 | 990 | pillar = bugtask.pillar | 990 | pillar = bugtask.pillar |
250 | 991 | if pillar.bug_supervisor is None: | 991 | if pillar.bug_supervisor is None and pillar.official_malone: |
251 | 992 | also_notified_subscribers.add(pillar.owner) | 992 | also_notified_subscribers.add(pillar.owner) |
252 | 993 | if recipients is not None: | 993 | if recipients is not None: |
253 | 994 | recipients.addRegistrant(pillar.owner, pillar) | 994 | recipients.addRegistrant(pillar.owner, pillar) |
254 | 995 | 995 | ||
255 | 996 | # Structural subscribers. | 996 | # Structural subscribers. |
256 | 997 | also_notified_subscribers.update( | 997 | also_notified_subscribers.update( |
257 | @@ -1790,8 +1790,12 @@ | |||
258 | 1790 | 1790 | ||
259 | 1791 | @cachedproperty | 1791 | @cachedproperty |
260 | 1792 | def _known_viewers(self): | 1792 | def _known_viewers(self): |
263 | 1793 | """A dict of of known persons able to view this bug.""" | 1793 | """A set of known persons able to view this bug. |
264 | 1794 | return set() | 1794 | |
265 | 1795 | Seed it by including the list of all owners of bugtasks for the bug. | ||
266 | 1796 | """ | ||
267 | 1797 | bugtask_owners = [bt.pillar.owner.id for bt in self.bugtasks] | ||
268 | 1798 | return set(bugtask_owners) | ||
269 | 1795 | 1799 | ||
270 | 1796 | def userCanView(self, user): | 1800 | def userCanView(self, user): |
271 | 1797 | """See `IBug`. | 1801 | """See `IBug`. |
272 | 1798 | 1802 | ||
273 | === modified file 'lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions.txt' | |||
274 | --- lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions.txt 2010-10-19 14:55:46 +0000 | |||
275 | +++ lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions.txt 2010-12-23 19:45:24 +0000 | |||
276 | @@ -192,7 +192,6 @@ | |||
277 | 192 | ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-content") | 192 | ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-content") |
278 | 193 | >>> print_also_notified(stevea_browser.contents) | 193 | >>> print_also_notified(stevea_browser.contents) |
279 | 194 | Also notified: | 194 | Also notified: |
280 | 195 | Mark Shuttleworth | ||
281 | 196 | 195 | ||
282 | 197 | But if bug #2, a bug that Steve is directly subscribed to, is marked as | 196 | But if bug #2, a bug that Steve is directly subscribed to, is marked as |
283 | 198 | a dupe of bug #3, then Steve gets indirectly subscribed to bug #3, and | 197 | a dupe of bug #3, then Steve gets indirectly subscribed to bug #3, and |
284 | @@ -243,7 +242,6 @@ | |||
285 | 243 | ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-content") | 242 | ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-content") |
286 | 244 | >>> print_also_notified(stevea_browser.contents) | 243 | >>> print_also_notified(stevea_browser.contents) |
287 | 245 | Also notified: | 244 | Also notified: |
288 | 246 | Mark Shuttleworth | ||
289 | 247 | 245 | ||
290 | 248 | Let's repeat this example, with Steve subscribed to two different dupes, | 246 | Let's repeat this example, with Steve subscribed to two different dupes, |
291 | 249 | to see how the unsubscribe notification changes slightly, because he | 247 | to see how the unsubscribe notification changes slightly, because he |
292 | @@ -274,7 +272,6 @@ | |||
293 | 274 | ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-content") | 272 | ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-content") |
294 | 275 | >>> print_also_notified(stevea_browser.contents) | 273 | >>> print_also_notified(stevea_browser.contents) |
295 | 276 | Also notified: | 274 | Also notified: |
296 | 277 | Mark Shuttleworth | ||
297 | 278 | 275 | ||
298 | 279 | >>> stevea_browser.open("http://launchpad.dev/bugs/3") | 276 | >>> stevea_browser.open("http://launchpad.dev/bugs/3") |
299 | 280 | >>> stevea_browser.getLink("Unsubscribe").click() | 277 | >>> stevea_browser.getLink("Unsubscribe").click() |
300 | @@ -317,7 +314,6 @@ | |||
301 | 317 | 314 | ||
302 | 318 | >>> print_also_notified(foobar_browser.contents) | 315 | >>> print_also_notified(foobar_browser.contents) |
303 | 319 | Also notified: | 316 | Also notified: |
304 | 320 | Mark Shuttleworth | ||
305 | 321 | 317 | ||
306 | 322 | The subscribe link for Foo Bar still says "Subscribe", because | 318 | The subscribe link for Foo Bar still says "Subscribe", because |
307 | 323 | Foo Bar can subscribe himself directly to this bug. For unsubscribing | 319 | Foo Bar can subscribe himself directly to this bug. For unsubscribing |
308 | @@ -350,7 +346,6 @@ | |||
309 | 350 | ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-content") | 346 | ... "http://launchpad.dev/bugs/3/+bug-portlet-subscribers-content") |
310 | 351 | >>> print_also_notified(foobar_browser.contents) | 347 | >>> print_also_notified(foobar_browser.contents) |
311 | 352 | Also notified: | 348 | Also notified: |
312 | 353 | Mark Shuttleworth | ||
313 | 354 | 349 | ||
314 | 355 | 350 | ||
315 | 356 | == Displaying subscribers == | 351 | == Displaying subscribers == |
316 | 357 | 352 | ||
317 | === modified file 'lib/lp/bugs/subscribers/bug.py' | |||
318 | --- lib/lp/bugs/subscribers/bug.py 2010-10-25 13:22:46 +0000 | |||
319 | +++ lib/lp/bugs/subscribers/bug.py 2010-12-23 19:45:24 +0000 | |||
320 | @@ -187,7 +187,7 @@ | |||
321 | 187 | # If the target's bug supervisor isn't set, | 187 | # If the target's bug supervisor isn't set, |
322 | 188 | # we add the owner as a subscriber. | 188 | # we add the owner as a subscriber. |
323 | 189 | pillar = bugtask.pillar | 189 | pillar = bugtask.pillar |
325 | 190 | if pillar.bug_supervisor is None: | 190 | if pillar.bug_supervisor is None and pillar.official_malone: |
326 | 191 | also_notified_subscribers.add(pillar.owner) | 191 | also_notified_subscribers.add(pillar.owner) |
327 | 192 | if recipients is not None: | 192 | if recipients is not None: |
328 | 193 | recipients.addRegistrant(pillar.owner, pillar) | 193 | recipients.addRegistrant(pillar.owner, pillar) |
329 | 194 | 194 | ||
330 | === modified file 'lib/lp/bugs/tests/test_bugchanges.py' | |||
331 | --- lib/lp/bugs/tests/test_bugchanges.py 2010-10-04 19:50:45 +0000 | |||
332 | +++ lib/lp/bugs/tests/test_bugchanges.py 2010-12-23 19:45:24 +0000 | |||
333 | @@ -932,7 +932,8 @@ | |||
334 | 932 | new_target = self.factory.makeDistributionSourcePackage( | 932 | new_target = self.factory.makeDistributionSourcePackage( |
335 | 933 | distribution=target.distribution) | 933 | distribution=target.distribution) |
336 | 934 | 934 | ||
338 | 935 | source_package_bug = self.factory.makeBug(owner=self.user) | 935 | source_package_bug = self.factory.makeBug( |
339 | 936 | owner=self.user) | ||
340 | 936 | source_package_bug_task = source_package_bug.addTask( | 937 | source_package_bug_task = source_package_bug.addTask( |
341 | 937 | owner=self.user, target=target) | 938 | owner=self.user, target=target) |
342 | 938 | self.saveOldChanges(source_package_bug) | 939 | self.saveOldChanges(source_package_bug) |
343 | @@ -956,8 +957,8 @@ | |||
344 | 956 | expected_recipients = [self.user, metadata_subscriber] | 957 | expected_recipients = [self.user, metadata_subscriber] |
345 | 957 | expected_recipients.extend( | 958 | expected_recipients.extend( |
346 | 958 | bug_task.pillar.owner | 959 | bug_task.pillar.owner |
349 | 959 | for bug_task in source_package_bug.bugtasks) | 960 | for bug_task in source_package_bug.bugtasks |
350 | 960 | 961 | if bug_task.pillar.official_malone) | |
351 | 961 | expected_notification = { | 962 | expected_notification = { |
352 | 962 | 'text': u"** Package changed: %s => %s" % ( | 963 | 'text': u"** Package changed: %s => %s" % ( |
353 | 963 | bug_task_before_modification.bugtargetname, | 964 | bug_task_before_modification.bugtargetname, |
354 | 964 | 965 | ||
355 | === modified file 'lib/lp/bugs/tests/test_bugnotification.py' | |||
356 | --- lib/lp/bugs/tests/test_bugnotification.py 2010-10-19 22:06:16 +0000 | |||
357 | +++ lib/lp/bugs/tests/test_bugnotification.py 2010-12-23 19:45:24 +0000 | |||
358 | @@ -9,6 +9,7 @@ | |||
359 | 9 | 9 | ||
360 | 10 | from lazr.lifecycle.event import ObjectModifiedEvent | 10 | from lazr.lifecycle.event import ObjectModifiedEvent |
361 | 11 | from lazr.lifecycle.snapshot import Snapshot | 11 | from lazr.lifecycle.snapshot import Snapshot |
362 | 12 | from testtools.matchers import Not | ||
363 | 12 | from zope.event import notify | 13 | from zope.event import notify |
364 | 13 | from zope.interface import providedBy | 14 | from zope.interface import providedBy |
365 | 14 | 15 | ||
366 | @@ -30,6 +31,7 @@ | |||
367 | 30 | ) | 31 | ) |
368 | 31 | from lp.testing import TestCaseWithFactory | 32 | from lp.testing import TestCaseWithFactory |
369 | 32 | from lp.testing.factory import LaunchpadObjectFactory | 33 | from lp.testing.factory import LaunchpadObjectFactory |
370 | 34 | from lp.testing.matchers import Contains | ||
371 | 33 | from lp.testing.mail_helpers import pop_notifications | 35 | from lp.testing.mail_helpers import pop_notifications |
372 | 34 | 36 | ||
373 | 35 | 37 | ||
374 | @@ -41,12 +43,16 @@ | |||
375 | 41 | def setUp(self): | 43 | def setUp(self): |
376 | 42 | login('foo.bar@canonical.com') | 44 | login('foo.bar@canonical.com') |
377 | 43 | factory = LaunchpadObjectFactory() | 45 | factory = LaunchpadObjectFactory() |
380 | 44 | self.product = factory.makeProduct() | 46 | self.product_owner = factory.makePerson(name="product-owner") |
381 | 45 | self.product_subscriber = factory.makePerson() | 47 | self.product = factory.makeProduct(owner=self.product_owner) |
382 | 48 | self.product_subscriber = factory.makePerson( | ||
383 | 49 | name="product-subscriber") | ||
384 | 46 | self.product.addBugSubscription( | 50 | self.product.addBugSubscription( |
385 | 47 | self.product_subscriber, self.product_subscriber) | 51 | self.product_subscriber, self.product_subscriber) |
388 | 48 | self.bug_subscriber = factory.makePerson() | 52 | self.bug_subscriber = factory.makePerson(name="bug-subscriber") |
389 | 49 | self.private_bug = factory.makeBug(product=self.product, private=True) | 53 | self.bug_owner = factory.makePerson(name="bug-owner") |
390 | 54 | self.private_bug = factory.makeBug( | ||
391 | 55 | product=self.product, private=True, owner=self.bug_owner) | ||
392 | 50 | self.reporter = self.private_bug.owner | 56 | self.reporter = self.private_bug.owner |
393 | 51 | self.private_bug.subscribe(self.bug_subscriber, self.reporter) | 57 | self.private_bug.subscribe(self.bug_subscriber, self.reporter) |
394 | 52 | [self.product_bugtask] = self.private_bug.bugtasks | 58 | [self.product_bugtask] = self.private_bug.bugtasks |
395 | @@ -66,7 +72,7 @@ | |||
396 | 66 | notified_people = set( | 72 | notified_people = set( |
397 | 67 | recipient.person.name | 73 | recipient.person.name |
398 | 68 | for recipient in latest_notification.recipients) | 74 | for recipient in latest_notification.recipients) |
400 | 69 | self.assertEqual(notified_people, self.direct_subscribers) | 75 | self.assertEqual(self.direct_subscribers, notified_people) |
401 | 70 | 76 | ||
402 | 71 | def test_add_comment(self): | 77 | def test_add_comment(self): |
403 | 72 | # Comment additions are sent to the direct subscribers only. | 78 | # Comment additions are sent to the direct subscribers only. |
404 | @@ -76,7 +82,7 @@ | |||
405 | 76 | notified_people = set( | 82 | notified_people = set( |
406 | 77 | recipient.person.name | 83 | recipient.person.name |
407 | 78 | for recipient in latest_notification.recipients) | 84 | for recipient in latest_notification.recipients) |
409 | 79 | self.assertEqual(notified_people, self.direct_subscribers) | 85 | self.assertEqual(self.direct_subscribers, notified_people) |
410 | 80 | 86 | ||
411 | 81 | def test_bug_edit(self): | 87 | def test_bug_edit(self): |
412 | 82 | # Bug edits are sent to direct the subscribers only. | 88 | # Bug edits are sent to direct the subscribers only. |
413 | @@ -90,7 +96,7 @@ | |||
414 | 90 | notified_people = set( | 96 | notified_people = set( |
415 | 91 | recipient.person.name | 97 | recipient.person.name |
416 | 92 | for recipient in latest_notification.recipients) | 98 | for recipient in latest_notification.recipients) |
418 | 93 | self.assertEqual(notified_people, self.direct_subscribers) | 99 | self.assertEqual(self.direct_subscribers, notified_people) |
419 | 94 | 100 | ||
420 | 95 | 101 | ||
421 | 96 | class TestNotificationsSentForBugExpiration(TestCaseWithFactory): | 102 | class TestNotificationsSentForBugExpiration(TestCaseWithFactory): |
422 | @@ -206,3 +212,104 @@ | |||
423 | 206 | recipient.person | 212 | recipient.person |
424 | 207 | for recipient in latest_notification.recipients) | 213 | for recipient in latest_notification.recipients) |
425 | 208 | self.assertEqual(self.dupe_subscribers, recipients) | 214 | self.assertEqual(self.dupe_subscribers, recipients) |
426 | 215 | |||
427 | 216 | |||
428 | 217 | class NotificationForRegistrantsMixin: | ||
429 | 218 | """Mixin for testing when registrants get notified.""" | ||
430 | 219 | |||
431 | 220 | layer = DatabaseFunctionalLayer | ||
432 | 221 | |||
433 | 222 | def setUp(self): | ||
434 | 223 | super(NotificationForRegistrantsMixin, self).setUp( | ||
435 | 224 | user='foo.bar@canonical.com') | ||
436 | 225 | self.pillar_owner = self.factory.makePerson(name="distro-owner") | ||
437 | 226 | self.bug_owner = self.factory.makePerson(name="bug-owner") | ||
438 | 227 | self.pillar = self.makePillar() | ||
439 | 228 | self.bug = self.makeBug() | ||
440 | 229 | |||
441 | 230 | def test_notification_uses_malone(self): | ||
442 | 231 | self.pillar.official_malone = True | ||
443 | 232 | direct = self.bug.getDirectSubscribers() | ||
444 | 233 | indirect = self.bug.getIndirectSubscribers() | ||
445 | 234 | self.assertThat(direct, Not(Contains(self.pillar_owner))) | ||
446 | 235 | self.assertThat(indirect, Contains(self.pillar_owner)) | ||
447 | 236 | |||
448 | 237 | def test_notification_does_not_use_malone(self): | ||
449 | 238 | self.pillar.official_malone = False | ||
450 | 239 | direct = self.bug.getDirectSubscribers() | ||
451 | 240 | indirect = self.bug.getIndirectSubscribers() | ||
452 | 241 | self.assertThat(direct, Not(Contains(self.pillar_owner))) | ||
453 | 242 | self.assertThat(indirect, Not(Contains(self.pillar_owner))) | ||
454 | 243 | |||
455 | 244 | def test_status_change_uses_malone(self): | ||
456 | 245 | # Status changes are sent to the direct and indirect subscribers. | ||
457 | 246 | self.pillar.official_malone = True | ||
458 | 247 | [bugtask] = self.bug.bugtasks | ||
459 | 248 | all_subscribers = set( | ||
460 | 249 | [person.name for person in | ||
461 | 250 | self.bug.getDirectSubscribers() + | ||
462 | 251 | self.bug.getIndirectSubscribers()]) | ||
463 | 252 | bugtask_before_modification = Snapshot( | ||
464 | 253 | bugtask, providing=providedBy(bugtask)) | ||
465 | 254 | bugtask.transitionToStatus( | ||
466 | 255 | BugTaskStatus.INVALID, self.bug.owner) | ||
467 | 256 | notify(ObjectModifiedEvent( | ||
468 | 257 | bugtask, bugtask_before_modification, ['status'], | ||
469 | 258 | user=self.bug.owner)) | ||
470 | 259 | latest_notification = BugNotification.selectFirst(orderBy='-id') | ||
471 | 260 | notified_people = set( | ||
472 | 261 | recipient.person.name | ||
473 | 262 | for recipient in latest_notification.recipients) | ||
474 | 263 | self.assertEqual(all_subscribers, notified_people) | ||
475 | 264 | self.assertThat(all_subscribers, Contains(self.pillar_owner.name)) | ||
476 | 265 | |||
477 | 266 | def test_status_change_does_not_use_malone(self): | ||
478 | 267 | # Status changes are sent to the direct and indirect subscribers. | ||
479 | 268 | self.pillar.official_malone = False | ||
480 | 269 | [bugtask] = self.bug.bugtasks | ||
481 | 270 | all_subscribers = set( | ||
482 | 271 | [person.name for person in | ||
483 | 272 | self.bug.getDirectSubscribers() + | ||
484 | 273 | self.bug.getIndirectSubscribers()]) | ||
485 | 274 | bugtask_before_modification = Snapshot( | ||
486 | 275 | bugtask, providing=providedBy(bugtask)) | ||
487 | 276 | bugtask.transitionToStatus( | ||
488 | 277 | BugTaskStatus.INVALID, self.bug.owner) | ||
489 | 278 | notify(ObjectModifiedEvent( | ||
490 | 279 | bugtask, bugtask_before_modification, ['status'], | ||
491 | 280 | user=self.bug.owner)) | ||
492 | 281 | latest_notification = BugNotification.selectFirst(orderBy='-id') | ||
493 | 282 | notified_people = set( | ||
494 | 283 | recipient.person.name | ||
495 | 284 | for recipient in latest_notification.recipients) | ||
496 | 285 | self.assertEqual(all_subscribers, notified_people) | ||
497 | 286 | self.assertThat( | ||
498 | 287 | all_subscribers, Not(Contains(self.pillar_owner.name))) | ||
499 | 288 | |||
500 | 289 | |||
501 | 290 | class TestNotificationsForRegistrantsForDistros( | ||
502 | 291 | NotificationForRegistrantsMixin, TestCaseWithFactory): | ||
503 | 292 | """Test when distribution registrants get notified.""" | ||
504 | 293 | |||
505 | 294 | def makePillar(self): | ||
506 | 295 | return self.factory.makeDistribution( | ||
507 | 296 | owner=self.pillar_owner) | ||
508 | 297 | |||
509 | 298 | def makeBug(self): | ||
510 | 299 | return self.factory.makeBug( | ||
511 | 300 | distribution=self.pillar, | ||
512 | 301 | owner=self.bug_owner) | ||
513 | 302 | |||
514 | 303 | |||
515 | 304 | class TestNotificationsForRegistrantsForProducts( | ||
516 | 305 | NotificationForRegistrantsMixin, TestCaseWithFactory): | ||
517 | 306 | """Test when product registrants get notified.""" | ||
518 | 307 | |||
519 | 308 | def makePillar(self): | ||
520 | 309 | return self.factory.makeProduct( | ||
521 | 310 | owner=self.pillar_owner) | ||
522 | 311 | |||
523 | 312 | def makeBug(self): | ||
524 | 313 | return self.factory.makeBug( | ||
525 | 314 | product=self.pillar, | ||
526 | 315 | owner=self.bug_owner) | ||
527 | 209 | 316 | ||
528 | === modified file 'lib/lp/bugs/tests/test_bugs_webservice.py' | |||
529 | --- lib/lp/bugs/tests/test_bugs_webservice.py 2010-10-04 19:50:45 +0000 | |||
530 | +++ lib/lp/bugs/tests/test_bugs_webservice.py 2010-12-23 19:45:24 +0000 | |||
531 | @@ -166,18 +166,18 @@ | |||
532 | 166 | collector.register() | 166 | collector.register() |
533 | 167 | self.addCleanup(collector.unregister) | 167 | self.addCleanup(collector.unregister) |
534 | 168 | url = '/bugs/%d/attachments?ws.size=75' % self.bug.id | 168 | url = '/bugs/%d/attachments?ws.size=75' % self.bug.id |
536 | 169 | #First request | 169 | # First request. |
537 | 170 | store.flush() | 170 | store.flush() |
538 | 171 | store.reset() | 171 | store.reset() |
539 | 172 | response = webservice.get(url) | 172 | response = webservice.get(url) |
541 | 173 | self.assertThat(collector, HasQueryCount(LessThan(21))) | 173 | self.assertThat(collector, HasQueryCount(LessThan(22))) |
542 | 174 | with_2_count = collector.count | 174 | with_2_count = collector.count |
543 | 175 | self.failUnlessEqual(response.status, 200) | 175 | self.failUnlessEqual(response.status, 200) |
544 | 176 | login(USER_EMAIL) | 176 | login(USER_EMAIL) |
545 | 177 | for i in range(5): | 177 | for i in range(5): |
546 | 178 | self.factory.makeBugAttachment(self.bug) | 178 | self.factory.makeBugAttachment(self.bug) |
547 | 179 | logout() | 179 | logout() |
549 | 180 | #Second request | 180 | # Second request. |
550 | 181 | store.flush() | 181 | store.flush() |
551 | 182 | store.reset() | 182 | store.reset() |
552 | 183 | response = webservice.get(url) | 183 | response = webservice.get(url) |
553 | @@ -202,11 +202,11 @@ | |||
554 | 202 | collector.register() | 202 | collector.register() |
555 | 203 | self.addCleanup(collector.unregister) | 203 | self.addCleanup(collector.unregister) |
556 | 204 | url = '/bugs/%d/messages?ws.size=75' % bug.id | 204 | url = '/bugs/%d/messages?ws.size=75' % bug.id |
558 | 205 | #First request | 205 | # First request. |
559 | 206 | store.flush() | 206 | store.flush() |
560 | 207 | store.reset() | 207 | store.reset() |
561 | 208 | response = webservice.get(url) | 208 | response = webservice.get(url) |
563 | 209 | self.assertThat(collector, HasQueryCount(LessThan(21))) | 209 | self.assertThat(collector, HasQueryCount(LessThan(22))) |
564 | 210 | with_2_count = collector.count | 210 | with_2_count = collector.count |
565 | 211 | self.failUnlessEqual(response.status, 200) | 211 | self.failUnlessEqual(response.status, 200) |
566 | 212 | login(USER_EMAIL) | 212 | login(USER_EMAIL) |
567 | @@ -214,7 +214,7 @@ | |||
568 | 214 | self.factory.makeBugComment(bug) | 214 | self.factory.makeBugComment(bug) |
569 | 215 | self.factory.makeBugAttachment(bug) | 215 | self.factory.makeBugAttachment(bug) |
570 | 216 | logout() | 216 | logout() |
572 | 217 | #Second request | 217 | # Second request. |
573 | 218 | store.flush() | 218 | store.flush() |
574 | 219 | store.reset() | 219 | store.reset() |
575 | 220 | response = webservice.get(url) | 220 | response = webservice.get(url) |
576 | 221 | 221 | ||
577 | === modified file 'lib/lp/bugs/tests/test_bugtask_search.py' | |||
578 | --- lib/lp/bugs/tests/test_bugtask_search.py 2010-12-14 02:18:32 +0000 | |||
579 | +++ lib/lp/bugs/tests/test_bugtask_search.py 2010-12-23 19:45:24 +0000 | |||
580 | @@ -91,7 +91,8 @@ | |||
581 | 91 | # search result. | 91 | # search result. |
582 | 92 | user = self.factory.makePerson() | 92 | user = self.factory.makePerson() |
583 | 93 | with person_logged_in(self.owner): | 93 | with person_logged_in(self.owner): |
585 | 94 | self.bugtasks[-1].bug.subscribe(user, self.owner) | 94 | bug = self.bugtasks[-1].bug |
586 | 95 | bug.subscribe(user, self.owner) | ||
587 | 95 | params = self.getBugTaskSearchParams(user=user) | 96 | params = self.getBugTaskSearchParams(user=user) |
588 | 96 | self.assertSearchFinds(params, self.bugtasks) | 97 | self.assertSearchFinds(params, self.bugtasks) |
589 | 97 | 98 |
This looks good to land. I think this will do a lot to stop the avalanche of email.