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