Merge lp:~wgrant/launchpad/improve-branch-edit-type into lp:launchpad
- improve-branch-edit-type
- Merge into devel
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 15617 | ||||||||
Proposed branch: | lp:~wgrant/launchpad/improve-branch-edit-type | ||||||||
Merge into: | lp:launchpad | ||||||||
Diff against target: |
531 lines (+185/-228) 5 files modified
lib/lp/code/browser/branch.py (+50/-25) lib/lp/code/browser/tests/test_branch.py (+80/-58) lib/lp/code/interfaces/branch.py (+5/-17) lib/lp/code/model/branch.py (+14/-32) lib/lp/code/model/tests/test_branch.py (+36/-96) |
||||||||
To merge this branch: | bzr merge lp:~wgrant/launchpad/improve-branch-edit-type | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
j.c.sackett (community) | Approve | ||
Review via email: mp+114591@code.launchpad.net |
Commit message
Rework BranchEditView's information type widget to support the new sharing model.
Description of the change
This branch reworks BranchEditView's information type widget to support the new sharing model a little more easily. It used to choose types to show based on Branch.
I adjusted Branch.
I've dropped the commercial subscription check from Branch.canBePrivate for now, as it's new, not yet enabled, and will be replaced by a branch configuration option shortly.
There's still some awfulness in BranchEditView around restricting type changes for stacked branches, but I plan to disentangle that in a followup.
Preview Diff
1 | === modified file 'lib/lp/code/browser/branch.py' |
2 | --- lib/lp/code/browser/branch.py 2012-07-10 10:58:27 +0000 |
3 | +++ lib/lp/code/browser/branch.py 2012-07-12 21:59:20 +0000 |
4 | @@ -91,7 +91,11 @@ |
5 | from lp.blueprints.interfaces.specificationbranch import ISpecificationBranch |
6 | from lp.bugs.interfaces.bug import IBugSet |
7 | from lp.bugs.interfaces.bugbranch import IBugBranch |
8 | -from lp.bugs.interfaces.bugtask import UNRESOLVED_BUGTASK_STATUSES |
9 | +from lp.bugs.interfaces.bugtask import ( |
10 | + BugTaskSearchParams, |
11 | + IBugTaskSet, |
12 | + UNRESOLVED_BUGTASK_STATUSES, |
13 | + ) |
14 | from lp.code.browser.branchmergeproposal import ( |
15 | latest_proposals_for_each_branch, |
16 | ) |
17 | @@ -121,6 +125,7 @@ |
18 | from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal |
19 | from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference |
20 | from lp.registry.enums import ( |
21 | + InformationType, |
22 | PRIVATE_INFORMATION_TYPES, |
23 | PUBLIC_INFORMATION_TYPES, |
24 | ) |
25 | @@ -1095,36 +1100,56 @@ |
26 | if branch.branch_type in (BranchType.HOSTED, BranchType.IMPORTED): |
27 | self.form_fields = self.form_fields.omit('url') |
28 | |
29 | + def getInformationTypesToShow(self): |
30 | + """Get the information types to display on the edit form. |
31 | + |
32 | + We display a highly customised set of information types: |
33 | + anything allowed by the namespace, plus the current type, |
34 | + except some of the obscure types unless there's a linked |
35 | + bug with an obscure type. |
36 | + """ |
37 | + allowed_types = self.context.getAllowedInformationTypes(self.user) |
38 | + shown_types = ( |
39 | + InformationType.PUBLIC, |
40 | + InformationType.USERDATA, |
41 | + InformationType.PROPRIETARY, |
42 | + ) |
43 | + |
44 | + # We only show Embargoed Security and Unembargoed Security |
45 | + # if the branch is linked to a bug with one of those types, |
46 | + # as they're confusing and not generally useful otherwise. |
47 | + # Once Proprietary is fully deployed, User Data should be |
48 | + # added here. |
49 | + hidden_types = ( |
50 | + InformationType.UNEMBARGOEDSECURITY, |
51 | + InformationType.EMBARGOEDSECURITY, |
52 | + ) |
53 | + if set(allowed_types).intersection(hidden_types): |
54 | + params = BugTaskSearchParams( |
55 | + user=self.user, linked_branches=self.context.id, |
56 | + information_type=hidden_types) |
57 | + if getUtility(IBugTaskSet).searchBugIds(params).count() > 0: |
58 | + shown_types += hidden_types |
59 | + |
60 | + # Now take the intersection of the allowed and shown types. |
61 | + combined_types = set(allowed_types).intersection(shown_types) |
62 | + combined_types.add(self.context.information_type) |
63 | + return combined_types |
64 | + |
65 | def setUpWidgets(self, context=None): |
66 | super(BranchEditView, self).setUpWidgets() |
67 | - branch = self.context |
68 | - |
69 | if self.form_fields.get('information_type') is not None: |
70 | + # Customise the set of shown types. |
71 | + types_to_show = self.getInformationTypesToShow() |
72 | + # Grab the types from the vocab if they exist. |
73 | # The vocab uses feature flags to control what is displayed so we |
74 | # need to pull info_types from the vocab to use to make the subset |
75 | - # of what we show the user. |
76 | + # of what we show the user. This is mostly to hide Proprietary |
77 | + # while it's disabled. |
78 | info_type_vocab = self.widgets['information_type'].vocabulary |
79 | - public_types = [ |
80 | - info_type |
81 | - for info_type in info_type_vocab |
82 | - if info_type.value in PUBLIC_INFORMATION_TYPES] |
83 | - private_types = [ |
84 | - info_type |
85 | - for info_type in info_type_vocab |
86 | - if info_type.value in PRIVATE_INFORMATION_TYPES] |
87 | - |
88 | - allowed_information_types = [] |
89 | - if branch.private: |
90 | - if branch.canBePublic(self.user): |
91 | - allowed_information_types.extend(public_types) |
92 | - allowed_information_types.extend(private_types) |
93 | - else: |
94 | - allowed_information_types.extend(public_types) |
95 | - if branch.canBePrivate(self.user): |
96 | - allowed_information_types.extend(private_types) |
97 | - |
98 | - self.widgets['information_type'].vocabulary = ( |
99 | - SimpleVocabulary(allowed_information_types)) |
100 | + self.widgets['information_type'].vocabulary = SimpleVocabulary( |
101 | + [info_type for info_type in info_type_vocab |
102 | + if info_type.value in types_to_show]) |
103 | |
104 | def validate(self, data): |
105 | # Check that we're not moving a team branch to the +junk |
106 | |
107 | === modified file 'lib/lp/code/browser/tests/test_branch.py' |
108 | --- lib/lp/code/browser/tests/test_branch.py 2012-07-09 04:14:09 +0000 |
109 | +++ lib/lp/code/browser/tests/test_branch.py 2012-07-12 21:59:20 +0000 |
110 | @@ -47,6 +47,7 @@ |
111 | from lp.services.webapp.publisher import canonical_url |
112 | from lp.services.webapp.servers import LaunchpadTestRequest |
113 | from lp.testing import ( |
114 | + admin_logged_in, |
115 | BrowserTestCase, |
116 | login, |
117 | login_person, |
118 | @@ -915,32 +916,11 @@ |
119 | admin = admins.teamowner |
120 | browser = self.getUserBrowser( |
121 | canonical_url(branch) + '/+edit', user=admin) |
122 | - browser.getControl("Embargoed Security").click() |
123 | + browser.getControl("User Data").click() |
124 | browser.getControl("Change Branch").click() |
125 | with person_logged_in(person): |
126 | self.assertEqual( |
127 | - InformationType.EMBARGOEDSECURITY, branch.information_type) |
128 | - |
129 | - def test_proprietary_in_ui_vocabulary_commercial_projects(self): |
130 | - # Commercial projects can have information type Proprietary. |
131 | - owner = self.factory.makePerson() |
132 | - product = self.factory.makeProduct() |
133 | - self.factory.makeCommercialSubscription(product) |
134 | - branch = self.factory.makeProductBranch(product=product, owner=owner) |
135 | - with person_logged_in(owner): |
136 | - browser = self.getUserBrowser( |
137 | - canonical_url(branch) + '/+edit', user=owner) |
138 | - self.assertIsNotNone(browser.getControl("Proprietary")) |
139 | - |
140 | - def test_proprietary_not_in_ui_vocabulary_normal_projects(self): |
141 | - # Non-commercial projects can not have information type Proprietary. |
142 | - owner = self.factory.makePerson() |
143 | - product = self.factory.makeProduct() |
144 | - branch = self.factory.makeProductBranch(product=product, owner=owner) |
145 | - with person_logged_in(owner): |
146 | - browser = self.getUserBrowser( |
147 | - canonical_url(branch) + '/+edit', user=owner) |
148 | - self.assertRaises(LookupError, browser.getControl, "Proprietary") |
149 | + InformationType.USERDATA, branch.information_type) |
150 | |
151 | def test_can_not_change_privacy_of_stacked_on_private(self): |
152 | # The privacy field is not shown if the branch is stacked on a |
153 | @@ -958,41 +938,83 @@ |
154 | self.assertRaises( |
155 | LookupError, browser.getControl, "Information Type") |
156 | |
157 | - def test_authorised_user_can_change_branch_to_private(self): |
158 | - # An authorised user can make the information type private. |
159 | - team_owner = self.factory.makePerson() |
160 | - user = self.factory.makePerson() |
161 | - team = self.factory.makeTeam( |
162 | - owner=team_owner, |
163 | - visibility=PersonVisibility.PRIVATE, |
164 | - subscription_policy=TeamSubscriptionPolicy.RESTRICTED) |
165 | - with person_logged_in(team_owner): |
166 | - team.addMember(user, team_owner) |
167 | - with person_logged_in(user): |
168 | - branch = self.factory.makeBranch(owner=team) |
169 | - browser = self.getUserBrowser( |
170 | - canonical_url(branch) + '/+edit', user=user) |
171 | - self.assertIsNotNone(browser.getControl, "Embargoed Security") |
172 | - |
173 | - def test_unauthorised_user_cannot_change_branch_to_private(self): |
174 | - # An unauthorised user cannot make the information type private. |
175 | - user = self.factory.makePerson() |
176 | - with person_logged_in(user): |
177 | - branch = self.factory.makeBranch(owner=user) |
178 | - browser = self.getUserBrowser( |
179 | - canonical_url(branch) + '/+edit', user=user) |
180 | - self.assertRaises( |
181 | - LookupError, browser.getControl, "Embargoed Security") |
182 | - |
183 | - def test_branch_for_commercial_project(self): |
184 | - # A branch for a commercial project can be private. |
185 | - product = self.factory.makeProduct() |
186 | - self.factory.makeCommercialSubscription(product) |
187 | - branch = self.factory.makeProductBranch(product=product) |
188 | - with person_logged_in(branch.owner): |
189 | - browser = self.getUserBrowser( |
190 | - canonical_url(branch) + '/+edit', user=branch.owner) |
191 | - self.assertIsNotNone(browser.getControl, "Embargoed Security") |
192 | + |
193 | +class TestBranchEditViewInformationTypes(TestCaseWithFactory): |
194 | + """Tests for BranchEditView.getInformationTypesToShow.""" |
195 | + |
196 | + layer = DatabaseFunctionalLayer |
197 | + |
198 | + def assertShownTypes(self, types, branch, user=None): |
199 | + if user is None: |
200 | + user = removeSecurityProxy(branch).owner |
201 | + with person_logged_in(user): |
202 | + view = create_initialized_view(branch, '+edit', user=user) |
203 | + self.assertContentEqual(types, view.getInformationTypesToShow()) |
204 | + |
205 | + def test_public_branch(self): |
206 | + # A normal public branch on a public project can only be public. |
207 | + # We don't show information types like Unembargoed Security |
208 | + # unless there's a linked branch of that type, as they're not |
209 | + # useful or unconfusing otherwise. |
210 | + # The model doesn't enforce this, so it's just a UI thing. |
211 | + branch = self.factory.makeBranch( |
212 | + information_type=InformationType.PUBLIC) |
213 | + self.assertShownTypes([InformationType.PUBLIC], branch) |
214 | + |
215 | + def test_public_branch_with_security_bug(self): |
216 | + # A public branch can be set to Unembargoed Security if it has a |
217 | + # linked Unembargoed Security bug. The project policy doesn't |
218 | + # allow private branches, so Embargoed Security and User Data |
219 | + # are unavailable. |
220 | + branch = self.factory.makeBranch( |
221 | + information_type=InformationType.PUBLIC) |
222 | + bug = self.factory.makeBug( |
223 | + information_type=InformationType.UNEMBARGOEDSECURITY) |
224 | + with admin_logged_in(): |
225 | + branch.linkBug(bug, branch.owner) |
226 | + self.assertShownTypes( |
227 | + [InformationType.PUBLIC, InformationType.UNEMBARGOEDSECURITY], |
228 | + branch) |
229 | + |
230 | + def test_branch_with_disallowed_type(self): |
231 | + # We don't force branches with a disallowed type (eg. Proprietary on a |
232 | + # non-commercial project) to change, so the current type is |
233 | + # shown. |
234 | + branch = self.factory.makeBranch( |
235 | + information_type=InformationType.PROPRIETARY) |
236 | + self.assertShownTypes( |
237 | + [InformationType.PUBLIC, InformationType.PROPRIETARY], branch) |
238 | + |
239 | + def test_private_branch(self): |
240 | + # Branches on projects with a private policy can be set to |
241 | + # User Data (aka. Private) |
242 | + branch = self.factory.makeBranch( |
243 | + information_type=InformationType.PUBLIC) |
244 | + with admin_logged_in(): |
245 | + branch.product.setBranchVisibilityTeamPolicy( |
246 | + branch.owner, BranchVisibilityRule.PRIVATE) |
247 | + self.assertShownTypes( |
248 | + [InformationType.PUBLIC, InformationType.USERDATA, |
249 | + InformationType.PROPRIETARY], branch) |
250 | + |
251 | + def test_private_branch_with_security_bug(self): |
252 | + # Branches on projects that allow private branches can use the |
253 | + # Embargoed Security information type if they have a security |
254 | + # bug linked. |
255 | + branch = self.factory.makeBranch( |
256 | + information_type=InformationType.PUBLIC) |
257 | + with admin_logged_in(): |
258 | + branch.product.setBranchVisibilityTeamPolicy( |
259 | + branch.owner, BranchVisibilityRule.PRIVATE) |
260 | + bug = self.factory.makeBug( |
261 | + information_type=InformationType.UNEMBARGOEDSECURITY) |
262 | + with admin_logged_in(): |
263 | + branch.linkBug(bug, branch.owner) |
264 | + self.assertShownTypes( |
265 | + [InformationType.PUBLIC, InformationType.UNEMBARGOEDSECURITY, |
266 | + InformationType.EMBARGOEDSECURITY, InformationType.USERDATA, |
267 | + InformationType.PROPRIETARY], |
268 | + branch) |
269 | |
270 | |
271 | class TestBranchUpgradeView(TestCaseWithFactory): |
272 | |
273 | === modified file 'lib/lp/code/interfaces/branch.py' |
274 | --- lib/lp/code/interfaces/branch.py 2012-07-06 03:33:59 +0000 |
275 | +++ lib/lp/code/interfaces/branch.py 2012-07-12 21:59:20 +0000 |
276 | @@ -968,23 +968,11 @@ |
277 | def visibleByUser(user): |
278 | """Can the specified user see this branch?""" |
279 | |
280 | - def canBePublic(user): |
281 | - """Can this branch be public? |
282 | - |
283 | - A branch can be made public if: |
284 | - - the branch has a visibility policy which allows it |
285 | - - the user is an admin or bzr expert |
286 | - """ |
287 | - |
288 | - def canBePrivate(user): |
289 | - """Can this branch be private? |
290 | - |
291 | - A branch can be made private if: |
292 | - - the branch has a visibility policy which allows it |
293 | - - the user is an admin or bzr expert |
294 | - - the branch is owned by a private team |
295 | - (The branch is already implicitly private) |
296 | - - the branch is linked to a private bug the user can access |
297 | + def getAllowedInformationTypes(user): |
298 | + """Get a list of acceptable `InformationType`s for this branch. |
299 | + |
300 | + If the user is a Launchpad admin, any type is acceptable. Otherwise |
301 | + the `IBranchNamespace` is consulted. |
302 | """ |
303 | |
304 | |
305 | |
306 | === modified file 'lib/lp/code/model/branch.py' |
307 | --- lib/lp/code/model/branch.py 2012-07-11 09:43:04 +0000 |
308 | +++ lib/lp/code/model/branch.py 2012-07-12 21:59:20 +0000 |
309 | @@ -237,6 +237,17 @@ |
310 | information_type = InformationType.PUBLIC |
311 | return self.transitionToInformationType(information_type, user) |
312 | |
313 | + def getAllowedInformationTypes(self, who): |
314 | + """See `IBranch`.""" |
315 | + if user_has_special_branch_access(who): |
316 | + # Until sharing settles down, admins can set any type. |
317 | + types = set(PUBLIC_INFORMATION_TYPES + PRIVATE_INFORMATION_TYPES) |
318 | + else: |
319 | + # Otherwise the permitted types are defined by the namespace. |
320 | + policy = IBranchNamespacePolicy(self.namespace) |
321 | + types = set(policy.getAllowedInformationTypes()) |
322 | + return types |
323 | + |
324 | def transitionToInformationType(self, information_type, who, |
325 | verify_policy=True): |
326 | """See `IBranch`.""" |
327 | @@ -246,11 +257,9 @@ |
328 | and self.stacked_on.information_type in PRIVATE_INFORMATION_TYPES |
329 | and information_type in PUBLIC_INFORMATION_TYPES): |
330 | raise BranchCannotChangeInformationType() |
331 | - # Only check the privacy policy if the user is not special. |
332 | - if verify_policy and not user_has_special_branch_access(who): |
333 | - policy = IBranchNamespacePolicy(self.namespace) |
334 | - if information_type not in policy.getAllowedInformationTypes(): |
335 | - raise BranchCannotChangeInformationType() |
336 | + if (verify_policy |
337 | + and information_type not in self.getAllowedInformationTypes(who)): |
338 | + raise BranchCannotChangeInformationType() |
339 | self.information_type = information_type |
340 | self._reconcileAccess() |
341 | if information_type in PRIVATE_INFORMATION_TYPES: |
342 | @@ -1292,33 +1301,6 @@ |
343 | user, checked_branches) |
344 | return can_access |
345 | |
346 | - def canBePublic(self, user): |
347 | - """See `IBranch`.""" |
348 | - policy = IBranchNamespacePolicy(self.namespace) |
349 | - return InformationType.PUBLIC in policy.getAllowedInformationTypes() |
350 | - |
351 | - def canBePrivate(self, user): |
352 | - """See `IBranch`.""" |
353 | - policy = IBranchNamespacePolicy(self.namespace) |
354 | - # Do the easy checks first. |
355 | - policy_allows = ( |
356 | - InformationType.USERDATA in policy.getAllowedInformationTypes()) |
357 | - if (policy_allows or |
358 | - user_has_special_branch_access(user) or |
359 | - user.visibility == PersonVisibility.PRIVATE): |
360 | - return True |
361 | - # Branches linked to commercial projects can be private. |
362 | - target = self.target.context |
363 | - if (IProduct.providedBy(target) and |
364 | - target.has_current_commercial_subscription): |
365 | - return True |
366 | - # Branches linked to private bugs can be private. |
367 | - params = BugTaskSearchParams( |
368 | - user=user, linked_branches=self.id, |
369 | - information_type=PRIVATE_INFORMATION_TYPES) |
370 | - bug_ids = getUtility(IBugTaskSet).searchBugIds(params) |
371 | - return bug_ids.count() > 0 |
372 | - |
373 | @property |
374 | def recipes(self): |
375 | """See `IHasRecipes`.""" |
376 | |
377 | === modified file 'lib/lp/code/model/tests/test_branch.py' |
378 | --- lib/lp/code/model/tests/test_branch.py 2012-07-11 09:36:13 +0000 |
379 | +++ lib/lp/code/model/tests/test_branch.py 2012-07-12 21:59:20 +0000 |
380 | @@ -76,7 +76,10 @@ |
381 | from lp.code.interfaces.branchmergeproposal import ( |
382 | BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES, |
383 | ) |
384 | -from lp.code.interfaces.branchnamespace import IBranchNamespaceSet |
385 | +from lp.code.interfaces.branchnamespace import ( |
386 | + IBranchNamespacePolicy, |
387 | + IBranchNamespaceSet, |
388 | + ) |
389 | from lp.code.interfaces.branchrevision import IBranchRevision |
390 | from lp.code.interfaces.codehosting import branch_id_alias |
391 | from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch |
392 | @@ -2388,6 +2391,38 @@ |
393 | self.assertEqual([], get_policies_for_artifact(branch)) |
394 | |
395 | |
396 | +class TestBranchGetAllowedInformationTypes(TestCaseWithFactory): |
397 | + """Test Branch.getAllowedInformationTypes.""" |
398 | + |
399 | + layer = DatabaseFunctionalLayer |
400 | + |
401 | + def test_normal_user_sees_namespace_types(self): |
402 | + # An unprivileged user sees the types allowed by the namespace. |
403 | + branch = self.factory.makeBranch() |
404 | + policy = IBranchNamespacePolicy(branch.namespace) |
405 | + self.assertContentEqual( |
406 | + policy.getAllowedInformationTypes(), |
407 | + branch.getAllowedInformationTypes(branch.owner)) |
408 | + self.assertNotIn( |
409 | + InformationType.PROPRIETARY, |
410 | + branch.getAllowedInformationTypes(branch.owner)) |
411 | + |
412 | + def test_admin_sees_namespace_types(self): |
413 | + # An admin sees all the types, since they occasionally need to |
414 | + # override the namespace rules. This is hopefully temporary, and |
415 | + # can go away once the new sharing rules (granting |
416 | + # non-commercial projects limited use of private branches) are |
417 | + # deployed. |
418 | + branch = self.factory.makeBranch() |
419 | + admin = self.factory.makeAdministrator() |
420 | + self.assertContentEqual( |
421 | + PUBLIC_INFORMATION_TYPES + PRIVATE_INFORMATION_TYPES, |
422 | + branch.getAllowedInformationTypes(admin)) |
423 | + self.assertIn( |
424 | + InformationType.PROPRIETARY, |
425 | + branch.getAllowedInformationTypes(admin)) |
426 | + |
427 | + |
428 | class TestBranchSetPrivate(TestCaseWithFactory): |
429 | """Test IBranch.setPrivate.""" |
430 | |
431 | @@ -2507,101 +2542,6 @@ |
432 | get_policies_for_artifact(branch)[0].type) |
433 | |
434 | |
435 | -class TestBranchCanBePrivate(TestCaseWithFactory): |
436 | - """Test IBranch.canBePrivate.""" |
437 | - |
438 | - layer = DatabaseFunctionalLayer |
439 | - |
440 | - def setUp(self): |
441 | - # Use an admin user as we aren't checking edit permissions here. |
442 | - TestCaseWithFactory.setUp(self, 'admin@canonical.com') |
443 | - |
444 | - def test_arbitary_branch(self): |
445 | - # By default branches cannot be private. |
446 | - branch = self.factory.makeBranch() |
447 | - self.assertFalse(branch.canBePrivate(branch.owner)) |
448 | - |
449 | - def test_admin(self): |
450 | - # Admins can make a branch private. |
451 | - branch = self.factory.makeBranch() |
452 | - admin = getUtility(ILaunchpadCelebrities).admin |
453 | - self.assertTrue(branch.canBePrivate(admin)) |
454 | - |
455 | - def test_visibility_policy_private(self): |
456 | - # Users with a suitable visibility policy can make a branch private. |
457 | - team = self.factory.makeTeam( |
458 | - subscription_policy=TeamSubscriptionPolicy.RESTRICTED) |
459 | - product = self.factory.makeProduct() |
460 | - product.setBranchVisibilityTeamPolicy( |
461 | - team, BranchVisibilityRule.PRIVATE) |
462 | - branch = self.factory.makeBranch(product=product, owner=team) |
463 | - self.assertTrue(branch.canBePrivate(team)) |
464 | - |
465 | - def test_private_owner(self): |
466 | - # Private team owners can make a branch private. |
467 | - team = self.factory.makeTeam( |
468 | - visibility=PersonVisibility.PRIVATE, |
469 | - subscription_policy=TeamSubscriptionPolicy.RESTRICTED) |
470 | - branch = self.factory.makeBranch(owner=team) |
471 | - self.assertTrue(branch.canBePrivate(team)) |
472 | - |
473 | - def test_commercial_project(self): |
474 | - # Branches linked to commercial projects can be private. |
475 | - product = self.factory.makeProduct() |
476 | - self.factory.makeCommercialSubscription(product) |
477 | - branch = self.factory.makeProductBranch(product=product) |
478 | - user = self.factory.makePerson() |
479 | - self.assertTrue(branch.canBePrivate(user)) |
480 | - |
481 | - def test_linked_private_bug(self): |
482 | - # Users with access to linked private bugs can make a branch private. |
483 | - for info_type in PRIVATE_INFORMATION_TYPES: |
484 | - user = self.factory.makePerson() |
485 | - bug = self.factory.makeBug( |
486 | - owner=user, information_type=info_type) |
487 | - branch = self.factory.makeBranch() |
488 | - removeSecurityProxy(bug).linkBranch(branch, user) |
489 | - self.assertTrue(branch.canBePrivate(user)) |
490 | - |
491 | - def test_linked_public_bug(self): |
492 | - # Users with access to linked public bugs cannot make a branch private. |
493 | - for info_type in PUBLIC_INFORMATION_TYPES: |
494 | - user = self.factory.makePerson() |
495 | - bug = self.factory.makeBug( |
496 | - owner=user, information_type=info_type) |
497 | - branch = self.factory.makeBranch() |
498 | - removeSecurityProxy(bug).linkBranch(branch, user) |
499 | - self.assertFalse(branch.canBePrivate(user)) |
500 | - |
501 | - |
502 | -class TestBranchCanBePublic(TestCaseWithFactory): |
503 | - """Test IBranch.canBePublic.""" |
504 | - |
505 | - layer = DatabaseFunctionalLayer |
506 | - |
507 | - def setUp(self): |
508 | - # Use an admin user as we aren't checking edit permissions here. |
509 | - TestCaseWithFactory.setUp(self, 'admin@canonical.com') |
510 | - |
511 | - def test_arbitrary_branch(self): |
512 | - # By default branches can be public. |
513 | - branch = self.factory.makeBranch( |
514 | - information_type=InformationType.USERDATA) |
515 | - self.assertTrue(branch.canBePublic(branch.owner)) |
516 | - |
517 | - def test_visibility_policy_public_not_allowed(self): |
518 | - # Branches cannot be public if the visibility policy forbids it. |
519 | - team = self.factory.makeTeam( |
520 | - subscription_policy=TeamSubscriptionPolicy.RESTRICTED) |
521 | - product = self.factory.makeProduct() |
522 | - product.setBranchVisibilityTeamPolicy( |
523 | - team, BranchVisibilityRule.PRIVATE_ONLY) |
524 | - branch = self.factory.makeBranch( |
525 | - product=product, owner=team, |
526 | - information_type=InformationType.USERDATA) |
527 | - self.assertFalse(branch.canBePublic(team)) |
528 | - |
529 | - |
530 | class TestBranchCommitsForDays(TestCaseWithFactory): |
531 | """Tests for `Branch.commitsForDays`.""" |
532 |
Thanks William. One minor point.
At line #60 you have a comment about using the widget vocabulary in `getInformation TypesToShow` , but you actually do that vocal step on #103, in setUpWidgets. Might be worth moving the comment to the place where the code is actually happening.