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