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
diff --git a/lib/lp/code/model/branchmergeproposal.py b/lib/lp/code/model/branchmergeproposal.py
index 5ebcf04..338cc29 100644
--- a/lib/lp/code/model/branchmergeproposal.py
+++ b/lib/lp/code/model/branchmergeproposal.py
@@ -1,4 +1,4 @@
1# Copyright 2009-2018 Canonical Ltd. This software is licensed under the1# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Database class for branch merge proposals."""4"""Database class for branch merge proposals."""
@@ -70,6 +70,7 @@ from lp.code.event.branchmergeproposal import (
70 BranchMergeProposalNeedsReviewEvent,70 BranchMergeProposalNeedsReviewEvent,
71 ReviewerNominatedEvent,71 ReviewerNominatedEvent,
72 )72 )
73from lp.code.interfaces.branch import IBranch
73from lp.code.interfaces.branchcollection import IAllBranches74from lp.code.interfaces.branchcollection import IAllBranches
74from lp.code.interfaces.branchmergeproposal import (75from lp.code.interfaces.branchmergeproposal import (
75 BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,76 BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,
@@ -881,7 +882,7 @@ class BranchMergeProposal(SQLBase, BugLinkTargetMixin):
881 BranchSubscriptionDiffSize.NODIFF,882 BranchSubscriptionDiffSize.NODIFF,
882 CodeReviewNotificationLevel.FULL,883 CodeReviewNotificationLevel.FULL,
883 user)884 user)
884 if branch.stacked_on is not None:885 if IBranch.providedBy(branch) and branch.stacked_on is not None:
885 checked_branches.append(branch)886 checked_branches.append(branch)
886 if branch.stacked_on not in checked_branches:887 if branch.stacked_on not in checked_branches:
887 self._subscribeUserToStackedBranch(888 self._subscribeUserToStackedBranch(
@@ -903,14 +904,11 @@ class BranchMergeProposal(SQLBase, BugLinkTargetMixin):
903 the reviewer if the branch is private and the reviewer is an open904 the reviewer if the branch is private and the reviewer is an open
904 team.905 team.
905 """906 """
906 if self.source_branch is None:907 source = self.merge_source
907 # This only applies to Bazaar, which has stacked branches.
908 return
909 source = self.source_branch
910 if (not source.visibleByUser(reviewer) and908 if (not source.visibleByUser(reviewer) and
911 self._acceptable_to_give_visibility(source, reviewer)):909 self._acceptable_to_give_visibility(source, reviewer)):
912 self._subscribeUserToStackedBranch(source, reviewer)910 self._subscribeUserToStackedBranch(source, reviewer)
913 target = self.target_branch911 target = self.merge_target
914 if (not target.visibleByUser(reviewer) and912 if (not target.visibleByUser(reviewer) and
915 self._acceptable_to_give_visibility(source, reviewer)):913 self._acceptable_to_give_visibility(source, reviewer)):
916 self._subscribeUserToStackedBranch(target, reviewer)914 self._subscribeUserToStackedBranch(target, reviewer)
diff --git a/lib/lp/code/model/tests/test_branchmergeproposal.py b/lib/lp/code/model/tests/test_branchmergeproposal.py
index 8936574..0d2457d 100644
--- a/lib/lp/code/model/tests/test_branchmergeproposal.py
+++ b/lib/lp/code/model/tests/test_branchmergeproposal.py
@@ -1,4 +1,4 @@
1# Copyright 2009-2019 Canonical Ltd. This software is licensed under the1# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for BranchMergeProposals."""4"""Tests for BranchMergeProposals."""
@@ -57,6 +57,7 @@ from lp.code.event.branchmergeproposal import (
57 BranchMergeProposalNeedsReviewEvent,57 BranchMergeProposalNeedsReviewEvent,
58 ReviewerNominatedEvent,58 ReviewerNominatedEvent,
59 )59 )
60from lp.code.interfaces.branch import IBranch
60from lp.code.interfaces.branchmergeproposal import (61from lp.code.interfaces.branchmergeproposal import (
61 BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,62 BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,
62 BRANCH_MERGE_PROPOSAL_OBSOLETE_STATES as OBSOLETE_STATES,63 BRANCH_MERGE_PROPOSAL_OBSOLETE_STATES as OBSOLETE_STATES,
@@ -91,6 +92,7 @@ from lp.services.webapp import canonical_url
91from lp.services.webhooks.testing import LogsScheduledWebhooks92from lp.services.webhooks.testing import LogsScheduledWebhooks
92from lp.services.xref.interfaces import IXRefSet93from lp.services.xref.interfaces import IXRefSet
93from lp.testing import (94from lp.testing import (
95 admin_logged_in,
94 ExpectedException,96 ExpectedException,
95 launchpadlib_for,97 launchpadlib_for,
96 login,98 login,
@@ -109,6 +111,49 @@ from lp.testing.layers import (
109 )111 )
110112
111113
114class WithVCSScenarios(WithScenarios):
115
116 scenarios = [
117 ("bzr", {"git": False}),
118 ("git", {"git": True}),
119 ]
120
121 def makeBranch(self, same_target_as=None, product=None, stacked_on=None,
122 information_type=None, owner=None):
123 # Create the product pillar and its access policy if information
124 # type is "PROPRIETARY".
125 if product is None and same_target_as is None:
126 product = self.factory.makeProduct()
127 if information_type == InformationType.PROPRIETARY:
128 self.factory.makeAccessPolicy(product)
129 elif product is None:
130 same_target_as = removeSecurityProxy(same_target_as)
131 product = (same_target_as.target if self.git else
132 same_target_as.product)
133
134 kwargs = {
135 "information_type": information_type,
136 "owner": owner}
137 if self.git:
138 kwargs["target"] = product
139 return self.factory.makeGitRefs(**kwargs)[0]
140 else:
141 kwargs["product"] = product
142 kwargs["stacked_on"] = stacked_on
143 return self.factory.makeProductBranch(**kwargs)
144
145 def makeBranchMergeProposal(self, source=None, target=None,
146 prerequisite=None, **kwargs):
147 if self.git:
148 return self.factory.makeBranchMergeProposalForGit(
149 target_ref=target, source_ref=source,
150 prerequisite_ref=prerequisite, **kwargs)
151 else:
152 return self.factory.makeBranchMergeProposal(
153 source_branch=source, target_branch=target,
154 prerequisite_branch=prerequisite, **kwargs)
155
156
112class TestBranchMergeProposalInterface(TestCaseWithFactory):157class TestBranchMergeProposalInterface(TestCaseWithFactory):
113 """Ensure that BranchMergeProposal implements its interface."""158 """Ensure that BranchMergeProposal implements its interface."""
114159
@@ -120,26 +165,16 @@ class TestBranchMergeProposalInterface(TestCaseWithFactory):
120 verifyObject(IBranchMergeProposal, bmp)165 verifyObject(IBranchMergeProposal, bmp)
121166
122167
123class TestBranchMergeProposalCanonicalUrl(WithScenarios, TestCaseWithFactory):168class TestBranchMergeProposalCanonicalUrl(
169 WithVCSScenarios, TestCaseWithFactory):
124 """Tests canonical_url for merge proposals."""170 """Tests canonical_url for merge proposals."""
125171
126 layer = DatabaseFunctionalLayer172 layer = DatabaseFunctionalLayer
127173
128 scenarios = [
129 ("bzr", {"git": False}),
130 ("git", {"git": True}),
131 ]
132
133 def _makeBranchMergeProposal(self):
134 if self.git:
135 return self.factory.makeBranchMergeProposalForGit()
136 else:
137 return self.factory.makeBranchMergeProposal()
138
139 def test_BranchMergeProposal_canonical_url_base(self):174 def test_BranchMergeProposal_canonical_url_base(self):
140 # The URL for a merge proposal starts with the parent (source branch175 # The URL for a merge proposal starts with the parent (source branch
141 # or source Git repository).176 # or source Git repository).
142 bmp = self._makeBranchMergeProposal()177 bmp = self.makeBranchMergeProposal()
143 url = canonical_url(bmp)178 url = canonical_url(bmp)
144 parent_url = canonical_url(bmp.parent)179 parent_url = canonical_url(bmp.parent)
145 self.assertTrue(url.startswith(parent_url))180 self.assertTrue(url.startswith(parent_url))
@@ -147,7 +182,7 @@ class TestBranchMergeProposalCanonicalUrl(WithScenarios, TestCaseWithFactory):
147 def test_BranchMergeProposal_canonical_url_rest(self):182 def test_BranchMergeProposal_canonical_url_rest(self):
148 # The rest of the URL for a merge proposal is +merge followed by the183 # The rest of the URL for a merge proposal is +merge followed by the
149 # db id.184 # db id.
150 bmp = self._makeBranchMergeProposal()185 bmp = self.makeBranchMergeProposal()
151 url = canonical_url(bmp)186 url = canonical_url(bmp)
152 parent_url = canonical_url(bmp.parent)187 parent_url = canonical_url(bmp.parent)
153 rest = url[len(parent_url):]188 rest = url[len(parent_url):]
@@ -718,45 +753,14 @@ class TestMergeProposalGetPreviewDiff(TestCaseWithFactory):
718 self.mp_two.getPreviewDiff, self.preview_diff.id)753 self.mp_two.getPreviewDiff, self.preview_diff.id)
719754
720755
721class TestMergeProposalNotification(WithScenarios, TestCaseWithFactory):756class TestMergeProposalNotification(WithVCSScenarios, TestCaseWithFactory):
722 """Test that events are created when merge proposals are manipulated"""757 """Test that events are created when merge proposals are manipulated"""
723758
724 layer = DatabaseFunctionalLayer759 layer = DatabaseFunctionalLayer
725760
726 scenarios = [
727 ("bzr", {"git": False}),
728 ("git", {"git": True}),
729 ]
730
731 def setUp(self):761 def setUp(self):
732 TestCaseWithFactory.setUp(self, user='test@canonical.com')762 TestCaseWithFactory.setUp(self, user='test@canonical.com')
733763
734 def makeBranch(self, same_target_as=None, target=None, **kwargs):
735 kwargs = dict(kwargs)
736 if self.git:
737 if same_target_as is not None:
738 kwargs["target"] = same_target_as.target
739 elif target is not None:
740 kwargs["target"] = target
741 return self.factory.makeGitRefs(**kwargs)[0]
742 else:
743 if same_target_as is not None:
744 kwargs["product"] = same_target_as.product
745 elif target is not None:
746 kwargs["product"] = target
747 return self.factory.makeProductBranch(**kwargs)
748
749 def makeBranchMergeProposal(self, source=None, target=None,
750 prerequisite=None, **kwargs):
751 if self.git:
752 return self.factory.makeBranchMergeProposalForGit(
753 source_ref=source, target_ref=target,
754 prerequisite_ref=prerequisite, **kwargs)
755 else:
756 return self.factory.makeBranchMergeProposal(
757 source_branch=source, target_branch=target,
758 prerequisite_branch=prerequisite, **kwargs)
759
760 def test_notifyOnCreate_needs_review(self):764 def test_notifyOnCreate_needs_review(self):
761 # When a merge proposal is created needing review, the765 # When a merge proposal is created needing review, the
762 # BranchMergeProposalNeedsReviewEvent is raised as well as the usual766 # BranchMergeProposalNeedsReviewEvent is raised as well as the usual
@@ -988,8 +992,8 @@ class TestMergeProposalNotification(WithScenarios, TestCaseWithFactory):
988 # they do not get email about the proposal.992 # they do not get email about the proposal.
989 owner = self.factory.makePerson()993 owner = self.factory.makePerson()
990 product = self.factory.makeProduct()994 product = self.factory.makeProduct()
991 source = self.makeBranch(owner=owner, target=product)995 source = self.makeBranch(owner=owner, product=product)
992 target = self.makeBranch(owner=owner, target=product)996 target = self.makeBranch(owner=owner, product=product)
993 bmp = self.makeBranchMergeProposal(source=source, target=target)997 bmp = self.makeBranchMergeProposal(source=source, target=target)
994 # Subscribe eric to the source branch only.998 # Subscribe eric to the source branch only.
995 eric = self.factory.makePerson()999 eric = self.factory.makePerson()
@@ -1021,31 +1025,15 @@ class TestMergeProposalNotification(WithScenarios, TestCaseWithFactory):
1021 self.assertIn(charlie, recipients)1025 self.assertIn(charlie, recipients)
10221026
10231027
1024class TestMergeProposalWebhooks(WithScenarios, TestCaseWithFactory):1028class TestMergeProposalWebhooks(WithVCSScenarios, TestCaseWithFactory):
10251029
1026 layer = DatabaseFunctionalLayer1030 layer = DatabaseFunctionalLayer
10271031
1028 scenarios = [
1029 ("bzr", {"git": False}),
1030 ("git", {"git": True}),
1031 ]
1032
1033 def setUp(self):1032 def setUp(self):
1034 super(TestMergeProposalWebhooks, self).setUp()1033 super(TestMergeProposalWebhooks, self).setUp()
1035 self.useFixture(FeatureFixture(1034 self.useFixture(FeatureFixture(
1036 {BRANCH_MERGE_PROPOSAL_WEBHOOKS_FEATURE_FLAG: "on"}))1035 {BRANCH_MERGE_PROPOSAL_WEBHOOKS_FEATURE_FLAG: "on"}))
10371036
1038 def makeBranch(self, same_target_as=None):
1039 kwargs = {}
1040 if self.git:
1041 if same_target_as is not None:
1042 kwargs["target"] = same_target_as.target
1043 return self.factory.makeGitRefs(**kwargs)[0]
1044 else:
1045 if same_target_as is not None:
1046 kwargs["product"] = same_target_as.product
1047 return self.factory.makeProductBranch(**kwargs)
1048
1049 def getWebhookTarget(self, branch):1037 def getWebhookTarget(self, branch):
1050 if self.git:1038 if self.git:
1051 return branch.repository1039 return branch.repository
@@ -1102,6 +1090,39 @@ class TestMergeProposalWebhooks(WithScenarios, TestCaseWithFactory):
1102 (hook, "merge-proposal:0.1",1090 (hook, "merge-proposal:0.1",
1103 MatchesDict(expected_redacted_payload))]))1091 MatchesDict(expected_redacted_payload))]))
11041092
1093 def test_create_private_repo_triggers_webhooks(self):
1094 # When a merge proposal is created, any relevant webhooks are
1095 # triggered even if the repository is proprietary.
1096 logger = self.useFixture(FakeLogger())
1097 source = self.makeBranch(information_type=InformationType.PROPRIETARY)
1098 target = self.makeBranch(
1099 same_target_as=source,
1100 information_type=InformationType.PROPRIETARY)
1101
1102 with admin_logged_in():
1103 # Create the web hook and the proposal.
1104 registrant = self.factory.makePerson()
1105 hook = self.factory.makeWebhook(
1106 target=self.getWebhookTarget(target),
1107 event_types=["merge-proposal:0.1"])
1108 proposal = source.addLandingTarget(
1109 registrant, target, needs_review=True)
1110 target_owner = target.owner
1111
1112 login_person(target_owner)
1113 delivery = hook.deliveries.one()
1114 expected_payload = {
1115 "merge_proposal": Equals(self.getURL(proposal)),
1116 "action": Equals("created"),
1117 "new": MatchesDict(self.getExpectedPayload(proposal)),
1118 }
1119 expected_redacted_payload = dict(
1120 expected_payload,
1121 new=MatchesDict(
1122 self.getExpectedPayload(proposal, redact=True)))
1123 self.assertCorrectDelivery(expected_payload, hook, delivery)
1124 self.assertCorrectLogging(expected_redacted_payload, hook, logger)
1125
1105 def test_create_triggers_webhooks(self):1126 def test_create_triggers_webhooks(self):
1106 # When a merge proposal is created, any relevant webhooks are1127 # When a merge proposal is created, any relevant webhooks are
1107 # triggered.1128 # triggered.
@@ -1486,15 +1507,10 @@ class TestBranchMergeProposalDeletion(TestCaseWithFactory):
1486 SQLObjectNotFound, BranchMergeProposalJob.get, job_id)1507 SQLObjectNotFound, BranchMergeProposalJob.get, job_id)
14871508
14881509
1489class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):1510class TestBranchMergeProposalBugs(WithVCSScenarios, TestCaseWithFactory):
14901511
1491 layer = DatabaseFunctionalLayer1512 layer = DatabaseFunctionalLayer
14921513
1493 scenarios = [
1494 ("bzr", {"git": False}),
1495 ("git", {"git": True}),
1496 ]
1497
1498 def setUp(self):1514 def setUp(self):
1499 super(TestBranchMergeProposalBugs, self).setUp()1515 super(TestBranchMergeProposalBugs, self).setUp()
1500 self.user = self.factory.makePerson()1516 self.user = self.factory.makePerson()
@@ -1502,15 +1518,9 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1502 if self.git:1518 if self.git:
1503 self.hosting_fixture = self.useFixture(GitHostingFixture())1519 self.hosting_fixture = self.useFixture(GitHostingFixture())
15041520
1505 def _makeBranchMergeProposal(self):
1506 if self.git:
1507 return self.factory.makeBranchMergeProposalForGit()
1508 else:
1509 return self.factory.makeBranchMergeProposal()
1510
1511 def test_bugs(self):1521 def test_bugs(self):
1512 # bugs includes all linked bugs.1522 # bugs includes all linked bugs.
1513 bmp = self._makeBranchMergeProposal()1523 bmp = self.makeBranchMergeProposal()
1514 self.assertEqual([], bmp.bugs)1524 self.assertEqual([], bmp.bugs)
1515 bugs = [self.factory.makeBug() for _ in range(2)]1525 bugs = [self.factory.makeBug() for _ in range(2)]
1516 bmp.linkBug(bugs[0], bmp.registrant)1526 bmp.linkBug(bugs[0], bmp.registrant)
@@ -1525,7 +1535,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1525 def test_related_bugtasks_includes_source_bugtasks(self):1535 def test_related_bugtasks_includes_source_bugtasks(self):
1526 # related_bugtasks includes bugtasks linked to the source branch in1536 # related_bugtasks includes bugtasks linked to the source branch in
1527 # the Bazaar case, or directly to the MP in the Git case.1537 # the Bazaar case, or directly to the MP in the Git case.
1528 bmp = self._makeBranchMergeProposal()1538 bmp = self.makeBranchMergeProposal()
1529 bug = self.factory.makeBug()1539 bug = self.factory.makeBug()
1530 bmp.linkBug(bug, bmp.registrant)1540 bmp.linkBug(bug, bmp.registrant)
1531 self.assertEqual(1541 self.assertEqual(
@@ -1533,7 +1543,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
15331543
1534 def test_related_bugtasks_excludes_private_bugs(self):1544 def test_related_bugtasks_excludes_private_bugs(self):
1535 # related_bugtasks ignores private bugs for non-authorised users.1545 # related_bugtasks ignores private bugs for non-authorised users.
1536 bmp = self._makeBranchMergeProposal()1546 bmp = self.makeBranchMergeProposal()
1537 bug = self.factory.makeBug()1547 bug = self.factory.makeBug()
1538 bmp.linkBug(bug, bmp.registrant)1548 bmp.linkBug(bug, bmp.registrant)
1539 person = self.factory.makePerson()1549 person = self.factory.makePerson()
@@ -1553,7 +1563,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1553 # related_bugtasks ignores bugs linked to the target branch.1563 # related_bugtasks ignores bugs linked to the target branch.
1554 if self.git:1564 if self.git:
1555 self.skipTest("Only relevant for Bazaar.")1565 self.skipTest("Only relevant for Bazaar.")
1556 bmp = self._makeBranchMergeProposal()1566 bmp = self.makeBranchMergeProposal()
1557 bug = self.factory.makeBug()1567 bug = self.factory.makeBug()
1558 bmp.target_branch.linkBug(bug, bmp.registrant)1568 bmp.target_branch.linkBug(bug, bmp.registrant)
1559 self.assertEqual([], list(bmp.getRelatedBugTasks(self.user)))1569 self.assertEqual([], list(bmp.getRelatedBugTasks(self.user)))
@@ -1562,7 +1572,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1562 # related_bugtasks ignores bugs linked to both branches.1572 # related_bugtasks ignores bugs linked to both branches.
1563 if self.git:1573 if self.git:
1564 self.skipTest("Only relevant for Bazaar.")1574 self.skipTest("Only relevant for Bazaar.")
1565 bmp = self._makeBranchMergeProposal()1575 bmp = self.makeBranchMergeProposal()
1566 bug = self.factory.makeBug()1576 bug = self.factory.makeBug()
1567 bmp.source_branch.linkBug(bug, bmp.registrant)1577 bmp.source_branch.linkBug(bug, bmp.registrant)
1568 bmp.target_branch.linkBug(bug, bmp.registrant)1578 bmp.target_branch.linkBug(bug, bmp.registrant)
@@ -1574,7 +1584,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1574 if not self.git:1584 if not self.git:
1575 self.skipTest("Only relevant for Git.")1585 self.skipTest("Only relevant for Git.")
1576 bugs = [self.factory.makeBug() for _ in range(3)]1586 bugs = [self.factory.makeBug() for _ in range(3)]
1577 bmp = self._makeBranchMergeProposal()1587 bmp = self.makeBranchMergeProposal()
1578 self.hosting_fixture.getLog.result = [1588 self.hosting_fixture.getLog.result = [
1579 {1589 {
1580 "sha1": bmp.source_git_commit_sha1,1590 "sha1": bmp.source_git_commit_sha1,
@@ -1621,7 +1631,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1621 # bugs in either the database or the source branch.1631 # bugs in either the database or the source branch.
1622 if not self.git:1632 if not self.git:
1623 self.skipTest("Only relevant for Git.")1633 self.skipTest("Only relevant for Git.")
1624 bmp = self._makeBranchMergeProposal()1634 bmp = self.makeBranchMergeProposal()
1625 self._setUpLog([])1635 self._setUpLog([])
1626 bmp.updateRelatedBugsFromSource()1636 bmp.updateRelatedBugsFromSource()
1627 self.assertEqual([], bmp.bugs)1637 self.assertEqual([], bmp.bugs)
@@ -1632,7 +1642,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1632 if not self.git:1642 if not self.git:
1633 self.skipTest("Only relevant for Git.")1643 self.skipTest("Only relevant for Git.")
1634 bugs = [self.factory.makeBug() for _ in range(3)]1644 bugs = [self.factory.makeBug() for _ in range(3)]
1635 bmp = self._makeBranchMergeProposal()1645 bmp = self.makeBranchMergeProposal()
1636 self._setUpLog([bugs[0]])1646 self._setUpLog([bugs[0]])
1637 bmp.updateRelatedBugsFromSource()1647 bmp.updateRelatedBugsFromSource()
1638 self.assertEqual([bugs[0]], bmp.bugs)1648 self.assertEqual([bugs[0]], bmp.bugs)
@@ -1646,7 +1656,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1646 if not self.git:1656 if not self.git:
1647 self.skipTest("Only relevant for Git.")1657 self.skipTest("Only relevant for Git.")
1648 bug = self.factory.makeBug()1658 bug = self.factory.makeBug()
1649 bmp = self._makeBranchMergeProposal()1659 bmp = self.makeBranchMergeProposal()
1650 self._setUpLog([bug])1660 self._setUpLog([bug])
1651 bmp.updateRelatedBugsFromSource()1661 bmp.updateRelatedBugsFromSource()
1652 self.assertEqual([bug], bmp.bugs)1662 self.assertEqual([bug], bmp.bugs)
@@ -1661,7 +1671,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1661 if not self.git:1671 if not self.git:
1662 self.skipTest("Only relevant for Git.")1672 self.skipTest("Only relevant for Git.")
1663 bug = self.factory.makeBug()1673 bug = self.factory.makeBug()
1664 bmp = self._makeBranchMergeProposal()1674 bmp = self.makeBranchMergeProposal()
1665 self._setUpLog([bug])1675 self._setUpLog([bug])
1666 bmp.updateRelatedBugsFromSource()1676 bmp.updateRelatedBugsFromSource()
1667 self.assertEqual([bug], bmp.bugs)1677 self.assertEqual([bug], bmp.bugs)
@@ -1676,7 +1686,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1676 if not self.git:1686 if not self.git:
1677 self.skipTest("Only relevant for Git.")1687 self.skipTest("Only relevant for Git.")
1678 bug = self.factory.makeBug()1688 bug = self.factory.makeBug()
1679 bmp = self._makeBranchMergeProposal()1689 bmp = self.makeBranchMergeProposal()
1680 bmp.linkBug(bug)1690 bmp.linkBug(bug)
1681 self._setUpLog([])1691 self._setUpLog([])
1682 bmp.updateRelatedBugsFromSource()1692 bmp.updateRelatedBugsFromSource()
@@ -1701,7 +1711,7 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1701 self.skipTest("Only relevant for Git.")1711 self.skipTest("Only relevant for Git.")
1702 self.pushConfig("codehosting", related_bugs_from_source_limit=3)1712 self.pushConfig("codehosting", related_bugs_from_source_limit=3)
1703 bugs = [self.factory.makeBug() for _ in range(5)]1713 bugs = [self.factory.makeBug() for _ in range(5)]
1704 bmp = self._makeBranchMergeProposal()1714 bmp = self.makeBranchMergeProposal()
1705 self._setUpLog([bugs[0]])1715 self._setUpLog([bugs[0]])
1706 bmp.updateRelatedBugsFromSource()1716 bmp.updateRelatedBugsFromSource()
1707 self.assertEqual([bugs[0]], bmp.bugs)1717 self.assertEqual([bugs[0]], bmp.bugs)
@@ -1713,45 +1723,48 @@ class TestBranchMergeProposalBugs(WithScenarios, TestCaseWithFactory):
1713 self.assertEqual("TooManyRelatedBugs", self.oopses[0]["type"])1723 self.assertEqual("TooManyRelatedBugs", self.oopses[0]["type"])
17141724
17151725
1716class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):1726class TestBranchMergeProposalNominateReviewer(
1727 WithVCSScenarios, TestCaseWithFactory):
1717 """Test that the appropriate vote references get created."""1728 """Test that the appropriate vote references get created."""
17181729
1719 layer = DatabaseFunctionalLayer1730 layer = DatabaseFunctionalLayer
17201731
1721 def setUp(self):1732 def setUp(self):
1722 TestCaseWithFactory.setUp(self, user='test@canonical.com')1733 TestCaseWithFactory.setUp(self, user='test@canonical.com')
1734 if self.git:
1735 self.hosting_fixture = self.useFixture(GitHostingFixture())
17231736
1724 def test_notify_on_nominate(self):1737 def test_notify_on_nominate(self):
1725 # Ensure that a notification is emitted on nomination.1738 # Ensure that a notification is emitted on nomination.
1726 merge_proposal = self.factory.makeBranchMergeProposal()1739 merge_proposal = self.makeBranchMergeProposal()
1727 login_person(merge_proposal.source_branch.owner)1740 login_person(merge_proposal.merge_source.owner)
1728 reviewer = self.factory.makePerson()1741 reviewer = self.factory.makePerson()
1729 result, events = self.assertNotifies(1742 result, events = self.assertNotifies(
1730 ReviewerNominatedEvent, False,1743 ReviewerNominatedEvent, False,
1731 merge_proposal.nominateReviewer,1744 merge_proposal.nominateReviewer,
1732 reviewer=reviewer,1745 reviewer=reviewer,
1733 registrant=merge_proposal.source_branch.owner)1746 registrant=merge_proposal.merge_source.owner)
1734 self.assertEqual(result, events[0].object)1747 self.assertEqual(result, events[0].object)
17351748
1736 def test_notify_on_nominate_suppressed_if_requested(self):1749 def test_notify_on_nominate_suppressed_if_requested(self):
1737 # Ensure that a notification is suppressed if notify listeners is set1750 # Ensure that a notification is suppressed if notify listeners is set
1738 # to False.1751 # to False.
1739 merge_proposal = self.factory.makeBranchMergeProposal()1752 merge_proposal = self.makeBranchMergeProposal()
1740 login_person(merge_proposal.source_branch.owner)1753 login_person(merge_proposal.merge_source.owner)
1741 reviewer = self.factory.makePerson()1754 reviewer = self.factory.makePerson()
1742 self.assertNoNotification(1755 self.assertNoNotification(
1743 merge_proposal.nominateReviewer,1756 merge_proposal.nominateReviewer,
1744 reviewer=reviewer,1757 reviewer=reviewer,
1745 registrant=merge_proposal.source_branch.owner,1758 registrant=merge_proposal.merge_source.owner,
1746 _notify_listeners=False)1759 _notify_listeners=False)
17471760
1748 def test_one_initial_votes(self):1761 def test_one_initial_votes(self):
1749 """A new merge proposal has one vote of the default reviewer."""1762 """A new merge proposal has one vote of the default reviewer."""
1750 merge_proposal = self.factory.makeBranchMergeProposal()1763 merge_proposal = self.makeBranchMergeProposal()
1751 self.assertEqual(1, len(list(merge_proposal.votes)))1764 self.assertEqual(1, len(list(merge_proposal.votes)))
1752 [vote] = list(merge_proposal.votes)1765 [vote] = list(merge_proposal.votes)
1753 self.assertEqual(1766 self.assertEqual(
1754 merge_proposal.target_branch.owner, vote.reviewer)1767 merge_proposal.merge_target.owner, vote.reviewer)
17551768
1756 def makeProposalWithReviewer(self, reviewer=None, review_type=None,1769 def makeProposalWithReviewer(self, reviewer=None, review_type=None,
1757 registrant=None, **kwargs):1770 registrant=None, **kwargs):
@@ -1764,8 +1777,9 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
1764 if registrant is None:1777 if registrant is None:
1765 registrant = self.factory.makePerson()1778 registrant = self.factory.makePerson()
1766 merge_proposal = make_merge_proposal_without_reviewers(1779 merge_proposal = make_merge_proposal_without_reviewers(
1767 factory=self.factory, registrant=registrant, **kwargs)1780 factory=self.factory, for_git=self.git, registrant=registrant,
1768 login_person(merge_proposal.source_branch.owner)1781 **kwargs)
1782 login_person(merge_proposal.merge_source.owner)
1769 merge_proposal.nominateReviewer(1783 merge_proposal.nominateReviewer(
1770 reviewer=reviewer, registrant=registrant, review_type=review_type)1784 reviewer=reviewer, registrant=registrant, review_type=review_type)
1771 return merge_proposal, reviewer1785 return merge_proposal, reviewer
@@ -1871,15 +1885,15 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
18711885
1872 def test_nominate_updates_reference(self):1886 def test_nominate_updates_reference(self):
1873 """The existing reference is updated on re-nomination."""1887 """The existing reference is updated on re-nomination."""
1874 merge_proposal = self.factory.makeBranchMergeProposal()1888 merge_proposal = self.makeBranchMergeProposal()
1875 login_person(merge_proposal.source_branch.owner)1889 login_person(merge_proposal.merge_source.owner)
1876 reviewer = self.factory.makePerson()1890 reviewer = self.factory.makePerson()
1877 reference = merge_proposal.nominateReviewer(1891 reference = merge_proposal.nominateReviewer(
1878 reviewer=reviewer, registrant=merge_proposal.source_branch.owner,1892 reviewer=reviewer, registrant=merge_proposal.merge_source.owner,
1879 review_type='General')1893 review_type='General')
1880 self.assertEqual('general', reference.review_type)1894 self.assertEqual('general', reference.review_type)
1881 merge_proposal.nominateReviewer(1895 merge_proposal.nominateReviewer(
1882 reviewer=reviewer, registrant=merge_proposal.source_branch.owner,1896 reviewer=reviewer, registrant=merge_proposal.merge_source.owner,
1883 review_type='Specific')1897 review_type='Specific')
1884 # Note we're using the reference from the first call1898 # Note we're using the reference from the first call
1885 self.assertEqual('specific', reference.review_type)1899 self.assertEqual('specific', reference.review_type)
@@ -1896,31 +1910,37 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
1896 CodeReviewNotificationLevel.FULL, sub.review_level)1910 CodeReviewNotificationLevel.FULL, sub.review_level)
1897 # The reviewer can see the branch.1911 # The reviewer can see the branch.
1898 self.assertTrue(branch.visibleByUser(reviewer))1912 self.assertTrue(branch.visibleByUser(reviewer))
1899 if branch.stacked_on is not None:1913 if IBranch.providedBy(branch) and branch.stacked_on is not None:
1900 self._check_mp_branch_visibility(branch.stacked_on, reviewer)1914 self._check_mp_branch_visibility(branch.stacked_on, reviewer)
19011915
1902 def _test_nominate_grants_visibility(self, reviewer):1916 def _test_nominate_grants_visibility(self, reviewer):
1903 """Nominated reviewers can see the source and target branches."""1917 """Nominated reviewers can see the source and target branches."""
1904 owner = self.factory.makePerson()1918 owner = self.factory.makePerson()
1905 product = self.factory.makeProduct()1919 product = self.factory.makeProduct()
1906 # We make a source branch stacked on a private one.1920 # For bzr, we make a source branch stacked on a private one.
1907 base_branch = self.factory.makeBranch(1921 # For git, we make the gitref itself private.
1922 if self.git:
1923 source_branch = self.makeBranch(
1924 product=product, owner=owner,
1925 information_type=InformationType.USERDATA)
1926 else:
1927 base_branch = self.makeBranch(
1928 owner=owner, product=product,
1929 information_type=InformationType.USERDATA)
1930 source_branch = self.makeBranch(
1931 stacked_on=base_branch, product=product, owner=owner)
1932 target_branch = self.makeBranch(
1908 owner=owner, product=product,1933 owner=owner, product=product,
1909 information_type=InformationType.USERDATA)1934 information_type=InformationType.USERDATA)
1910 source_branch = self.factory.makeBranch(
1911 stacked_on=base_branch, product=product, owner=owner)
1912 target_branch = self.factory.makeBranch(owner=owner, product=product)
1913 login_person(owner)1935 login_person(owner)
1914 merge_proposal = self.factory.makeBranchMergeProposal(1936 merge_proposal = self.makeBranchMergeProposal(
1915 source_branch=source_branch,1937 source=source_branch, target=target_branch)
1916 target_branch=target_branch)
1917 target_branch.setPrivate(True, owner)
1918 # The reviewer can't see the source or target branches.1938 # The reviewer can't see the source or target branches.
1919 self.assertFalse(source_branch.visibleByUser(reviewer))1939 self.assertFalse(source_branch.visibleByUser(reviewer))
1920 self.assertFalse(target_branch.visibleByUser(reviewer))1940 self.assertFalse(target_branch.visibleByUser(reviewer))
1921 merge_proposal.nominateReviewer(1941 merge_proposal.nominateReviewer(
1922 reviewer=reviewer,1942 reviewer=reviewer,
1923 registrant=merge_proposal.source_branch.owner)1943 registrant=merge_proposal.merge_source.owner)
1924 for branch in [source_branch, target_branch]:1944 for branch in [source_branch, target_branch]:
1925 self._check_mp_branch_visibility(branch, reviewer)1945 self._check_mp_branch_visibility(branch, reviewer)
19261946
@@ -1944,7 +1964,7 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
1944 def test_comment_with_vote_creates_reference(self):1964 def test_comment_with_vote_creates_reference(self):
1945 """A comment with a vote creates a vote reference."""1965 """A comment with a vote creates a vote reference."""
1946 reviewer = self.factory.makePerson()1966 reviewer = self.factory.makePerson()
1947 merge_proposal = self.factory.makeBranchMergeProposal(1967 merge_proposal = self.makeBranchMergeProposal(
1948 reviewer=reviewer, registrant=reviewer)1968 reviewer=reviewer, registrant=reviewer)
1949 comment = merge_proposal.createComment(1969 comment = merge_proposal.createComment(
1950 reviewer, 'Message subject', 'Message content',1970 reviewer, 'Message subject', 'Message content',
@@ -1955,7 +1975,8 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
1955 def test_comment_without_a_vote_does_not_create_reference(self):1975 def test_comment_without_a_vote_does_not_create_reference(self):
1956 """A comment with a vote creates a vote reference."""1976 """A comment with a vote creates a vote reference."""
1957 reviewer = self.factory.makePerson()1977 reviewer = self.factory.makePerson()
1958 merge_proposal = make_merge_proposal_without_reviewers(self.factory)1978 merge_proposal = make_merge_proposal_without_reviewers(
1979 self.factory, for_git=self.git)
1959 merge_proposal.createComment(1980 merge_proposal.createComment(
1960 reviewer, 'Message subject', 'Message content')1981 reviewer, 'Message subject', 'Message content')
1961 self.assertEqual([], list(merge_proposal.votes))1982 self.assertEqual([], list(merge_proposal.votes))
@@ -1963,7 +1984,7 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
1963 def test_second_vote_by_person_just_alters_reference(self):1984 def test_second_vote_by_person_just_alters_reference(self):
1964 """A second vote changes the comment reference only."""1985 """A second vote changes the comment reference only."""
1965 reviewer = self.factory.makePerson()1986 reviewer = self.factory.makePerson()
1966 merge_proposal = self.factory.makeBranchMergeProposal(1987 merge_proposal = self.makeBranchMergeProposal(
1967 reviewer=reviewer, registrant=reviewer)1988 reviewer=reviewer, registrant=reviewer)
1968 merge_proposal.createComment(1989 merge_proposal.createComment(
1969 reviewer, 'Message subject', 'Message content',1990 reviewer, 'Message subject', 'Message content',
@@ -1979,7 +2000,7 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
1979 reviewer = self.factory.makePerson()2000 reviewer = self.factory.makePerson()
1980 merge_proposal, ignore = self.makeProposalWithReviewer(2001 merge_proposal, ignore = self.makeProposalWithReviewer(
1981 reviewer=reviewer, review_type='general')2002 reviewer=reviewer, review_type='general')
1982 login(merge_proposal.source_branch.owner.preferredemail.email)2003 login(merge_proposal.merge_source.owner.preferredemail.email)
1983 comment = merge_proposal.createComment(2004 comment = merge_proposal.createComment(
1984 reviewer, 'Message subject', 'Message content',2005 reviewer, 'Message subject', 'Message content',
1985 vote=CodeReviewVote.APPROVE, review_type='general')2006 vote=CodeReviewVote.APPROVE, review_type='general')
@@ -1999,7 +2020,7 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
1999 team = self.factory.makeTeam(owner=reviewer)2020 team = self.factory.makeTeam(owner=reviewer)
2000 merge_proposal, ignore = self.makeProposalWithReviewer(2021 merge_proposal, ignore = self.makeProposalWithReviewer(
2001 reviewer=team, review_type='general')2022 reviewer=team, review_type='general')
2002 login(merge_proposal.source_branch.owner.preferredemail.email)2023 login(merge_proposal.merge_source.owner.preferredemail.email)
2003 [vote] = list(merge_proposal.votes)2024 [vote] = list(merge_proposal.votes)
2004 self.assertEqual(team, vote.reviewer)2025 self.assertEqual(team, vote.reviewer)
2005 comment = merge_proposal.createComment(2026 comment = merge_proposal.createComment(
@@ -2016,8 +2037,8 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
2016 # one.2037 # one.
2017 reviewer = self.factory.makePerson()2038 reviewer = self.factory.makePerson()
2018 team = self.factory.makeTeam(owner=reviewer)2039 team = self.factory.makeTeam(owner=reviewer)
2019 merge_proposal = self.factory.makeBranchMergeProposal(reviewer=team)2040 merge_proposal = self.makeBranchMergeProposal(reviewer=team)
2020 login(merge_proposal.source_branch.owner.preferredemail.email)2041 login(merge_proposal.merge_source.owner.preferredemail.email)
2021 [vote] = list(merge_proposal.votes)2042 [vote] = list(merge_proposal.votes)
2022 self.assertEqual(team, vote.reviewer)2043 self.assertEqual(team, vote.reviewer)
2023 comment = merge_proposal.createComment(2044 comment = merge_proposal.createComment(
@@ -2035,8 +2056,8 @@ class TestBranchMergeProposalNominateReviewer(TestCaseWithFactory):
2035 merge_proposal_1, reviewer_1 = self.makeProposalWithReviewer(2056 merge_proposal_1, reviewer_1 = self.makeProposalWithReviewer(
2036 set_state=BranchMergeProposalStatus.MERGED)2057 set_state=BranchMergeProposalStatus.MERGED)
2037 merge_proposal_2, _ = self.makeProposalWithReviewer(2058 merge_proposal_2, _ = self.makeProposalWithReviewer(
2038 target_branch=merge_proposal_1.target_branch,2059 target=merge_proposal_1.merge_target,
2039 source_branch=merge_proposal_1.source_branch)2060 source=merge_proposal_1.merge_source)
2040 merge_proposal_2.nominateReviewer(2061 merge_proposal_2.nominateReviewer(
2041 reviewer=reviewer_1, registrant=merge_proposal_2.registrant)2062 reviewer=reviewer_1, registrant=merge_proposal_2.registrant)
2042 votes_1 = list(merge_proposal_1.votes)2063 votes_1 = list(merge_proposal_1.votes)
diff --git a/lib/lp/code/tests/helpers.py b/lib/lp/code/tests/helpers.py
index 46f12ed..0e784f0 100644
--- a/lib/lp/code/tests/helpers.py
+++ b/lib/lp/code/tests/helpers.py
@@ -1,4 +1,4 @@
1# Copyright 2009-2018 Canonical Ltd. This software is licensed under the1# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Helper functions for code testing live here."""4"""Helper functions for code testing live here."""
@@ -49,8 +49,8 @@ from lp.code.model.seriessourcepackagebranch import (
49from lp.registry.interfaces.pocket import PackagePublishingPocket49from lp.registry.interfaces.pocket import PackagePublishingPocket
50from lp.registry.interfaces.series import SeriesStatus50from lp.registry.interfaces.series import SeriesStatus
51from lp.services.database.sqlbase import cursor51from lp.services.database.sqlbase import cursor
52from lp.services.propertycache import get_property_cache
53from lp.services.memcache.testing import MemcacheFixture52from lp.services.memcache.testing import MemcacheFixture
53from lp.services.propertycache import get_property_cache
54from lp.testing import (54from lp.testing import (
55 run_with_login,55 run_with_login,
56 time_counter,56 time_counter,
@@ -271,9 +271,18 @@ def recipe_parser_newest_version(version):
271 RecipeParser.NEWEST_VERSION = old_version271 RecipeParser.NEWEST_VERSION = old_version
272272
273273
274def make_merge_proposal_without_reviewers(factory, **kwargs):274def make_merge_proposal_without_reviewers(
275 factory, for_git=False, source=None, target=None, **kwargs):
275 """Make a merge proposal and strip of any review votes."""276 """Make a merge proposal and strip of any review votes."""
276 proposal = factory.makeBranchMergeProposal(**kwargs)277 kwargs = dict(kwargs)
278 if for_git:
279 kwargs["source_ref"] = source
280 kwargs["target_ref"] = target
281 proposal = factory.makeBranchMergeProposalForGit(**kwargs)
282 else:
283 kwargs["source_branch"] = source
284 kwargs["target_branch"] = target
285 proposal = factory.makeBranchMergeProposal(**kwargs)
277 for vote in proposal.votes:286 for vote in proposal.votes:
278 removeSecurityProxy(vote).destroySelf()287 removeSecurityProxy(vote).destroySelf()
279 del get_property_cache(proposal).votes288 del get_property_cache(proposal).votes