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