Merge lp:~bac/launchpad/bug-720147 into lp:launchpad

Proposed by Brad Crittenden
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 12967
Proposed branch: lp:~bac/launchpad/bug-720147
Merge into: lp:launchpad
Diff against target: 432 lines (+220/-32)
5 files modified
lib/lp/bugs/browser/bugtarget.py (+10/-10)
lib/lp/bugs/interfaces/bug.py (+5/-1)
lib/lp/bugs/model/bug.py (+14/-4)
lib/lp/bugs/scripts/tests/test_bugnotification.py (+65/-13)
lib/lp/bugs/tests/test_bug.py (+126/-4)
To merge this branch: bzr merge lp:~bac/launchpad/bug-720147
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+59677@code.launchpad.net

Commit message

[r=benji][bug=720147] Correctly handle filtering for bugs created without the default status.

Description of the change

= Summary =

Bugs created with a status other than NEW were sending notifications for
subscriptions with other filters.

== Proposed fix ==

The bug notification was kicked off before the status change was
effected resulting in improper notices being sent.

== Pre-implementation notes ==

This work was done by Graham and Gary. I'm merely shepherding it
through review and landing in their absence.

I did, however, clean up some lint.

== Implementation details ==

As above.

== Tests ==

bin/test -vvm lp.bugs -t test_bugnotification

== Demo and Q/A ==

Create a structural subscription with filter with NEW status. Create a
bug with initial status of TRIAGE. Ensure notification is not sent.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/bugs/browser/bugtarget.py
  lib/lp/bugs/scripts/tests/test_bugnotification.py
  lib/lp/bugs/model/bug.py
  lib/lp/bugs/tests/test_bug.py
  lib/lp/bugs/interfaces/bug.py

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch looks good.

The only thought I had was that the part starting on line 13 of the diff
is a bit repetitive and I had to look very closely to see if it was
exactly the same pattern for each line or if one or more were subtly
different. However, I'm not sure my version is any better:
http://pastebin.ubuntu.com/602393/ (untested).

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/bugs/browser/bugtarget.py'
--- lib/lp/bugs/browser/bugtarget.py 2011-04-15 02:56:03 +0000
+++ lib/lp/bugs/browser/bugtarget.py 2011-05-02 17:11:01 +0000
@@ -556,20 +556,20 @@
556556
557 if extra_data.private:557 if extra_data.private:
558 params.private = extra_data.private558 params.private = extra_data.private
559559 if 'status' in data:
560 self.added_bug = bug = context.createBug(params)560 params.status = data['status']
561
562 # Apply any extra options given by a bug supervisor.
563 bugtask = self.added_bug.default_bugtask
564 if 'assignee' in data:561 if 'assignee' in data:
565 bugtask.transitionToAssignee(data['assignee'])562 params.assignee = data['assignee']
566 if 'status' in data:563 if 'status' in data:
567 bugtask.transitionToStatus(data['status'], self.user)564 params.status = data['status']
568 if 'importance' in data:565 if 'importance' in data:
569 bugtask.transitionToImportance(data['importance'], self.user)566 params.importance = data['importance']
570 if 'milestone' in data:567 if 'milestone' in data:
571 bugtask.milestone = data['milestone']568 params.milestone = data['milestone']
572569
570 self.added_bug = bug = context.createBug(params)
571
572 # Apply any extra options given by a bug supervisor.
573 for comment in extra_data.comments:573 for comment in extra_data.comments:
574 bug.newMessage(self.user, bug.followup_subject(), comment)574 bug.newMessage(self.user, bug.followup_subject(), comment)
575 notifications.append(575 notifications.append(
576576
=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py 2011-04-29 11:20:01 +0000
+++ lib/lp/bugs/interfaces/bug.py 2011-05-02 17:11:01 +0000
@@ -97,7 +97,8 @@
97 def __init__(self, owner, title, comment=None, description=None, msg=None,97 def __init__(self, owner, title, comment=None, description=None, msg=None,
98 status=None, datecreated=None, security_related=False,98 status=None, datecreated=None, security_related=False,
99 private=False, subscribers=(), binarypackagename=None,99 private=False, subscribers=(), binarypackagename=None,
100 tags=None, subscribe_owner=True, filed_by=None):100 tags=None, subscribe_owner=True, filed_by=None,
101 importance=None, milestone=None, assignee=None):
101 self.owner = owner102 self.owner = owner
102 self.title = title103 self.title = title
103 self.comment = comment104 self.comment = comment
@@ -115,6 +116,9 @@
115 self.tags = tags116 self.tags = tags
116 self.subscribe_owner = subscribe_owner117 self.subscribe_owner = subscribe_owner
117 self.filed_by = filed_by118 self.filed_by = filed_by
119 self.importance = importance
120 self.milestone = milestone
121 self.assignee = assignee
118122
119 def setBugTarget(self, product=None, distribution=None,123 def setBugTarget(self, product=None, distribution=None,
120 sourcepackagename=None):124 sourcepackagename=None):
121125
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2011-05-02 01:27:43 +0000
+++ lib/lp/bugs/model/bug.py 2011-05-02 17:11:01 +0000
@@ -222,7 +222,8 @@
222 "datecreated", "security_related", "private",222 "datecreated", "security_related", "private",
223 "distribution", "sourcepackagename", "binarypackagename",223 "distribution", "sourcepackagename", "binarypackagename",
224 "product", "status", "subscribers", "tags",224 "product", "status", "subscribers", "tags",
225 "subscribe_owner", "filed_by"])225 "subscribe_owner", "filed_by", "importance",
226 "milestone", "assignee"])
226227
227228
228class BugTag(SQLBase):229class BugTag(SQLBase):
@@ -2442,15 +2443,24 @@
2442 # Create the task on a product if one was passed.2443 # Create the task on a product if one was passed.
2443 if params.product:2444 if params.product:
2444 getUtility(IBugTaskSet).createTask(2445 getUtility(IBugTaskSet).createTask(
2445 bug=bug, product=params.product, owner=params.owner,2446 bug=bug, product=params.product, owner=params.owner)
2446 status=params.status)
24472447
2448 # Create the task on a source package name if one was passed.2448 # Create the task on a source package name if one was passed.
2449 if params.distribution:2449 if params.distribution:
2450 getUtility(IBugTaskSet).createTask(2450 getUtility(IBugTaskSet).createTask(
2451 bug=bug, distribution=params.distribution,2451 bug=bug, distribution=params.distribution,
2452 sourcepackagename=params.sourcepackagename,2452 sourcepackagename=params.sourcepackagename,
2453 owner=params.owner, status=params.status)2453 owner=params.owner)
2454
2455 bug_task = bug.default_bugtask
2456 if params.assignee:
2457 bug_task.transitionToAssignee(params.assignee)
2458 if params.status:
2459 bug_task.transitionToStatus(params.status, params.owner)
2460 if params.importance:
2461 bug_task.transitionToImportance(params.importance, params.owner)
2462 if params.milestone:
2463 bug_task.transitionToMilestone(params.milestone, params.owner)
24542464
2455 # Tell everyone.2465 # Tell everyone.
2456 notify(event)2466 notify(event)
24572467
=== modified file 'lib/lp/bugs/scripts/tests/test_bugnotification.py'
--- lib/lp/bugs/scripts/tests/test_bugnotification.py 2011-04-05 22:34:35 +0000
+++ lib/lp/bugs/scripts/tests/test_bugnotification.py 2011-05-02 17:11:01 +0000
@@ -21,6 +21,7 @@
21 )21 )
22from canonical.launchpad.ftests import login22from canonical.launchpad.ftests import login
23from canonical.launchpad.helpers import get_contact_email_addresses23from canonical.launchpad.helpers import get_contact_email_addresses
24from canonical.launchpad.interfaces.lpstorm import IStore
24from canonical.launchpad.interfaces.message import IMessageSet25from canonical.launchpad.interfaces.message import IMessageSet
25from canonical.testing.layers import LaunchpadZopelessLayer26from canonical.testing.layers import LaunchpadZopelessLayer
26from lp.bugs.adapters.bugchange import (27from lp.bugs.adapters.bugchange import (
@@ -38,16 +39,20 @@
38 CveUnlinkedFromBug,39 CveUnlinkedFromBug,
39 )40 )
40from lp.bugs.interfaces.bug import (41from lp.bugs.interfaces.bug import (
42 CreateBugParams,
41 IBug,43 IBug,
42 IBugSet,44 IBugSet,
43 )45 )
44from lp.bugs.interfaces.bugnotification import IBugNotificationSet46from lp.bugs.interfaces.bugnotification import IBugNotificationSet
45from lp.bugs.interfaces.bugtask import BugTaskStatus47from lp.bugs.interfaces.bugtask import (
48 BugTaskImportance,
49 BugTaskStatus,
50 )
46from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients51from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients
47from lp.bugs.model.bugnotification import (52from lp.bugs.model.bugnotification import (
48 BugNotification,53 BugNotification,
49 BugNotificationFilter,54 BugNotificationFilter,
50# BugNotificationRecipient,55 BugNotificationRecipient,
51 )56 )
52from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilterMute57from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilterMute
53from lp.bugs.model.bugtask import BugTask58from lp.bugs.model.bugtask import BugTask
@@ -1021,7 +1026,7 @@
1021 def test_header_single(self):1026 def test_header_single(self):
1022 # A single filter with a description makes all emails1027 # A single filter with a description makes all emails
1023 # include that particular filter description in a header.1028 # include that particular filter description in a header.
1024 bug_filter = self.addFilter(u"Test filter")1029 self.addFilter(u"Test filter")
10251030
1026 self.assertContentEqual([u"Test filter"],1031 self.assertContentEqual([u"Test filter"],
1027 self.getSubscriptionEmailHeaders())1032 self.getSubscriptionEmailHeaders())
@@ -1029,8 +1034,8 @@
1029 def test_header_multiple(self):1034 def test_header_multiple(self):
1030 # Multiple filters with a description make all emails1035 # Multiple filters with a description make all emails
1031 # include all filter descriptions in the header.1036 # include all filter descriptions in the header.
1032 bug_filter = self.addFilter(u"First filter")1037 self.addFilter(u"First filter")
1033 bug_filter = self.addFilter(u"Second filter")1038 self.addFilter(u"Second filter")
10341039
1035 self.assertContentEqual([u"First filter", u"Second filter"],1040 self.assertContentEqual([u"First filter", u"Second filter"],
1036 self.getSubscriptionEmailHeaders())1041 self.getSubscriptionEmailHeaders())
@@ -1042,10 +1047,10 @@
1042 other_person = self.factory.makePerson()1047 other_person = self.factory.makePerson()
1043 other_subscription = self.bug.default_bugtask.target.addSubscription(1048 other_subscription = self.bug.default_bugtask.target.addSubscription(
1044 other_person, other_person)1049 other_person, other_person)
1045 bug_filter = self.addFilter(u"Someone's filter", other_subscription)1050 self.addFilter(u"Someone's filter", other_subscription)
1046 self.addNotificationRecipient(self.notification, other_person)1051 self.addNotificationRecipient(self.notification, other_person)
10471052
1048 this_filter = self.addFilter(u"Test filter")1053 self.addFilter(u"Test filter")
10491054
1050 the_subscriber = self.subscription.subscriber1055 the_subscriber = self.subscription.subscriber
1051 self.assertEquals(1056 self.assertEquals(
@@ -1062,7 +1067,7 @@
1062 def test_body_single(self):1067 def test_body_single(self):
1063 # A single filter with a description makes all emails1068 # A single filter with a description makes all emails
1064 # include that particular filter description in the body.1069 # include that particular filter description in the body.
1065 bug_filter = self.addFilter(u"Test filter")1070 self.addFilter(u"Test filter")
10661071
1067 self.assertContentEqual([u"Matching subscriptions: Test filter"],1072 self.assertContentEqual([u"Matching subscriptions: Test filter"],
1068 self.getSubscriptionEmailBody())1073 self.getSubscriptionEmailBody())
@@ -1070,15 +1075,15 @@
1070 def test_body_multiple(self):1075 def test_body_multiple(self):
1071 # Multiple filters with description make all emails1076 # Multiple filters with description make all emails
1072 # include them in the email body.1077 # include them in the email body.
1073 bug_filter = self.addFilter(u"First filter")1078 self.addFilter(u"First filter")
1074 bug_filter = self.addFilter(u"Second filter")1079 self.addFilter(u"Second filter")
10751080
1076 self.assertContentEqual(1081 self.assertContentEqual(
1077 [u"Matching subscriptions: First filter, Second filter"],1082 [u"Matching subscriptions: First filter, Second filter"],
1078 self.getSubscriptionEmailBody())1083 self.getSubscriptionEmailBody())
10791084
1080 def test_muted(self):1085 def test_muted(self):
1081 bug_filter = self.addFilter(u"Test filter")1086 self.addFilter(u"Test filter")
1082 BugSubscriptionFilterMute(1087 BugSubscriptionFilterMute(
1083 person=self.subscription.subscriber,1088 person=self.subscription.subscriber,
1084 filter=self.notification.bug_filters.one())1089 filter=self.notification.bug_filters.one())
@@ -1089,11 +1094,58 @@
1089 def test_header_multiple_one_muted(self):1094 def test_header_multiple_one_muted(self):
1090 # Multiple filters with a description make all emails1095 # Multiple filters with a description make all emails
1091 # include all filter descriptions in the header.1096 # include all filter descriptions in the header.
1092 bug_filter = self.addFilter(u"First filter")1097 self.addFilter(u"First filter")
1093 bug_filter = self.addFilter(u"Second filter")1098 self.addFilter(u"Second filter")
1094 BugSubscriptionFilterMute(1099 BugSubscriptionFilterMute(
1095 person=self.subscription.subscriber,1100 person=self.subscription.subscriber,
1096 filter=self.notification.bug_filters[0])1101 filter=self.notification.bug_filters[0])
10971102
1098 self.assertContentEqual([u"Second filter"],1103 self.assertContentEqual([u"Second filter"],
1099 self.getSubscriptionEmailHeaders())1104 self.getSubscriptionEmailHeaders())
1105
1106
1107class TestEmailNotificationsWithFiltersWhenBugCreated(TestCaseWithFactory):
1108 # See bug 720147.
1109
1110 layer = LaunchpadZopelessLayer
1111
1112 def setUp(self):
1113 super(TestEmailNotificationsWithFiltersWhenBugCreated, self).setUp()
1114 self.subscriber = self.factory.makePerson()
1115 self.submitter = self.factory.makePerson()
1116 self.product = self.factory.makeProduct(
1117 bug_supervisor=self.submitter)
1118 self.subscription = self.product.addSubscription(
1119 self.subscriber, self.subscriber)
1120 self.filter = self.subscription.bug_filters[0]
1121 self.filter.description = u'Needs triage'
1122 self.filter.statuses = [BugTaskStatus.NEW, BugTaskStatus.INCOMPLETE]
1123
1124 def test_filters_match_when_bug_is_created(self):
1125 message = u"this is an unfiltered comment"
1126 params = CreateBugParams(
1127 title=u"crashes all the time",
1128 comment=message, owner=self.submitter,
1129 status=BugTaskStatus.NEW)
1130 bug = self.product.createBug(params)
1131 notification = IStore(BugNotification).find(
1132 BugNotification,
1133 BugNotification.id==BugNotificationRecipient.bug_notificationID,
1134 BugNotificationRecipient.personID == self.subscriber.id,
1135 BugNotification.bug == bug).one()
1136 self.assertEqual(notification.message.text_contents, message)
1137
1138 def test_filters_do_not_match_when_bug_is_created(self):
1139 message = u"this is a filtered comment"
1140 params = CreateBugParams(
1141 title=u"crashes all the time",
1142 comment=message, owner=self.submitter,
1143 status=BugTaskStatus.TRIAGED,
1144 importance=BugTaskImportance.HIGH)
1145 bug = self.product.createBug(params)
1146 notifications = IStore(BugNotification).find(
1147 BugNotification,
1148 BugNotification.id==BugNotificationRecipient.bug_notificationID,
1149 BugNotificationRecipient.personID == self.subscriber.id,
1150 BugNotification.bug == bug)
1151 self.assertTrue(notifications.is_empty())
11001152
=== modified file 'lib/lp/bugs/tests/test_bug.py'
--- lib/lp/bugs/tests/test_bug.py 2011-04-13 15:23:54 +0000
+++ lib/lp/bugs/tests/test_bug.py 2011-05-02 17:11:01 +0000
@@ -1,4 +1,4 @@
1# Copyright 2011 Canonical Ltd. This software is licensed under the1
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for lp.bugs.model.Bug."""4"""Tests for lp.bugs.model.Bug."""
@@ -6,11 +6,23 @@
6__metaclass__ = type6__metaclass__ = type
77
8from lazr.lifecycle.snapshot import Snapshot8from lazr.lifecycle.snapshot import Snapshot
9from zope.component import getUtility
9from zope.interface import providedBy10from zope.interface import providedBy
1011
11from canonical.testing.layers import DatabaseFunctionalLayer12from canonical.testing.layers import DatabaseFunctionalLayer
1213
13from lp.bugs.enum import BugNotificationLevel14from lp.bugs.enum import BugNotificationLevel
15from lp.bugs.interfaces.bug import(
16 CreateBugParams,
17 IBugSet,
18 )
19from lp.bugs.interfaces.bugtask import (
20 BugTaskImportance,
21 BugTaskStatus,
22 UserCannotEditBugTaskAssignee,
23 UserCannotEditBugTaskImportance,
24 UserCannotEditBugTaskMilestone,
25 )
14from lp.testing import (26from lp.testing import (
15 person_logged_in,27 person_logged_in,
16 StormStatementRecorder,28 StormStatementRecorder,
@@ -30,7 +42,7 @@
30 # Bug.isMuted() will return True if the passed to it has a42 # Bug.isMuted() will return True if the passed to it has a
31 # BugSubscription with a BugNotificationLevel of NOTHING.43 # BugSubscription with a BugNotificationLevel of NOTHING.
32 with person_logged_in(self.person):44 with person_logged_in(self.person):
33 subscription = self.bug.subscribe(45 self.bug.subscribe(
34 self.person, self.person, level=BugNotificationLevel.NOTHING)46 self.person, self.person, level=BugNotificationLevel.NOTHING)
35 self.assertEqual(True, self.bug.isMuted(self.person))47 self.assertEqual(True, self.bug.isMuted(self.person))
3648
@@ -38,7 +50,7 @@
38 # Bug.isMuted() will return False if the user has a subscription50 # Bug.isMuted() will return False if the user has a subscription
39 # with BugNotificationLevel that's not NOTHING.51 # with BugNotificationLevel that's not NOTHING.
40 with person_logged_in(self.person):52 with person_logged_in(self.person):
41 subscription = self.bug.subscribe(53 self.bug.subscribe(
42 self.person, self.person, level=BugNotificationLevel.METADATA)54 self.person, self.person, level=BugNotificationLevel.METADATA)
43 self.assertEqual(False, self.bug.isMuted(self.person))55 self.assertEqual(False, self.bug.isMuted(self.person))
4456
@@ -122,7 +134,7 @@
122 # optimizations that might trigger the problem again.134 # optimizations that might trigger the problem again.
123 with person_logged_in(self.person):135 with person_logged_in(self.person):
124 with StormStatementRecorder() as recorder:136 with StormStatementRecorder() as recorder:
125 snapshot = Snapshot(self.bug, providing=providedBy(self.bug))137 Snapshot(self.bug, providing=providedBy(self.bug))
126 sql_statements = recorder.statements138 sql_statements = recorder.statements
127 # This uses "self" as a marker to show that the attribute does not139 # This uses "self" as a marker to show that the attribute does not
128 # exist. We do not use hasattr because it eats exceptions.140 # exist. We do not use hasattr because it eats exceptions.
@@ -139,3 +151,113 @@
139 [token for token in sql_tokens151 [token for token in sql_tokens
140 if token.startswith('message')],152 if token.startswith('message')],
141 [])153 [])
154
155
156class TestBugCreation(TestCaseWithFactory):
157 """Tests for bug creation."""
158
159 layer = DatabaseFunctionalLayer
160
161 def test_CreateBugParams_accepts_importance(self):
162 # The importance of the initial bug task can be set using
163 # CreateBugParams
164 owner = self.factory.makePerson()
165 target = self.factory.makeProduct(owner=owner)
166 with person_logged_in(owner):
167 params = CreateBugParams(
168 owner=owner, title="A bug", comment="Nothing important.",
169 importance=BugTaskImportance.HIGH)
170 params.setBugTarget(product=target)
171 bug = getUtility(IBugSet).createBug(params)
172 self.assertEqual(
173 bug.default_bugtask.importance, params.importance)
174
175 def test_CreateBugParams_accepts_assignee(self):
176 # The assignee of the initial bug task can be set using
177 # CreateBugParams
178 owner = self.factory.makePerson()
179 target = self.factory.makeProduct(owner=owner)
180 with person_logged_in(owner):
181 params = CreateBugParams(
182 owner=owner, title="A bug", comment="Nothing important.",
183 assignee=owner)
184 params.setBugTarget(product=target)
185 bug = getUtility(IBugSet).createBug(params)
186 self.assertEqual(
187 bug.default_bugtask.assignee, params.assignee)
188
189 def test_CreateBugParams_accepts_milestone(self):
190 # The milestone of the initial bug task can be set using
191 # CreateBugParams
192 owner = self.factory.makePerson()
193 target = self.factory.makeProduct(owner=owner)
194 with person_logged_in(owner):
195 params = CreateBugParams(
196 owner=owner, title="A bug", comment="Nothing important.",
197 milestone=self.factory.makeMilestone(product=target))
198 params.setBugTarget(product=target)
199 bug = getUtility(IBugSet).createBug(params)
200 self.assertEqual(
201 bug.default_bugtask.milestone, params.milestone)
202
203 def test_CreateBugParams_accepts_status(self):
204 # The status of the initial bug task can be set using
205 # CreateBugParams
206 owner = self.factory.makePerson()
207 target = self.factory.makeProduct(owner=owner)
208 with person_logged_in(owner):
209 params = CreateBugParams(
210 owner=owner, title="A bug", comment="Nothing important.",
211 status=BugTaskStatus.TRIAGED)
212 params.setBugTarget(product=target)
213 bug = getUtility(IBugSet).createBug(params)
214 self.assertEqual(
215 bug.default_bugtask.status, params.status)
216
217 def test_CreateBugParams_rejects_not_allowed_importance_changes(self):
218 # createBug() will reject any importance value passed by users
219 # who don't have the right to set the importance.
220 person = self.factory.makePerson()
221 target = self.factory.makeProduct()
222 with person_logged_in(person):
223 params = CreateBugParams(
224 owner=person, title="A bug", comment="Nothing important.",
225 importance=BugTaskImportance.HIGH)
226 params.setBugTarget(product=target)
227 self.assertRaises(
228 UserCannotEditBugTaskImportance,
229 getUtility(IBugSet).createBug, params)
230
231 def test_CreateBugParams_rejects_not_allowed_assignee_changes(self):
232 # createBug() will reject any importance value passed by users
233 # who don't have the right to set the assignee.
234 person = self.factory.makePerson()
235 person_2 = self.factory.makePerson()
236 target = self.factory.makeProduct()
237 # Setting the target's bug supervisor means that
238 # canTransitionToAssignee() will return False for `person` if
239 # another Person is passed as `assignee`.
240 with person_logged_in(target.owner):
241 target.setBugSupervisor(target.owner, target.owner)
242 with person_logged_in(person):
243 params = CreateBugParams(
244 owner=person, title="A bug", comment="Nothing important.",
245 assignee=person_2)
246 params.setBugTarget(product=target)
247 self.assertRaises(
248 UserCannotEditBugTaskAssignee,
249 getUtility(IBugSet).createBug, params)
250
251 def test_CreateBugParams_rejects_not_allowed_milestone_changes(self):
252 # createBug() will reject any importance value passed by users
253 # who don't have the right to set the milestone.
254 person = self.factory.makePerson()
255 target = self.factory.makeProduct()
256 with person_logged_in(person):
257 params = CreateBugParams(
258 owner=person, title="A bug", comment="Nothing important.",
259 milestone=self.factory.makeMilestone(product=target))
260 params.setBugTarget(product=target)
261 self.assertRaises(
262 UserCannotEditBugTaskMilestone,
263 getUtility(IBugSet).createBug, params)