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