Merge lp:~wgrant/launchpad/bug-restrict-type into lp:launchpad
- bug-restrict-type
- Merge into devel
Proposed by
William Grant
Status: | Merged |
---|---|
Approved by: | William Grant |
Approved revision: | no longer in the source branch. |
Merged at revision: | 15887 |
Proposed branch: | lp:~wgrant/launchpad/bug-restrict-type |
Merge into: | lp:launchpad |
Diff against target: |
934 lines (+188/-282) 19 files modified
lib/lp/bugs/browser/bugalsoaffects.py (+1/-1) lib/lp/bugs/browser/bugtask.py (+11/-21) lib/lp/bugs/browser/tests/test_bug_views.py (+10/-3) lib/lp/bugs/browser/tests/test_bugs.py (+1/-1) lib/lp/bugs/browser/tests/test_bugtask.py (+14/-25) lib/lp/bugs/doc/bug.txt (+0/-95) lib/lp/bugs/errors.py (+0/-6) lib/lp/bugs/mail/tests/test_commands.py (+8/-4) lib/lp/bugs/model/bug.py (+9/-8) lib/lp/bugs/model/bugtask.py (+9/-3) lib/lp/bugs/model/tests/test_bug.py (+38/-7) lib/lp/bugs/model/tests/test_bugtask.py (+42/-69) lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt (+14/-14) lib/lp/bugs/tests/test_bugs_webservice.py (+7/-3) lib/lp/code/errors.py (+0/-5) lib/lp/code/model/branch.py (+3/-3) lib/lp/code/model/tests/test_branch.py (+11/-14) lib/lp/registry/enums.py (+4/-0) lib/lp/registry/errors.py (+6/-0) |
To merge this branch: | bzr merge lp:~wgrant/launchpad/bug-restrict-type |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Steve Kowalik (community) | code | Approve | |
Review via email: mp+121989@code.launchpad.net |
Commit message
Reject bug target or information type changes that would violate sharing policies.
Description of the change
This branch prevents bugs from transitioning to an information type that's illegal in their targets, or affecting a new target that disallows their information type.
I replaced the exceptions BranchCannotCha
I also extended the Proprietary single-pillar checks to Embargoed, as they were intended to have been from the start.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/bugs/browser/bugalsoaffects.py' |
2 | --- lib/lp/bugs/browser/bugalsoaffects.py 2012-06-29 08:40:05 +0000 |
3 | +++ lib/lp/bugs/browser/bugalsoaffects.py 2012-08-30 06:21:19 +0000 |
4 | @@ -174,7 +174,7 @@ |
5 | def validateStep(self, data): |
6 | if data.get('product'): |
7 | try: |
8 | - validate_target(self.context.bug, data.get('product')) |
9 | + validate_new_target(self.context.bug, data.get('product')) |
10 | except IllegalTarget as e: |
11 | self.setFieldError('product', e[0]) |
12 | return |
13 | |
14 | === modified file 'lib/lp/bugs/browser/bugtask.py' |
15 | --- lib/lp/bugs/browser/bugtask.py 2012-08-29 06:24:05 +0000 |
16 | +++ lib/lp/bugs/browser/bugtask.py 2012-08-30 06:21:19 +0000 |
17 | @@ -222,7 +222,10 @@ |
18 | from lp.bugs.model.bugtasksearch import orderby_expression |
19 | from lp.code.interfaces.branchcollection import IAllBranches |
20 | from lp.layers import FeedsLayer |
21 | -from lp.registry.enums import InformationType |
22 | +from lp.registry.enums import ( |
23 | + InformationType, |
24 | + PROPRIETARY_INFORMATION_TYPES, |
25 | + ) |
26 | from lp.registry.interfaces.distribution import ( |
27 | IDistribution, |
28 | IDistributionSet, |
29 | @@ -699,7 +702,6 @@ |
30 | cancel_url = canonical_url(self.context) |
31 | return cancel_url |
32 | |
33 | - |
34 | @cachedproperty |
35 | def is_duplicate_active(self): |
36 | active = True |
37 | @@ -3892,18 +3894,12 @@ |
38 | def canAddProjectTask(self): |
39 | """Can a new bug task on a project be added to this bug? |
40 | |
41 | - If a bug has any bug tasks already, were it to be private, it cannot |
42 | - be marked as also affecting any other project, so return False. |
43 | - |
44 | - Note: this check is currently only relevant if a bug is private. |
45 | - Eventually, even public bugs will have this restriction too. So what |
46 | - happens now is that this API is used by the tales to add a class |
47 | - called 'disallow-private' to the Also Affects Project link. A css rule |
48 | - is used to hide the link when body.private is True. |
49 | - |
50 | + If a bug has any bug tasks already, were it to be Proprietary or |
51 | + Embargoed, it cannot be marked as also affecting any other |
52 | + project, so return False. |
53 | """ |
54 | bug = self.context |
55 | - if bug.information_type != InformationType.PROPRIETARY: |
56 | + if bug.information_type not in PROPRIETARY_INFORMATION_TYPES: |
57 | return True |
58 | return len(bug.bugtasks) == 0 |
59 | |
60 | @@ -3911,20 +3907,14 @@ |
61 | """Can a new bug task on a src pkg be added to this bug? |
62 | |
63 | If a bug has any existing bug tasks on a project, were it to |
64 | - be private, then it cannot be marked as affecting a package, |
65 | - so return False. |
66 | + be Proprietary or Embargoed, then it cannot be marked as |
67 | + affecting a package, so return False. |
68 | |
69 | A task on a given package may still be illegal to add, but |
70 | this will be caught when bug.addTask() is attempted. |
71 | - |
72 | - Note: this check is currently only relevant if a bug is private. |
73 | - Eventually, even public bugs will have this restriction too. So what |
74 | - happens now is that this API is used by the tales to add a class |
75 | - called 'disallow-private' to the Also Affects Package link. A css rule |
76 | - is used to hide the link when body.private is True. |
77 | """ |
78 | bug = self.context |
79 | - if bug.information_type != InformationType.PROPRIETARY: |
80 | + if bug.information_type not in PROPRIETARY_INFORMATION_TYPES: |
81 | return True |
82 | for pillar in bug.affected_pillars: |
83 | if IProduct.providedBy(pillar): |
84 | |
85 | === modified file 'lib/lp/bugs/browser/tests/test_bug_views.py' |
86 | --- lib/lp/bugs/browser/tests/test_bug_views.py 2012-08-29 06:57:53 +0000 |
87 | +++ lib/lp/bugs/browser/tests/test_bug_views.py 2012-08-30 06:21:19 +0000 |
88 | @@ -84,8 +84,11 @@ |
89 | # We expect that both Also Affects links (for project and distro) are |
90 | # disallowed. |
91 | owner = self.factory.makePerson() |
92 | + product = self.factory.makeProduct( |
93 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY) |
94 | bug = self.factory.makeBug( |
95 | - information_type=InformationType.PROPRIETARY, owner=owner) |
96 | + target=product, owner=owner, |
97 | + information_type=InformationType.PROPRIETARY) |
98 | url = canonical_url(bug, rootsite="bugs") |
99 | browser = self.getUserBrowser(url, user=owner) |
100 | also_affects = find_tag_by_id( |
101 | @@ -101,10 +104,14 @@ |
102 | # We expect that only the Also Affects Project link is disallowed. |
103 | distro = self.factory.makeDistribution() |
104 | owner = self.factory.makePerson() |
105 | - self.factory.makeAccessPolicy(pillar=distro) |
106 | + # XXX wgrant 2012-08-30 bug=1041002: Distributions don't have |
107 | + # sharing policies yet, so it isn't possible legitimately create |
108 | + # a Proprietary distro bug. |
109 | bug = self.factory.makeBug( |
110 | target=distro, |
111 | - information_type=InformationType.PROPRIETARY, owner=owner) |
112 | + information_type=InformationType.PRIVATESECURITY, owner=owner) |
113 | + removeSecurityProxy(bug).information_type = ( |
114 | + InformationType.PROPRIETARY) |
115 | url = canonical_url(bug, rootsite="bugs") |
116 | browser = self.getUserBrowser(url, user=owner) |
117 | also_affects = find_tag_by_id( |
118 | |
119 | === modified file 'lib/lp/bugs/browser/tests/test_bugs.py' |
120 | --- lib/lp/bugs/browser/tests/test_bugs.py 2012-08-29 03:19:38 +0000 |
121 | +++ lib/lp/bugs/browser/tests/test_bugs.py 2012-08-30 06:21:19 +0000 |
122 | @@ -222,7 +222,7 @@ |
123 | |
124 | def test_createBug_private_bug_private_bugs_false(self): |
125 | # createBug() adapts a kwarg to InformationType if one is is not None. |
126 | - project = self.factory.makeProduct( |
127 | + project = self.factory.makeLegacyProduct( |
128 | licenses=[License.OTHER_PROPRIETARY]) |
129 | with person_logged_in(project.owner): |
130 | project.setPrivateBugs(False, project.owner) |
131 | |
132 | === modified file 'lib/lp/bugs/browser/tests/test_bugtask.py' |
133 | --- lib/lp/bugs/browser/tests/test_bugtask.py 2012-08-28 09:36:42 +0000 |
134 | +++ lib/lp/bugs/browser/tests/test_bugtask.py 2012-08-30 06:21:19 +0000 |
135 | @@ -58,7 +58,10 @@ |
136 | FeedsLayer, |
137 | setFirstLayer, |
138 | ) |
139 | -from lp.registry.enums import InformationType |
140 | +from lp.registry.enums import ( |
141 | + BugSharingPolicy, |
142 | + InformationType, |
143 | + ) |
144 | from lp.registry.interfaces.person import PersonVisibility |
145 | from lp.services.config import config |
146 | from lp.services.database.constants import UTC_NOW |
147 | @@ -1105,8 +1108,11 @@ |
148 | # A bug affecting a project cannot also affect another project or |
149 | # package. |
150 | owner = self.factory.makePerson() |
151 | + product = self.factory.makeProduct( |
152 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY_OR_PUBLIC) |
153 | bug = self.factory.makeBug( |
154 | - information_type=InformationType.PROPRIETARY, owner=owner) |
155 | + target=product, owner=owner, |
156 | + information_type=InformationType.PROPRIETARY) |
157 | with person_logged_in(owner): |
158 | view = self._createView(bug) |
159 | self.assertFalse(view.canAddProjectTask()) |
160 | @@ -1120,31 +1126,14 @@ |
161 | # could affect another package. |
162 | distro = self.factory.makeDistribution() |
163 | owner = self.factory.makePerson() |
164 | - self.factory.makeAccessPolicy(pillar=distro) |
165 | bug = self.factory.makeBug( |
166 | target=distro, owner=owner, |
167 | - information_type=InformationType.PROPRIETARY) |
168 | - with person_logged_in(owner): |
169 | - view = self._createView(bug) |
170 | - self.assertFalse(view.canAddProjectTask()) |
171 | - self.assertTrue(view.canAddPackageTask()) |
172 | - bug.transitionToInformationType(InformationType.USERDATA, owner) |
173 | - self.assertTrue(view.canAddProjectTask()) |
174 | - self.assertTrue(view.canAddPackageTask()) |
175 | - |
176 | - def test_sourcepkg_bug_cannot_affect_project(self): |
177 | - # A bug affecting a source pkg cannot also affect another project but |
178 | - # it could affect another package. |
179 | - distro = self.factory.makeDistribution() |
180 | - distroseries = self.factory.makeDistroSeries(distribution=distro) |
181 | - sp_name = self.factory.getOrMakeSourcePackageName() |
182 | - sp = self.factory.makeSourcePackage( |
183 | - sourcepackagename=sp_name, distroseries=distroseries) |
184 | - owner = self.factory.makePerson() |
185 | - self.factory.makeAccessPolicy(pillar=distro) |
186 | - bug = self.factory.makeBug( |
187 | - target=sp.distribution_sourcepackage, owner=owner, |
188 | - information_type=InformationType.PROPRIETARY) |
189 | + information_type=InformationType.PRIVATESECURITY) |
190 | + # XXX wgrant 2012-08-30 bug=1041002: Distributions don't have |
191 | + # sharing policies yet, so it isn't possible legitimately create |
192 | + # a Proprietary distro bug. |
193 | + removeSecurityProxy(bug).information_type = ( |
194 | + InformationType.PROPRIETARY) |
195 | with person_logged_in(owner): |
196 | view = self._createView(bug) |
197 | self.assertFalse(view.canAddProjectTask()) |
198 | |
199 | === modified file 'lib/lp/bugs/doc/bug.txt' |
200 | --- lib/lp/bugs/doc/bug.txt 2012-08-27 23:58:18 +0000 |
201 | +++ lib/lp/bugs/doc/bug.txt 2012-08-30 06:21:19 +0000 |
202 | @@ -654,101 +654,6 @@ |
203 | >>> print firefox_bug.default_bugtask.bugtargetdisplayname |
204 | Mozilla Firefox |
205 | |
206 | -It's not always possible to add another bug task to a bug. |
207 | -Proprietary bugs are only allowed to affect a single project or distribution. |
208 | - |
209 | - >>> params = CreateBugParams( |
210 | - ... title="a test private bug", |
211 | - ... comment="a description of the bug", |
212 | - ... owner=current_user()) |
213 | - >>> private_bug = firefox.createBug(params) |
214 | - >>> ignored = factory.makeAccessPolicy(pillar=firefox) |
215 | - >>> private_bug.transitionToInformationType( |
216 | - ... InformationType.PROPRIETARY, current_user()) |
217 | - True |
218 | - |
219 | - >>> params = CreateBugParams( |
220 | - ... title="a test public bug", |
221 | - ... comment="a description of the bug", |
222 | - ... owner=current_user()) |
223 | - >>> public_bug = firefox.createBug(params) |
224 | - |
225 | -We can always add any new bug task to a public bug. |
226 | - |
227 | - >>> tomcat = getUtility(IProductSet).getByName('tomcat') |
228 | - >>> public_bug.addTask(owner=foobar, target=tomcat) |
229 | - <BugTask for bug... |
230 | - |
231 | - >>> target = tomcat.getSeries('trunk') |
232 | - >>> public_bug.addTask(owner=foobar, target=target) |
233 | - <BugTask for bug... |
234 | - |
235 | -If we try and add an invalid bug task to a private bug we get an exception. |
236 | - |
237 | - >>> private_bug.addTask(owner=foobar, target=tomcat) |
238 | - Traceback (most recent call last): |
239 | - ... |
240 | - IllegalTarget: This proprietary bug already affects Mozilla Firefox. |
241 | - Proprietary bugs cannot affect multiple projects. |
242 | - |
243 | - >>> private_bug.addTask(owner=foobar, target=ubuntu) |
244 | - Traceback (most recent call last): |
245 | - ... |
246 | - IllegalTarget: This proprietary bug already affects Mozilla Firefox. |
247 | - Proprietary bugs cannot affect multiple projects. |
248 | - |
249 | -We can add a new product series task so long as it's for the same product as |
250 | -is already targeted. |
251 | - |
252 | - >>> target = firefox.getSeries('1.0') |
253 | - >>> private_bug.addTask(owner=foobar, target=target) |
254 | - <BugTask for bug... |
255 | - |
256 | - >>> target = tomcat.getSeries('trunk') |
257 | - >>> private_bug.addTask(owner=foobar, target=target) |
258 | - Traceback (most recent call last): |
259 | - ... |
260 | - IllegalTarget: This proprietary bug already affects Mozilla Firefox. |
261 | - Proprietary bugs cannot affect multiple projects. |
262 | - |
263 | -Now we create a bug on a distribution. |
264 | - |
265 | - >>> private_bug = ubuntu.createBug(params) |
266 | - >>> private_bug.setPrivate(True, current_user()) |
267 | - True |
268 | - |
269 | -We can add a new distro series task so long as it's for the same distro as |
270 | -is already targeted. |
271 | - |
272 | - >>> private_bug.addTask(owner=foobar, target=warty) |
273 | - <BugTask for bug... |
274 | - |
275 | -We can also add an allowed distro series source package. |
276 | - |
277 | - >>> private_bug.addTask(owner=foobar, target=warty_fox_package) |
278 | - <BugTask for bug... |
279 | - |
280 | -We cannot add distro series or source package tasks for different distros. |
281 | - |
282 | - >>> private_bug = tubuntu.createBug(params) |
283 | - >>> ignored = factory.makeAccessPolicy(pillar=tubuntu) |
284 | - >>> private_bug.transitionToInformationType( |
285 | - ... InformationType.PROPRIETARY, current_user()) |
286 | - True |
287 | - |
288 | - >>> private_bug.addTask(owner=foobar, target=warty) |
289 | - Traceback (most recent call last): |
290 | - ... |
291 | - IllegalTarget: This proprietary bug already affects Tubuntu. |
292 | - Proprietary bugs cannot affect multiple projects. |
293 | - |
294 | - >>> private_bug.addTask(owner=foobar, target=warty_fox_package) |
295 | - Traceback (most recent call last): |
296 | - ... |
297 | - IllegalTarget: This proprietary bug already affects Tubuntu. |
298 | - Proprietary bugs cannot affect multiple projects. |
299 | - |
300 | - |
301 | Changing bug visibility. |
302 | |
303 | >>> bug_before_modification = Snapshot(firefox_bug, providing=IBug) |
304 | |
305 | === modified file 'lib/lp/bugs/errors.py' |
306 | --- lib/lp/bugs/errors.py 2012-08-08 04:48:40 +0000 |
307 | +++ lib/lp/bugs/errors.py 2012-08-30 06:21:19 +0000 |
308 | @@ -5,7 +5,6 @@ |
309 | |
310 | __metaclass__ = type |
311 | __all__ = [ |
312 | - 'BugCannotBePrivate', |
313 | 'InvalidDuplicateValue', |
314 | ] |
315 | |
316 | @@ -19,8 +18,3 @@ |
317 | @error_status(httplib.EXPECTATION_FAILED) |
318 | class InvalidDuplicateValue(LaunchpadValidationError): |
319 | """A bug cannot be set as the duplicate of another.""" |
320 | - |
321 | - |
322 | -@error_status(httplib.BAD_REQUEST) |
323 | -class BugCannotBePrivate(Exception): |
324 | - """The bug is not allowed to be private.""" |
325 | |
326 | === modified file 'lib/lp/bugs/mail/tests/test_commands.py' |
327 | --- lib/lp/bugs/mail/tests/test_commands.py 2012-08-27 23:58:18 +0000 |
328 | +++ lib/lp/bugs/mail/tests/test_commands.py 2012-08-30 06:21:19 +0000 |
329 | @@ -20,7 +20,10 @@ |
330 | TagEmailCommand, |
331 | UnsubscribeEmailCommand, |
332 | ) |
333 | -from lp.registry.enums import InformationType |
334 | +from lp.registry.enums import ( |
335 | + BugSharingPolicy, |
336 | + InformationType, |
337 | + ) |
338 | from lp.services.mail.interfaces import ( |
339 | BugTargetNotFound, |
340 | EmailProcessingError, |
341 | @@ -286,11 +289,12 @@ |
342 | def test_execute_bug_cannot_add_task(self): |
343 | # Test that attempts to invalidly add a new bug task results in the |
344 | # expected error message. |
345 | - product = self.factory.makeProduct() |
346 | - self.factory.makeAccessPolicy(pillar=product) |
347 | + product = self.factory.makeProduct( |
348 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY) |
349 | bug = self.factory.makeBug( |
350 | target=product, information_type=InformationType.PROPRIETARY) |
351 | - self.factory.makeProduct(name='fnord') |
352 | + self.factory.makeProduct( |
353 | + name='fnord', bug_sharing_policy=BugSharingPolicy.PROPRIETARY) |
354 | login_celebrity('admin') |
355 | login_person(bug.owner) |
356 | command = AffectsEmailCommand('affects', ['fnord']) |
357 | |
358 | === modified file 'lib/lp/bugs/model/bug.py' |
359 | --- lib/lp/bugs/model/bug.py 2012-08-24 05:09:51 +0000 |
360 | +++ lib/lp/bugs/model/bug.py 2012-08-30 06:21:19 +0000 |
361 | @@ -100,10 +100,7 @@ |
362 | UnsubscribedFromBug, |
363 | ) |
364 | from lp.bugs.enums import BugNotificationLevel |
365 | -from lp.bugs.errors import ( |
366 | - BugCannotBePrivate, |
367 | - InvalidDuplicateValue, |
368 | - ) |
369 | +from lp.bugs.errors import InvalidDuplicateValue |
370 | from lp.bugs.interfaces.bug import ( |
371 | IBug, |
372 | IBugBecameQuestionEvent, |
373 | @@ -160,8 +157,10 @@ |
374 | from lp.registry.enums import ( |
375 | InformationType, |
376 | PRIVATE_INFORMATION_TYPES, |
377 | + PROPRIETARY_INFORMATION_TYPES, |
378 | SECURITY_INFORMATION_TYPES, |
379 | ) |
380 | +from lp.registry.errors import CannotChangeInformationType |
381 | from lp.registry.interfaces.accesspolicy import ( |
382 | IAccessArtifactGrantSource, |
383 | IAccessArtifactSource, |
384 | @@ -1710,10 +1709,12 @@ |
385 | """See `IBug`.""" |
386 | if self.information_type == information_type: |
387 | return False |
388 | - if (information_type == InformationType.PROPRIETARY and |
389 | - len(self.affected_pillars) > 1): |
390 | - raise BugCannotBePrivate( |
391 | - "Multi-pillar bugs cannot be proprietary.") |
392 | + if information_type not in self.getAllowedInformationTypes(who): |
393 | + raise CannotChangeInformationType("Forbidden by project policy.") |
394 | + if (information_type in PROPRIETARY_INFORMATION_TYPES |
395 | + and len(self.affected_pillars) > 1): |
396 | + raise CannotChangeInformationType( |
397 | + "Proprietary bugs can only affect one project.") |
398 | if information_type in PRIVATE_INFORMATION_TYPES: |
399 | self.who_made_private = who |
400 | self.date_made_private = UTC_NOW |
401 | |
402 | === modified file 'lib/lp/bugs/model/bugtask.py' |
403 | --- lib/lp/bugs/model/bugtask.py 2012-08-29 06:24:05 +0000 |
404 | +++ lib/lp/bugs/model/bugtask.py 2012-08-30 06:21:19 +0000 |
405 | @@ -94,7 +94,7 @@ |
406 | ) |
407 | from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams |
408 | from lp.registry.enums import ( |
409 | - InformationType, |
410 | + PROPRIETARY_INFORMATION_TYPES, |
411 | PUBLIC_INFORMATION_TYPES, |
412 | ) |
413 | from lp.registry.interfaces.distribution import ( |
414 | @@ -337,7 +337,13 @@ |
415 | except NotFoundError as e: |
416 | raise IllegalTarget(e[0]) |
417 | |
418 | - if bug.information_type == InformationType.PROPRIETARY: |
419 | + legal_types = target.pillar.getAllowedBugInformationTypes() |
420 | + if bug.information_type not in legal_types: |
421 | + raise IllegalTarget( |
422 | + "%s doesn't allow %s bugs." % ( |
423 | + target.pillar.bugtargetdisplayname, bug.information_type.title)) |
424 | + |
425 | + if bug.information_type in PROPRIETARY_INFORMATION_TYPES: |
426 | # Perhaps we are replacing the one and only existing bugtask, in |
427 | # which case that's ok. |
428 | if retarget_existing and len(bug.bugtasks) <= 1: |
429 | @@ -348,7 +354,7 @@ |
430 | raise IllegalTarget( |
431 | "This proprietary bug already affects %s. " |
432 | "Proprietary bugs cannot affect multiple projects." |
433 | - % bug.default_bugtask.target.bugtargetdisplayname) |
434 | + % bug.default_bugtask.target.pillar.bugtargetdisplayname) |
435 | |
436 | |
437 | def validate_new_target(bug, target): |
438 | |
439 | === modified file 'lib/lp/bugs/model/tests/test_bug.py' |
440 | --- lib/lp/bugs/model/tests/test_bug.py 2012-08-28 01:35:08 +0000 |
441 | +++ lib/lp/bugs/model/tests/test_bug.py 2012-08-30 06:21:19 +0000 |
442 | @@ -20,7 +20,6 @@ |
443 | BugNotificationLevel, |
444 | BugNotificationStatus, |
445 | ) |
446 | -from lp.bugs.errors import BugCannotBePrivate |
447 | from lp.bugs.interfaces.bugnotification import IBugNotificationSet |
448 | from lp.bugs.interfaces.bugtask import BugTaskStatus |
449 | from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients |
450 | @@ -28,7 +27,11 @@ |
451 | BugNotification, |
452 | BugSubscriptionInfo, |
453 | ) |
454 | -from lp.registry.enums import InformationType |
455 | +from lp.registry.enums import ( |
456 | + BugSharingPolicy, |
457 | + InformationType, |
458 | + ) |
459 | +from lp.registry.errors import CannotChangeInformationType |
460 | from lp.registry.interfaces.accesspolicy import ( |
461 | IAccessArtifactSource, |
462 | IAccessPolicyArtifactSource, |
463 | @@ -753,12 +756,17 @@ |
464 | |
465 | def test_multipillar_proprietary_bugs_disallowed(self): |
466 | # A multi-pillar bug cannot be made proprietary. |
467 | - bug = self.factory.makeBug() |
468 | - product = self.factory.makeProduct() |
469 | - self.factory.makeBugTask(bug=bug, target=product) |
470 | + p1 = self.factory.makeProduct( |
471 | + bug_sharing_policy=BugSharingPolicy.PUBLIC_OR_PROPRIETARY) |
472 | + p2 = self.factory.makeProduct( |
473 | + bug_sharing_policy=BugSharingPolicy.PUBLIC_OR_PROPRIETARY) |
474 | + bug = self.factory.makeBug(target=p1) |
475 | + self.factory.makeBugTask(bug=bug, target=p2) |
476 | login_person(bug.owner) |
477 | - self.assertRaises( |
478 | - BugCannotBePrivate, bug.transitionToInformationType, |
479 | + self.assertRaisesWithContent( |
480 | + CannotChangeInformationType, |
481 | + "Proprietary bugs can only affect one project.", |
482 | + bug.transitionToInformationType, |
483 | InformationType.PROPRIETARY, bug.owner) |
484 | bug.transitionToInformationType(InformationType.USERDATA, bug.owner) |
485 | self.assertTrue(bug.private) |
486 | @@ -869,6 +877,29 @@ |
487 | InformationType.PRIVATESECURITY, InformationType.USERDATA], |
488 | self.factory.makeBug().getAllowedInformationTypes(None)) |
489 | |
490 | + def test_transitionToInformationType_respects_allowed_proprietary(self): |
491 | + # transitionToInformationType rejects types that aren't allowed |
492 | + # for the bug. |
493 | + product = self.factory.makeProduct() |
494 | + with person_logged_in(product.owner): |
495 | + bug = self.factory.makeBug(target=product) |
496 | + self.assertRaisesWithContent( |
497 | + CannotChangeInformationType, "Forbidden by project policy.", |
498 | + bug.transitionToInformationType, |
499 | + InformationType.PROPRIETARY, bug.owner) |
500 | + |
501 | + def test_transitionToInformationType_respects_allowed_public(self): |
502 | + # transitionToInformationType rejects types that aren't allowed |
503 | + # for the bug. |
504 | + product = self.factory.makeProduct( |
505 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY) |
506 | + with person_logged_in(product.owner): |
507 | + bug = self.factory.makeBug(target=product) |
508 | + self.assertRaisesWithContent( |
509 | + CannotChangeInformationType, "Forbidden by project policy.", |
510 | + bug.transitionToInformationType, |
511 | + InformationType.PUBLIC, bug.owner) |
512 | + |
513 | |
514 | class TestBugPrivateAndSecurityRelatedUpdatesPrivateProject( |
515 | TestBugPrivateAndSecurityRelatedUpdatesMixin, TestCaseWithFactory): |
516 | |
517 | === modified file 'lib/lp/bugs/model/tests/test_bugtask.py' |
518 | --- lib/lp/bugs/model/tests/test_bugtask.py 2012-08-29 06:24:05 +0000 |
519 | +++ lib/lp/bugs/model/tests/test_bugtask.py 2012-08-30 06:21:19 +0000 |
520 | @@ -56,6 +56,7 @@ |
521 | ) |
522 | from lp.bugs.tests.bug import create_old_bug |
523 | from lp.registry.enums import ( |
524 | + BugSharingPolicy, |
525 | InformationType, |
526 | TeamMembershipPolicy, |
527 | ) |
528 | @@ -2847,52 +2848,26 @@ |
529 | a private bugs to check for multi-tenant constraints. |
530 | """ |
531 | |
532 | - def test_private_multi_tenanted_forbidden(self): |
533 | - # A new task project cannot be added if there is already one from |
534 | - # another pillar. |
535 | - d = self.factory.makeDistribution() |
536 | - bug = self.factory.makeBug(target=d) |
537 | - if not self.multi_tenant_test_one_task_only: |
538 | - self.factory.makeBugTask(bug=bug) |
539 | - p = self.factory.makeProduct() |
540 | - self.factory.makeAccessPolicy(pillar=d) |
541 | - with person_logged_in(bug.owner): |
542 | - bug.transitionToInformationType( |
543 | - InformationType.PROPRIETARY, bug.owner) |
544 | - self.assertRaisesWithContent( |
545 | - IllegalTarget, |
546 | - "This proprietary bug already affects %s. " |
547 | - "Proprietary bugs cannot affect multiple projects." |
548 | - % d.displayname, |
549 | - self.validate_method, bug, p) |
550 | - bug.transitionToInformationType( |
551 | - InformationType.USERDATA, bug.owner) |
552 | - self.validate_method(bug, p) |
553 | - |
554 | def test_private_incorrect_pillar_task_forbidden(self): |
555 | - # A product or distro cannot be added if there is already a bugtask. |
556 | - p1 = self.factory.makeProduct() |
557 | - p2 = self.factory.makeProduct() |
558 | - d = self.factory.makeDistribution() |
559 | - bug = self.factory.makeBug(target=p1) |
560 | + # Another pillar cannot be added if there is already a bugtask. |
561 | + p1 = self.factory.makeProduct( |
562 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY_OR_PUBLIC) |
563 | + p2 = self.factory.makeProduct( |
564 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY_OR_PUBLIC) |
565 | + owner = self.factory.makePerson() |
566 | + bug = self.factory.makeBug(target=p1, owner=owner) |
567 | + # validate_target allows cross-pillar transitions if there's |
568 | + # only one task, so we might need to create a second task to test. |
569 | if not self.multi_tenant_test_one_task_only: |
570 | - self.factory.makeBugTask(bug=bug) |
571 | - self.factory.makeAccessPolicy(pillar=p1) |
572 | - with person_logged_in(bug.owner): |
573 | - bug.transitionToInformationType( |
574 | - InformationType.PROPRIETARY, bug.owner) |
575 | + self.factory.makeBugTask( |
576 | + bug=bug, target=self.factory.makeProductSeries(product=p1)) |
577 | + with person_logged_in(owner): |
578 | self.assertRaisesWithContent( |
579 | IllegalTarget, |
580 | "This proprietary bug already affects %s. " |
581 | "Proprietary bugs cannot affect multiple projects." |
582 | % p1.displayname, |
583 | self.validate_method, bug, p2) |
584 | - self.assertRaisesWithContent( |
585 | - IllegalTarget, |
586 | - "This proprietary bug already affects %s. " |
587 | - "Proprietary bugs cannot affect multiple projects." |
588 | - % p1.displayname, |
589 | - self.validate_method, bug, d) |
590 | bug.transitionToInformationType( |
591 | InformationType.USERDATA, bug.owner) |
592 | self.validate_method(bug, p2) |
593 | @@ -2900,16 +2875,19 @@ |
594 | def test_private_incorrect_product_series_task_forbidden(self): |
595 | # A product series cannot be added if there is already a bugtask for |
596 | # a different product. |
597 | - p1 = self.factory.makeProduct() |
598 | - p2 = self.factory.makeProduct() |
599 | + p1 = self.factory.makeProduct( |
600 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY_OR_PUBLIC) |
601 | + p2 = self.factory.makeProduct( |
602 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY_OR_PUBLIC) |
603 | series = self.factory.makeProductSeries(product=p2) |
604 | - bug = self.factory.makeBug(target=p1) |
605 | + owner = self.factory.makePerson() |
606 | + bug = self.factory.makeBug(target=p1, owner=owner) |
607 | + # validate_target allows cross-pillar transitions if there's |
608 | + # only one task, so we might need to create a second task to test. |
609 | if not self.multi_tenant_test_one_task_only: |
610 | - self.factory.makeBugTask(bug=bug) |
611 | - self.factory.makeAccessPolicy(pillar=p1) |
612 | - with person_logged_in(bug.owner): |
613 | - bug.transitionToInformationType( |
614 | - InformationType.PROPRIETARY, bug.owner) |
615 | + self.factory.makeBugTask( |
616 | + bug=bug, target=self.factory.makeProductSeries(product=p1)) |
617 | + with person_logged_in(owner): |
618 | self.assertRaisesWithContent( |
619 | IllegalTarget, |
620 | "This proprietary bug already affects %s. " |
621 | @@ -2920,29 +2898,6 @@ |
622 | InformationType.USERDATA, bug.owner) |
623 | self.validate_method(bug, series) |
624 | |
625 | - def test_private_incorrect_distro_series_task_forbidden(self): |
626 | - # A distro series cannot be added if there is already a bugtask for |
627 | - # a different distro. |
628 | - d1 = self.factory.makeDistribution() |
629 | - d2 = self.factory.makeDistribution() |
630 | - series = self.factory.makeDistroSeries(distribution=d2) |
631 | - bug = self.factory.makeBug(target=d1) |
632 | - if not self.multi_tenant_test_one_task_only: |
633 | - self.factory.makeBugTask(bug=bug) |
634 | - self.factory.makeAccessPolicy(pillar=d1) |
635 | - with person_logged_in(bug.owner): |
636 | - bug.transitionToInformationType( |
637 | - InformationType.PROPRIETARY, bug.owner) |
638 | - self.assertRaisesWithContent( |
639 | - IllegalTarget, |
640 | - "This proprietary bug already affects %s. " |
641 | - "Proprietary bugs cannot affect multiple projects." |
642 | - % d1.displayname, |
643 | - self.validate_method, bug, series) |
644 | - bug.transitionToInformationType( |
645 | - InformationType.USERDATA, bug.owner) |
646 | - self.validate_method(bug, series) |
647 | - |
648 | |
649 | class TestValidateTarget(TestCaseWithFactory, ValidateTargetMixin): |
650 | |
651 | @@ -3082,6 +3037,24 @@ |
652 | % (dsp.sourcepackagename.name, dsp.distribution.displayname), |
653 | validate_target, task.bug, dsp) |
654 | |
655 | + def test_illegal_information_type_disallowed(self): |
656 | + # The bug's current information_type must be permitted by the |
657 | + # new target. |
658 | + free_prod = self.factory.makeProduct() |
659 | + other_free_prod = self.factory.makeProduct() |
660 | + commercial_prod = self.factory.makeProduct( |
661 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY) |
662 | + bug = self.factory.makeBug(target=free_prod) |
663 | + |
664 | + # The new bug is Public, which is legal on the other free product. |
665 | + self.assertIs(None, validate_target(bug, other_free_prod)) |
666 | + |
667 | + # But not on the proprietary-only product. |
668 | + self.assertRaisesWithContent( |
669 | + IllegalTarget, |
670 | + "%s doesn't allow Public bugs." % commercial_prod.displayname, |
671 | + validate_target, bug, commercial_prod) |
672 | + |
673 | |
674 | class TestValidateNewTarget(TestCaseWithFactory, ValidateTargetMixin): |
675 | |
676 | |
677 | === modified file 'lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt' |
678 | --- lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt 2012-08-27 23:58:18 +0000 |
679 | +++ lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt 2012-08-30 06:21:19 +0000 |
680 | @@ -201,41 +201,41 @@ |
681 | ...>pmount (Debian)</a>... |
682 | ... |
683 | |
684 | -We cannot allow private bugs to affect more than one pillar. |
685 | +We cannot allow proprietary bugs to affect more than one pillar. |
686 | |
687 | >>> from lp.services.webapp import canonical_url |
688 | >>> from lp.services.webapp.interfaces import ILaunchBag |
689 | >>> from lp.bugs.interfaces.bug import CreateBugParams |
690 | - >>> from lp.registry.interfaces.product import IProductSet |
691 | - >>> from lp.registry.enums import InformationType |
692 | + >>> from lp.registry.enums import BugSharingPolicy, InformationType |
693 | |
694 | >>> def current_user(): |
695 | ... return getUtility(ILaunchBag).user |
696 | |
697 | - >>> login("foo.bar@canonical.com") |
698 | - >>> productset = getUtility(IProductSet) |
699 | - >>> firefox = productset.get(4) |
700 | - >>> ignored = factory.makeAccessPolicy(pillar=firefox) |
701 | + >>> login("test@canonical.com") |
702 | + >>> product = factory.makeProduct( |
703 | + ... displayname='Proprietary Product', name='proprietary-product', |
704 | + ... bug_sharing_policy=BugSharingPolicy.PROPRIETARY) |
705 | + >>> other_product = factory.makeProduct( |
706 | + ... official_malone=True, |
707 | + ... bug_sharing_policy=BugSharingPolicy.PROPRIETARY) |
708 | >>> params = CreateBugParams( |
709 | ... title="a test private bug", |
710 | ... comment="a description of the bug", |
711 | ... information_type=InformationType.PROPRIETARY, |
712 | ... owner=current_user()) |
713 | - >>> private_bug = firefox.createBug(params) |
714 | - >>> private_bug.subscribe(firefox.owner, firefox.owner) |
715 | - <lp.bugs.model.bugsubscription.BugSubscription object at ...> |
716 | + >>> private_bug = product.createBug(params) |
717 | >>> logout() |
718 | |
719 | >>> browser.open(canonical_url(private_bug, rootsite='bugs')) |
720 | - >>> browser.getLink(url='+distrotask').click() |
721 | - >>> browser.getControl(name='field.distribution').value = ['debian'] |
722 | + >>> browser.getLink(url='+choose-affected-product').click() |
723 | + >>> browser.getControl(name='field.product').value = other_product.name |
724 | >>> browser.getControl('Continue').click() |
725 | >>> print browser.url |
726 | - http://bugs.launchpad.dev/firefox/+bug/16/+distrotask |
727 | + http://bugs.launchpad.dev/proprietary-product/+bug/16/+choose-affected-product |
728 | |
729 | >>> print get_feedback_messages(browser.contents) |
730 | [u'There is 1 error.', |
731 | - u'This proprietary bug already affects Mozilla Firefox. |
732 | + u'This proprietary bug already affects Proprietary Product. |
733 | Proprietary bugs cannot affect multiple projects.'] |
734 | |
735 | |
736 | |
737 | === modified file 'lib/lp/bugs/tests/test_bugs_webservice.py' |
738 | --- lib/lp/bugs/tests/test_bugs_webservice.py 2012-08-29 06:57:53 +0000 |
739 | +++ lib/lp/bugs/tests/test_bugs_webservice.py 2012-08-30 06:21:19 +0000 |
740 | @@ -361,15 +361,19 @@ |
741 | # a proprietary bug. In this case, we cannot mark a proprietary bug |
742 | # as affecting more than one project. |
743 | owner = self.factory.makePerson() |
744 | + product1 = self.factory.makeProduct( |
745 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY) |
746 | + product2 = self.factory.makeProduct( |
747 | + bug_sharing_policy=BugSharingPolicy.PROPRIETARY) |
748 | bug = self.factory.makeBug( |
749 | - owner=owner, information_type=InformationType.PROPRIETARY) |
750 | - product = self.factory.makeProduct() |
751 | + target=product1, owner=owner, |
752 | + information_type=InformationType.PROPRIETARY) |
753 | |
754 | login_person(owner) |
755 | launchpad = launchpadlib_for('test', owner) |
756 | lp_bug = launchpad.load(api_url(bug)) |
757 | self.assertRaises( |
758 | - BadRequest, lp_bug.addTask, target=api_url(product)) |
759 | + BadRequest, lp_bug.addTask, target=api_url(product2)) |
760 | |
761 | |
762 | class BugSetTestCase(TestCaseWithFactory): |
763 | |
764 | === modified file 'lib/lp/code/errors.py' |
765 | --- lib/lp/code/errors.py 2012-07-11 09:36:13 +0000 |
766 | +++ lib/lp/code/errors.py 2012-08-30 06:21:19 +0000 |
767 | @@ -8,7 +8,6 @@ |
768 | 'AlreadyLatestFormat', |
769 | 'BadBranchMergeProposalSearchContext', |
770 | 'BadStateTransition', |
771 | - 'BranchCannotChangeInformationType', |
772 | 'BranchCreationException', |
773 | 'BranchCreationForbidden', |
774 | 'BranchCreatorNotMemberOfOwnerTeam', |
775 | @@ -146,10 +145,6 @@ |
776 | """ |
777 | |
778 | |
779 | -class BranchCannotChangeInformationType(Exception): |
780 | - """The information type of this branch cannot be changed.""" |
781 | - |
782 | - |
783 | class InvalidBranchException(Exception): |
784 | """Base exception for an error resolving a branch for a component. |
785 | |
786 | |
787 | === modified file 'lib/lp/code/model/branch.py' |
788 | --- lib/lp/code/model/branch.py 2012-08-29 02:40:36 +0000 |
789 | +++ lib/lp/code/model/branch.py 2012-08-30 06:21:19 +0000 |
790 | @@ -80,7 +80,6 @@ |
791 | ) |
792 | from lp.code.errors import ( |
793 | AlreadyLatestFormat, |
794 | - BranchCannotChangeInformationType, |
795 | BranchMergeProposalExists, |
796 | BranchTargetError, |
797 | BranchTypeError, |
798 | @@ -137,6 +136,7 @@ |
799 | PRIVATE_INFORMATION_TYPES, |
800 | PUBLIC_INFORMATION_TYPES, |
801 | ) |
802 | +from lp.registry.errors import CannotChangeInformationType |
803 | from lp.registry.interfaces.accesspolicy import ( |
804 | IAccessArtifactGrantSource, |
805 | IAccessArtifactSource, |
806 | @@ -249,10 +249,10 @@ |
807 | if (self.stacked_on |
808 | and self.stacked_on.information_type in PRIVATE_INFORMATION_TYPES |
809 | and information_type in PUBLIC_INFORMATION_TYPES): |
810 | - raise BranchCannotChangeInformationType() |
811 | + raise CannotChangeInformationType("Must match stacked-on branch.") |
812 | if (verify_policy |
813 | and information_type not in self.getAllowedInformationTypes(who)): |
814 | - raise BranchCannotChangeInformationType() |
815 | + raise CannotChangeInformationType("Forbidden by project policy.") |
816 | self.information_type = information_type |
817 | self._reconcileAccess() |
818 | if information_type in PRIVATE_INFORMATION_TYPES and self.subscribers: |
819 | |
820 | === modified file 'lib/lp/code/model/tests/test_branch.py' |
821 | --- lib/lp/code/model/tests/test_branch.py 2012-08-29 03:19:38 +0000 |
822 | +++ lib/lp/code/model/tests/test_branch.py 2012-08-30 06:21:19 +0000 |
823 | @@ -54,7 +54,6 @@ |
824 | ) |
825 | from lp.code.errors import ( |
826 | AlreadyLatestFormat, |
827 | - BranchCannotChangeInformationType, |
828 | BranchCreatorNotMemberOfOwnerTeam, |
829 | BranchCreatorNotOwner, |
830 | BranchTargetError, |
831 | @@ -120,6 +119,7 @@ |
832 | PUBLIC_INFORMATION_TYPES, |
833 | TeamMembershipPolicy, |
834 | ) |
835 | +from lp.registry.errors import CannotChangeInformationType |
836 | from lp.registry.interfaces.accesspolicy import ( |
837 | IAccessArtifactSource, |
838 | IAccessPolicyArtifactSource, |
839 | @@ -2480,13 +2480,12 @@ |
840 | |
841 | def test_public_to_private_not_allowed(self): |
842 | # If there are no privacy policies allowing private branches, then |
843 | - # BranchCannotChangeInformationType is rasied. |
844 | + # CannotChangeInformationType is raised. |
845 | product = self.factory.makeLegacyProduct() |
846 | branch = self.factory.makeBranch(product=product) |
847 | - self.assertRaises( |
848 | - BranchCannotChangeInformationType, |
849 | - branch.setPrivate, |
850 | - True, branch.owner) |
851 | + self.assertRaisesWithContent( |
852 | + CannotChangeInformationType, 'Forbidden by project policy.', |
853 | + branch.setPrivate, True, branch.owner) |
854 | |
855 | def test_public_to_private_for_admins(self): |
856 | # Admins can override the default behaviour and make any public branch |
857 | @@ -2522,8 +2521,7 @@ |
858 | |
859 | def test_private_to_public_not_allowed(self): |
860 | # If the namespace policy does not allow public branches, attempting |
861 | - # to change the branch to be public raises |
862 | - # BranchCannotChangeInformationType. |
863 | + # to change the branch to be public raises CannotChangeInformationType. |
864 | product = self.factory.makeLegacyProduct() |
865 | branch = self.factory.makeBranch( |
866 | product=product, |
867 | @@ -2532,10 +2530,9 @@ |
868 | None, BranchVisibilityRule.FORBIDDEN) |
869 | branch.product.setBranchVisibilityTeamPolicy( |
870 | branch.owner, BranchVisibilityRule.PRIVATE_ONLY) |
871 | - self.assertRaises( |
872 | - BranchCannotChangeInformationType, |
873 | - branch.setPrivate, |
874 | - False, branch.owner) |
875 | + self.assertRaisesWithContent( |
876 | + CannotChangeInformationType, 'Forbidden by project policy.', |
877 | + branch.setPrivate, False, branch.owner) |
878 | |
879 | def test_cannot_transition_with_private_stacked_on(self): |
880 | # If a public branch is stacked on a private branch, it can not |
881 | @@ -2543,8 +2540,8 @@ |
882 | stacked_on = self.factory.makeBranch( |
883 | information_type=InformationType.USERDATA) |
884 | branch = self.factory.makeBranch(stacked_on=stacked_on) |
885 | - self.assertRaises( |
886 | - BranchCannotChangeInformationType, |
887 | + self.assertRaisesWithContent( |
888 | + CannotChangeInformationType, 'Must match stacked-on branch.', |
889 | branch.transitionToInformationType, InformationType.PUBLIC, |
890 | branch.owner) |
891 | |
892 | |
893 | === modified file 'lib/lp/registry/enums.py' |
894 | --- lib/lp/registry/enums.py 2012-08-20 13:26:21 +0000 |
895 | +++ lib/lp/registry/enums.py 2012-08-30 06:21:19 +0000 |
896 | @@ -18,6 +18,7 @@ |
897 | 'PersonTransferJobType', |
898 | 'PersonVisibility', |
899 | 'PRIVATE_INFORMATION_TYPES', |
900 | + 'PROPRIETARY_INFORMATION_TYPES', |
901 | 'PUBLIC_INFORMATION_TYPES', |
902 | 'ProductJobType', |
903 | 'SECURITY_INFORMATION_TYPES', |
904 | @@ -97,6 +98,9 @@ |
905 | FREE_INFORMATION_TYPES = ( |
906 | PUBLIC_INFORMATION_TYPES + FREE_PRIVATE_INFORMATION_TYPES) |
907 | |
908 | +PROPRIETARY_INFORMATION_TYPES = ( |
909 | + InformationType.PROPRIETARY, InformationType.EMBARGOED) |
910 | + |
911 | |
912 | class SharingPermission(DBEnumeratedType): |
913 | """Sharing permission. |
914 | |
915 | === modified file 'lib/lp/registry/errors.py' |
916 | --- lib/lp/registry/errors.py 2012-08-13 20:17:56 +0000 |
917 | +++ lib/lp/registry/errors.py 2012-08-30 06:21:19 +0000 |
918 | @@ -5,6 +5,7 @@ |
919 | __all__ = [ |
920 | 'DistroSeriesDifferenceError', |
921 | 'NotADerivedSeriesError', |
922 | + 'CannotChangeInformationType', |
923 | 'CannotDeleteCommercialSubscription', |
924 | 'CannotTransitionToCountryMirror', |
925 | 'CommercialSubscribersOnly', |
926 | @@ -190,3 +191,8 @@ |
927 | |
928 | class CannotDeleteCommercialSubscription(Exception): |
929 | """Raised when a commercial subscription cannot be deleted.""" |
930 | + |
931 | + |
932 | +@error_status(httplib.BAD_REQUEST) |
933 | +class CannotChangeInformationType(Exception): |
934 | + """The information type cannot be changed.""" |
This looks like excellent work. I have one mis-giving:
373 - BranchCannotCha ngeInformationT ype, ormationType,
374 - branch.setPrivate,
375 - True, branch.owner)
376 + CannotChangeInf
377 + branch.setPrivate, True, branch.owner)
395 - BranchCannotCha ngeInformationT ype, ormationType,
396 - branch.setPrivate,
397 - False, branch.owner)
398 + CannotChangeInf
399 + branch.setPrivate, False, branch.owner)
Given you've added text to the branch cases that raise CannotChangeInf ormationType, can you flip these tests to making use of self.assertRais esWithContent( ) and make certain the exception text is right.