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