Merge ~pappacena/launchpad:vcs-scenarios-for-bmp into launchpad:master

Proposed by Thiago F. Pappacena
Status: Superseded
Proposed branch: ~pappacena/launchpad:vcs-scenarios-for-bmp
Merge into: launchpad:master
Diff against target: 671 lines (+164/-136)
3 files modified
lib/lp/code/model/branchmergeproposal.py (+5/-7)
lib/lp/code/model/tests/test_branchmergeproposal.py (+146/-125)
lib/lp/code/tests/helpers.py (+13/-4)
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+387877@code.launchpad.net

Commit message

Refactoring BMP test cases to extend scenarios on TestBranchMergeProposalNominateReviewer

To post a comment you must log in.

Unmerged commits

04850f4... by Thiago F. Pappacena

Refactoring BMP to extend scenarios to TestBranchMergeProposalNominateReviewer

7b57c36... by Thiago F. Pappacena

Refactoring test code nad removing unused attribute from GitRef

5577f7b... by Thiago F. Pappacena

LP: #1888396 Fixing webhook trigger for private git repositories

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/code/model/branchmergeproposal.py b/lib/lp/code/model/branchmergeproposal.py
2index 5ebcf04..338cc29 100644
3--- a/lib/lp/code/model/branchmergeproposal.py
4+++ b/lib/lp/code/model/branchmergeproposal.py
5@@ -1,4 +1,4 @@
6-# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
7+# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
8 # GNU Affero General Public License version 3 (see the file LICENSE).
9
10 """Database class for branch merge proposals."""
11@@ -70,6 +70,7 @@ from lp.code.event.branchmergeproposal import (
12 BranchMergeProposalNeedsReviewEvent,
13 ReviewerNominatedEvent,
14 )
15+from lp.code.interfaces.branch import IBranch
16 from lp.code.interfaces.branchcollection import IAllBranches
17 from lp.code.interfaces.branchmergeproposal import (
18 BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,
19@@ -881,7 +882,7 @@ class BranchMergeProposal(SQLBase, BugLinkTargetMixin):
20 BranchSubscriptionDiffSize.NODIFF,
21 CodeReviewNotificationLevel.FULL,
22 user)
23- if branch.stacked_on is not None:
24+ if IBranch.providedBy(branch) and branch.stacked_on is not None:
25 checked_branches.append(branch)
26 if branch.stacked_on not in checked_branches:
27 self._subscribeUserToStackedBranch(
28@@ -903,14 +904,11 @@ class BranchMergeProposal(SQLBase, BugLinkTargetMixin):
29 the reviewer if the branch is private and the reviewer is an open
30 team.
31 """
32- if self.source_branch is None:
33- # This only applies to Bazaar, which has stacked branches.
34- return
35- source = self.source_branch
36+ source = self.merge_source
37 if (not source.visibleByUser(reviewer) and
38 self._acceptable_to_give_visibility(source, reviewer)):
39 self._subscribeUserToStackedBranch(source, reviewer)
40- target = self.target_branch
41+ target = self.merge_target
42 if (not target.visibleByUser(reviewer) and
43 self._acceptable_to_give_visibility(source, reviewer)):
44 self._subscribeUserToStackedBranch(target, reviewer)
45diff --git a/lib/lp/code/model/tests/test_branchmergeproposal.py b/lib/lp/code/model/tests/test_branchmergeproposal.py
46index 8936574..0d2457d 100644
47--- a/lib/lp/code/model/tests/test_branchmergeproposal.py
48+++ b/lib/lp/code/model/tests/test_branchmergeproposal.py
49@@ -1,4 +1,4 @@
50-# Copyright 2009-2019 Canonical Ltd. This software is licensed under the
51+# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
52 # GNU Affero General Public License version 3 (see the file LICENSE).
53
54 """Tests for BranchMergeProposals."""
55@@ -57,6 +57,7 @@ from lp.code.event.branchmergeproposal import (
56 BranchMergeProposalNeedsReviewEvent,
57 ReviewerNominatedEvent,
58 )
59+from lp.code.interfaces.branch import IBranch
60 from lp.code.interfaces.branchmergeproposal import (
61 BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,
62 BRANCH_MERGE_PROPOSAL_OBSOLETE_STATES as OBSOLETE_STATES,
63@@ -91,6 +92,7 @@ from lp.services.webapp import canonical_url
64 from lp.services.webhooks.testing import LogsScheduledWebhooks
65 from lp.services.xref.interfaces import IXRefSet
66 from lp.testing import (
67+ admin_logged_in,
68 ExpectedException,
69 launchpadlib_for,
70 login,
71@@ -109,6 +111,49 @@ from lp.testing.layers import (
72 )
73
74
75+class WithVCSScenarios(WithScenarios):
76+
77+ scenarios = [
78+ ("bzr", {"git": False}),
79+ ("git", {"git": True}),
80+ ]
81+
82+ def makeBranch(self, same_target_as=None, product=None, stacked_on=None,
83+ information_type=None, owner=None):
84+ # Create the product pillar and its access policy if information
85+ # type is "PROPRIETARY".
86+ if product is None and same_target_as is None:
87+ product = self.factory.makeProduct()
88+ if information_type == InformationType.PROPRIETARY:
89+ self.factory.makeAccessPolicy(product)
90+ elif product is None:
91+ same_target_as = removeSecurityProxy(same_target_as)
92+ product = (same_target_as.target if self.git else
93+ same_target_as.product)
94+
95+ kwargs = {
96+ "information_type": information_type,
97+ "owner": owner}
98+ if self.git:
99+ kwargs["target"] = product
100+ return self.factory.makeGitRefs(**kwargs)[0]
101+ else:
102+ kwargs["product"] = product
103+ kwargs["stacked_on"] = stacked_on
104+ return self.factory.makeProductBranch(**kwargs)
105+
106+ def makeBranchMergeProposal(self, source=None, target=None,
107+ prerequisite=None, **kwargs):
108+ if self.git:
109+ return self.factory.makeBranchMergeProposalForGit(
110+ target_ref=target, source_ref=source,
111+ prerequisite_ref=prerequisite, **kwargs)
112+ else:
113+ return self.factory.makeBranchMergeProposal(
114+ source_branch=source, target_branch=target,
115+ prerequisite_branch=prerequisite, **kwargs)
116+
117+
118 class TestBranchMergeProposalInterface(TestCaseWithFactory):
119 """Ensure that BranchMergeProposal implements its interface."""
120
121@@ -120,26 +165,16 @@ class TestBranchMergeProposalInterface(TestCaseWithFactory):
122 verifyObject(IBranchMergeProposal, bmp)
123
124
125-class TestBranchMergeProposalCanonicalUrl(WithScenarios, TestCaseWithFactory):
126+class TestBranchMergeProposalCanonicalUrl(
127+ WithVCSScenarios, TestCaseWithFactory):
128 """Tests canonical_url for merge proposals."""
129
130 layer = DatabaseFunctionalLayer
131
132- scenarios = [
133- ("bzr", {"git": False}),
134- ("git", {"git": True}),
135- ]
136-
137- def _makeBranchMergeProposal(self):
138- if self.git:
139- return self.factory.makeBranchMergeProposalForGit()
140- else:
141- return self.factory.makeBranchMergeProposal()
142-
143 def test_BranchMergeProposal_canonical_url_base(self):
144 # The URL for a merge proposal starts with the parent (source branch
145 # or source Git repository).
146- bmp = self._makeBranchMergeProposal()
147+ bmp = self.makeBranchMergeProposal()
148 url = canonical_url(bmp)
149 parent_url = canonical_url(bmp.parent)
150 self.assertTrue(url.startswith(parent_url))
151@@ -147,7 +182,7 @@ class TestBranchMergeProposalCanonicalUrl(WithScenarios, TestCaseWithFactory):
152 def test_BranchMergeProposal_canonical_url_rest(self):
153 # The rest of the URL for a merge proposal is +merge followed by the
154 # db id.
155- bmp = self._makeBranchMergeProposal()
156+ bmp = self.makeBranchMergeProposal()
157 url = canonical_url(bmp)
158 parent_url = canonical_url(bmp.parent)
159 rest = url[len(parent_url):]
160@@ -718,45 +753,14 @@ class TestMergeProposalGetPreviewDiff(TestCaseWithFactory):
161 self.mp_two.getPreviewDiff, self.preview_diff.id)
162
163
164-class TestMergeProposalNotification(WithScenarios, TestCaseWithFactory):
165+class TestMergeProposalNotification(WithVCSScenarios, TestCaseWithFactory):
166 """Test that events are created when merge proposals are manipulated"""
167
168 layer = DatabaseFunctionalLayer
169
170- scenarios = [
171- ("bzr", {"git": False}),
172- ("git", {"git": True}),
173- ]
174-
175 def setUp(self):
176 TestCaseWithFactory.setUp(self, user='test@canonical.com')
177
178- def makeBranch(self, same_target_as=None, target=None, **kwargs):
179- kwargs = dict(kwargs)
180- if self.git:
181- if same_target_as is not None:
182- kwargs["target"] = same_target_as.target
183- elif target is not None:
184- kwargs["target"] = target
185- return self.factory.makeGitRefs(**kwargs)[0]
186- else:
187- if same_target_as is not None:
188- kwargs["product"] = same_target_as.product
189- elif target is not None:
190- kwargs["product"] = target
191- return self.factory.makeProductBranch(**kwargs)
192-
193- def makeBranchMergeProposal(self, source=None, target=None,
194- prerequisite=None, **kwargs):
195- if self.git:
196- return self.factory.makeBranchMergeProposalForGit(
197- source_ref=source, target_ref=target,
198- prerequisite_ref=prerequisite, **kwargs)
199- else:
200- return self.factory.makeBranchMergeProposal(
201- source_branch=source, target_branch=target,
202- prerequisite_branch=prerequisite, **kwargs)
203-
204 def test_notifyOnCreate_needs_review(self):
205 # When a merge proposal is created needing review, the
206 # BranchMergeProposalNeedsReviewEvent is raised as well as the usual
207@@ -988,8 +992,8 @@ class TestMergeProposalNotification(WithScenarios, TestCaseWithFactory):
208 # they do not get email about the proposal.
209 owner = self.factory.makePerson()
210 product = self.factory.makeProduct()
211- source = self.makeBranch(owner=owner, target=product)
212- target = self.makeBranch(owner=owner, target=product)
213+ source = self.makeBranch(owner=owner, product=product)
214+ target = self.makeBranch(owner=owner, product=product)
215 bmp = self.makeBranchMergeProposal(source=source, target=target)
216 # Subscribe eric to the source branch only.
217 eric = self.factory.makePerson()
218@@ -1021,31 +1025,15 @@ class TestMergeProposalNotification(WithScenarios, TestCaseWithFactory):
219 self.assertIn(charlie, recipients)
220
221
222-class TestMergeProposalWebhooks(WithScenarios, TestCaseWithFactory):
223+class TestMergeProposalWebhooks(WithVCSScenarios, TestCaseWithFactory):
224
225 layer = DatabaseFunctionalLayer
226
227- scenarios = [
228- ("bzr", {"git": False}),
229- ("git", {"git": True}),
230- ]
231-
232 def setUp(self):
233 super(TestMergeProposalWebhooks, self).setUp()
234 self.useFixture(FeatureFixture(
235 {BRANCH_MERGE_PROPOSAL_WEBHOOKS_FEATURE_FLAG: "on"}))
236
237- def makeBranch(self, same_target_as=None):
238- kwargs = {}
239- if self.git:
240- if same_target_as is not None:
241- kwargs["target"] = same_target_as.target
242- return self.factory.makeGitRefs(**kwargs)[0]
243- else:
244- if same_target_as is not None:
245- kwargs["product"] = same_target_as.product
246- return self.factory.makeProductBranch(**kwargs)
247-
248 def getWebhookTarget(self, branch):
249 if self.git:
250 return branch.repository
251@@ -1102,6 +1090,39 @@ class TestMergeProposalWebhooks(WithScenarios, TestCaseWithFactory):
252 (hook, "merge-proposal:0.1",
253 MatchesDict(expected_redacted_payload))]))
254
255+ def test_create_private_repo_triggers_webhooks(self):
256+ # When a merge proposal is created, any relevant webhooks are
257+ # triggered even if the repository is proprietary.
258+ logger = self.useFixture(FakeLogger())
259+ source = self.makeBranch(information_type=InformationType.PROPRIETARY)
260+ target = self.makeBranch(
261+ same_target_as=source,
262+ information_type=InformationType.PROPRIETARY)
263+
264+ with admin_logged_in():
265+ # Create the web hook and the proposal.
266+ registrant = self.factory.makePerson()
267+ hook = self.factory.makeWebhook(
268+ target=self.getWebhookTarget(target),
269+ event_types=["merge-proposal:0.1"])
270+ proposal = source.addLandingTarget(
271+ registrant, target, needs_review=True)
272+ target_owner = target.owner
273+
274+ login_person(target_owner)
275+ delivery = hook.deliveries.one()
276+ expected_payload = {
277+ "merge_proposal": Equals(self.getURL(proposal)),
278+ "action": Equals("created"),
279+ "new": MatchesDict(self.getExpectedPayload(proposal)),
280+ }
281+ expected_redacted_payload = dict(
282+ expected_payload,
283+ new=MatchesDict(
284+ self.getExpectedPayload(proposal, redact=True)))
285+ self.assertCorrectDelivery(expected_payload, hook, delivery)
286+ self.assertCorrectLogging(expected_redacted_payload, hook, logger)
287+
288 def test_create_triggers_webhooks(self):
289 # When a merge proposal is created, any relevant webhooks are
290 # triggered.
291@@ -1486,15 +1507,10 @@ class TestBranchMergeProposalDeletion(TestCaseWithFactory):
292 SQLObjectNotFound, BranchMergeProposalJob.get, job_id)
293
294
295-class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
296+class TestBranchMergeProposalBugs(WithVCSScenarios, TestCaseWithFactory):
297
298 layer = DatabaseFunctionalLayer
299
300- scenarios = [
301- ("bzr", {"git": False}),
302- ("git", {"git": True}),
303- ]
304-
305 def setUp(self):
306 super(TestBranchMergeProposalBugs, self).setUp()
307 self.user = self.factory.makePerson()
308@@ -1502,15 +1518,9 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
309 if self.git:
310 self.hosting_fixture = self.useFixture(GitHostingFixture())
311
312- def _makeBranchMergeProposal(self):
313- if self.git:
314- return self.factory.makeBranchMergeProposalForGit()
315- else:
316- return self.factory.makeBranchMergeProposal()
317-
318 def test_bugs(self):
319 # bugs includes all linked bugs.
320- bmp = self._makeBranchMergeProposal()
321+ bmp = self.makeBranchMergeProposal()
322 self.assertEqual([], bmp.bugs)
323 bugs = [self.factory.makeBug() for _ in range(2)]
324 bmp.linkBug(bugs[0], bmp.registrant)
325@@ -1525,7 +1535,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
326 def test_related_bugtasks_includes_source_bugtasks(self):
327 # related_bugtasks includes bugtasks linked to the source branch in
328 # the Bazaar case, or directly to the MP in the Git case.
329- bmp = self._makeBranchMergeProposal()
330+ bmp = self.makeBranchMergeProposal()
331 bug = self.factory.makeBug()
332 bmp.linkBug(bug, bmp.registrant)
333 self.assertEqual(
334@@ -1533,7 +1543,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
335
336 def test_related_bugtasks_excludes_private_bugs(self):
337 # related_bugtasks ignores private bugs for non-authorised users.
338- bmp = self._makeBranchMergeProposal()
339+ bmp = self.makeBranchMergeProposal()
340 bug = self.factory.makeBug()
341 bmp.linkBug(bug, bmp.registrant)
342 person = self.factory.makePerson()
343@@ -1553,7 +1563,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
344 # related_bugtasks ignores bugs linked to the target branch.
345 if self.git:
346 self.skipTest("Only relevant for Bazaar.")
347- bmp = self._makeBranchMergeProposal()
348+ bmp = self.makeBranchMergeProposal()
349 bug = self.factory.makeBug()
350 bmp.target_branch.linkBug(bug, bmp.registrant)
351 self.assertEqual([], list(bmp.getRelatedBugTasks(self.user)))
352@@ -1562,7 +1572,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
353 # related_bugtasks ignores bugs linked to both branches.
354 if self.git:
355 self.skipTest("Only relevant for Bazaar.")
356- bmp = self._makeBranchMergeProposal()
357+ bmp = self.makeBranchMergeProposal()
358 bug = self.factory.makeBug()
359 bmp.source_branch.linkBug(bug, bmp.registrant)
360 bmp.target_branch.linkBug(bug, bmp.registrant)
361@@ -1574,7 +1584,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
362 if not self.git:
363 self.skipTest("Only relevant for Git.")
364 bugs = [self.factory.makeBug() for _ in range(3)]
365- bmp = self._makeBranchMergeProposal()
366+ bmp = self.makeBranchMergeProposal()
367 self.hosting_fixture.getLog.result = [
368 {
369 "sha1": bmp.source_git_commit_sha1,
370@@ -1621,7 +1631,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
371 # bugs in either the database or the source branch.
372 if not self.git:
373 self.skipTest("Only relevant for Git.")
374- bmp = self._makeBranchMergeProposal()
375+ bmp = self.makeBranchMergeProposal()
376 self._setUpLog([])
377 bmp.updateRelatedBugsFromSource()
378 self.assertEqual([], bmp.bugs)
379@@ -1632,7 +1642,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
380 if not self.git:
381 self.skipTest("Only relevant for Git.")
382 bugs = [self.factory.makeBug() for _ in range(3)]
383- bmp = self._makeBranchMergeProposal()
384+ bmp = self.makeBranchMergeProposal()
385 self._setUpLog([bugs[0]])
386 bmp.updateRelatedBugsFromSource()
387 self.assertEqual([bugs[0]], bmp.bugs)
388@@ -1646,7 +1656,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
389 if not self.git:
390 self.skipTest("Only relevant for Git.")
391 bug = self.factory.makeBug()
392- bmp = self._makeBranchMergeProposal()
393+ bmp = self.makeBranchMergeProposal()
394 self._setUpLog([bug])
395 bmp.updateRelatedBugsFromSource()
396 self.assertEqual([bug], bmp.bugs)
397@@ -1661,7 +1671,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
398 if not self.git:
399 self.skipTest("Only relevant for Git.")
400 bug = self.factory.makeBug()
401- bmp = self._makeBranchMergeProposal()
402+ bmp = self.makeBranchMergeProposal()
403 self._setUpLog([bug])
404 bmp.updateRelatedBugsFromSource()
405 self.assertEqual([bug], bmp.bugs)
406@@ -1676,7 +1686,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
407 if not self.git:
408 self.skipTest("Only relevant for Git.")
409 bug = self.factory.makeBug()
410- bmp = self._makeBranchMergeProposal()
411+ bmp = self.makeBranchMergeProposal()
412 bmp.linkBug(bug)
413 self._setUpLog([])
414 bmp.updateRelatedBugsFromSource()
415@@ -1701,7 +1711,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
416 self.skipTest("Only relevant for Git.")
417 self.pushConfig("codehosting", related_bugs_from_source_limit=3)
418 bugs = [self.factory.makeBug() for _ in range(5)]
419- bmp = self._makeBranchMergeProposal()
420+ bmp = self.makeBranchMergeProposal()
421 self._setUpLog([bugs[0]])
422 bmp.updateRelatedBugsFromSource()
423 self.assertEqual([bugs[0]], bmp.bugs)
424@@ -1713,45 +1723,48 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
425 self.assertEqual("TooManyRelatedBugs", self.oopses[0]["type"])
426
427
428-class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
429+class TestBranchMergeProposalNominateReviewer(
430+ WithVCSScenarios, TestCaseWithFactory):
431 """Test that the appropriate vote references get created."""
432
433 layer = DatabaseFunctionalLayer
434
435 def setUp(self):
436 TestCaseWithFactory.setUp(self, user='test@canonical.com')
437+ if self.git:
438+ self.hosting_fixture = self.useFixture(GitHostingFixture())
439
440 def test_notify_on_nominate(self):
441 # Ensure that a notification is emitted on nomination.
442- merge_proposal = self.factory.makeBranchMergeProposal()
443- login_person(merge_proposal.source_branch.owner)
444+ merge_proposal = self.makeBranchMergeProposal()
445+ login_person(merge_proposal.merge_source.owner)
446 reviewer = self.factory.makePerson()
447 result, events = self.assertNotifies(
448 ReviewerNominatedEvent, False,
449 merge_proposal.nominateReviewer,
450 reviewer=reviewer,
451- registrant=merge_proposal.source_branch.owner)
452+ registrant=merge_proposal.merge_source.owner)
453 self.assertEqual(result, events[0].object)
454
455 def test_notify_on_nominate_suppressed_if_requested(self):
456 # Ensure that a notification is suppressed if notify listeners is set
457 # to False.
458- merge_proposal = self.factory.makeBranchMergeProposal()
459- login_person(merge_proposal.source_branch.owner)
460+ merge_proposal = self.makeBranchMergeProposal()
461+ login_person(merge_proposal.merge_source.owner)
462 reviewer = self.factory.makePerson()
463 self.assertNoNotification(
464 merge_proposal.nominateReviewer,
465 reviewer=reviewer,
466- registrant=merge_proposal.source_branch.owner,
467+ registrant=merge_proposal.merge_source.owner,
468 _notify_listeners=False)
469
470 def test_one_initial_votes(self):
471 """A new merge proposal has one vote of the default reviewer."""
472- merge_proposal = self.factory.makeBranchMergeProposal()
473+ merge_proposal = self.makeBranchMergeProposal()
474 self.assertEqual(1, len(list(merge_proposal.votes)))
475 [vote] = list(merge_proposal.votes)
476 self.assertEqual(
477- merge_proposal.target_branch.owner, vote.reviewer)
478+ merge_proposal.merge_target.owner, vote.reviewer)
479
480 def makeProposalWithReviewer(self, reviewer=None, review_type=None,
481 registrant=None, **kwargs):
482@@ -1764,8 +1777,9 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
483 if registrant is None:
484 registrant = self.factory.makePerson()
485 merge_proposal = make_merge_proposal_without_reviewers(
486- factory=self.factory, registrant=registrant, **kwargs)
487- login_person(merge_proposal.source_branch.owner)
488+ factory=self.factory, for_git=self.git, registrant=registrant,
489+ **kwargs)
490+ login_person(merge_proposal.merge_source.owner)
491 merge_proposal.nominateReviewer(
492 reviewer=reviewer, registrant=registrant, review_type=review_type)
493 return merge_proposal, reviewer
494@@ -1871,15 +1885,15 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
495
496 def test_nominate_updates_reference(self):
497 """The existing reference is updated on re-nomination."""
498- merge_proposal = self.factory.makeBranchMergeProposal()
499- login_person(merge_proposal.source_branch.owner)
500+ merge_proposal = self.makeBranchMergeProposal()
501+ login_person(merge_proposal.merge_source.owner)
502 reviewer = self.factory.makePerson()
503 reference = merge_proposal.nominateReviewer(
504- reviewer=reviewer, registrant=merge_proposal.source_branch.owner,
505+ reviewer=reviewer, registrant=merge_proposal.merge_source.owner,
506 review_type='General')
507 self.assertEqual('general', reference.review_type)
508 merge_proposal.nominateReviewer(
509- reviewer=reviewer, registrant=merge_proposal.source_branch.owner,
510+ reviewer=reviewer, registrant=merge_proposal.merge_source.owner,
511 review_type='Specific')
512 # Note we're using the reference from the first call
513 self.assertEqual('specific', reference.review_type)
514@@ -1896,31 +1910,37 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
515 CodeReviewNotificationLevel.FULL, sub.review_level)
516 # The reviewer can see the branch.
517 self.assertTrue(branch.visibleByUser(reviewer))
518- if branch.stacked_on is not None:
519+ if IBranch.providedBy(branch) and branch.stacked_on is not None:
520 self._check_mp_branch_visibility(branch.stacked_on, reviewer)
521
522 def _test_nominate_grants_visibility(self, reviewer):
523 """Nominated reviewers can see the source and target branches."""
524 owner = self.factory.makePerson()
525 product = self.factory.makeProduct()
526- # We make a source branch stacked on a private one.
527- base_branch = self.factory.makeBranch(
528+ # For bzr, we make a source branch stacked on a private one.
529+ # For git, we make the gitref itself private.
530+ if self.git:
531+ source_branch = self.makeBranch(
532+ product=product, owner=owner,
533+ information_type=InformationType.USERDATA)
534+ else:
535+ base_branch = self.makeBranch(
536+ owner=owner, product=product,
537+ information_type=InformationType.USERDATA)
538+ source_branch = self.makeBranch(
539+ stacked_on=base_branch, product=product, owner=owner)
540+ target_branch = self.makeBranch(
541 owner=owner, product=product,
542 information_type=InformationType.USERDATA)
543- source_branch = self.factory.makeBranch(
544- stacked_on=base_branch, product=product, owner=owner)
545- target_branch = self.factory.makeBranch(owner=owner, product=product)
546 login_person(owner)
547- merge_proposal = self.factory.makeBranchMergeProposal(
548- source_branch=source_branch,
549- target_branch=target_branch)
550- target_branch.setPrivate(True, owner)
551+ merge_proposal = self.makeBranchMergeProposal(
552+ source=source_branch, target=target_branch)
553 # The reviewer can't see the source or target branches.
554 self.assertFalse(source_branch.visibleByUser(reviewer))
555 self.assertFalse(target_branch.visibleByUser(reviewer))
556 merge_proposal.nominateReviewer(
557 reviewer=reviewer,
558- registrant=merge_proposal.source_branch.owner)
559+ registrant=merge_proposal.merge_source.owner)
560 for branch in [source_branch, target_branch]:
561 self._check_mp_branch_visibility(branch, reviewer)
562
563@@ -1944,7 +1964,7 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
564 def test_comment_with_vote_creates_reference(self):
565 """A comment with a vote creates a vote reference."""
566 reviewer = self.factory.makePerson()
567- merge_proposal = self.factory.makeBranchMergeProposal(
568+ merge_proposal = self.makeBranchMergeProposal(
569 reviewer=reviewer, registrant=reviewer)
570 comment = merge_proposal.createComment(
571 reviewer, 'Message subject', 'Message content',
572@@ -1955,7 +1975,8 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
573 def test_comment_without_a_vote_does_not_create_reference(self):
574 """A comment with a vote creates a vote reference."""
575 reviewer = self.factory.makePerson()
576- merge_proposal = make_merge_proposal_without_reviewers(self.factory)
577+ merge_proposal = make_merge_proposal_without_reviewers(
578+ self.factory, for_git=self.git)
579 merge_proposal.createComment(
580 reviewer, 'Message subject', 'Message content')
581 self.assertEqual([], list(merge_proposal.votes))
582@@ -1963,7 +1984,7 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
583 def test_second_vote_by_person_just_alters_reference(self):
584 """A second vote changes the comment reference only."""
585 reviewer = self.factory.makePerson()
586- merge_proposal = self.factory.makeBranchMergeProposal(
587+ merge_proposal = self.makeBranchMergeProposal(
588 reviewer=reviewer, registrant=reviewer)
589 merge_proposal.createComment(
590 reviewer, 'Message subject', 'Message content',
591@@ -1979,7 +2000,7 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
592 reviewer = self.factory.makePerson()
593 merge_proposal, ignore = self.makeProposalWithReviewer(
594 reviewer=reviewer, review_type='general')
595- login(merge_proposal.source_branch.owner.preferredemail.email)
596+ login(merge_proposal.merge_source.owner.preferredemail.email)
597 comment = merge_proposal.createComment(
598 reviewer, 'Message subject', 'Message content',
599 vote=CodeReviewVote.APPROVE, review_type='general')
600@@ -1999,7 +2020,7 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
601 team = self.factory.makeTeam(owner=reviewer)
602 merge_proposal, ignore = self.makeProposalWithReviewer(
603 reviewer=team, review_type='general')
604- login(merge_proposal.source_branch.owner.preferredemail.email)
605+ login(merge_proposal.merge_source.owner.preferredemail.email)
606 [vote] = list(merge_proposal.votes)
607 self.assertEqual(team, vote.reviewer)
608 comment = merge_proposal.createComment(
609@@ -2016,8 +2037,8 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
610 # one.
611 reviewer = self.factory.makePerson()
612 team = self.factory.makeTeam(owner=reviewer)
613- merge_proposal = self.factory.makeBranchMergeProposal(reviewer=team)
614- login(merge_proposal.source_branch.owner.preferredemail.email)
615+ merge_proposal = self.makeBranchMergeProposal(reviewer=team)
616+ login(merge_proposal.merge_source.owner.preferredemail.email)
617 [vote] = list(merge_proposal.votes)
618 self.assertEqual(team, vote.reviewer)
619 comment = merge_proposal.createComment(
620@@ -2035,8 +2056,8 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
621 merge_proposal_1, reviewer_1 = self.makeProposalWithReviewer(
622 set_state=BranchMergeProposalStatus.MERGED)
623 merge_proposal_2, _ = self.makeProposalWithReviewer(
624- target_branch=merge_proposal_1.target_branch,
625- source_branch=merge_proposal_1.source_branch)
626+ target=merge_proposal_1.merge_target,
627+ source=merge_proposal_1.merge_source)
628 merge_proposal_2.nominateReviewer(
629 reviewer=reviewer_1, registrant=merge_proposal_2.registrant)
630 votes_1 = list(merge_proposal_1.votes)
631diff --git a/lib/lp/code/tests/helpers.py b/lib/lp/code/tests/helpers.py
632index 46f12ed..0e784f0 100644
633--- a/lib/lp/code/tests/helpers.py
634+++ b/lib/lp/code/tests/helpers.py
635@@ -1,4 +1,4 @@
636-# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
637+# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
638 # GNU Affero General Public License version 3 (see the file LICENSE).
639
640 """Helper functions for code testing live here."""
641@@ -49,8 +49,8 @@ from lp.code.model.seriessourcepackagebranch import (
642 from lp.registry.interfaces.pocket import PackagePublishingPocket
643 from lp.registry.interfaces.series import SeriesStatus
644 from lp.services.database.sqlbase import cursor
645-from lp.services.propertycache import get_property_cache
646 from lp.services.memcache.testing import MemcacheFixture
647+from lp.services.propertycache import get_property_cache
648 from lp.testing import (
649 run_with_login,
650 time_counter,
651@@ -271,9 +271,18 @@ def recipe_parser_newest_version(version):
652 RecipeParser.NEWEST_VERSION = old_version
653
654
655-def make_merge_proposal_without_reviewers(factory, **kwargs):
656+def make_merge_proposal_without_reviewers(
657+ factory, for_git=False, source=None, target=None, **kwargs):
658 """Make a merge proposal and strip of any review votes."""
659- proposal = factory.makeBranchMergeProposal(**kwargs)
660+ kwargs = dict(kwargs)
661+ if for_git:
662+ kwargs["source_ref"] = source
663+ kwargs["target_ref"] = target
664+ proposal = factory.makeBranchMergeProposalForGit(**kwargs)
665+ else:
666+ kwargs["source_branch"] = source
667+ kwargs["target_branch"] = target
668+ proposal = factory.makeBranchMergeProposal(**kwargs)
669 for vote in proposal.votes:
670 removeSecurityProxy(vote).destroySelf()
671 del get_property_cache(proposal).votes