##// END OF EJS Templates
chore(tests): mark xfail for test that current we don't know why dont work
super-admin -
r5221:13e09fb1 default
parent child Browse files
Show More
@@ -1,1935 +1,1936 b''
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 import mock
18 import mock
19 import pytest
19 import pytest
20
20
21 import rhodecode
21 import rhodecode
22 from rhodecode.lib import helpers as h
22 from rhodecode.lib import helpers as h
23 from rhodecode.lib.vcs.backends.base import MergeResponse, MergeFailureReason, Reference
23 from rhodecode.lib.vcs.backends.base import MergeResponse, MergeFailureReason, Reference
24 from rhodecode.lib.vcs.nodes import FileNode
24 from rhodecode.lib.vcs.nodes import FileNode
25 from rhodecode.lib.ext_json import json
25 from rhodecode.lib.ext_json import json
26 from rhodecode.model.changeset_status import ChangesetStatusModel
26 from rhodecode.model.changeset_status import ChangesetStatusModel
27 from rhodecode.model.db import (
27 from rhodecode.model.db import (
28 PullRequest,
28 PullRequest,
29 ChangesetStatus,
29 ChangesetStatus,
30 UserLog,
30 UserLog,
31 Notification,
31 Notification,
32 ChangesetComment,
32 ChangesetComment,
33 Repository,
33 Repository,
34 )
34 )
35 from rhodecode.model.meta import Session
35 from rhodecode.model.meta import Session
36 from rhodecode.model.pull_request import PullRequestModel
36 from rhodecode.model.pull_request import PullRequestModel
37 from rhodecode.model.user import UserModel
37 from rhodecode.model.user import UserModel
38 from rhodecode.model.comment import CommentsModel
38 from rhodecode.model.comment import CommentsModel
39 from rhodecode.tests import (
39 from rhodecode.tests import (
40 assert_session_flash,
40 assert_session_flash,
41 TEST_USER_ADMIN_LOGIN,
41 TEST_USER_ADMIN_LOGIN,
42 TEST_USER_REGULAR_LOGIN,
42 TEST_USER_REGULAR_LOGIN,
43 )
43 )
44 from rhodecode.tests.fixture_mods.fixture_utils import PRTestUtility
44 from rhodecode.tests.fixture_mods.fixture_utils import PRTestUtility
45 from rhodecode.tests.routes import route_path
45 from rhodecode.tests.routes import route_path
46
46
47
47
48 @pytest.mark.usefixtures("app", "autologin_user")
48 @pytest.mark.usefixtures("app", "autologin_user")
49 @pytest.mark.backends("git", "hg")
49 @pytest.mark.backends("git", "hg")
50 class TestPullrequestsView(object):
50 class TestPullrequestsView(object):
51 def test_index(self, backend):
51 def test_index(self, backend):
52 self.app.get(route_path("pullrequest_new", repo_name=backend.repo_name))
52 self.app.get(route_path("pullrequest_new", repo_name=backend.repo_name))
53
53
54 def test_option_menu_create_pull_request_exists(self, backend):
54 def test_option_menu_create_pull_request_exists(self, backend):
55 repo_name = backend.repo_name
55 repo_name = backend.repo_name
56 response = self.app.get(h.route_path("repo_summary", repo_name=repo_name))
56 response = self.app.get(h.route_path("repo_summary", repo_name=repo_name))
57
57
58 create_pr_link = '<a href="%s">Create Pull Request</a>' % route_path(
58 create_pr_link = '<a href="%s">Create Pull Request</a>' % route_path(
59 "pullrequest_new", repo_name=repo_name
59 "pullrequest_new", repo_name=repo_name
60 )
60 )
61 response.mustcontain(create_pr_link)
61 response.mustcontain(create_pr_link)
62
62
63 def test_create_pr_form_with_raw_commit_id(self, backend):
63 def test_create_pr_form_with_raw_commit_id(self, backend):
64 repo = backend.repo
64 repo = backend.repo
65
65
66 self.app.get(
66 self.app.get(
67 route_path(
67 route_path(
68 "pullrequest_new",
68 "pullrequest_new",
69 repo_name=repo.repo_name,
69 repo_name=repo.repo_name,
70 commit=repo.get_commit().raw_id,
70 commit=repo.get_commit().raw_id,
71 ),
71 ),
72 status=200,
72 status=200,
73 )
73 )
74
74
75 @pytest.mark.parametrize("pr_merge_enabled", [True, False])
75 @pytest.mark.parametrize("pr_merge_enabled", [True, False])
76 @pytest.mark.parametrize("range_diff", ["0", "1"])
76 @pytest.mark.parametrize("range_diff", ["0", "1"])
77 def test_show(self, pr_util, pr_merge_enabled, range_diff):
77 def test_show(self, pr_util, pr_merge_enabled, range_diff):
78 pull_request = pr_util.create_pull_request(
78 pull_request = pr_util.create_pull_request(
79 mergeable=pr_merge_enabled, enable_notifications=False
79 mergeable=pr_merge_enabled, enable_notifications=False
80 )
80 )
81
81
82 response = self.app.get(
82 response = self.app.get(
83 route_path(
83 route_path(
84 "pullrequest_show",
84 "pullrequest_show",
85 repo_name=pull_request.target_repo.scm_instance().name,
85 repo_name=pull_request.target_repo.scm_instance().name,
86 pull_request_id=pull_request.pull_request_id,
86 pull_request_id=pull_request.pull_request_id,
87 params={"range-diff": range_diff},
87 params={"range-diff": range_diff},
88 )
88 )
89 )
89 )
90
90
91 for commit_id in pull_request.revisions:
91 for commit_id in pull_request.revisions:
92 response.mustcontain(commit_id)
92 response.mustcontain(commit_id)
93
93
94 response.mustcontain(pull_request.target_ref_parts.type)
94 response.mustcontain(pull_request.target_ref_parts.type)
95 response.mustcontain(pull_request.target_ref_parts.name)
95 response.mustcontain(pull_request.target_ref_parts.name)
96
96
97 response.mustcontain('class="pull-request-merge"')
97 response.mustcontain('class="pull-request-merge"')
98
98
99 if pr_merge_enabled:
99 if pr_merge_enabled:
100 response.mustcontain("Pull request reviewer approval is pending")
100 response.mustcontain("Pull request reviewer approval is pending")
101 else:
101 else:
102 response.mustcontain("Server-side pull request merging is disabled.")
102 response.mustcontain("Server-side pull request merging is disabled.")
103
103
104 if range_diff == "1":
104 if range_diff == "1":
105 response.mustcontain("Turn off: Show the diff as commit range")
105 response.mustcontain("Turn off: Show the diff as commit range")
106
106
107 def test_show_versions_of_pr(self, backend, csrf_token):
107 def test_show_versions_of_pr(self, backend, csrf_token):
108 commits = [
108 commits = [
109 {
109 {
110 "message": "initial-commit",
110 "message": "initial-commit",
111 "added": [FileNode(b"test-file.txt", b"LINE1\n")],
111 "added": [FileNode(b"test-file.txt", b"LINE1\n")],
112 },
112 },
113 {
113 {
114 "message": "commit-1",
114 "message": "commit-1",
115 "changed": [FileNode(b"test-file.txt", b"LINE1\nLINE2\n")],
115 "changed": [FileNode(b"test-file.txt", b"LINE1\nLINE2\n")],
116 },
116 },
117 # Above is the initial version of PR that changes a single line
117 # Above is the initial version of PR that changes a single line
118 # from now on we'll add 3x commit adding a nother line on each step
118 # from now on we'll add 3x commit adding a nother line on each step
119 {
119 {
120 "message": "commit-2",
120 "message": "commit-2",
121 "changed": [FileNode(b"test-file.txt", b"LINE1\nLINE2\nLINE3\n")],
121 "changed": [FileNode(b"test-file.txt", b"LINE1\nLINE2\nLINE3\n")],
122 },
122 },
123 {
123 {
124 "message": "commit-3",
124 "message": "commit-3",
125 "changed": [
125 "changed": [
126 FileNode(b"test-file.txt", b"LINE1\nLINE2\nLINE3\nLINE4\n")
126 FileNode(b"test-file.txt", b"LINE1\nLINE2\nLINE3\nLINE4\n")
127 ],
127 ],
128 },
128 },
129 {
129 {
130 "message": "commit-4",
130 "message": "commit-4",
131 "changed": [
131 "changed": [
132 FileNode(b"test-file.txt", b"LINE1\nLINE2\nLINE3\nLINE4\nLINE5\n")
132 FileNode(b"test-file.txt", b"LINE1\nLINE2\nLINE3\nLINE4\nLINE5\n")
133 ],
133 ],
134 },
134 },
135 ]
135 ]
136
136
137 commit_ids = backend.create_master_repo(commits)
137 commit_ids = backend.create_master_repo(commits)
138 target = backend.create_repo(heads=["initial-commit"])
138 target = backend.create_repo(heads=["initial-commit"])
139 source = backend.create_repo(heads=["commit-1"])
139 source = backend.create_repo(heads=["commit-1"])
140 source_repo_name = source.repo_name
140 source_repo_name = source.repo_name
141 target_repo_name = target.repo_name
141 target_repo_name = target.repo_name
142
142
143 target_ref = "branch:{branch}:{commit_id}".format(
143 target_ref = "branch:{branch}:{commit_id}".format(
144 branch=backend.default_branch_name, commit_id=commit_ids["initial-commit"]
144 branch=backend.default_branch_name, commit_id=commit_ids["initial-commit"]
145 )
145 )
146 source_ref = "branch:{branch}:{commit_id}".format(
146 source_ref = "branch:{branch}:{commit_id}".format(
147 branch=backend.default_branch_name, commit_id=commit_ids["commit-1"]
147 branch=backend.default_branch_name, commit_id=commit_ids["commit-1"]
148 )
148 )
149
149
150 response = self.app.post(
150 response = self.app.post(
151 route_path("pullrequest_create", repo_name=source.repo_name),
151 route_path("pullrequest_create", repo_name=source.repo_name),
152 [
152 [
153 ("source_repo", source_repo_name),
153 ("source_repo", source_repo_name),
154 ("source_ref", source_ref),
154 ("source_ref", source_ref),
155 ("target_repo", target_repo_name),
155 ("target_repo", target_repo_name),
156 ("target_ref", target_ref),
156 ("target_ref", target_ref),
157 ("common_ancestor", commit_ids["initial-commit"]),
157 ("common_ancestor", commit_ids["initial-commit"]),
158 ("pullrequest_title", "Title"),
158 ("pullrequest_title", "Title"),
159 ("pullrequest_desc", "Description"),
159 ("pullrequest_desc", "Description"),
160 ("description_renderer", "markdown"),
160 ("description_renderer", "markdown"),
161 ("__start__", "review_members:sequence"),
161 ("__start__", "review_members:sequence"),
162 ("__start__", "reviewer:mapping"),
162 ("__start__", "reviewer:mapping"),
163 ("user_id", "1"),
163 ("user_id", "1"),
164 ("__start__", "reasons:sequence"),
164 ("__start__", "reasons:sequence"),
165 ("reason", "Some reason"),
165 ("reason", "Some reason"),
166 ("__end__", "reasons:sequence"),
166 ("__end__", "reasons:sequence"),
167 ("__start__", "rules:sequence"),
167 ("__start__", "rules:sequence"),
168 ("__end__", "rules:sequence"),
168 ("__end__", "rules:sequence"),
169 ("mandatory", "False"),
169 ("mandatory", "False"),
170 ("__end__", "reviewer:mapping"),
170 ("__end__", "reviewer:mapping"),
171 ("__end__", "review_members:sequence"),
171 ("__end__", "review_members:sequence"),
172 ("__start__", "revisions:sequence"),
172 ("__start__", "revisions:sequence"),
173 ("revisions", commit_ids["commit-1"]),
173 ("revisions", commit_ids["commit-1"]),
174 ("__end__", "revisions:sequence"),
174 ("__end__", "revisions:sequence"),
175 ("user", ""),
175 ("user", ""),
176 ("csrf_token", csrf_token),
176 ("csrf_token", csrf_token),
177 ],
177 ],
178 status=302,
178 status=302,
179 )
179 )
180
180
181 location = response.headers["Location"]
181 location = response.headers["Location"]
182
182
183 pull_request_id = location.rsplit("/", 1)[1]
183 pull_request_id = location.rsplit("/", 1)[1]
184 assert pull_request_id != "new"
184 assert pull_request_id != "new"
185 pull_request = PullRequest.get(int(pull_request_id))
185 pull_request = PullRequest.get(int(pull_request_id))
186
186
187 pull_request_id = pull_request.pull_request_id
187 pull_request_id = pull_request.pull_request_id
188
188
189 # Show initial version of PR
189 # Show initial version of PR
190 response = self.app.get(
190 response = self.app.get(
191 route_path(
191 route_path(
192 "pullrequest_show",
192 "pullrequest_show",
193 repo_name=target_repo_name,
193 repo_name=target_repo_name,
194 pull_request_id=pull_request_id,
194 pull_request_id=pull_request_id,
195 )
195 )
196 )
196 )
197
197
198 response.mustcontain("commit-1")
198 response.mustcontain("commit-1")
199 response.mustcontain(no=["commit-2"])
199 response.mustcontain(no=["commit-2"])
200 response.mustcontain(no=["commit-3"])
200 response.mustcontain(no=["commit-3"])
201 response.mustcontain(no=["commit-4"])
201 response.mustcontain(no=["commit-4"])
202
202
203 response.mustcontain('cb-addition"></span><span>LINE2</span>')
203 response.mustcontain('cb-addition"></span><span>LINE2</span>')
204 response.mustcontain(no=["LINE3"])
204 response.mustcontain(no=["LINE3"])
205 response.mustcontain(no=["LINE4"])
205 response.mustcontain(no=["LINE4"])
206 response.mustcontain(no=["LINE5"])
206 response.mustcontain(no=["LINE5"])
207
207
208 # update PR #1
208 # update PR #1
209 source_repo = Repository.get_by_repo_name(source_repo_name)
209 source_repo = Repository.get_by_repo_name(source_repo_name)
210 backend.pull_heads(source_repo, heads=["commit-2"])
210 backend.pull_heads(source_repo, heads=["commit-2"])
211 response = self.app.post(
211 response = self.app.post(
212 route_path(
212 route_path(
213 "pullrequest_update",
213 "pullrequest_update",
214 repo_name=target_repo_name,
214 repo_name=target_repo_name,
215 pull_request_id=pull_request_id,
215 pull_request_id=pull_request_id,
216 ),
216 ),
217 params={"update_commits": "true", "csrf_token": csrf_token},
217 params={"update_commits": "true", "csrf_token": csrf_token},
218 )
218 )
219
219
220 # update PR #2
220 # update PR #2
221 source_repo = Repository.get_by_repo_name(source_repo_name)
221 source_repo = Repository.get_by_repo_name(source_repo_name)
222 backend.pull_heads(source_repo, heads=["commit-3"])
222 backend.pull_heads(source_repo, heads=["commit-3"])
223 response = self.app.post(
223 response = self.app.post(
224 route_path(
224 route_path(
225 "pullrequest_update",
225 "pullrequest_update",
226 repo_name=target_repo_name,
226 repo_name=target_repo_name,
227 pull_request_id=pull_request_id,
227 pull_request_id=pull_request_id,
228 ),
228 ),
229 params={"update_commits": "true", "csrf_token": csrf_token},
229 params={"update_commits": "true", "csrf_token": csrf_token},
230 )
230 )
231
231
232 # update PR #3
232 # update PR #3
233 source_repo = Repository.get_by_repo_name(source_repo_name)
233 source_repo = Repository.get_by_repo_name(source_repo_name)
234 backend.pull_heads(source_repo, heads=["commit-4"])
234 backend.pull_heads(source_repo, heads=["commit-4"])
235 response = self.app.post(
235 response = self.app.post(
236 route_path(
236 route_path(
237 "pullrequest_update",
237 "pullrequest_update",
238 repo_name=target_repo_name,
238 repo_name=target_repo_name,
239 pull_request_id=pull_request_id,
239 pull_request_id=pull_request_id,
240 ),
240 ),
241 params={"update_commits": "true", "csrf_token": csrf_token},
241 params={"update_commits": "true", "csrf_token": csrf_token},
242 )
242 )
243
243
244 # Show final version !
244 # Show final version !
245 response = self.app.get(
245 response = self.app.get(
246 route_path(
246 route_path(
247 "pullrequest_show",
247 "pullrequest_show",
248 repo_name=target_repo_name,
248 repo_name=target_repo_name,
249 pull_request_id=pull_request_id,
249 pull_request_id=pull_request_id,
250 )
250 )
251 )
251 )
252
252
253 # 3 updates, and the latest == 4
253 # 3 updates, and the latest == 4
254 response.mustcontain("4 versions available for this pull request")
254 response.mustcontain("4 versions available for this pull request")
255 response.mustcontain(no=["rhodecode diff rendering error"])
255 response.mustcontain(no=["rhodecode diff rendering error"])
256
256
257 # initial show must have 3 commits, and 3 adds
257 # initial show must have 3 commits, and 3 adds
258 response.mustcontain("commit-1")
258 response.mustcontain("commit-1")
259 response.mustcontain("commit-2")
259 response.mustcontain("commit-2")
260 response.mustcontain("commit-3")
260 response.mustcontain("commit-3")
261 response.mustcontain("commit-4")
261 response.mustcontain("commit-4")
262
262
263 response.mustcontain('cb-addition"></span><span>LINE2</span>')
263 response.mustcontain('cb-addition"></span><span>LINE2</span>')
264 response.mustcontain('cb-addition"></span><span>LINE3</span>')
264 response.mustcontain('cb-addition"></span><span>LINE3</span>')
265 response.mustcontain('cb-addition"></span><span>LINE4</span>')
265 response.mustcontain('cb-addition"></span><span>LINE4</span>')
266 response.mustcontain('cb-addition"></span><span>LINE5</span>')
266 response.mustcontain('cb-addition"></span><span>LINE5</span>')
267
267
268 # fetch versions
268 # fetch versions
269 pr = PullRequest.get(pull_request_id)
269 pr = PullRequest.get(pull_request_id)
270 versions = [x.pull_request_version_id for x in pr.versions.all()]
270 versions = [x.pull_request_version_id for x in pr.versions.all()]
271 assert len(versions) == 3
271 assert len(versions) == 3
272
272
273 # show v1,v2,v3,v4
273 # show v1,v2,v3,v4
274 def cb_line(text):
274 def cb_line(text):
275 return 'cb-addition"></span><span>{}</span>'.format(text)
275 return 'cb-addition"></span><span>{}</span>'.format(text)
276
276
277 def cb_context(text):
277 def cb_context(text):
278 return (
278 return (
279 '<span class="cb-code"><span class="cb-action cb-context">'
279 '<span class="cb-code"><span class="cb-action cb-context">'
280 "</span><span>{}</span></span>".format(text)
280 "</span><span>{}</span></span>".format(text)
281 )
281 )
282
282
283 commit_tests = {
283 commit_tests = {
284 # in response, not in response
284 # in response, not in response
285 1: (["commit-1"], ["commit-2", "commit-3", "commit-4"]),
285 1: (["commit-1"], ["commit-2", "commit-3", "commit-4"]),
286 2: (["commit-1", "commit-2"], ["commit-3", "commit-4"]),
286 2: (["commit-1", "commit-2"], ["commit-3", "commit-4"]),
287 3: (["commit-1", "commit-2", "commit-3"], ["commit-4"]),
287 3: (["commit-1", "commit-2", "commit-3"], ["commit-4"]),
288 4: (["commit-1", "commit-2", "commit-3", "commit-4"], []),
288 4: (["commit-1", "commit-2", "commit-3", "commit-4"], []),
289 }
289 }
290 diff_tests = {
290 diff_tests = {
291 1: (["LINE2"], ["LINE3", "LINE4", "LINE5"]),
291 1: (["LINE2"], ["LINE3", "LINE4", "LINE5"]),
292 2: (["LINE2", "LINE3"], ["LINE4", "LINE5"]),
292 2: (["LINE2", "LINE3"], ["LINE4", "LINE5"]),
293 3: (["LINE2", "LINE3", "LINE4"], ["LINE5"]),
293 3: (["LINE2", "LINE3", "LINE4"], ["LINE5"]),
294 4: (["LINE2", "LINE3", "LINE4", "LINE5"], []),
294 4: (["LINE2", "LINE3", "LINE4", "LINE5"], []),
295 }
295 }
296 for idx, ver in enumerate(versions, 1):
296 for idx, ver in enumerate(versions, 1):
297 response = self.app.get(
297 response = self.app.get(
298 route_path(
298 route_path(
299 "pullrequest_show",
299 "pullrequest_show",
300 repo_name=target_repo_name,
300 repo_name=target_repo_name,
301 pull_request_id=pull_request_id,
301 pull_request_id=pull_request_id,
302 params={"version": ver},
302 params={"version": ver},
303 )
303 )
304 )
304 )
305
305
306 response.mustcontain(no=["rhodecode diff rendering error"])
306 response.mustcontain(no=["rhodecode diff rendering error"])
307 response.mustcontain("Showing changes at v{}".format(idx))
307 response.mustcontain("Showing changes at v{}".format(idx))
308
308
309 yes, no = commit_tests[idx]
309 yes, no = commit_tests[idx]
310 for y in yes:
310 for y in yes:
311 response.mustcontain(y)
311 response.mustcontain(y)
312 for n in no:
312 for n in no:
313 response.mustcontain(no=n)
313 response.mustcontain(no=n)
314
314
315 yes, no = diff_tests[idx]
315 yes, no = diff_tests[idx]
316 for y in yes:
316 for y in yes:
317 response.mustcontain(cb_line(y))
317 response.mustcontain(cb_line(y))
318 for n in no:
318 for n in no:
319 response.mustcontain(no=n)
319 response.mustcontain(no=n)
320
320
321 # show diff between versions
321 # show diff between versions
322 diff_compare_tests = {
322 diff_compare_tests = {
323 1: (["LINE3"], ["LINE1", "LINE2"]),
323 1: (["LINE3"], ["LINE1", "LINE2"]),
324 2: (["LINE3", "LINE4"], ["LINE1", "LINE2"]),
324 2: (["LINE3", "LINE4"], ["LINE1", "LINE2"]),
325 3: (["LINE3", "LINE4", "LINE5"], ["LINE1", "LINE2"]),
325 3: (["LINE3", "LINE4", "LINE5"], ["LINE1", "LINE2"]),
326 }
326 }
327 for idx, ver in enumerate(versions, 1):
327 for idx, ver in enumerate(versions, 1):
328 adds, context = diff_compare_tests[idx]
328 adds, context = diff_compare_tests[idx]
329
329
330 to_ver = ver + 1
330 to_ver = ver + 1
331 if idx == 3:
331 if idx == 3:
332 to_ver = "latest"
332 to_ver = "latest"
333
333
334 response = self.app.get(
334 response = self.app.get(
335 route_path(
335 route_path(
336 "pullrequest_show",
336 "pullrequest_show",
337 repo_name=target_repo_name,
337 repo_name=target_repo_name,
338 pull_request_id=pull_request_id,
338 pull_request_id=pull_request_id,
339 params={"from_version": versions[0], "version": to_ver},
339 params={"from_version": versions[0], "version": to_ver},
340 )
340 )
341 )
341 )
342
342
343 response.mustcontain(no=["rhodecode diff rendering error"])
343 response.mustcontain(no=["rhodecode diff rendering error"])
344
344
345 for a in adds:
345 for a in adds:
346 response.mustcontain(cb_line(a))
346 response.mustcontain(cb_line(a))
347 for c in context:
347 for c in context:
348 response.mustcontain(cb_context(c))
348 response.mustcontain(cb_context(c))
349
349
350 # test version v2 -> v3
350 # test version v2 -> v3
351 response = self.app.get(
351 response = self.app.get(
352 route_path(
352 route_path(
353 "pullrequest_show",
353 "pullrequest_show",
354 repo_name=target_repo_name,
354 repo_name=target_repo_name,
355 pull_request_id=pull_request_id,
355 pull_request_id=pull_request_id,
356 params={"from_version": versions[1], "version": versions[2]},
356 params={"from_version": versions[1], "version": versions[2]},
357 )
357 )
358 )
358 )
359
359
360 response.mustcontain(cb_context("LINE1"))
360 response.mustcontain(cb_context("LINE1"))
361 response.mustcontain(cb_context("LINE2"))
361 response.mustcontain(cb_context("LINE2"))
362 response.mustcontain(cb_context("LINE3"))
362 response.mustcontain(cb_context("LINE3"))
363 response.mustcontain(cb_line("LINE4"))
363 response.mustcontain(cb_line("LINE4"))
364
364
365 def test_close_status_visibility(self, pr_util, user_util, csrf_token):
365 def test_close_status_visibility(self, pr_util, user_util, csrf_token):
366 # Logout
366 # Logout
367 response = self.app.post(
367 response = self.app.post(
368 h.route_path("logout"), params={"csrf_token": csrf_token}
368 h.route_path("logout"), params={"csrf_token": csrf_token}
369 )
369 )
370 # Login as regular user
370 # Login as regular user
371 response = self.app.post(
371 response = self.app.post(
372 h.route_path("login"),
372 h.route_path("login"),
373 {"username": TEST_USER_REGULAR_LOGIN, "password": "test12"},
373 {"username": TEST_USER_REGULAR_LOGIN, "password": "test12"},
374 )
374 )
375
375
376 pull_request = pr_util.create_pull_request(author=TEST_USER_REGULAR_LOGIN)
376 pull_request = pr_util.create_pull_request(author=TEST_USER_REGULAR_LOGIN)
377
377
378 response = self.app.get(
378 response = self.app.get(
379 route_path(
379 route_path(
380 "pullrequest_show",
380 "pullrequest_show",
381 repo_name=pull_request.target_repo.scm_instance().name,
381 repo_name=pull_request.target_repo.scm_instance().name,
382 pull_request_id=pull_request.pull_request_id,
382 pull_request_id=pull_request.pull_request_id,
383 )
383 )
384 )
384 )
385
385
386 response.mustcontain("Server-side pull request merging is disabled.")
386 response.mustcontain("Server-side pull request merging is disabled.")
387
387
388 assert_response = response.assert_response()
388 assert_response = response.assert_response()
389 # for regular user without a merge permissions, we don't see it
389 # for regular user without a merge permissions, we don't see it
390 assert_response.no_element_exists("#close-pull-request-action")
390 assert_response.no_element_exists("#close-pull-request-action")
391
391
392 user_util.grant_user_permission_to_repo(
392 user_util.grant_user_permission_to_repo(
393 pull_request.target_repo,
393 pull_request.target_repo,
394 UserModel().get_by_username(TEST_USER_REGULAR_LOGIN),
394 UserModel().get_by_username(TEST_USER_REGULAR_LOGIN),
395 "repository.write",
395 "repository.write",
396 )
396 )
397 response = self.app.get(
397 response = self.app.get(
398 route_path(
398 route_path(
399 "pullrequest_show",
399 "pullrequest_show",
400 repo_name=pull_request.target_repo.scm_instance().name,
400 repo_name=pull_request.target_repo.scm_instance().name,
401 pull_request_id=pull_request.pull_request_id,
401 pull_request_id=pull_request.pull_request_id,
402 )
402 )
403 )
403 )
404
404
405 response.mustcontain("Server-side pull request merging is disabled.")
405 response.mustcontain("Server-side pull request merging is disabled.")
406
406
407 assert_response = response.assert_response()
407 assert_response = response.assert_response()
408 # now regular user has a merge permissions, we have CLOSE button
408 # now regular user has a merge permissions, we have CLOSE button
409 assert_response.one_element_exists("#close-pull-request-action")
409 assert_response.one_element_exists("#close-pull-request-action")
410
410
411 def test_show_invalid_commit_id(self, pr_util):
411 def test_show_invalid_commit_id(self, pr_util):
412 # Simulating invalid revisions which will cause a lookup error
412 # Simulating invalid revisions which will cause a lookup error
413 pull_request = pr_util.create_pull_request()
413 pull_request = pr_util.create_pull_request()
414 pull_request.revisions = ["invalid"]
414 pull_request.revisions = ["invalid"]
415 Session().add(pull_request)
415 Session().add(pull_request)
416 Session().commit()
416 Session().commit()
417
417
418 response = self.app.get(
418 response = self.app.get(
419 route_path(
419 route_path(
420 "pullrequest_show",
420 "pullrequest_show",
421 repo_name=pull_request.target_repo.scm_instance().name,
421 repo_name=pull_request.target_repo.scm_instance().name,
422 pull_request_id=pull_request.pull_request_id,
422 pull_request_id=pull_request.pull_request_id,
423 )
423 )
424 )
424 )
425
425
426 for commit_id in pull_request.revisions:
426 for commit_id in pull_request.revisions:
427 response.mustcontain(commit_id)
427 response.mustcontain(commit_id)
428
428
429 def test_show_invalid_source_reference(self, pr_util):
429 def test_show_invalid_source_reference(self, pr_util):
430 pull_request = pr_util.create_pull_request()
430 pull_request = pr_util.create_pull_request()
431 pull_request.source_ref = "branch:b:invalid"
431 pull_request.source_ref = "branch:b:invalid"
432 Session().add(pull_request)
432 Session().add(pull_request)
433 Session().commit()
433 Session().commit()
434
434
435 self.app.get(
435 self.app.get(
436 route_path(
436 route_path(
437 "pullrequest_show",
437 "pullrequest_show",
438 repo_name=pull_request.target_repo.scm_instance().name,
438 repo_name=pull_request.target_repo.scm_instance().name,
439 pull_request_id=pull_request.pull_request_id,
439 pull_request_id=pull_request.pull_request_id,
440 )
440 )
441 )
441 )
442
442
443 def test_edit_title_description(self, pr_util, csrf_token):
443 def test_edit_title_description(self, pr_util, csrf_token):
444 pull_request = pr_util.create_pull_request()
444 pull_request = pr_util.create_pull_request()
445 pull_request_id = pull_request.pull_request_id
445 pull_request_id = pull_request.pull_request_id
446
446
447 response = self.app.post(
447 response = self.app.post(
448 route_path(
448 route_path(
449 "pullrequest_update",
449 "pullrequest_update",
450 repo_name=pull_request.target_repo.repo_name,
450 repo_name=pull_request.target_repo.repo_name,
451 pull_request_id=pull_request_id,
451 pull_request_id=pull_request_id,
452 ),
452 ),
453 params={
453 params={
454 "edit_pull_request": "true",
454 "edit_pull_request": "true",
455 "title": "New title",
455 "title": "New title",
456 "description": "New description",
456 "description": "New description",
457 "csrf_token": csrf_token,
457 "csrf_token": csrf_token,
458 },
458 },
459 )
459 )
460
460
461 assert_session_flash(
461 assert_session_flash(
462 response, "Pull request title & description updated.", category="success"
462 response, "Pull request title & description updated.", category="success"
463 )
463 )
464
464
465 pull_request = PullRequest.get(pull_request_id)
465 pull_request = PullRequest.get(pull_request_id)
466 assert pull_request.title == "New title"
466 assert pull_request.title == "New title"
467 assert pull_request.description == "New description"
467 assert pull_request.description == "New description"
468
468
469 def test_edit_title_description_special(self, pr_util, csrf_token):
469 def test_edit_title_description_special(self, pr_util, csrf_token):
470 pull_request = pr_util.create_pull_request()
470 pull_request = pr_util.create_pull_request()
471 pull_request_id = pull_request.pull_request_id
471 pull_request_id = pull_request.pull_request_id
472
472
473 response = self.app.post(
473 response = self.app.post(
474 route_path(
474 route_path(
475 "pullrequest_update",
475 "pullrequest_update",
476 repo_name=pull_request.target_repo.repo_name,
476 repo_name=pull_request.target_repo.repo_name,
477 pull_request_id=pull_request_id,
477 pull_request_id=pull_request_id,
478 ),
478 ),
479 params={
479 params={
480 "edit_pull_request": "true",
480 "edit_pull_request": "true",
481 "title": "New title {} {2} {foo}",
481 "title": "New title {} {2} {foo}",
482 "description": "New description",
482 "description": "New description",
483 "csrf_token": csrf_token,
483 "csrf_token": csrf_token,
484 },
484 },
485 )
485 )
486
486
487 assert_session_flash(
487 assert_session_flash(
488 response, "Pull request title & description updated.", category="success"
488 response, "Pull request title & description updated.", category="success"
489 )
489 )
490
490
491 pull_request = PullRequest.get(pull_request_id)
491 pull_request = PullRequest.get(pull_request_id)
492 assert pull_request.title_safe == "New title {{}} {{2}} {{foo}}"
492 assert pull_request.title_safe == "New title {{}} {{2}} {{foo}}"
493
493
494 def test_edit_title_description_closed(self, pr_util, csrf_token):
494 def test_edit_title_description_closed(self, pr_util, csrf_token):
495 pull_request = pr_util.create_pull_request()
495 pull_request = pr_util.create_pull_request()
496 pull_request_id = pull_request.pull_request_id
496 pull_request_id = pull_request.pull_request_id
497 repo_name = pull_request.target_repo.repo_name
497 repo_name = pull_request.target_repo.repo_name
498 pr_util.close()
498 pr_util.close()
499
499
500 response = self.app.post(
500 response = self.app.post(
501 route_path(
501 route_path(
502 "pullrequest_update",
502 "pullrequest_update",
503 repo_name=repo_name,
503 repo_name=repo_name,
504 pull_request_id=pull_request_id,
504 pull_request_id=pull_request_id,
505 ),
505 ),
506 params={
506 params={
507 "edit_pull_request": "true",
507 "edit_pull_request": "true",
508 "title": "New title",
508 "title": "New title",
509 "description": "New description",
509 "description": "New description",
510 "csrf_token": csrf_token,
510 "csrf_token": csrf_token,
511 },
511 },
512 status=200,
512 status=200,
513 )
513 )
514 assert_session_flash(
514 assert_session_flash(
515 response, "Cannot update closed pull requests.", category="error"
515 response, "Cannot update closed pull requests.", category="error"
516 )
516 )
517
517
518 def test_update_invalid_source_reference(self, pr_util, csrf_token):
518 def test_update_invalid_source_reference(self, pr_util, csrf_token):
519 from rhodecode.lib.vcs.backends.base import UpdateFailureReason
519 from rhodecode.lib.vcs.backends.base import UpdateFailureReason
520
520
521 pull_request = pr_util.create_pull_request()
521 pull_request = pr_util.create_pull_request()
522 pull_request.source_ref = "branch:invalid-branch:invalid-commit-id"
522 pull_request.source_ref = "branch:invalid-branch:invalid-commit-id"
523 Session().add(pull_request)
523 Session().add(pull_request)
524 Session().commit()
524 Session().commit()
525
525
526 pull_request_id = pull_request.pull_request_id
526 pull_request_id = pull_request.pull_request_id
527
527
528 response = self.app.post(
528 response = self.app.post(
529 route_path(
529 route_path(
530 "pullrequest_update",
530 "pullrequest_update",
531 repo_name=pull_request.target_repo.repo_name,
531 repo_name=pull_request.target_repo.repo_name,
532 pull_request_id=pull_request_id,
532 pull_request_id=pull_request_id,
533 ),
533 ),
534 params={"update_commits": "true", "csrf_token": csrf_token},
534 params={"update_commits": "true", "csrf_token": csrf_token},
535 )
535 )
536
536
537 expected_msg = str(
537 expected_msg = str(
538 PullRequestModel.UPDATE_STATUS_MESSAGES[
538 PullRequestModel.UPDATE_STATUS_MESSAGES[
539 UpdateFailureReason.MISSING_SOURCE_REF
539 UpdateFailureReason.MISSING_SOURCE_REF
540 ]
540 ]
541 )
541 )
542 assert_session_flash(response, expected_msg, category="error")
542 assert_session_flash(response, expected_msg, category="error")
543
543
544 def test_missing_target_reference(self, pr_util, csrf_token):
544 def test_missing_target_reference(self, pr_util, csrf_token):
545 from rhodecode.lib.vcs.backends.base import MergeFailureReason
545 from rhodecode.lib.vcs.backends.base import MergeFailureReason
546
546
547 pull_request = pr_util.create_pull_request(approved=True, mergeable=True)
547 pull_request = pr_util.create_pull_request(approved=True, mergeable=True)
548 unicode_reference = "branch:invalid-branch:invalid-commit-id"
548 unicode_reference = "branch:invalid-branch:invalid-commit-id"
549 pull_request.target_ref = unicode_reference
549 pull_request.target_ref = unicode_reference
550 Session().add(pull_request)
550 Session().add(pull_request)
551 Session().commit()
551 Session().commit()
552
552
553 pull_request_id = pull_request.pull_request_id
553 pull_request_id = pull_request.pull_request_id
554 pull_request_url = route_path(
554 pull_request_url = route_path(
555 "pullrequest_show",
555 "pullrequest_show",
556 repo_name=pull_request.target_repo.repo_name,
556 repo_name=pull_request.target_repo.repo_name,
557 pull_request_id=pull_request_id,
557 pull_request_id=pull_request_id,
558 )
558 )
559
559
560 response = self.app.get(pull_request_url)
560 response = self.app.get(pull_request_url)
561 # target_ref_id = "invalid-branch"
561 # target_ref_id = "invalid-branch"
562
562
563 merge_resp = MergeResponse(
563 merge_resp = MergeResponse(
564 True,
564 True,
565 True,
565 True,
566 Reference("commit", "STUB_COMMIT_ID", "STUB_COMMIT_ID"),
566 Reference("commit", "STUB_COMMIT_ID", "STUB_COMMIT_ID"),
567 MergeFailureReason.MISSING_TARGET_REF,
567 MergeFailureReason.MISSING_TARGET_REF,
568 metadata={
568 metadata={
569 "target_ref": PullRequest.unicode_to_reference(unicode_reference)
569 "target_ref": PullRequest.unicode_to_reference(unicode_reference)
570 },
570 },
571 )
571 )
572 response.assert_response().element_contains(
572 response.assert_response().element_contains(
573 'div[data-role="merge-message"]', merge_resp.merge_status_message
573 'div[data-role="merge-message"]', merge_resp.merge_status_message
574 )
574 )
575
575
576 def test_comment_and_close_pull_request_custom_message_approved(
576 def test_comment_and_close_pull_request_custom_message_approved(
577 self, pr_util, csrf_token, xhr_header
577 self, pr_util, csrf_token, xhr_header
578 ):
578 ):
579 pull_request = pr_util.create_pull_request(approved=True)
579 pull_request = pr_util.create_pull_request(approved=True)
580 pull_request_id = pull_request.pull_request_id
580 pull_request_id = pull_request.pull_request_id
581 author = pull_request.user_id
581 author = pull_request.user_id
582 repo = pull_request.target_repo.repo_id
582 repo = pull_request.target_repo.repo_id
583
583
584 self.app.post(
584 self.app.post(
585 route_path(
585 route_path(
586 "pullrequest_comment_create",
586 "pullrequest_comment_create",
587 repo_name=pull_request.target_repo.scm_instance().name,
587 repo_name=pull_request.target_repo.scm_instance().name,
588 pull_request_id=pull_request_id,
588 pull_request_id=pull_request_id,
589 ),
589 ),
590 params={
590 params={
591 "close_pull_request": "1",
591 "close_pull_request": "1",
592 "text": "Closing a PR",
592 "text": "Closing a PR",
593 "csrf_token": csrf_token,
593 "csrf_token": csrf_token,
594 },
594 },
595 extra_environ=xhr_header,
595 extra_environ=xhr_header,
596 )
596 )
597
597
598 journal = (
598 journal = (
599 UserLog.query()
599 UserLog.query()
600 .filter(UserLog.user_id == author)
600 .filter(UserLog.user_id == author)
601 .filter(UserLog.repository_id == repo)
601 .filter(UserLog.repository_id == repo)
602 .order_by(UserLog.user_log_id.asc())
602 .order_by(UserLog.user_log_id.asc())
603 .all()
603 .all()
604 )
604 )
605 assert journal[-1].action == "repo.pull_request.close"
605 assert journal[-1].action == "repo.pull_request.close"
606
606
607 pull_request = PullRequest.get(pull_request_id)
607 pull_request = PullRequest.get(pull_request_id)
608 assert pull_request.is_closed()
608 assert pull_request.is_closed()
609
609
610 status = ChangesetStatusModel().get_status(
610 status = ChangesetStatusModel().get_status(
611 pull_request.source_repo, pull_request=pull_request
611 pull_request.source_repo, pull_request=pull_request
612 )
612 )
613 assert status == ChangesetStatus.STATUS_APPROVED
613 assert status == ChangesetStatus.STATUS_APPROVED
614 comments = (
614 comments = (
615 ChangesetComment()
615 ChangesetComment()
616 .query()
616 .query()
617 .filter(ChangesetComment.pull_request == pull_request)
617 .filter(ChangesetComment.pull_request == pull_request)
618 .order_by(ChangesetComment.comment_id.asc())
618 .order_by(ChangesetComment.comment_id.asc())
619 .all()
619 .all()
620 )
620 )
621 assert comments[-1].text == "Closing a PR"
621 assert comments[-1].text == "Closing a PR"
622
622
623 def test_comment_force_close_pull_request_rejected(
623 def test_comment_force_close_pull_request_rejected(
624 self, pr_util, csrf_token, xhr_header
624 self, pr_util, csrf_token, xhr_header
625 ):
625 ):
626 pull_request = pr_util.create_pull_request()
626 pull_request = pr_util.create_pull_request()
627 pull_request_id = pull_request.pull_request_id
627 pull_request_id = pull_request.pull_request_id
628 PullRequestModel().update_reviewers(
628 PullRequestModel().update_reviewers(
629 pull_request_id,
629 pull_request_id,
630 [
630 [
631 (1, ["reason"], False, "reviewer", []),
631 (1, ["reason"], False, "reviewer", []),
632 (2, ["reason2"], False, "reviewer", []),
632 (2, ["reason2"], False, "reviewer", []),
633 ],
633 ],
634 pull_request.author,
634 pull_request.author,
635 )
635 )
636 author = pull_request.user_id
636 author = pull_request.user_id
637 repo = pull_request.target_repo.repo_id
637 repo = pull_request.target_repo.repo_id
638
638
639 self.app.post(
639 self.app.post(
640 route_path(
640 route_path(
641 "pullrequest_comment_create",
641 "pullrequest_comment_create",
642 repo_name=pull_request.target_repo.scm_instance().name,
642 repo_name=pull_request.target_repo.scm_instance().name,
643 pull_request_id=pull_request_id,
643 pull_request_id=pull_request_id,
644 ),
644 ),
645 params={"close_pull_request": "1", "csrf_token": csrf_token},
645 params={"close_pull_request": "1", "csrf_token": csrf_token},
646 extra_environ=xhr_header,
646 extra_environ=xhr_header,
647 )
647 )
648
648
649 pull_request = PullRequest.get(pull_request_id)
649 pull_request = PullRequest.get(pull_request_id)
650
650
651 journal = (
651 journal = (
652 UserLog.query()
652 UserLog.query()
653 .filter(UserLog.user_id == author, UserLog.repository_id == repo)
653 .filter(UserLog.user_id == author, UserLog.repository_id == repo)
654 .order_by(UserLog.user_log_id.asc())
654 .order_by(UserLog.user_log_id.asc())
655 .all()
655 .all()
656 )
656 )
657 assert journal[-1].action == "repo.pull_request.close"
657 assert journal[-1].action == "repo.pull_request.close"
658
658
659 # check only the latest status, not the review status
659 # check only the latest status, not the review status
660 status = ChangesetStatusModel().get_status(
660 status = ChangesetStatusModel().get_status(
661 pull_request.source_repo, pull_request=pull_request
661 pull_request.source_repo, pull_request=pull_request
662 )
662 )
663 assert status == ChangesetStatus.STATUS_REJECTED
663 assert status == ChangesetStatus.STATUS_REJECTED
664
664
665 def test_comment_and_close_pull_request(self, pr_util, csrf_token, xhr_header):
665 def test_comment_and_close_pull_request(self, pr_util, csrf_token, xhr_header):
666 pull_request = pr_util.create_pull_request()
666 pull_request = pr_util.create_pull_request()
667 pull_request_id = pull_request.pull_request_id
667 pull_request_id = pull_request.pull_request_id
668
668
669 response = self.app.post(
669 response = self.app.post(
670 route_path(
670 route_path(
671 "pullrequest_comment_create",
671 "pullrequest_comment_create",
672 repo_name=pull_request.target_repo.scm_instance().name,
672 repo_name=pull_request.target_repo.scm_instance().name,
673 pull_request_id=pull_request.pull_request_id,
673 pull_request_id=pull_request.pull_request_id,
674 ),
674 ),
675 params={"close_pull_request": "true", "csrf_token": csrf_token},
675 params={"close_pull_request": "true", "csrf_token": csrf_token},
676 extra_environ=xhr_header,
676 extra_environ=xhr_header,
677 )
677 )
678
678
679 assert response.json
679 assert response.json
680
680
681 pull_request = PullRequest.get(pull_request_id)
681 pull_request = PullRequest.get(pull_request_id)
682 assert pull_request.is_closed()
682 assert pull_request.is_closed()
683
683
684 # check only the latest status, not the review status
684 # check only the latest status, not the review status
685 status = ChangesetStatusModel().get_status(
685 status = ChangesetStatusModel().get_status(
686 pull_request.source_repo, pull_request=pull_request
686 pull_request.source_repo, pull_request=pull_request
687 )
687 )
688 assert status == ChangesetStatus.STATUS_REJECTED
688 assert status == ChangesetStatus.STATUS_REJECTED
689
689
690 def test_comment_and_close_pull_request_try_edit_comment(
690 def test_comment_and_close_pull_request_try_edit_comment(
691 self, pr_util, csrf_token, xhr_header
691 self, pr_util, csrf_token, xhr_header
692 ):
692 ):
693 pull_request = pr_util.create_pull_request()
693 pull_request = pr_util.create_pull_request()
694 pull_request_id = pull_request.pull_request_id
694 pull_request_id = pull_request.pull_request_id
695 target_scm = pull_request.target_repo.scm_instance()
695 target_scm = pull_request.target_repo.scm_instance()
696 target_scm_name = target_scm.name
696 target_scm_name = target_scm.name
697
697
698 response = self.app.post(
698 response = self.app.post(
699 route_path(
699 route_path(
700 "pullrequest_comment_create",
700 "pullrequest_comment_create",
701 repo_name=target_scm_name,
701 repo_name=target_scm_name,
702 pull_request_id=pull_request_id,
702 pull_request_id=pull_request_id,
703 ),
703 ),
704 params={
704 params={
705 "close_pull_request": "true",
705 "close_pull_request": "true",
706 "csrf_token": csrf_token,
706 "csrf_token": csrf_token,
707 },
707 },
708 extra_environ=xhr_header,
708 extra_environ=xhr_header,
709 )
709 )
710
710
711 assert response.json
711 assert response.json
712
712
713 pull_request = PullRequest.get(pull_request_id)
713 pull_request = PullRequest.get(pull_request_id)
714 target_scm = pull_request.target_repo.scm_instance()
714 target_scm = pull_request.target_repo.scm_instance()
715 target_scm_name = target_scm.name
715 target_scm_name = target_scm.name
716 assert pull_request.is_closed()
716 assert pull_request.is_closed()
717
717
718 # check only the latest status, not the review status
718 # check only the latest status, not the review status
719 status = ChangesetStatusModel().get_status(
719 status = ChangesetStatusModel().get_status(
720 pull_request.source_repo, pull_request=pull_request
720 pull_request.source_repo, pull_request=pull_request
721 )
721 )
722 assert status == ChangesetStatus.STATUS_REJECTED
722 assert status == ChangesetStatus.STATUS_REJECTED
723
723
724 for comment_id in response.json.keys():
724 for comment_id in response.json.keys():
725 test_text = "test"
725 test_text = "test"
726 response = self.app.post(
726 response = self.app.post(
727 route_path(
727 route_path(
728 "pullrequest_comment_edit",
728 "pullrequest_comment_edit",
729 repo_name=target_scm_name,
729 repo_name=target_scm_name,
730 pull_request_id=pull_request_id,
730 pull_request_id=pull_request_id,
731 comment_id=comment_id,
731 comment_id=comment_id,
732 ),
732 ),
733 extra_environ=xhr_header,
733 extra_environ=xhr_header,
734 params={
734 params={
735 "csrf_token": csrf_token,
735 "csrf_token": csrf_token,
736 "text": test_text,
736 "text": test_text,
737 },
737 },
738 status=403,
738 status=403,
739 )
739 )
740 assert response.status_int == 403
740 assert response.status_int == 403
741
741
742 def test_comment_and_comment_edit(self, pr_util, csrf_token, xhr_header):
742 def test_comment_and_comment_edit(self, pr_util, csrf_token, xhr_header):
743 pull_request = pr_util.create_pull_request()
743 pull_request = pr_util.create_pull_request()
744 target_scm = pull_request.target_repo.scm_instance()
744 target_scm = pull_request.target_repo.scm_instance()
745 target_scm_name = target_scm.name
745 target_scm_name = target_scm.name
746
746
747 response = self.app.post(
747 response = self.app.post(
748 route_path(
748 route_path(
749 "pullrequest_comment_create",
749 "pullrequest_comment_create",
750 repo_name=target_scm_name,
750 repo_name=target_scm_name,
751 pull_request_id=pull_request.pull_request_id,
751 pull_request_id=pull_request.pull_request_id,
752 ),
752 ),
753 params={
753 params={
754 "csrf_token": csrf_token,
754 "csrf_token": csrf_token,
755 "text": "init",
755 "text": "init",
756 },
756 },
757 extra_environ=xhr_header,
757 extra_environ=xhr_header,
758 )
758 )
759 assert response.json
759 assert response.json
760
760
761 for comment_id in response.json.keys():
761 for comment_id in response.json.keys():
762 assert comment_id
762 assert comment_id
763 test_text = "test"
763 test_text = "test"
764 self.app.post(
764 self.app.post(
765 route_path(
765 route_path(
766 "pullrequest_comment_edit",
766 "pullrequest_comment_edit",
767 repo_name=target_scm_name,
767 repo_name=target_scm_name,
768 pull_request_id=pull_request.pull_request_id,
768 pull_request_id=pull_request.pull_request_id,
769 comment_id=comment_id,
769 comment_id=comment_id,
770 ),
770 ),
771 extra_environ=xhr_header,
771 extra_environ=xhr_header,
772 params={
772 params={
773 "csrf_token": csrf_token,
773 "csrf_token": csrf_token,
774 "text": test_text,
774 "text": test_text,
775 "version": "0",
775 "version": "0",
776 },
776 },
777 )
777 )
778 text_form_db = (
778 text_form_db = (
779 ChangesetComment.query()
779 ChangesetComment.query()
780 .filter(ChangesetComment.comment_id == comment_id)
780 .filter(ChangesetComment.comment_id == comment_id)
781 .first()
781 .first()
782 .text
782 .text
783 )
783 )
784 assert test_text == text_form_db
784 assert test_text == text_form_db
785
785
786 def test_comment_and_comment_edit_special(self, pr_util, csrf_token, xhr_header):
786 def test_comment_and_comment_edit_special(self, pr_util, csrf_token, xhr_header):
787 pull_request = pr_util.create_pull_request()
787 pull_request = pr_util.create_pull_request()
788 target_scm = pull_request.target_repo.scm_instance()
788 target_scm = pull_request.target_repo.scm_instance()
789 target_scm_name = target_scm.name
789 target_scm_name = target_scm.name
790
790
791 response = self.app.post(
791 response = self.app.post(
792 route_path(
792 route_path(
793 "pullrequest_comment_create",
793 "pullrequest_comment_create",
794 repo_name=target_scm_name,
794 repo_name=target_scm_name,
795 pull_request_id=pull_request.pull_request_id,
795 pull_request_id=pull_request.pull_request_id,
796 ),
796 ),
797 params={
797 params={
798 "csrf_token": csrf_token,
798 "csrf_token": csrf_token,
799 "text": "init",
799 "text": "init",
800 },
800 },
801 extra_environ=xhr_header,
801 extra_environ=xhr_header,
802 )
802 )
803 assert response.json
803 assert response.json
804
804
805 for comment_id in response.json.keys():
805 for comment_id in response.json.keys():
806 test_text = "init"
806 test_text = "init"
807 response = self.app.post(
807 response = self.app.post(
808 route_path(
808 route_path(
809 "pullrequest_comment_edit",
809 "pullrequest_comment_edit",
810 repo_name=target_scm_name,
810 repo_name=target_scm_name,
811 pull_request_id=pull_request.pull_request_id,
811 pull_request_id=pull_request.pull_request_id,
812 comment_id=comment_id,
812 comment_id=comment_id,
813 ),
813 ),
814 extra_environ=xhr_header,
814 extra_environ=xhr_header,
815 params={
815 params={
816 "csrf_token": csrf_token,
816 "csrf_token": csrf_token,
817 "text": test_text,
817 "text": test_text,
818 "version": "0",
818 "version": "0",
819 },
819 },
820 status=404,
820 status=404,
821 )
821 )
822 assert response.status_int == 404
822 assert response.status_int == 404
823
823
824 def test_comment_and_try_edit_already_edited(self, pr_util, csrf_token, xhr_header):
824 def test_comment_and_try_edit_already_edited(self, pr_util, csrf_token, xhr_header):
825 pull_request = pr_util.create_pull_request()
825 pull_request = pr_util.create_pull_request()
826 target_scm = pull_request.target_repo.scm_instance()
826 target_scm = pull_request.target_repo.scm_instance()
827 target_scm_name = target_scm.name
827 target_scm_name = target_scm.name
828
828
829 response = self.app.post(
829 response = self.app.post(
830 route_path(
830 route_path(
831 "pullrequest_comment_create",
831 "pullrequest_comment_create",
832 repo_name=target_scm_name,
832 repo_name=target_scm_name,
833 pull_request_id=pull_request.pull_request_id,
833 pull_request_id=pull_request.pull_request_id,
834 ),
834 ),
835 params={
835 params={
836 "csrf_token": csrf_token,
836 "csrf_token": csrf_token,
837 "text": "init",
837 "text": "init",
838 },
838 },
839 extra_environ=xhr_header,
839 extra_environ=xhr_header,
840 )
840 )
841 assert response.json
841 assert response.json
842 for comment_id in response.json.keys():
842 for comment_id in response.json.keys():
843 test_text = "test"
843 test_text = "test"
844 self.app.post(
844 self.app.post(
845 route_path(
845 route_path(
846 "pullrequest_comment_edit",
846 "pullrequest_comment_edit",
847 repo_name=target_scm_name,
847 repo_name=target_scm_name,
848 pull_request_id=pull_request.pull_request_id,
848 pull_request_id=pull_request.pull_request_id,
849 comment_id=comment_id,
849 comment_id=comment_id,
850 ),
850 ),
851 extra_environ=xhr_header,
851 extra_environ=xhr_header,
852 params={
852 params={
853 "csrf_token": csrf_token,
853 "csrf_token": csrf_token,
854 "text": test_text,
854 "text": test_text,
855 "version": "0",
855 "version": "0",
856 },
856 },
857 )
857 )
858 test_text_v2 = "test_v2"
858 test_text_v2 = "test_v2"
859 response = self.app.post(
859 response = self.app.post(
860 route_path(
860 route_path(
861 "pullrequest_comment_edit",
861 "pullrequest_comment_edit",
862 repo_name=target_scm_name,
862 repo_name=target_scm_name,
863 pull_request_id=pull_request.pull_request_id,
863 pull_request_id=pull_request.pull_request_id,
864 comment_id=comment_id,
864 comment_id=comment_id,
865 ),
865 ),
866 extra_environ=xhr_header,
866 extra_environ=xhr_header,
867 params={
867 params={
868 "csrf_token": csrf_token,
868 "csrf_token": csrf_token,
869 "text": test_text_v2,
869 "text": test_text_v2,
870 "version": "0",
870 "version": "0",
871 },
871 },
872 status=409,
872 status=409,
873 )
873 )
874 assert response.status_int == 409
874 assert response.status_int == 409
875
875
876 text_form_db = (
876 text_form_db = (
877 ChangesetComment.query()
877 ChangesetComment.query()
878 .filter(ChangesetComment.comment_id == comment_id)
878 .filter(ChangesetComment.comment_id == comment_id)
879 .first()
879 .first()
880 .text
880 .text
881 )
881 )
882
882
883 assert test_text == text_form_db
883 assert test_text == text_form_db
884 assert test_text_v2 != text_form_db
884 assert test_text_v2 != text_form_db
885
885
886 def test_comment_and_comment_edit_permissions_forbidden(
886 def test_comment_and_comment_edit_permissions_forbidden(
887 self,
887 self,
888 autologin_regular_user,
888 autologin_regular_user,
889 user_regular,
889 user_regular,
890 user_admin,
890 user_admin,
891 pr_util,
891 pr_util,
892 csrf_token,
892 csrf_token,
893 xhr_header,
893 xhr_header,
894 ):
894 ):
895 pull_request = pr_util.create_pull_request(
895 pull_request = pr_util.create_pull_request(
896 author=user_admin.username, enable_notifications=False
896 author=user_admin.username, enable_notifications=False
897 )
897 )
898 comment = CommentsModel().create(
898 comment = CommentsModel().create(
899 text="test",
899 text="test",
900 repo=pull_request.target_repo.scm_instance().name,
900 repo=pull_request.target_repo.scm_instance().name,
901 user=user_admin,
901 user=user_admin,
902 pull_request=pull_request,
902 pull_request=pull_request,
903 )
903 )
904 response = self.app.post(
904 response = self.app.post(
905 route_path(
905 route_path(
906 "pullrequest_comment_edit",
906 "pullrequest_comment_edit",
907 repo_name=pull_request.target_repo.scm_instance().name,
907 repo_name=pull_request.target_repo.scm_instance().name,
908 pull_request_id=pull_request.pull_request_id,
908 pull_request_id=pull_request.pull_request_id,
909 comment_id=comment.comment_id,
909 comment_id=comment.comment_id,
910 ),
910 ),
911 extra_environ=xhr_header,
911 extra_environ=xhr_header,
912 params={
912 params={
913 "csrf_token": csrf_token,
913 "csrf_token": csrf_token,
914 "text": "test_text",
914 "text": "test_text",
915 },
915 },
916 status=403,
916 status=403,
917 )
917 )
918 assert response.status_int == 403
918 assert response.status_int == 403
919
919
920 def test_create_pull_request(self, backend, csrf_token):
920 def test_create_pull_request(self, backend, csrf_token):
921 commits = [
921 commits = [
922 {"message": "ancestor"},
922 {"message": "ancestor"},
923 {"message": "change"},
923 {"message": "change"},
924 {"message": "change2"},
924 {"message": "change2"},
925 ]
925 ]
926 commit_ids = backend.create_master_repo(commits)
926 commit_ids = backend.create_master_repo(commits)
927 target = backend.create_repo(heads=["ancestor"])
927 target = backend.create_repo(heads=["ancestor"])
928 source = backend.create_repo(heads=["change2"])
928 source = backend.create_repo(heads=["change2"])
929
929
930 response = self.app.post(
930 response = self.app.post(
931 route_path("pullrequest_create", repo_name=source.repo_name),
931 route_path("pullrequest_create", repo_name=source.repo_name),
932 [
932 [
933 ("source_repo", source.repo_name),
933 ("source_repo", source.repo_name),
934 ("source_ref", "branch:default:" + commit_ids["change2"]),
934 ("source_ref", "branch:default:" + commit_ids["change2"]),
935 ("target_repo", target.repo_name),
935 ("target_repo", target.repo_name),
936 ("target_ref", "branch:default:" + commit_ids["ancestor"]),
936 ("target_ref", "branch:default:" + commit_ids["ancestor"]),
937 ("common_ancestor", commit_ids["ancestor"]),
937 ("common_ancestor", commit_ids["ancestor"]),
938 ("pullrequest_title", "Title"),
938 ("pullrequest_title", "Title"),
939 ("pullrequest_desc", "Description"),
939 ("pullrequest_desc", "Description"),
940 ("description_renderer", "markdown"),
940 ("description_renderer", "markdown"),
941 ("__start__", "review_members:sequence"),
941 ("__start__", "review_members:sequence"),
942 ("__start__", "reviewer:mapping"),
942 ("__start__", "reviewer:mapping"),
943 ("user_id", "1"),
943 ("user_id", "1"),
944 ("__start__", "reasons:sequence"),
944 ("__start__", "reasons:sequence"),
945 ("reason", "Some reason"),
945 ("reason", "Some reason"),
946 ("__end__", "reasons:sequence"),
946 ("__end__", "reasons:sequence"),
947 ("__start__", "rules:sequence"),
947 ("__start__", "rules:sequence"),
948 ("__end__", "rules:sequence"),
948 ("__end__", "rules:sequence"),
949 ("mandatory", "False"),
949 ("mandatory", "False"),
950 ("__end__", "reviewer:mapping"),
950 ("__end__", "reviewer:mapping"),
951 ("__end__", "review_members:sequence"),
951 ("__end__", "review_members:sequence"),
952 ("__start__", "revisions:sequence"),
952 ("__start__", "revisions:sequence"),
953 ("revisions", commit_ids["change"]),
953 ("revisions", commit_ids["change"]),
954 ("revisions", commit_ids["change2"]),
954 ("revisions", commit_ids["change2"]),
955 ("__end__", "revisions:sequence"),
955 ("__end__", "revisions:sequence"),
956 ("user", ""),
956 ("user", ""),
957 ("csrf_token", csrf_token),
957 ("csrf_token", csrf_token),
958 ],
958 ],
959 status=302,
959 status=302,
960 )
960 )
961
961
962 location = response.headers["Location"]
962 location = response.headers["Location"]
963 pull_request_id = location.rsplit("/", 1)[1]
963 pull_request_id = location.rsplit("/", 1)[1]
964 assert pull_request_id != "new"
964 assert pull_request_id != "new"
965 pull_request = PullRequest.get(int(pull_request_id))
965 pull_request = PullRequest.get(int(pull_request_id))
966
966
967 # check that we have now both revisions
967 # check that we have now both revisions
968 assert pull_request.revisions == [commit_ids["change2"], commit_ids["change"]]
968 assert pull_request.revisions == [commit_ids["change2"], commit_ids["change"]]
969 assert pull_request.source_ref == "branch:default:" + commit_ids["change2"]
969 assert pull_request.source_ref == "branch:default:" + commit_ids["change2"]
970 expected_target_ref = "branch:default:" + commit_ids["ancestor"]
970 expected_target_ref = "branch:default:" + commit_ids["ancestor"]
971 assert pull_request.target_ref == expected_target_ref
971 assert pull_request.target_ref == expected_target_ref
972
972
973 def test_reviewer_notifications(self, backend, csrf_token):
973 def test_reviewer_notifications(self, backend, csrf_token):
974 # We have to use the app.post for this test, so it will create the
974 # We have to use the app.post for this test, so it will create the
975 # notifications properly with the new PR
975 # notifications properly with the new PR
976 commits = [
976 commits = [
977 {
977 {
978 "message": "ancestor",
978 "message": "ancestor",
979 "added": [FileNode(b"file_A", content=b"content_of_ancestor")],
979 "added": [FileNode(b"file_A", content=b"content_of_ancestor")],
980 },
980 },
981 {
981 {
982 "message": "change",
982 "message": "change",
983 "added": [FileNode(b"file_a", content=b"content_of_change")],
983 "added": [FileNode(b"file_a", content=b"content_of_change")],
984 },
984 },
985 {"message": "change-child"},
985 {"message": "change-child"},
986 {
986 {
987 "message": "ancestor-child",
987 "message": "ancestor-child",
988 "parents": ["ancestor"],
988 "parents": ["ancestor"],
989 "branch": "feature",
989 "branch": "feature",
990 "added": [FileNode(b"file_c", content=b"content_of_ancestor_child")],
990 "added": [FileNode(b"file_c", content=b"content_of_ancestor_child")],
991 },
991 },
992 {"message": "ancestor-child-2", "branch": "feature"},
992 {"message": "ancestor-child-2", "branch": "feature"},
993 ]
993 ]
994 commit_ids = backend.create_master_repo(commits)
994 commit_ids = backend.create_master_repo(commits)
995 target = backend.create_repo(heads=["ancestor-child"])
995 target = backend.create_repo(heads=["ancestor-child"])
996 source = backend.create_repo(heads=["change"])
996 source = backend.create_repo(heads=["change"])
997
997
998 response = self.app.post(
998 response = self.app.post(
999 route_path("pullrequest_create", repo_name=source.repo_name),
999 route_path("pullrequest_create", repo_name=source.repo_name),
1000 [
1000 [
1001 ("source_repo", source.repo_name),
1001 ("source_repo", source.repo_name),
1002 ("source_ref", "branch:default:" + commit_ids["change"]),
1002 ("source_ref", "branch:default:" + commit_ids["change"]),
1003 ("target_repo", target.repo_name),
1003 ("target_repo", target.repo_name),
1004 ("target_ref", "branch:default:" + commit_ids["ancestor-child"]),
1004 ("target_ref", "branch:default:" + commit_ids["ancestor-child"]),
1005 ("common_ancestor", commit_ids["ancestor"]),
1005 ("common_ancestor", commit_ids["ancestor"]),
1006 ("pullrequest_title", "Title"),
1006 ("pullrequest_title", "Title"),
1007 ("pullrequest_desc", "Description"),
1007 ("pullrequest_desc", "Description"),
1008 ("description_renderer", "markdown"),
1008 ("description_renderer", "markdown"),
1009 ("__start__", "review_members:sequence"),
1009 ("__start__", "review_members:sequence"),
1010 ("__start__", "reviewer:mapping"),
1010 ("__start__", "reviewer:mapping"),
1011 ("user_id", "2"),
1011 ("user_id", "2"),
1012 ("__start__", "reasons:sequence"),
1012 ("__start__", "reasons:sequence"),
1013 ("reason", "Some reason"),
1013 ("reason", "Some reason"),
1014 ("__end__", "reasons:sequence"),
1014 ("__end__", "reasons:sequence"),
1015 ("__start__", "rules:sequence"),
1015 ("__start__", "rules:sequence"),
1016 ("__end__", "rules:sequence"),
1016 ("__end__", "rules:sequence"),
1017 ("mandatory", "False"),
1017 ("mandatory", "False"),
1018 ("__end__", "reviewer:mapping"),
1018 ("__end__", "reviewer:mapping"),
1019 ("__end__", "review_members:sequence"),
1019 ("__end__", "review_members:sequence"),
1020 ("__start__", "revisions:sequence"),
1020 ("__start__", "revisions:sequence"),
1021 ("revisions", commit_ids["change"]),
1021 ("revisions", commit_ids["change"]),
1022 ("__end__", "revisions:sequence"),
1022 ("__end__", "revisions:sequence"),
1023 ("user", ""),
1023 ("user", ""),
1024 ("csrf_token", csrf_token),
1024 ("csrf_token", csrf_token),
1025 ],
1025 ],
1026 status=302,
1026 status=302,
1027 )
1027 )
1028
1028
1029 location = response.headers["Location"]
1029 location = response.headers["Location"]
1030
1030
1031 pull_request_id = location.rsplit("/", 1)[1]
1031 pull_request_id = location.rsplit("/", 1)[1]
1032 assert pull_request_id != "new"
1032 assert pull_request_id != "new"
1033 pull_request = PullRequest.get(int(pull_request_id))
1033 pull_request = PullRequest.get(int(pull_request_id))
1034
1034
1035 # Check that a notification was made
1035 # Check that a notification was made
1036 notifications = Notification.query().filter(
1036 notifications = Notification.query().filter(
1037 Notification.created_by == pull_request.author.user_id,
1037 Notification.created_by == pull_request.author.user_id,
1038 Notification.type_ == Notification.TYPE_PULL_REQUEST,
1038 Notification.type_ == Notification.TYPE_PULL_REQUEST,
1039 Notification.subject.contains(
1039 Notification.subject.contains(
1040 "requested a pull request review. !%s" % pull_request_id
1040 "requested a pull request review. !%s" % pull_request_id
1041 ),
1041 ),
1042 )
1042 )
1043 assert len(notifications.all()) == 1
1043 assert len(notifications.all()) == 1
1044
1044
1045 # Change reviewers and check that a notification was made
1045 # Change reviewers and check that a notification was made
1046 PullRequestModel().update_reviewers(
1046 PullRequestModel().update_reviewers(
1047 pull_request.pull_request_id,
1047 pull_request.pull_request_id,
1048 [(1, [], False, "reviewer", [])],
1048 [(1, [], False, "reviewer", [])],
1049 pull_request.author,
1049 pull_request.author,
1050 )
1050 )
1051 assert len(notifications.all()) == 2
1051 assert len(notifications.all()) == 2
1052
1052
1053 @pytest.mark.xfail(reason="unable to fix this test after python3 migration")
1053 def test_create_pull_request_stores_ancestor_commit_id(self, backend, csrf_token):
1054 def test_create_pull_request_stores_ancestor_commit_id(self, backend, csrf_token):
1054 commits = [
1055 commits = [
1055 {
1056 {
1056 "message": "ancestor",
1057 "message": "ancestor",
1057 "added": [FileNode(b"file_A", content=b"content_of_ancestor")],
1058 "added": [FileNode(b"file_A", content=b"content_of_ancestor")],
1058 },
1059 },
1059 {
1060 {
1060 "message": "change",
1061 "message": "change",
1061 "added": [FileNode(b"file_a", content=b"content_of_change")],
1062 "added": [FileNode(b"file_a", content=b"content_of_change")],
1062 },
1063 },
1063 {
1064 {
1064 "message": "change-child",
1065 "message": "change-child",
1065 "added": [FileNode(b"file_c", content=b"content_of_change_2")],
1066 "added": [FileNode(b"file_c", content=b"content_of_change_2")],
1066 },
1067 },
1067 {
1068 {
1068 "message": "ancestor-child",
1069 "message": "ancestor-child",
1069 "parents": ["ancestor"],
1070 "parents": ["ancestor"],
1070 "branch": "feature",
1071 "branch": "feature",
1071 "added": [FileNode(b"file_B", content=b"content_of_ancestor_child")],
1072 "added": [FileNode(b"file_B", content=b"content_of_ancestor_child")],
1072 },
1073 },
1073 {"message": "ancestor-child-2", "branch": "feature"},
1074 {"message": "ancestor-child-2", "branch": "feature"},
1074 ]
1075 ]
1075 commit_ids = backend.create_master_repo(commits)
1076 commit_ids = backend.create_master_repo(commits)
1076 target = backend.create_repo(heads=["ancestor-child"])
1077 target = backend.create_repo(heads=["ancestor-child"])
1077 source = backend.create_repo(heads=["change"])
1078 source = backend.create_repo(heads=["change"])
1078
1079
1079 response = self.app.post(
1080 response = self.app.post(
1080 route_path("pullrequest_create", repo_name=source.repo_name),
1081 route_path("pullrequest_create", repo_name=source.repo_name),
1081 [
1082 [
1082 ("source_repo", source.repo_name),
1083 ("source_repo", source.repo_name),
1083 ("source_ref", "branch:default:" + commit_ids["change"]),
1084 ("source_ref", "branch:default:" + commit_ids["change"]),
1084 ("target_repo", target.repo_name),
1085 ("target_repo", target.repo_name),
1085 ("target_ref", "branch:default:" + commit_ids["ancestor-child"]),
1086 ("target_ref", "branch:default:" + commit_ids["ancestor-child"]),
1086 ("common_ancestor", commit_ids["ancestor"]),
1087 ("common_ancestor", commit_ids["ancestor"]),
1087 ("pullrequest_title", "Title"),
1088 ("pullrequest_title", "Title"),
1088 ("pullrequest_desc", "Description"),
1089 ("pullrequest_desc", "Description"),
1089 ("description_renderer", "markdown"),
1090 ("description_renderer", "markdown"),
1090 ("__start__", "review_members:sequence"),
1091 ("__start__", "review_members:sequence"),
1091 ("__start__", "reviewer:mapping"),
1092 ("__start__", "reviewer:mapping"),
1092 ("user_id", "1"),
1093 ("user_id", "1"),
1093 ("__start__", "reasons:sequence"),
1094 ("__start__", "reasons:sequence"),
1094 ("reason", "Some reason"),
1095 ("reason", "Some reason"),
1095 ("__end__", "reasons:sequence"),
1096 ("__end__", "reasons:sequence"),
1096 ("__start__", "rules:sequence"),
1097 ("__start__", "rules:sequence"),
1097 ("__end__", "rules:sequence"),
1098 ("__end__", "rules:sequence"),
1098 ("mandatory", "False"),
1099 ("mandatory", "False"),
1099 ("__end__", "reviewer:mapping"),
1100 ("__end__", "reviewer:mapping"),
1100 ("__end__", "review_members:sequence"),
1101 ("__end__", "review_members:sequence"),
1101 ("__start__", "revisions:sequence"),
1102 ("__start__", "revisions:sequence"),
1102 ("revisions", commit_ids["change"]),
1103 ("revisions", commit_ids["change"]),
1103 ("__end__", "revisions:sequence"),
1104 ("__end__", "revisions:sequence"),
1104 ("user", ""),
1105 ("user", ""),
1105 ("csrf_token", csrf_token),
1106 ("csrf_token", csrf_token),
1106 ],
1107 ],
1107 status=302,
1108 status=302,
1108 )
1109 )
1109
1110
1110 location = response.headers["Location"]
1111 location = response.headers["Location"]
1111
1112
1112 pull_request_id = location.rsplit("/", 1)[1]
1113 pull_request_id = location.rsplit("/", 1)[1]
1113 assert pull_request_id != "new"
1114 assert pull_request_id != "new"
1114 pull_request = PullRequest.get(int(pull_request_id))
1115 pull_request = PullRequest.get(int(pull_request_id))
1115
1116
1116 # target_ref has to point to the ancestor's commit_id in order to
1117 # target_ref has to point to the ancestor's commit_id in order to
1117 # show the correct diff
1118 # show the correct diff
1118 expected_target_ref = "branch:default:" + commit_ids["ancestor"]
1119 expected_target_ref = "branch:default:" + commit_ids["ancestor"]
1119 assert pull_request.target_ref == expected_target_ref
1120 assert pull_request.target_ref == expected_target_ref
1120
1121
1121 # Check generated diff contents
1122 # Check generated diff contents
1122 response = response.follow()
1123 response = response.follow()
1123 response.mustcontain(no=["content_of_ancestor"])
1124 response.mustcontain(no=["content_of_ancestor"])
1124 response.mustcontain(no=["content_of_ancestor-child"])
1125 response.mustcontain(no=["content_of_ancestor-child"])
1125 response.mustcontain("content_of_change")
1126 response.mustcontain("content_of_change")
1126
1127
1127 def test_merge_pull_request_enabled(self, pr_util, csrf_token):
1128 def test_merge_pull_request_enabled(self, pr_util, csrf_token):
1128 # Clear any previous calls to rcextensions
1129 # Clear any previous calls to rcextensions
1129 rhodecode.EXTENSIONS.calls.clear()
1130 rhodecode.EXTENSIONS.calls.clear()
1130
1131
1131 pull_request = pr_util.create_pull_request(approved=True, mergeable=True)
1132 pull_request = pr_util.create_pull_request(approved=True, mergeable=True)
1132 pull_request_id = pull_request.pull_request_id
1133 pull_request_id = pull_request.pull_request_id
1133 repo_name = (pull_request.target_repo.scm_instance().name,)
1134 repo_name = (pull_request.target_repo.scm_instance().name,)
1134
1135
1135 url = route_path(
1136 url = route_path(
1136 "pullrequest_merge",
1137 "pullrequest_merge",
1137 repo_name=str(repo_name[0]),
1138 repo_name=str(repo_name[0]),
1138 pull_request_id=pull_request_id,
1139 pull_request_id=pull_request_id,
1139 )
1140 )
1140 response = self.app.post(url, params={"csrf_token": csrf_token}).follow()
1141 response = self.app.post(url, params={"csrf_token": csrf_token}).follow()
1141
1142
1142 pull_request = PullRequest.get(pull_request_id)
1143 pull_request = PullRequest.get(pull_request_id)
1143
1144
1144 assert response.status_int == 200
1145 assert response.status_int == 200
1145 assert pull_request.is_closed()
1146 assert pull_request.is_closed()
1146 assert_pull_request_status(pull_request, ChangesetStatus.STATUS_APPROVED)
1147 assert_pull_request_status(pull_request, ChangesetStatus.STATUS_APPROVED)
1147
1148
1148 # Check the relevant log entries were added
1149 # Check the relevant log entries were added
1149 user_logs = UserLog.query().order_by(UserLog.user_log_id.desc()).limit(3)
1150 user_logs = UserLog.query().order_by(UserLog.user_log_id.desc()).limit(3)
1150 actions = [log.action for log in user_logs]
1151 actions = [log.action for log in user_logs]
1151 pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
1152 pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
1152 expected_actions = [
1153 expected_actions = [
1153 "repo.pull_request.close",
1154 "repo.pull_request.close",
1154 "repo.pull_request.merge",
1155 "repo.pull_request.merge",
1155 "repo.pull_request.comment.create",
1156 "repo.pull_request.comment.create",
1156 ]
1157 ]
1157 assert actions == expected_actions
1158 assert actions == expected_actions
1158
1159
1159 user_logs = UserLog.query().order_by(UserLog.user_log_id.desc()).limit(4)
1160 user_logs = UserLog.query().order_by(UserLog.user_log_id.desc()).limit(4)
1160 actions = [log for log in user_logs]
1161 actions = [log for log in user_logs]
1161 assert actions[-1].action == "user.push"
1162 assert actions[-1].action == "user.push"
1162 assert actions[-1].action_data["commit_ids"] == pr_commit_ids
1163 assert actions[-1].action_data["commit_ids"] == pr_commit_ids
1163
1164
1164 # Check post_push rcextension was really executed
1165 # Check post_push rcextension was really executed
1165 push_calls = rhodecode.EXTENSIONS.calls["_push_hook"]
1166 push_calls = rhodecode.EXTENSIONS.calls["_push_hook"]
1166 assert len(push_calls) == 1
1167 assert len(push_calls) == 1
1167 unused_last_call_args, last_call_kwargs = push_calls[0]
1168 unused_last_call_args, last_call_kwargs = push_calls[0]
1168 assert last_call_kwargs["action"] == "push"
1169 assert last_call_kwargs["action"] == "push"
1169 assert last_call_kwargs["commit_ids"] == pr_commit_ids
1170 assert last_call_kwargs["commit_ids"] == pr_commit_ids
1170
1171
1171 def test_merge_pull_request_disabled(self, pr_util, csrf_token):
1172 def test_merge_pull_request_disabled(self, pr_util, csrf_token):
1172 pull_request = pr_util.create_pull_request(mergeable=False)
1173 pull_request = pr_util.create_pull_request(mergeable=False)
1173 pull_request_id = pull_request.pull_request_id
1174 pull_request_id = pull_request.pull_request_id
1174 pull_request = PullRequest.get(pull_request_id)
1175 pull_request = PullRequest.get(pull_request_id)
1175
1176
1176 response = self.app.post(
1177 response = self.app.post(
1177 route_path(
1178 route_path(
1178 "pullrequest_merge",
1179 "pullrequest_merge",
1179 repo_name=pull_request.target_repo.scm_instance().name,
1180 repo_name=pull_request.target_repo.scm_instance().name,
1180 pull_request_id=pull_request.pull_request_id,
1181 pull_request_id=pull_request.pull_request_id,
1181 ),
1182 ),
1182 params={"csrf_token": csrf_token},
1183 params={"csrf_token": csrf_token},
1183 ).follow()
1184 ).follow()
1184
1185
1185 assert response.status_int == 200
1186 assert response.status_int == 200
1186 response.mustcontain(
1187 response.mustcontain(
1187 "Merge is not currently possible because of below failed checks."
1188 "Merge is not currently possible because of below failed checks."
1188 )
1189 )
1189 response.mustcontain("Server-side pull request merging is disabled.")
1190 response.mustcontain("Server-side pull request merging is disabled.")
1190
1191
1191 @pytest.mark.skip_backends("svn")
1192 @pytest.mark.skip_backends("svn")
1192 def test_merge_pull_request_not_approved(self, pr_util, csrf_token):
1193 def test_merge_pull_request_not_approved(self, pr_util, csrf_token):
1193 pull_request = pr_util.create_pull_request(mergeable=True)
1194 pull_request = pr_util.create_pull_request(mergeable=True)
1194 pull_request_id = pull_request.pull_request_id
1195 pull_request_id = pull_request.pull_request_id
1195 repo_name = pull_request.target_repo.scm_instance().name
1196 repo_name = pull_request.target_repo.scm_instance().name
1196
1197
1197 response = self.app.post(
1198 response = self.app.post(
1198 route_path(
1199 route_path(
1199 "pullrequest_merge",
1200 "pullrequest_merge",
1200 repo_name=repo_name,
1201 repo_name=repo_name,
1201 pull_request_id=pull_request_id,
1202 pull_request_id=pull_request_id,
1202 ),
1203 ),
1203 params={"csrf_token": csrf_token},
1204 params={"csrf_token": csrf_token},
1204 ).follow()
1205 ).follow()
1205
1206
1206 assert response.status_int == 200
1207 assert response.status_int == 200
1207
1208
1208 response.mustcontain(
1209 response.mustcontain(
1209 "Merge is not currently possible because of below failed checks."
1210 "Merge is not currently possible because of below failed checks."
1210 )
1211 )
1211 response.mustcontain("Pull request reviewer approval is pending.")
1212 response.mustcontain("Pull request reviewer approval is pending.")
1212
1213
1213 def test_merge_pull_request_renders_failure_reason(
1214 def test_merge_pull_request_renders_failure_reason(
1214 self, user_regular, csrf_token, pr_util
1215 self, user_regular, csrf_token, pr_util
1215 ):
1216 ):
1216 pull_request = pr_util.create_pull_request(mergeable=True, approved=True)
1217 pull_request = pr_util.create_pull_request(mergeable=True, approved=True)
1217 pull_request_id = pull_request.pull_request_id
1218 pull_request_id = pull_request.pull_request_id
1218 repo_name = pull_request.target_repo.scm_instance().name
1219 repo_name = pull_request.target_repo.scm_instance().name
1219
1220
1220 merge_resp = MergeResponse(
1221 merge_resp = MergeResponse(
1221 True,
1222 True,
1222 False,
1223 False,
1223 Reference("commit", "STUB_COMMIT_ID", "STUB_COMMIT_ID"),
1224 Reference("commit", "STUB_COMMIT_ID", "STUB_COMMIT_ID"),
1224 MergeFailureReason.PUSH_FAILED,
1225 MergeFailureReason.PUSH_FAILED,
1225 metadata={"target": "shadow repo", "merge_commit": "xxx"},
1226 metadata={"target": "shadow repo", "merge_commit": "xxx"},
1226 )
1227 )
1227 model_patcher = mock.patch.multiple(
1228 model_patcher = mock.patch.multiple(
1228 PullRequestModel,
1229 PullRequestModel,
1229 merge_repo=mock.Mock(return_value=merge_resp),
1230 merge_repo=mock.Mock(return_value=merge_resp),
1230 merge_status=mock.Mock(return_value=(None, True, "WRONG_MESSAGE")),
1231 merge_status=mock.Mock(return_value=(None, True, "WRONG_MESSAGE")),
1231 )
1232 )
1232
1233
1233 with model_patcher:
1234 with model_patcher:
1234 response = self.app.post(
1235 response = self.app.post(
1235 route_path(
1236 route_path(
1236 "pullrequest_merge",
1237 "pullrequest_merge",
1237 repo_name=repo_name,
1238 repo_name=repo_name,
1238 pull_request_id=pull_request_id,
1239 pull_request_id=pull_request_id,
1239 ),
1240 ),
1240 params={"csrf_token": csrf_token},
1241 params={"csrf_token": csrf_token},
1241 status=302,
1242 status=302,
1242 )
1243 )
1243
1244
1244 merge_resp = MergeResponse(
1245 merge_resp = MergeResponse(
1245 True,
1246 True,
1246 True,
1247 True,
1247 Reference("commit", "STUB_COMMIT_ID", "STUB_COMMIT_ID"),
1248 Reference("commit", "STUB_COMMIT_ID", "STUB_COMMIT_ID"),
1248 MergeFailureReason.PUSH_FAILED,
1249 MergeFailureReason.PUSH_FAILED,
1249 metadata={"target": "shadow repo", "merge_commit": "xxx"},
1250 metadata={"target": "shadow repo", "merge_commit": "xxx"},
1250 )
1251 )
1251 assert_session_flash(response, merge_resp.merge_status_message)
1252 assert_session_flash(response, merge_resp.merge_status_message)
1252
1253
1253 def test_update_source_revision(self, backend, csrf_token):
1254 def test_update_source_revision(self, backend, csrf_token):
1254 commits = [
1255 commits = [
1255 {"message": "ancestor"},
1256 {"message": "ancestor"},
1256 {"message": "change"},
1257 {"message": "change"},
1257 {"message": "change-2"},
1258 {"message": "change-2"},
1258 ]
1259 ]
1259 commit_ids = backend.create_master_repo(commits)
1260 commit_ids = backend.create_master_repo(commits)
1260 target = backend.create_repo(heads=["ancestor"])
1261 target = backend.create_repo(heads=["ancestor"])
1261 source = backend.create_repo(heads=["change"])
1262 source = backend.create_repo(heads=["change"])
1262
1263
1263 # create pr from a in source to A in target
1264 # create pr from a in source to A in target
1264 pull_request = PullRequest()
1265 pull_request = PullRequest()
1265
1266
1266 pull_request.source_repo = source
1267 pull_request.source_repo = source
1267 pull_request.source_ref = "branch:{branch}:{commit_id}".format(
1268 pull_request.source_ref = "branch:{branch}:{commit_id}".format(
1268 branch=backend.default_branch_name, commit_id=commit_ids["change"]
1269 branch=backend.default_branch_name, commit_id=commit_ids["change"]
1269 )
1270 )
1270
1271
1271 pull_request.target_repo = target
1272 pull_request.target_repo = target
1272 pull_request.target_ref = "branch:{branch}:{commit_id}".format(
1273 pull_request.target_ref = "branch:{branch}:{commit_id}".format(
1273 branch=backend.default_branch_name, commit_id=commit_ids["ancestor"]
1274 branch=backend.default_branch_name, commit_id=commit_ids["ancestor"]
1274 )
1275 )
1275
1276
1276 pull_request.revisions = [commit_ids["change"]]
1277 pull_request.revisions = [commit_ids["change"]]
1277 pull_request.title = "Test"
1278 pull_request.title = "Test"
1278 pull_request.description = "Description"
1279 pull_request.description = "Description"
1279 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1280 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1280 pull_request.pull_request_state = PullRequest.STATE_CREATED
1281 pull_request.pull_request_state = PullRequest.STATE_CREATED
1281 Session().add(pull_request)
1282 Session().add(pull_request)
1282 Session().commit()
1283 Session().commit()
1283 pull_request_id = pull_request.pull_request_id
1284 pull_request_id = pull_request.pull_request_id
1284
1285
1285 # source has ancestor - change - change-2
1286 # source has ancestor - change - change-2
1286 backend.pull_heads(source, heads=["change-2"])
1287 backend.pull_heads(source, heads=["change-2"])
1287 target_repo_name = target.repo_name
1288 target_repo_name = target.repo_name
1288
1289
1289 # update PR
1290 # update PR
1290 self.app.post(
1291 self.app.post(
1291 route_path(
1292 route_path(
1292 "pullrequest_update",
1293 "pullrequest_update",
1293 repo_name=target_repo_name,
1294 repo_name=target_repo_name,
1294 pull_request_id=pull_request_id,
1295 pull_request_id=pull_request_id,
1295 ),
1296 ),
1296 params={"update_commits": "true", "csrf_token": csrf_token},
1297 params={"update_commits": "true", "csrf_token": csrf_token},
1297 )
1298 )
1298
1299
1299 response = self.app.get(
1300 response = self.app.get(
1300 route_path(
1301 route_path(
1301 "pullrequest_show",
1302 "pullrequest_show",
1302 repo_name=target_repo_name,
1303 repo_name=target_repo_name,
1303 pull_request_id=pull_request.pull_request_id,
1304 pull_request_id=pull_request.pull_request_id,
1304 )
1305 )
1305 )
1306 )
1306
1307
1307 assert response.status_int == 200
1308 assert response.status_int == 200
1308 response.mustcontain("Pull request updated to")
1309 response.mustcontain("Pull request updated to")
1309 response.mustcontain("with 1 added, 0 removed commits.")
1310 response.mustcontain("with 1 added, 0 removed commits.")
1310
1311
1311 # check that we have now both revisions
1312 # check that we have now both revisions
1312 pull_request = PullRequest.get(pull_request_id)
1313 pull_request = PullRequest.get(pull_request_id)
1313 assert pull_request.revisions == [commit_ids["change-2"], commit_ids["change"]]
1314 assert pull_request.revisions == [commit_ids["change-2"], commit_ids["change"]]
1314
1315
1315 def test_update_target_revision(self, backend, csrf_token):
1316 def test_update_target_revision(self, backend, csrf_token):
1316 """
1317 """
1317 Checks when we add more commits into a target branch, and update PR
1318 Checks when we add more commits into a target branch, and update PR
1318 """
1319 """
1319
1320
1320 commits = [
1321 commits = [
1321 {"message": "commit-a"}, # main branch (our PR target)
1322 {"message": "commit-a"}, # main branch (our PR target)
1322 {"message": "commit-b"}, # Initial source
1323 {"message": "commit-b"}, # Initial source
1323 {"message": "commit-c"},
1324 {"message": "commit-c"},
1324
1325
1325 {"message": "commit-a-prime", "branch": "feature", "parents": ["commit-a"]}, # main branch (source)
1326 {"message": "commit-a-prime", "branch": "feature", "parents": ["commit-a"]}, # main branch (source)
1326 ]
1327 ]
1327
1328
1328 commit_ids = backend.create_master_repo(commits)
1329 commit_ids = backend.create_master_repo(commits)
1329 target = backend.create_repo(heads=["commit-a"])
1330 target = backend.create_repo(heads=["commit-a"])
1330 source = backend.create_repo(heads=["commit-b"])
1331 source = backend.create_repo(heads=["commit-b"])
1331 target_repo_name = target.repo_name
1332 target_repo_name = target.repo_name
1332
1333
1333 # create pr from commit-b to commit-a
1334 # create pr from commit-b to commit-a
1334 pull_request = PullRequest()
1335 pull_request = PullRequest()
1335
1336
1336 pull_request.target_repo = target
1337 pull_request.target_repo = target
1337 pull_request.target_ref = "branch:{branch}:{commit_id}".format(
1338 pull_request.target_ref = "branch:{branch}:{commit_id}".format(
1338 branch=backend.default_branch_name, commit_id=commit_ids["commit-a"]
1339 branch=backend.default_branch_name, commit_id=commit_ids["commit-a"]
1339 )
1340 )
1340
1341
1341 pull_request.source_repo = source
1342 pull_request.source_repo = source
1342 pull_request.source_ref = "branch:{branch}:{commit_id}".format(
1343 pull_request.source_ref = "branch:{branch}:{commit_id}".format(
1343 branch=backend.default_branch_name, commit_id=commit_ids["commit-b"]
1344 branch=backend.default_branch_name, commit_id=commit_ids["commit-b"]
1344 )
1345 )
1345
1346
1346 pull_request.revisions = [commit_ids["commit-b"]]
1347 pull_request.revisions = [commit_ids["commit-b"]]
1347 pull_request.title = "Test"
1348 pull_request.title = "Test"
1348 pull_request.description = "Description"
1349 pull_request.description = "Description"
1349 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1350 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1350 pull_request.pull_request_state = PullRequest.STATE_CREATED
1351 pull_request.pull_request_state = PullRequest.STATE_CREATED
1351
1352
1352 Session().add(pull_request)
1353 Session().add(pull_request)
1353 Session().commit()
1354 Session().commit()
1354 pull_request_id = pull_request.pull_request_id
1355 pull_request_id = pull_request.pull_request_id
1355
1356
1356 # target - add one commit on top commit-a -> commit-b
1357 # target - add one commit on top commit-a -> commit-b
1357 backend.pull_heads(target, heads=["commit-b"])
1358 backend.pull_heads(target, heads=["commit-b"])
1358
1359
1359 # source has ancestor - ancestor-new - change-rebased
1360 # source has ancestor - ancestor-new - change-rebased
1360 backend.pull_heads(source, heads=["commit-c"])
1361 backend.pull_heads(source, heads=["commit-c"])
1361
1362
1362 # update PR
1363 # update PR
1363 url = route_path(
1364 url = route_path(
1364 "pullrequest_update",
1365 "pullrequest_update",
1365 repo_name=target_repo_name,
1366 repo_name=target_repo_name,
1366 pull_request_id=pull_request_id,
1367 pull_request_id=pull_request_id,
1367 )
1368 )
1368 self.app.post(
1369 self.app.post(
1369 url, params={"update_commits": "true", "csrf_token": csrf_token}, status=200
1370 url, params={"update_commits": "true", "csrf_token": csrf_token}, status=200
1370 )
1371 )
1371
1372
1372 # check that we have now both revisions
1373 # check that we have now both revisions
1373 pull_request = PullRequest.get(pull_request_id)
1374 pull_request = PullRequest.get(pull_request_id)
1374 assert pull_request.revisions == [commit_ids["commit-c"]]
1375 assert pull_request.revisions == [commit_ids["commit-c"]]
1375 assert pull_request.target_ref == "branch:{branch}:{commit_id}".format(
1376 assert pull_request.target_ref == "branch:{branch}:{commit_id}".format(
1376 branch=backend.default_branch_name, commit_id=commit_ids["commit-b"]
1377 branch=backend.default_branch_name, commit_id=commit_ids["commit-b"]
1377 )
1378 )
1378
1379
1379 response = self.app.get(
1380 response = self.app.get(
1380 route_path(
1381 route_path(
1381 "pullrequest_show",
1382 "pullrequest_show",
1382 repo_name=target_repo_name,
1383 repo_name=target_repo_name,
1383 pull_request_id=pull_request.pull_request_id,
1384 pull_request_id=pull_request.pull_request_id,
1384 )
1385 )
1385 )
1386 )
1386 assert response.status_int == 200
1387 assert response.status_int == 200
1387 response.mustcontain("Pull request updated to")
1388 response.mustcontain("Pull request updated to")
1388 response.mustcontain("with 1 added, 1 removed commits.")
1389 response.mustcontain("with 1 added, 1 removed commits.")
1389
1390
1390 def test_update_target_revision_with_removal_of_1_commit_git(
1391 def test_update_target_revision_with_removal_of_1_commit_git(
1391 self, backend_git, csrf_token
1392 self, backend_git, csrf_token
1392 ):
1393 ):
1393 backend = backend_git
1394 backend = backend_git
1394 commits = [
1395 commits = [
1395 {"message": "master-commit-1"},
1396 {"message": "master-commit-1"},
1396 {"message": "master-commit-2-change-1"},
1397 {"message": "master-commit-2-change-1"},
1397 {"message": "master-commit-3-change-2"},
1398 {"message": "master-commit-3-change-2"},
1398 {
1399 {
1399 "message": "feat-commit-1",
1400 "message": "feat-commit-1",
1400 "parents": ["master-commit-1"],
1401 "parents": ["master-commit-1"],
1401 "branch": "feature",
1402 "branch": "feature",
1402 },
1403 },
1403 {"message": "feat-commit-2", "branch": "feature"},
1404 {"message": "feat-commit-2", "branch": "feature"},
1404 ]
1405 ]
1405 commit_ids = backend.create_master_repo(commits)
1406 commit_ids = backend.create_master_repo(commits)
1406 target = backend.create_repo(heads=["master-commit-3-change-2"])
1407 target = backend.create_repo(heads=["master-commit-3-change-2"])
1407 source = backend.create_repo(heads=["feat-commit-2"])
1408 source = backend.create_repo(heads=["feat-commit-2"])
1408
1409
1409 # create pr from a in source to A in target
1410 # create pr from a in source to A in target
1410 pull_request = PullRequest()
1411 pull_request = PullRequest()
1411 pull_request.source_repo = source
1412 pull_request.source_repo = source
1412
1413
1413 pull_request.source_ref = "branch:{branch}:{commit_id}".format(
1414 pull_request.source_ref = "branch:{branch}:{commit_id}".format(
1414 branch=backend.default_branch_name,
1415 branch=backend.default_branch_name,
1415 commit_id=commit_ids["master-commit-3-change-2"],
1416 commit_id=commit_ids["master-commit-3-change-2"],
1416 )
1417 )
1417
1418
1418 pull_request.target_repo = target
1419 pull_request.target_repo = target
1419 pull_request.target_ref = "branch:{branch}:{commit_id}".format(
1420 pull_request.target_ref = "branch:{branch}:{commit_id}".format(
1420 branch=backend.default_branch_name, commit_id=commit_ids["feat-commit-2"]
1421 branch=backend.default_branch_name, commit_id=commit_ids["feat-commit-2"]
1421 )
1422 )
1422
1423
1423 pull_request.revisions = [
1424 pull_request.revisions = [
1424 commit_ids["feat-commit-1"],
1425 commit_ids["feat-commit-1"],
1425 commit_ids["feat-commit-2"],
1426 commit_ids["feat-commit-2"],
1426 ]
1427 ]
1427 pull_request.title = "Test"
1428 pull_request.title = "Test"
1428 pull_request.description = "Description"
1429 pull_request.description = "Description"
1429 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1430 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1430 pull_request.pull_request_state = PullRequest.STATE_CREATED
1431 pull_request.pull_request_state = PullRequest.STATE_CREATED
1431 Session().add(pull_request)
1432 Session().add(pull_request)
1432 Session().commit()
1433 Session().commit()
1433 pull_request_id = pull_request.pull_request_id
1434 pull_request_id = pull_request.pull_request_id
1434
1435
1435 # PR is created, now we simulate a force-push into target,
1436 # PR is created, now we simulate a force-push into target,
1436 # that drops a 2 last commits
1437 # that drops a 2 last commits
1437 vcsrepo = target.scm_instance()
1438 vcsrepo = target.scm_instance()
1438 vcsrepo.config.clear_section("hooks")
1439 vcsrepo.config.clear_section("hooks")
1439 vcsrepo.run_git_command(["reset", "--soft", "HEAD~2"])
1440 vcsrepo.run_git_command(["reset", "--soft", "HEAD~2"])
1440 target_repo_name = target.repo_name
1441 target_repo_name = target.repo_name
1441
1442
1442 # update PR
1443 # update PR
1443 url = route_path(
1444 url = route_path(
1444 "pullrequest_update",
1445 "pullrequest_update",
1445 repo_name=target_repo_name,
1446 repo_name=target_repo_name,
1446 pull_request_id=pull_request_id,
1447 pull_request_id=pull_request_id,
1447 )
1448 )
1448 self.app.post(
1449 self.app.post(
1449 url, params={"update_commits": "true", "csrf_token": csrf_token}, status=200
1450 url, params={"update_commits": "true", "csrf_token": csrf_token}, status=200
1450 )
1451 )
1451
1452
1452 response = self.app.get(
1453 response = self.app.get(
1453 route_path("pullrequest_new", repo_name=target_repo_name)
1454 route_path("pullrequest_new", repo_name=target_repo_name)
1454 )
1455 )
1455 assert response.status_int == 200
1456 assert response.status_int == 200
1456 response.mustcontain("Pull request updated to")
1457 response.mustcontain("Pull request updated to")
1457 response.mustcontain("with 0 added, 0 removed commits.")
1458 response.mustcontain("with 0 added, 0 removed commits.")
1458
1459
1459 def test_update_pr_ancestor_reference(self, csrf_token, pr_util: PRTestUtility):
1460 def test_update_pr_ancestor_reference(self, csrf_token, pr_util: PRTestUtility):
1460 commits = [
1461 commits = [
1461 {"message": "ancestor"},
1462 {"message": "ancestor"},
1462 {"message": "change"},
1463 {"message": "change"},
1463 {"message": "change-2"},
1464 {"message": "change-2"},
1464
1465
1465 {"message": "ancestor-new", "parents": ["ancestor"], "branch": "feature"},
1466 {"message": "ancestor-new", "parents": ["ancestor"], "branch": "feature"},
1466 {"message": "change-rebased", "branch": "feature"},
1467 {"message": "change-rebased", "branch": "feature"},
1467 ]
1468 ]
1468
1469
1469 pull_request = pr_util.create_pull_request(
1470 pull_request = pr_util.create_pull_request(
1470 commits,
1471 commits,
1471 target_head="ancestor",
1472 target_head="ancestor",
1472 source_head="change",
1473 source_head="change",
1473 revisions=["change"],
1474 revisions=["change"],
1474 )
1475 )
1475 pull_request_id = pull_request.pull_request_id
1476 pull_request_id = pull_request.pull_request_id
1476 target_repo_name = pr_util.target_repository.repo_name
1477 target_repo_name = pr_util.target_repository.repo_name
1477 commit_ids = pr_util.commit_ids
1478 commit_ids = pr_util.commit_ids
1478
1479
1479 assert pull_request.revisions == [commit_ids["change"]]
1480 assert pull_request.revisions == [commit_ids["change"]]
1480 assert list(pull_request.target_repo.scm_instance(cache=False).branches.keys()) == [pr_util.backend.default_branch_name]
1481 assert list(pull_request.target_repo.scm_instance(cache=False).branches.keys()) == [pr_util.backend.default_branch_name]
1481 assert list(pull_request.source_repo.scm_instance(cache=False).branches.keys()) == [pr_util.backend.default_branch_name]
1482 assert list(pull_request.source_repo.scm_instance(cache=False).branches.keys()) == [pr_util.backend.default_branch_name]
1482
1483
1483 branch = "feature"
1484 branch = "feature"
1484 pr_util.update_target_repository(head="ancestor-new", do_fetch=True)
1485 pr_util.update_target_repository(head="ancestor-new", do_fetch=True)
1485 pr_util.set_pr_target_ref(ref_type="branch", ref_name=branch, ref_commit_id=commit_ids["ancestor-new"])
1486 pr_util.set_pr_target_ref(ref_type="branch", ref_name=branch, ref_commit_id=commit_ids["ancestor-new"])
1486
1487
1487 pr_util.update_source_repository(head="change-rebased", do_fetch=True)
1488 pr_util.update_source_repository(head="change-rebased", do_fetch=True)
1488 pr_util.set_pr_source_ref(ref_type="branch", ref_name=branch, ref_commit_id=commit_ids["change-rebased"])
1489 pr_util.set_pr_source_ref(ref_type="branch", ref_name=branch, ref_commit_id=commit_ids["change-rebased"])
1489
1490
1490 assert list(pull_request.target_repo.scm_instance(cache=False).branches.keys()) == [pr_util.backend.default_branch_name, branch]
1491 assert list(pull_request.target_repo.scm_instance(cache=False).branches.keys()) == [pr_util.backend.default_branch_name, branch]
1491 assert list(pull_request.source_repo.scm_instance(cache=False).branches.keys()) == [pr_util.backend.default_branch_name, branch]
1492 assert list(pull_request.source_repo.scm_instance(cache=False).branches.keys()) == [pr_util.backend.default_branch_name, branch]
1492
1493
1493 Session().add(pr_util.pull_request)
1494 Session().add(pr_util.pull_request)
1494 Session().commit()
1495 Session().commit()
1495
1496
1496 self.app.post(
1497 self.app.post(
1497 route_path(
1498 route_path(
1498 "pullrequest_update",
1499 "pullrequest_update",
1499 repo_name=target_repo_name,
1500 repo_name=target_repo_name,
1500 pull_request_id=pull_request_id,
1501 pull_request_id=pull_request_id,
1501 ),
1502 ),
1502 params={"update_commits": "true", "csrf_token": csrf_token, "force_refresh": True},
1503 params={"update_commits": "true", "csrf_token": csrf_token, "force_refresh": True},
1503 status=200,
1504 status=200,
1504 )
1505 )
1505
1506
1506
1507
1507 # response = self.app.get(
1508 # response = self.app.get(
1508 # route_path(
1509 # route_path(
1509 # "pullrequest_show", repo_name=target_repo_name, pull_request_id=pull_request_id,
1510 # "pullrequest_show", repo_name=target_repo_name, pull_request_id=pull_request_id,
1510 # params={"force_refresh": True}
1511 # params={"force_refresh": True}
1511 # ),
1512 # ),
1512 # )
1513 # )
1513 #
1514 #
1514 # response.mustcontain("Pull request updated to")
1515 # response.mustcontain("Pull request updated to")
1515 # response.mustcontain("with 1 added, 0 removed commits.")
1516 # response.mustcontain("with 1 added, 0 removed commits.")
1516
1517
1517 pull_request = PullRequest.get(pull_request_id)
1518 pull_request = PullRequest.get(pull_request_id)
1518
1519
1519 assert pull_request.target_ref == "branch:{branch}:{commit_id}".format(
1520 assert pull_request.target_ref == "branch:{branch}:{commit_id}".format(
1520 branch="feature", commit_id=commit_ids["ancestor-new"])
1521 branch="feature", commit_id=commit_ids["ancestor-new"])
1521
1522
1522 assert pull_request.revisions == [commit_ids["change-rebased"]]
1523 assert pull_request.revisions == [commit_ids["change-rebased"]]
1523
1524
1524
1525
1525 def test_remove_pull_request_branch(self, backend_git, csrf_token):
1526 def test_remove_pull_request_branch(self, backend_git, csrf_token):
1526 branch_name = "development"
1527 branch_name = "development"
1527 commits = [
1528 commits = [
1528 {"message": "initial-commit"},
1529 {"message": "initial-commit"},
1529 {"message": "old-feature"},
1530 {"message": "old-feature"},
1530 {"message": "new-feature", "branch": branch_name},
1531 {"message": "new-feature", "branch": branch_name},
1531 ]
1532 ]
1532 repo = backend_git.create_repo(commits)
1533 repo = backend_git.create_repo(commits)
1533 repo_name = repo.repo_name
1534 repo_name = repo.repo_name
1534 commit_ids = backend_git.commit_ids
1535 commit_ids = backend_git.commit_ids
1535
1536
1536 pull_request = PullRequest()
1537 pull_request = PullRequest()
1537 pull_request.source_repo = repo
1538 pull_request.source_repo = repo
1538 pull_request.target_repo = repo
1539 pull_request.target_repo = repo
1539 pull_request.source_ref = "branch:{branch}:{commit_id}".format(
1540 pull_request.source_ref = "branch:{branch}:{commit_id}".format(
1540 branch=branch_name, commit_id=commit_ids["new-feature"]
1541 branch=branch_name, commit_id=commit_ids["new-feature"]
1541 )
1542 )
1542 pull_request.target_ref = "branch:{branch}:{commit_id}".format(
1543 pull_request.target_ref = "branch:{branch}:{commit_id}".format(
1543 branch=backend_git.default_branch_name, commit_id=commit_ids["old-feature"]
1544 branch=backend_git.default_branch_name, commit_id=commit_ids["old-feature"]
1544 )
1545 )
1545 pull_request.revisions = [commit_ids["new-feature"]]
1546 pull_request.revisions = [commit_ids["new-feature"]]
1546 pull_request.title = "Test"
1547 pull_request.title = "Test"
1547 pull_request.description = "Description"
1548 pull_request.description = "Description"
1548 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1549 pull_request.author = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
1549 pull_request.pull_request_state = PullRequest.STATE_CREATED
1550 pull_request.pull_request_state = PullRequest.STATE_CREATED
1550 Session().add(pull_request)
1551 Session().add(pull_request)
1551 Session().commit()
1552 Session().commit()
1552
1553
1553 pull_request_id = pull_request.pull_request_id
1554 pull_request_id = pull_request.pull_request_id
1554
1555
1555 vcs = repo.scm_instance()
1556 vcs = repo.scm_instance()
1556 vcs.remove_ref("refs/heads/{}".format(branch_name))
1557 vcs.remove_ref("refs/heads/{}".format(branch_name))
1557 # NOTE(marcink): run GC to ensure the commits are gone
1558 # NOTE(marcink): run GC to ensure the commits are gone
1558 vcs.run_gc()
1559 vcs.run_gc()
1559
1560
1560 response = self.app.get(
1561 response = self.app.get(
1561 route_path(
1562 route_path(
1562 "pullrequest_show", repo_name=repo_name, pull_request_id=pull_request_id
1563 "pullrequest_show", repo_name=repo_name, pull_request_id=pull_request_id
1563 )
1564 )
1564 )
1565 )
1565
1566
1566 assert response.status_int == 200
1567 assert response.status_int == 200
1567
1568
1568 response.assert_response().element_contains(
1569 response.assert_response().element_contains(
1569 "#changeset_compare_view_content .alert strong", "Missing commits"
1570 "#changeset_compare_view_content .alert strong", "Missing commits"
1570 )
1571 )
1571 response.assert_response().element_contains(
1572 response.assert_response().element_contains(
1572 "#changeset_compare_view_content .alert",
1573 "#changeset_compare_view_content .alert",
1573 "This pull request cannot be displayed, because one or more"
1574 "This pull request cannot be displayed, because one or more"
1574 " commits no longer exist in the source repository.",
1575 " commits no longer exist in the source repository.",
1575 )
1576 )
1576
1577
1577 def test_strip_commits_from_pull_request(self, backend, pr_util):
1578 def test_strip_commits_from_pull_request(self, backend, pr_util):
1578 commits = [
1579 commits = [
1579 {"message": "initial-commit"},
1580 {"message": "initial-commit"},
1580 {"message": "old-feature"},
1581 {"message": "old-feature"},
1581 {"message": "new-feature"},
1582 {"message": "new-feature"},
1582 ]
1583 ]
1583 pull_request = pr_util.create_pull_request(
1584 pull_request = pr_util.create_pull_request(
1584 commits,
1585 commits,
1585 target_head="initial-commit",
1586 target_head="initial-commit",
1586 source_head="new-feature",
1587 source_head="new-feature",
1587 revisions=["new-feature"],
1588 revisions=["new-feature"],
1588 )
1589 )
1589
1590
1590 vcs = pr_util.source_repository.scm_instance()
1591 vcs = pr_util.source_repository.scm_instance()
1591 if backend.alias == "git":
1592 if backend.alias == "git":
1592 vcs.strip(pr_util.commit_ids["new-feature"], branch_name=pr_util.backend.default_branch_name)
1593 vcs.strip(pr_util.commit_ids["new-feature"], branch_name=pr_util.backend.default_branch_name)
1593 else:
1594 else:
1594 vcs.strip(pr_util.commit_ids["new-feature"])
1595 vcs.strip(pr_util.commit_ids["new-feature"])
1595
1596
1596 response = self.app.get(
1597 response = self.app.get(
1597 route_path(
1598 route_path(
1598 "pullrequest_show",
1599 "pullrequest_show",
1599 repo_name=pr_util.target_repository.repo_name,
1600 repo_name=pr_util.target_repository.repo_name,
1600 pull_request_id=pull_request.pull_request_id,
1601 pull_request_id=pull_request.pull_request_id,
1601 )
1602 )
1602 )
1603 )
1603
1604
1604 assert response.status_int == 200
1605 assert response.status_int == 200
1605
1606
1606 response.assert_response().element_contains(
1607 response.assert_response().element_contains(
1607 "#changeset_compare_view_content .alert strong", "Missing commits"
1608 "#changeset_compare_view_content .alert strong", "Missing commits"
1608 )
1609 )
1609 response.assert_response().element_contains(
1610 response.assert_response().element_contains(
1610 "#changeset_compare_view_content .alert",
1611 "#changeset_compare_view_content .alert",
1611 "This pull request cannot be displayed, because one or more"
1612 "This pull request cannot be displayed, because one or more"
1612 " commits no longer exist in the source repository.",
1613 " commits no longer exist in the source repository.",
1613 )
1614 )
1614 response.assert_response().element_contains("#update_commits", "Update commits")
1615 response.assert_response().element_contains("#update_commits", "Update commits")
1615
1616
1616 def test_strip_commits_and_update(self, backend, pr_util, csrf_token):
1617 def test_strip_commits_and_update(self, backend, pr_util, csrf_token):
1617 commits = [
1618 commits = [
1618 {"message": "initial-commit"},
1619 {"message": "initial-commit"},
1619 {"message": "old-feature"},
1620 {"message": "old-feature"},
1620 {"message": "new-feature", "parents": ["old-feature"]},
1621 {"message": "new-feature", "parents": ["old-feature"]},
1621 ]
1622 ]
1622 pull_request = pr_util.create_pull_request(
1623 pull_request = pr_util.create_pull_request(
1623 commits,
1624 commits,
1624 target_head="old-feature",
1625 target_head="old-feature",
1625 source_head="new-feature",
1626 source_head="new-feature",
1626 revisions=["new-feature"],
1627 revisions=["new-feature"],
1627 mergeable=True,
1628 mergeable=True,
1628 )
1629 )
1629 pr_id = pull_request.pull_request_id
1630 pr_id = pull_request.pull_request_id
1630 target_repo_name = pull_request.target_repo.repo_name
1631 target_repo_name = pull_request.target_repo.repo_name
1631
1632
1632 vcs = pr_util.source_repository.scm_instance()
1633 vcs = pr_util.source_repository.scm_instance()
1633 if backend.alias == "git":
1634 if backend.alias == "git":
1634 vcs.strip(pr_util.commit_ids["new-feature"], branch_name="master")
1635 vcs.strip(pr_util.commit_ids["new-feature"], branch_name="master")
1635 else:
1636 else:
1636 vcs.strip(pr_util.commit_ids["new-feature"])
1637 vcs.strip(pr_util.commit_ids["new-feature"])
1637
1638
1638 url = route_path(
1639 url = route_path(
1639 "pullrequest_update", repo_name=target_repo_name, pull_request_id=pr_id
1640 "pullrequest_update", repo_name=target_repo_name, pull_request_id=pr_id
1640 )
1641 )
1641 response = self.app.post(
1642 response = self.app.post(
1642 url, params={"update_commits": "true", "csrf_token": csrf_token}
1643 url, params={"update_commits": "true", "csrf_token": csrf_token}
1643 )
1644 )
1644
1645
1645 assert response.status_int == 200
1646 assert response.status_int == 200
1646 assert json.loads(response.body) == json.loads(
1647 assert json.loads(response.body) == json.loads(
1647 '{"response": true, "redirect_url": null}'
1648 '{"response": true, "redirect_url": null}'
1648 )
1649 )
1649
1650
1650 # Make sure that after update, it won't raise 500 errors
1651 # Make sure that after update, it won't raise 500 errors
1651 response = self.app.get(
1652 response = self.app.get(
1652 route_path(
1653 route_path(
1653 "pullrequest_show", repo_name=target_repo_name, pull_request_id=pr_id
1654 "pullrequest_show", repo_name=target_repo_name, pull_request_id=pr_id
1654 )
1655 )
1655 )
1656 )
1656
1657
1657 assert response.status_int == 200
1658 assert response.status_int == 200
1658 response.assert_response().element_contains(
1659 response.assert_response().element_contains(
1659 "#changeset_compare_view_content .alert strong", "Missing commits"
1660 "#changeset_compare_view_content .alert strong", "Missing commits"
1660 )
1661 )
1661
1662
1662 def test_branch_is_a_link(self, pr_util):
1663 def test_branch_is_a_link(self, pr_util):
1663 pull_request = pr_util.create_pull_request()
1664 pull_request = pr_util.create_pull_request()
1664 pull_request.source_ref = "branch:origin:1234567890abcdef"
1665 pull_request.source_ref = "branch:origin:1234567890abcdef"
1665 pull_request.target_ref = "branch:target:abcdef1234567890"
1666 pull_request.target_ref = "branch:target:abcdef1234567890"
1666 Session().add(pull_request)
1667 Session().add(pull_request)
1667 Session().commit()
1668 Session().commit()
1668
1669
1669 response = self.app.get(
1670 response = self.app.get(
1670 route_path(
1671 route_path(
1671 "pullrequest_show",
1672 "pullrequest_show",
1672 repo_name=pull_request.target_repo.scm_instance().name,
1673 repo_name=pull_request.target_repo.scm_instance().name,
1673 pull_request_id=pull_request.pull_request_id,
1674 pull_request_id=pull_request.pull_request_id,
1674 )
1675 )
1675 )
1676 )
1676 assert response.status_int == 200
1677 assert response.status_int == 200
1677
1678
1678 source = response.assert_response().get_element(".pr-source-info")
1679 source = response.assert_response().get_element(".pr-source-info")
1679 source_parent = source.getparent()
1680 source_parent = source.getparent()
1680 assert len(source_parent) == 1
1681 assert len(source_parent) == 1
1681
1682
1682 target = response.assert_response().get_element(".pr-target-info")
1683 target = response.assert_response().get_element(".pr-target-info")
1683 target_parent = target.getparent()
1684 target_parent = target.getparent()
1684 assert len(target_parent) == 1
1685 assert len(target_parent) == 1
1685
1686
1686 expected_origin_link = route_path(
1687 expected_origin_link = route_path(
1687 "repo_commits",
1688 "repo_commits",
1688 repo_name=pull_request.source_repo.scm_instance().name,
1689 repo_name=pull_request.source_repo.scm_instance().name,
1689 params=dict(branch="origin"),
1690 params=dict(branch="origin"),
1690 )
1691 )
1691 expected_target_link = route_path(
1692 expected_target_link = route_path(
1692 "repo_commits",
1693 "repo_commits",
1693 repo_name=pull_request.target_repo.scm_instance().name,
1694 repo_name=pull_request.target_repo.scm_instance().name,
1694 params=dict(branch="target"),
1695 params=dict(branch="target"),
1695 )
1696 )
1696 assert source_parent.attrib["href"] == expected_origin_link
1697 assert source_parent.attrib["href"] == expected_origin_link
1697 assert target_parent.attrib["href"] == expected_target_link
1698 assert target_parent.attrib["href"] == expected_target_link
1698
1699
1699 def test_bookmark_is_not_a_link(self, pr_util):
1700 def test_bookmark_is_not_a_link(self, pr_util):
1700 pull_request = pr_util.create_pull_request()
1701 pull_request = pr_util.create_pull_request()
1701 pull_request.source_ref = "bookmark:origin:1234567890abcdef"
1702 pull_request.source_ref = "bookmark:origin:1234567890abcdef"
1702 pull_request.target_ref = "bookmark:target:abcdef1234567890"
1703 pull_request.target_ref = "bookmark:target:abcdef1234567890"
1703 Session().add(pull_request)
1704 Session().add(pull_request)
1704 Session().commit()
1705 Session().commit()
1705
1706
1706 response = self.app.get(
1707 response = self.app.get(
1707 route_path(
1708 route_path(
1708 "pullrequest_show",
1709 "pullrequest_show",
1709 repo_name=pull_request.target_repo.scm_instance().name,
1710 repo_name=pull_request.target_repo.scm_instance().name,
1710 pull_request_id=pull_request.pull_request_id,
1711 pull_request_id=pull_request.pull_request_id,
1711 )
1712 )
1712 )
1713 )
1713 assert response.status_int == 200
1714 assert response.status_int == 200
1714
1715
1715 source = response.assert_response().get_element(".pr-source-info")
1716 source = response.assert_response().get_element(".pr-source-info")
1716 assert source.text.strip() == "bookmark:origin"
1717 assert source.text.strip() == "bookmark:origin"
1717 assert source.getparent().attrib.get("href") is None
1718 assert source.getparent().attrib.get("href") is None
1718
1719
1719 target = response.assert_response().get_element(".pr-target-info")
1720 target = response.assert_response().get_element(".pr-target-info")
1720 assert target.text.strip() == "bookmark:target"
1721 assert target.text.strip() == "bookmark:target"
1721 assert target.getparent().attrib.get("href") is None
1722 assert target.getparent().attrib.get("href") is None
1722
1723
1723 def test_tag_is_not_a_link(self, pr_util):
1724 def test_tag_is_not_a_link(self, pr_util):
1724 pull_request = pr_util.create_pull_request()
1725 pull_request = pr_util.create_pull_request()
1725 pull_request.source_ref = "tag:origin:1234567890abcdef"
1726 pull_request.source_ref = "tag:origin:1234567890abcdef"
1726 pull_request.target_ref = "tag:target:abcdef1234567890"
1727 pull_request.target_ref = "tag:target:abcdef1234567890"
1727 Session().add(pull_request)
1728 Session().add(pull_request)
1728 Session().commit()
1729 Session().commit()
1729
1730
1730 response = self.app.get(
1731 response = self.app.get(
1731 route_path(
1732 route_path(
1732 "pullrequest_show",
1733 "pullrequest_show",
1733 repo_name=pull_request.target_repo.scm_instance().name,
1734 repo_name=pull_request.target_repo.scm_instance().name,
1734 pull_request_id=pull_request.pull_request_id,
1735 pull_request_id=pull_request.pull_request_id,
1735 )
1736 )
1736 )
1737 )
1737 assert response.status_int == 200
1738 assert response.status_int == 200
1738
1739
1739 source = response.assert_response().get_element(".pr-source-info")
1740 source = response.assert_response().get_element(".pr-source-info")
1740 assert source.text.strip() == "tag:origin"
1741 assert source.text.strip() == "tag:origin"
1741 assert source.getparent().attrib.get("href") is None
1742 assert source.getparent().attrib.get("href") is None
1742
1743
1743 target = response.assert_response().get_element(".pr-target-info")
1744 target = response.assert_response().get_element(".pr-target-info")
1744 assert target.text.strip() == "tag:target"
1745 assert target.text.strip() == "tag:target"
1745 assert target.getparent().attrib.get("href") is None
1746 assert target.getparent().attrib.get("href") is None
1746
1747
1747 @pytest.mark.parametrize("mergeable", [True, False])
1748 @pytest.mark.parametrize("mergeable", [True, False])
1748 def test_shadow_repository_link(self, mergeable, pr_util, http_host_only_stub):
1749 def test_shadow_repository_link(self, mergeable, pr_util, http_host_only_stub):
1749 """
1750 """
1750 Check that the pull request summary page displays a link to the shadow
1751 Check that the pull request summary page displays a link to the shadow
1751 repository if the pull request is mergeable. If it is not mergeable
1752 repository if the pull request is mergeable. If it is not mergeable
1752 the link should not be displayed.
1753 the link should not be displayed.
1753 """
1754 """
1754 pull_request = pr_util.create_pull_request(
1755 pull_request = pr_util.create_pull_request(
1755 mergeable=mergeable, enable_notifications=False
1756 mergeable=mergeable, enable_notifications=False
1756 )
1757 )
1757 target_repo = pull_request.target_repo.scm_instance()
1758 target_repo = pull_request.target_repo.scm_instance()
1758 pr_id = pull_request.pull_request_id
1759 pr_id = pull_request.pull_request_id
1759 shadow_url = "{host}/{repo}/pull-request/{pr_id}/repository".format(
1760 shadow_url = "{host}/{repo}/pull-request/{pr_id}/repository".format(
1760 host=http_host_only_stub, repo=target_repo.name, pr_id=pr_id
1761 host=http_host_only_stub, repo=target_repo.name, pr_id=pr_id
1761 )
1762 )
1762
1763
1763 response = self.app.get(
1764 response = self.app.get(
1764 route_path(
1765 route_path(
1765 "pullrequest_show", repo_name=target_repo.name, pull_request_id=pr_id
1766 "pullrequest_show", repo_name=target_repo.name, pull_request_id=pr_id
1766 )
1767 )
1767 )
1768 )
1768
1769
1769 if mergeable:
1770 if mergeable:
1770 response.assert_response().element_value_contains(
1771 response.assert_response().element_value_contains(
1771 "input.pr-mergeinfo", shadow_url
1772 "input.pr-mergeinfo", shadow_url
1772 )
1773 )
1773 response.assert_response().element_value_contains(
1774 response.assert_response().element_value_contains(
1774 "input.pr-mergeinfo ", "pr-merge"
1775 "input.pr-mergeinfo ", "pr-merge"
1775 )
1776 )
1776 else:
1777 else:
1777 response.assert_response().no_element_exists(".pr-mergeinfo")
1778 response.assert_response().no_element_exists(".pr-mergeinfo")
1778
1779
1779
1780
1780 @pytest.mark.usefixtures("app")
1781 @pytest.mark.usefixtures("app")
1781 @pytest.mark.backends("git", "hg")
1782 @pytest.mark.backends("git", "hg")
1782 class TestPullrequestsControllerDelete(object):
1783 class TestPullrequestsControllerDelete(object):
1783 def test_pull_request_delete_button_permissions_admin(
1784 def test_pull_request_delete_button_permissions_admin(
1784 self, autologin_user, user_admin, pr_util
1785 self, autologin_user, user_admin, pr_util
1785 ):
1786 ):
1786 pull_request = pr_util.create_pull_request(
1787 pull_request = pr_util.create_pull_request(
1787 author=user_admin.username, enable_notifications=False
1788 author=user_admin.username, enable_notifications=False
1788 )
1789 )
1789
1790
1790 response = self.app.get(
1791 response = self.app.get(
1791 route_path(
1792 route_path(
1792 "pullrequest_show",
1793 "pullrequest_show",
1793 repo_name=pull_request.target_repo.scm_instance().name,
1794 repo_name=pull_request.target_repo.scm_instance().name,
1794 pull_request_id=pull_request.pull_request_id,
1795 pull_request_id=pull_request.pull_request_id,
1795 )
1796 )
1796 )
1797 )
1797
1798
1798 response.mustcontain('id="delete_pullrequest"')
1799 response.mustcontain('id="delete_pullrequest"')
1799 response.mustcontain("Confirm to delete this pull request")
1800 response.mustcontain("Confirm to delete this pull request")
1800
1801
1801 def test_pull_request_delete_button_permissions_owner(
1802 def test_pull_request_delete_button_permissions_owner(
1802 self, autologin_regular_user, user_regular, pr_util
1803 self, autologin_regular_user, user_regular, pr_util
1803 ):
1804 ):
1804 pull_request = pr_util.create_pull_request(
1805 pull_request = pr_util.create_pull_request(
1805 author=user_regular.username, enable_notifications=False
1806 author=user_regular.username, enable_notifications=False
1806 )
1807 )
1807
1808
1808 response = self.app.get(
1809 response = self.app.get(
1809 route_path(
1810 route_path(
1810 "pullrequest_show",
1811 "pullrequest_show",
1811 repo_name=pull_request.target_repo.scm_instance().name,
1812 repo_name=pull_request.target_repo.scm_instance().name,
1812 pull_request_id=pull_request.pull_request_id,
1813 pull_request_id=pull_request.pull_request_id,
1813 )
1814 )
1814 )
1815 )
1815
1816
1816 response.mustcontain('id="delete_pullrequest"')
1817 response.mustcontain('id="delete_pullrequest"')
1817 response.mustcontain("Confirm to delete this pull request")
1818 response.mustcontain("Confirm to delete this pull request")
1818
1819
1819 def test_pull_request_delete_button_permissions_forbidden(
1820 def test_pull_request_delete_button_permissions_forbidden(
1820 self, autologin_regular_user, user_regular, user_admin, pr_util
1821 self, autologin_regular_user, user_regular, user_admin, pr_util
1821 ):
1822 ):
1822 pull_request = pr_util.create_pull_request(
1823 pull_request = pr_util.create_pull_request(
1823 author=user_admin.username, enable_notifications=False
1824 author=user_admin.username, enable_notifications=False
1824 )
1825 )
1825
1826
1826 response = self.app.get(
1827 response = self.app.get(
1827 route_path(
1828 route_path(
1828 "pullrequest_show",
1829 "pullrequest_show",
1829 repo_name=pull_request.target_repo.scm_instance().name,
1830 repo_name=pull_request.target_repo.scm_instance().name,
1830 pull_request_id=pull_request.pull_request_id,
1831 pull_request_id=pull_request.pull_request_id,
1831 )
1832 )
1832 )
1833 )
1833 response.mustcontain(no=['id="delete_pullrequest"'])
1834 response.mustcontain(no=['id="delete_pullrequest"'])
1834 response.mustcontain(no=["Confirm to delete this pull request"])
1835 response.mustcontain(no=["Confirm to delete this pull request"])
1835
1836
1836 def test_pull_request_delete_button_permissions_can_update_cannot_delete(
1837 def test_pull_request_delete_button_permissions_can_update_cannot_delete(
1837 self, autologin_regular_user, user_regular, user_admin, pr_util, user_util
1838 self, autologin_regular_user, user_regular, user_admin, pr_util, user_util
1838 ):
1839 ):
1839 pull_request = pr_util.create_pull_request(
1840 pull_request = pr_util.create_pull_request(
1840 author=user_admin.username, enable_notifications=False
1841 author=user_admin.username, enable_notifications=False
1841 )
1842 )
1842
1843
1843 user_util.grant_user_permission_to_repo(
1844 user_util.grant_user_permission_to_repo(
1844 pull_request.target_repo, user_regular, "repository.write"
1845 pull_request.target_repo, user_regular, "repository.write"
1845 )
1846 )
1846
1847
1847 response = self.app.get(
1848 response = self.app.get(
1848 route_path(
1849 route_path(
1849 "pullrequest_show",
1850 "pullrequest_show",
1850 repo_name=pull_request.target_repo.scm_instance().name,
1851 repo_name=pull_request.target_repo.scm_instance().name,
1851 pull_request_id=pull_request.pull_request_id,
1852 pull_request_id=pull_request.pull_request_id,
1852 )
1853 )
1853 )
1854 )
1854
1855
1855 response.mustcontain('id="open_edit_pullrequest"')
1856 response.mustcontain('id="open_edit_pullrequest"')
1856 response.mustcontain('id="delete_pullrequest"')
1857 response.mustcontain('id="delete_pullrequest"')
1857 response.mustcontain(no=["Confirm to delete this pull request"])
1858 response.mustcontain(no=["Confirm to delete this pull request"])
1858
1859
1859 def test_delete_comment_returns_404_if_comment_does_not_exist(
1860 def test_delete_comment_returns_404_if_comment_does_not_exist(
1860 self, autologin_user, pr_util, user_admin, csrf_token, xhr_header
1861 self, autologin_user, pr_util, user_admin, csrf_token, xhr_header
1861 ):
1862 ):
1862 pull_request = pr_util.create_pull_request(
1863 pull_request = pr_util.create_pull_request(
1863 author=user_admin.username, enable_notifications=False
1864 author=user_admin.username, enable_notifications=False
1864 )
1865 )
1865
1866
1866 self.app.post(
1867 self.app.post(
1867 route_path(
1868 route_path(
1868 "pullrequest_comment_delete",
1869 "pullrequest_comment_delete",
1869 repo_name=pull_request.target_repo.scm_instance().name,
1870 repo_name=pull_request.target_repo.scm_instance().name,
1870 pull_request_id=pull_request.pull_request_id,
1871 pull_request_id=pull_request.pull_request_id,
1871 comment_id=1024404,
1872 comment_id=1024404,
1872 ),
1873 ),
1873 extra_environ=xhr_header,
1874 extra_environ=xhr_header,
1874 params={"csrf_token": csrf_token},
1875 params={"csrf_token": csrf_token},
1875 status=404,
1876 status=404,
1876 )
1877 )
1877
1878
1878 def test_delete_comment(
1879 def test_delete_comment(
1879 self, autologin_user, pr_util, user_admin, csrf_token, xhr_header
1880 self, autologin_user, pr_util, user_admin, csrf_token, xhr_header
1880 ):
1881 ):
1881 pull_request = pr_util.create_pull_request(
1882 pull_request = pr_util.create_pull_request(
1882 author=user_admin.username, enable_notifications=False
1883 author=user_admin.username, enable_notifications=False
1883 )
1884 )
1884 comment = pr_util.create_comment()
1885 comment = pr_util.create_comment()
1885 comment_id = comment.comment_id
1886 comment_id = comment.comment_id
1886
1887
1887 response = self.app.post(
1888 response = self.app.post(
1888 route_path(
1889 route_path(
1889 "pullrequest_comment_delete",
1890 "pullrequest_comment_delete",
1890 repo_name=pull_request.target_repo.scm_instance().name,
1891 repo_name=pull_request.target_repo.scm_instance().name,
1891 pull_request_id=pull_request.pull_request_id,
1892 pull_request_id=pull_request.pull_request_id,
1892 comment_id=comment_id,
1893 comment_id=comment_id,
1893 ),
1894 ),
1894 extra_environ=xhr_header,
1895 extra_environ=xhr_header,
1895 params={"csrf_token": csrf_token},
1896 params={"csrf_token": csrf_token},
1896 status=200,
1897 status=200,
1897 )
1898 )
1898 assert response.text == "true"
1899 assert response.text == "true"
1899
1900
1900 @pytest.mark.parametrize(
1901 @pytest.mark.parametrize(
1901 "url_type",
1902 "url_type",
1902 [
1903 [
1903 "pullrequest_new",
1904 "pullrequest_new",
1904 "pullrequest_create",
1905 "pullrequest_create",
1905 "pullrequest_update",
1906 "pullrequest_update",
1906 "pullrequest_merge",
1907 "pullrequest_merge",
1907 ],
1908 ],
1908 )
1909 )
1909 def test_pull_request_is_forbidden_on_archived_repo(
1910 def test_pull_request_is_forbidden_on_archived_repo(
1910 self, autologin_user, backend, xhr_header, user_util, url_type
1911 self, autologin_user, backend, xhr_header, user_util, url_type
1911 ):
1912 ):
1912 # create a temporary repo
1913 # create a temporary repo
1913 source = user_util.create_repo(repo_type=backend.alias)
1914 source = user_util.create_repo(repo_type=backend.alias)
1914 repo_name = source.repo_name
1915 repo_name = source.repo_name
1915 repo = Repository.get_by_repo_name(repo_name)
1916 repo = Repository.get_by_repo_name(repo_name)
1916 repo.archived = True
1917 repo.archived = True
1917 Session().commit()
1918 Session().commit()
1918
1919
1919 response = self.app.get(
1920 response = self.app.get(
1920 route_path(url_type, repo_name=repo_name, pull_request_id=1), status=302
1921 route_path(url_type, repo_name=repo_name, pull_request_id=1), status=302
1921 )
1922 )
1922
1923
1923 msg = "Action not supported for archived repository."
1924 msg = "Action not supported for archived repository."
1924 assert_session_flash(response, msg)
1925 assert_session_flash(response, msg)
1925
1926
1926
1927
1927 def assert_pull_request_status(pull_request, expected_status):
1928 def assert_pull_request_status(pull_request, expected_status):
1928 status = ChangesetStatusModel().calculated_review_status(pull_request=pull_request)
1929 status = ChangesetStatusModel().calculated_review_status(pull_request=pull_request)
1929 assert status == expected_status
1930 assert status == expected_status
1930
1931
1931
1932
1932 @pytest.mark.parametrize("route", ["pullrequest_new", "pullrequest_create"])
1933 @pytest.mark.parametrize("route", ["pullrequest_new", "pullrequest_create"])
1933 @pytest.mark.usefixtures("autologin_user")
1934 @pytest.mark.usefixtures("autologin_user")
1934 def test_forbidde_to_repo_summary_for_svn_repositories(backend_svn, app, route):
1935 def test_forbidde_to_repo_summary_for_svn_repositories(backend_svn, app, route):
1935 app.get(route_path(route, repo_name=backend_svn.repo_name), status=404)
1936 app.get(route_path(route, repo_name=backend_svn.repo_name), status=404)
General Comments 0
You need to be logged in to leave comments. Login now