##// END OF EJS Templates
tests: add test for pull request with multiple commits and reflect...
dan -
r956:f726bb30 default
parent child Browse files
Show More
@@ -1,940 +1,948 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import mock
22 22 import pytest
23 23 from webob.exc import HTTPNotFound
24 24
25 25 import rhodecode
26 26 from rhodecode.lib.vcs.nodes import FileNode
27 27 from rhodecode.model.changeset_status import ChangesetStatusModel
28 28 from rhodecode.model.db import (
29 29 PullRequest, ChangesetStatus, UserLog, Notification)
30 30 from rhodecode.model.meta import Session
31 31 from rhodecode.model.pull_request import PullRequestModel
32 32 from rhodecode.model.user import UserModel
33 33 from rhodecode.tests import assert_session_flash, url, TEST_USER_ADMIN_LOGIN
34 34 from rhodecode.tests.utils import AssertResponse
35 35
36 36
37 37 @pytest.mark.usefixtures('app', 'autologin_user')
38 38 @pytest.mark.backends("git", "hg")
39 39 class TestPullrequestsController:
40 40
41 41 def test_index(self, backend):
42 42 self.app.get(url(
43 43 controller='pullrequests', action='index',
44 44 repo_name=backend.repo_name))
45 45
46 46 def test_option_menu_create_pull_request_exists(self, backend):
47 47 repo_name = backend.repo_name
48 48 response = self.app.get(url('summary_home', repo_name=repo_name))
49 49
50 50 create_pr_link = '<a href="%s">Create Pull Request</a>' % url(
51 51 'pullrequest', repo_name=repo_name)
52 52 response.mustcontain(create_pr_link)
53 53
54 54 def test_global_redirect_of_pr(self, backend, pr_util):
55 55 pull_request = pr_util.create_pull_request()
56 56
57 57 response = self.app.get(
58 58 url('pull_requests_global',
59 59 pull_request_id=pull_request.pull_request_id))
60 60
61 61 repo_name = pull_request.target_repo.repo_name
62 62 redirect_url = url('pullrequest_show', repo_name=repo_name,
63 63 pull_request_id=pull_request.pull_request_id)
64 64 assert response.status == '302 Found'
65 65 assert redirect_url in response.location
66 66
67 67 def test_create_pr_form_with_raw_commit_id(self, backend):
68 68 repo = backend.repo
69 69
70 70 self.app.get(
71 71 url(controller='pullrequests', action='index',
72 72 repo_name=repo.repo_name,
73 73 commit=repo.get_commit().raw_id),
74 74 status=200)
75 75
76 76 @pytest.mark.parametrize('pr_merge_enabled', [True, False])
77 77 def test_show(self, pr_util, pr_merge_enabled):
78 78 pull_request = pr_util.create_pull_request(
79 79 mergeable=pr_merge_enabled, enable_notifications=False)
80 80
81 81 response = self.app.get(url(
82 82 controller='pullrequests', action='show',
83 83 repo_name=pull_request.target_repo.scm_instance().name,
84 84 pull_request_id=str(pull_request.pull_request_id)))
85 85
86 86 for commit_id in pull_request.revisions:
87 87 response.mustcontain(commit_id)
88 88
89 89 assert pull_request.target_ref_parts.type in response
90 90 assert pull_request.target_ref_parts.name in response
91 91 target_clone_url = pull_request.target_repo.clone_url()
92 92 assert target_clone_url in response
93 93
94 94 assert 'class="pull-request-merge"' in response
95 95 assert (
96 96 'Server-side pull request merging is disabled.'
97 97 in response) != pr_merge_enabled
98 98
99 99 def test_close_status_visibility(self, pr_util, csrf_token):
100 100 from rhodecode.tests.functional.test_login import login_url, logut_url
101 101 # Logout
102 102 response = self.app.post(
103 103 logut_url,
104 104 params={'csrf_token': csrf_token})
105 105 # Login as regular user
106 106 response = self.app.post(login_url,
107 107 {'username': 'test_regular',
108 108 'password': 'test12'})
109 109
110 110 pull_request = pr_util.create_pull_request(author='test_regular')
111 111
112 112 response = self.app.get(url(
113 113 controller='pullrequests', action='show',
114 114 repo_name=pull_request.target_repo.scm_instance().name,
115 115 pull_request_id=str(pull_request.pull_request_id)))
116 116
117 117 assert 'Server-side pull request merging is disabled.' in response
118 118 assert 'value="forced_closed"' in response
119 119
120 120 def test_show_invalid_commit_id(self, pr_util):
121 121 # Simulating invalid revisions which will cause a lookup error
122 122 pull_request = pr_util.create_pull_request()
123 123 pull_request.revisions = ['invalid']
124 124 Session().add(pull_request)
125 125 Session().commit()
126 126
127 127 response = self.app.get(url(
128 128 controller='pullrequests', action='show',
129 129 repo_name=pull_request.target_repo.scm_instance().name,
130 130 pull_request_id=str(pull_request.pull_request_id)))
131 131
132 132 for commit_id in pull_request.revisions:
133 133 response.mustcontain(commit_id)
134 134
135 135 def test_show_invalid_source_reference(self, pr_util):
136 136 pull_request = pr_util.create_pull_request()
137 137 pull_request.source_ref = 'branch:b:invalid'
138 138 Session().add(pull_request)
139 139 Session().commit()
140 140
141 141 self.app.get(url(
142 142 controller='pullrequests', action='show',
143 143 repo_name=pull_request.target_repo.scm_instance().name,
144 144 pull_request_id=str(pull_request.pull_request_id)))
145 145
146 146 def test_edit_title_description(self, pr_util, csrf_token):
147 147 pull_request = pr_util.create_pull_request()
148 148 pull_request_id = pull_request.pull_request_id
149 149
150 150 response = self.app.post(
151 151 url(controller='pullrequests', action='update',
152 152 repo_name=pull_request.target_repo.repo_name,
153 153 pull_request_id=str(pull_request_id)),
154 154 params={
155 155 'edit_pull_request': 'true',
156 156 '_method': 'put',
157 157 'title': 'New title',
158 158 'description': 'New description',
159 159 'csrf_token': csrf_token})
160 160
161 161 assert_session_flash(
162 162 response, u'Pull request title & description updated.',
163 163 category='success')
164 164
165 165 pull_request = PullRequest.get(pull_request_id)
166 166 assert pull_request.title == 'New title'
167 167 assert pull_request.description == 'New description'
168 168
169 169 def test_edit_title_description_closed(self, pr_util, csrf_token):
170 170 pull_request = pr_util.create_pull_request()
171 171 pull_request_id = pull_request.pull_request_id
172 172 pr_util.close()
173 173
174 174 response = self.app.post(
175 175 url(controller='pullrequests', action='update',
176 176 repo_name=pull_request.target_repo.repo_name,
177 177 pull_request_id=str(pull_request_id)),
178 178 params={
179 179 'edit_pull_request': 'true',
180 180 '_method': 'put',
181 181 'title': 'New title',
182 182 'description': 'New description',
183 183 'csrf_token': csrf_token})
184 184
185 185 assert_session_flash(
186 186 response, u'Cannot update closed pull requests.',
187 187 category='error')
188 188
189 189 def test_update_invalid_source_reference(self, pr_util, csrf_token):
190 190 pull_request = pr_util.create_pull_request()
191 191 pull_request.source_ref = 'branch:invalid-branch:invalid-commit-id'
192 192 Session().add(pull_request)
193 193 Session().commit()
194 194
195 195 pull_request_id = pull_request.pull_request_id
196 196
197 197 response = self.app.post(
198 198 url(controller='pullrequests', action='update',
199 199 repo_name=pull_request.target_repo.repo_name,
200 200 pull_request_id=str(pull_request_id)),
201 201 params={'update_commits': 'true', '_method': 'put',
202 202 'csrf_token': csrf_token})
203 203
204 204 assert_session_flash(
205 205 response, u'Update failed due to missing commits.',
206 206 category='error')
207 207
208 208 def test_comment_and_close_pull_request(self, pr_util, csrf_token):
209 209 pull_request = pr_util.create_pull_request(approved=True)
210 210 pull_request_id = pull_request.pull_request_id
211 211 author = pull_request.user_id
212 212 repo = pull_request.target_repo.repo_id
213 213
214 214 self.app.post(
215 215 url(controller='pullrequests',
216 216 action='comment',
217 217 repo_name=pull_request.target_repo.scm_instance().name,
218 218 pull_request_id=str(pull_request_id)),
219 219 params={
220 220 'changeset_status':
221 221 ChangesetStatus.STATUS_APPROVED + '_closed',
222 222 'change_changeset_status': 'on',
223 223 'text': '',
224 224 'csrf_token': csrf_token},
225 225 status=302)
226 226
227 227 action = 'user_closed_pull_request:%d' % pull_request_id
228 228 journal = UserLog.query()\
229 229 .filter(UserLog.user_id == author)\
230 230 .filter(UserLog.repository_id == repo)\
231 231 .filter(UserLog.action == action)\
232 232 .all()
233 233 assert len(journal) == 1
234 234
235 235 def test_reject_and_close_pull_request(self, pr_util, csrf_token):
236 236 pull_request = pr_util.create_pull_request()
237 237 pull_request_id = pull_request.pull_request_id
238 238 response = self.app.post(
239 239 url(controller='pullrequests',
240 240 action='update',
241 241 repo_name=pull_request.target_repo.scm_instance().name,
242 242 pull_request_id=str(pull_request.pull_request_id)),
243 243 params={'close_pull_request': 'true', '_method': 'put',
244 244 'csrf_token': csrf_token})
245 245
246 246 pull_request = PullRequest.get(pull_request_id)
247 247
248 248 assert response.json is True
249 249 assert pull_request.is_closed()
250 250
251 251 # check only the latest status, not the review status
252 252 status = ChangesetStatusModel().get_status(
253 253 pull_request.source_repo, pull_request=pull_request)
254 254 assert status == ChangesetStatus.STATUS_REJECTED
255 255
256 256 def test_comment_force_close_pull_request(self, pr_util, csrf_token):
257 257 pull_request = pr_util.create_pull_request()
258 258 pull_request_id = pull_request.pull_request_id
259 259 reviewers_data = [(1, ['reason']), (2, ['reason2'])]
260 260 PullRequestModel().update_reviewers(pull_request_id, reviewers_data)
261 261 author = pull_request.user_id
262 262 repo = pull_request.target_repo.repo_id
263 263 self.app.post(
264 264 url(controller='pullrequests',
265 265 action='comment',
266 266 repo_name=pull_request.target_repo.scm_instance().name,
267 267 pull_request_id=str(pull_request_id)),
268 268 params={
269 269 'changeset_status': 'forced_closed',
270 270 'csrf_token': csrf_token},
271 271 status=302)
272 272
273 273 pull_request = PullRequest.get(pull_request_id)
274 274
275 275 action = 'user_closed_pull_request:%d' % pull_request_id
276 276 journal = UserLog.query().filter(
277 277 UserLog.user_id == author,
278 278 UserLog.repository_id == repo,
279 279 UserLog.action == action).all()
280 280 assert len(journal) == 1
281 281
282 282 # check only the latest status, not the review status
283 283 status = ChangesetStatusModel().get_status(
284 284 pull_request.source_repo, pull_request=pull_request)
285 285 assert status == ChangesetStatus.STATUS_REJECTED
286 286
287 287 def test_create_pull_request(self, backend, csrf_token):
288 288 commits = [
289 289 {'message': 'ancestor'},
290 290 {'message': 'change'},
291 {'message': 'change2'},
291 292 ]
292 293 commit_ids = backend.create_master_repo(commits)
293 294 target = backend.create_repo(heads=['ancestor'])
294 source = backend.create_repo(heads=['change'])
295 source = backend.create_repo(heads=['change2'])
295 296
296 297 response = self.app.post(
297 298 url(
298 299 controller='pullrequests',
299 300 action='create',
300 301 repo_name=source.repo_name
301 302 ),
302 303 [
303 304 ('source_repo', source.repo_name),
304 ('source_ref', 'branch:default:' + commit_ids['change']),
305 ('source_ref', 'branch:default:' + commit_ids['change2']),
305 306 ('target_repo', target.repo_name),
306 307 ('target_ref', 'branch:default:' + commit_ids['ancestor']),
307 308 ('pullrequest_desc', 'Description'),
308 309 ('pullrequest_title', 'Title'),
309 310 ('__start__', 'review_members:sequence'),
310 311 ('__start__', 'reviewer:mapping'),
311 312 ('user_id', '1'),
312 313 ('__start__', 'reasons:sequence'),
313 314 ('reason', 'Some reason'),
314 315 ('__end__', 'reasons:sequence'),
315 316 ('__end__', 'reviewer:mapping'),
316 317 ('__end__', 'review_members:sequence'),
317 ('revisions', commit_ids['change']),
318 ('__start__', 'revisions:sequence'),
319 ('revisions', commit_ids['change']),
320 ('revisions', commit_ids['change2']),
321 ('__end__', 'revisions:sequence'),
318 322 ('user', ''),
319 323 ('csrf_token', csrf_token),
320 324 ],
321 325 status=302)
322 326
323 327 location = response.headers['Location']
324 328 pull_request_id = int(location.rsplit('/', 1)[1])
325 329 pull_request = PullRequest.get(pull_request_id)
326 330
327 331 # check that we have now both revisions
328 assert pull_request.revisions == [commit_ids['change']]
329 assert pull_request.source_ref == 'branch:default:' + commit_ids['change']
332 assert pull_request.revisions == [commit_ids['change2'], commit_ids['change']]
333 assert pull_request.source_ref == 'branch:default:' + commit_ids['change2']
330 334 expected_target_ref = 'branch:default:' + commit_ids['ancestor']
331 335 assert pull_request.target_ref == expected_target_ref
332 336
333 337 def test_reviewer_notifications(self, backend, csrf_token):
334 338 # We have to use the app.post for this test so it will create the
335 339 # notifications properly with the new PR
336 340 commits = [
337 341 {'message': 'ancestor',
338 342 'added': [FileNode('file_A', content='content_of_ancestor')]},
339 343 {'message': 'change',
340 344 'added': [FileNode('file_a', content='content_of_change')]},
341 345 {'message': 'change-child'},
342 346 {'message': 'ancestor-child', 'parents': ['ancestor'],
343 347 'added': [
344 348 FileNode('file_B', content='content_of_ancestor_child')]},
345 349 {'message': 'ancestor-child-2'},
346 350 ]
347 351 commit_ids = backend.create_master_repo(commits)
348 352 target = backend.create_repo(heads=['ancestor-child'])
349 353 source = backend.create_repo(heads=['change'])
350 354
351 355 response = self.app.post(
352 356 url(
353 357 controller='pullrequests',
354 358 action='create',
355 359 repo_name=source.repo_name
356 360 ),
357 361 [
358 362 ('source_repo', source.repo_name),
359 363 ('source_ref', 'branch:default:' + commit_ids['change']),
360 364 ('target_repo', target.repo_name),
361 365 ('target_ref', 'branch:default:' + commit_ids['ancestor-child']),
362 366 ('pullrequest_desc', 'Description'),
363 367 ('pullrequest_title', 'Title'),
364 368 ('__start__', 'review_members:sequence'),
365 369 ('__start__', 'reviewer:mapping'),
366 370 ('user_id', '2'),
367 371 ('__start__', 'reasons:sequence'),
368 372 ('reason', 'Some reason'),
369 373 ('__end__', 'reasons:sequence'),
370 374 ('__end__', 'reviewer:mapping'),
371 375 ('__end__', 'review_members:sequence'),
372 ('revisions', commit_ids['change']),
376 ('__start__', 'revisions:sequence'),
377 ('revisions', commit_ids['change']),
378 ('__end__', 'revisions:sequence'),
373 379 ('user', ''),
374 380 ('csrf_token', csrf_token),
375 381 ],
376 382 status=302)
377 383
378 384 location = response.headers['Location']
379 385 pull_request_id = int(location.rsplit('/', 1)[1])
380 386 pull_request = PullRequest.get(pull_request_id)
381 387
382 388 # Check that a notification was made
383 389 notifications = Notification.query()\
384 390 .filter(Notification.created_by == pull_request.author.user_id,
385 391 Notification.type_ == Notification.TYPE_PULL_REQUEST,
386 392 Notification.subject.contains("wants you to review "
387 393 "pull request #%d"
388 394 % pull_request_id))
389 395 assert len(notifications.all()) == 1
390 396
391 397 # Change reviewers and check that a notification was made
392 398 PullRequestModel().update_reviewers(
393 399 pull_request.pull_request_id, [(1, [])])
394 400 assert len(notifications.all()) == 2
395 401
396 402 def test_create_pull_request_stores_ancestor_commit_id(self, backend,
397 403 csrf_token):
398 404 commits = [
399 405 {'message': 'ancestor',
400 406 'added': [FileNode('file_A', content='content_of_ancestor')]},
401 407 {'message': 'change',
402 408 'added': [FileNode('file_a', content='content_of_change')]},
403 409 {'message': 'change-child'},
404 410 {'message': 'ancestor-child', 'parents': ['ancestor'],
405 411 'added': [
406 412 FileNode('file_B', content='content_of_ancestor_child')]},
407 413 {'message': 'ancestor-child-2'},
408 414 ]
409 415 commit_ids = backend.create_master_repo(commits)
410 416 target = backend.create_repo(heads=['ancestor-child'])
411 417 source = backend.create_repo(heads=['change'])
412 418
413 419 response = self.app.post(
414 420 url(
415 421 controller='pullrequests',
416 422 action='create',
417 423 repo_name=source.repo_name
418 424 ),
419 425 [
420 426 ('source_repo', source.repo_name),
421 427 ('source_ref', 'branch:default:' + commit_ids['change']),
422 428 ('target_repo', target.repo_name),
423 429 ('target_ref', 'branch:default:' + commit_ids['ancestor-child']),
424 430 ('pullrequest_desc', 'Description'),
425 431 ('pullrequest_title', 'Title'),
426 432 ('__start__', 'review_members:sequence'),
427 433 ('__start__', 'reviewer:mapping'),
428 434 ('user_id', '1'),
429 435 ('__start__', 'reasons:sequence'),
430 436 ('reason', 'Some reason'),
431 437 ('__end__', 'reasons:sequence'),
432 438 ('__end__', 'reviewer:mapping'),
433 439 ('__end__', 'review_members:sequence'),
434 ('revisions', commit_ids['change']),
440 ('__start__', 'revisions:sequence'),
441 ('revisions', commit_ids['change']),
442 ('__end__', 'revisions:sequence'),
435 443 ('user', ''),
436 444 ('csrf_token', csrf_token),
437 445 ],
438 446 status=302)
439 447
440 448 location = response.headers['Location']
441 449 pull_request_id = int(location.rsplit('/', 1)[1])
442 450 pull_request = PullRequest.get(pull_request_id)
443 451
444 452 # target_ref has to point to the ancestor's commit_id in order to
445 453 # show the correct diff
446 454 expected_target_ref = 'branch:default:' + commit_ids['ancestor']
447 455 assert pull_request.target_ref == expected_target_ref
448 456
449 457 # Check generated diff contents
450 458 response = response.follow()
451 459 assert 'content_of_ancestor' not in response.body
452 460 assert 'content_of_ancestor-child' not in response.body
453 461 assert 'content_of_change' in response.body
454 462
455 463 def test_merge_pull_request_enabled(self, pr_util, csrf_token):
456 464 # Clear any previous calls to rcextensions
457 465 rhodecode.EXTENSIONS.calls.clear()
458 466
459 467 pull_request = pr_util.create_pull_request(
460 468 approved=True, mergeable=True)
461 469 pull_request_id = pull_request.pull_request_id
462 470 repo_name = pull_request.target_repo.scm_instance().name,
463 471
464 472 response = self.app.post(
465 473 url(controller='pullrequests',
466 474 action='merge',
467 475 repo_name=str(repo_name[0]),
468 476 pull_request_id=str(pull_request_id)),
469 477 params={'csrf_token': csrf_token}).follow()
470 478
471 479 pull_request = PullRequest.get(pull_request_id)
472 480
473 481 assert response.status_int == 200
474 482 assert pull_request.is_closed()
475 483 assert_pull_request_status(
476 484 pull_request, ChangesetStatus.STATUS_APPROVED)
477 485
478 486 # Check the relevant log entries were added
479 487 user_logs = UserLog.query().order_by('-user_log_id').limit(4)
480 488 actions = [log.action for log in user_logs]
481 489 pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
482 490 expected_actions = [
483 491 u'user_closed_pull_request:%d' % pull_request_id,
484 492 u'user_merged_pull_request:%d' % pull_request_id,
485 493 # The action below reflect that the post push actions were executed
486 494 u'user_commented_pull_request:%d' % pull_request_id,
487 495 u'push:%s' % ','.join(pr_commit_ids),
488 496 ]
489 497 assert actions == expected_actions
490 498
491 499 # Check post_push rcextension was really executed
492 500 push_calls = rhodecode.EXTENSIONS.calls['post_push']
493 501 assert len(push_calls) == 1
494 502 unused_last_call_args, last_call_kwargs = push_calls[0]
495 503 assert last_call_kwargs['action'] == 'push'
496 504 assert last_call_kwargs['pushed_revs'] == pr_commit_ids
497 505
498 506 def test_merge_pull_request_disabled(self, pr_util, csrf_token):
499 507 pull_request = pr_util.create_pull_request(mergeable=False)
500 508 pull_request_id = pull_request.pull_request_id
501 509 pull_request = PullRequest.get(pull_request_id)
502 510
503 511 response = self.app.post(
504 512 url(controller='pullrequests',
505 513 action='merge',
506 514 repo_name=pull_request.target_repo.scm_instance().name,
507 515 pull_request_id=str(pull_request.pull_request_id)),
508 516 params={'csrf_token': csrf_token}).follow()
509 517
510 518 assert response.status_int == 200
511 519 assert 'Server-side pull request merging is disabled.' in response.body
512 520
513 521 @pytest.mark.skip_backends('svn')
514 522 def test_merge_pull_request_not_approved(self, pr_util, csrf_token):
515 523 pull_request = pr_util.create_pull_request(mergeable=True)
516 524 pull_request_id = pull_request.pull_request_id
517 525 repo_name = pull_request.target_repo.scm_instance().name,
518 526
519 527 response = self.app.post(
520 528 url(controller='pullrequests',
521 529 action='merge',
522 530 repo_name=str(repo_name[0]),
523 531 pull_request_id=str(pull_request_id)),
524 532 params={'csrf_token': csrf_token}).follow()
525 533
526 534 pull_request = PullRequest.get(pull_request_id)
527 535
528 536 assert response.status_int == 200
529 537 assert ' Reviewer approval is pending.' in response.body
530 538
531 539 def test_update_source_revision(self, backend, csrf_token):
532 540 commits = [
533 541 {'message': 'ancestor'},
534 542 {'message': 'change'},
535 543 {'message': 'change-2'},
536 544 ]
537 545 commit_ids = backend.create_master_repo(commits)
538 546 target = backend.create_repo(heads=['ancestor'])
539 547 source = backend.create_repo(heads=['change'])
540 548
541 549 # create pr from a in source to A in target
542 550 pull_request = PullRequest()
543 551 pull_request.source_repo = source
544 552 # TODO: johbo: Make sure that we write the source ref this way!
545 553 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
546 554 branch=backend.default_branch_name, commit_id=commit_ids['change'])
547 555 pull_request.target_repo = target
548 556
549 557 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
550 558 branch=backend.default_branch_name,
551 559 commit_id=commit_ids['ancestor'])
552 560 pull_request.revisions = [commit_ids['change']]
553 561 pull_request.title = u"Test"
554 562 pull_request.description = u"Description"
555 563 pull_request.author = UserModel().get_by_username(
556 564 TEST_USER_ADMIN_LOGIN)
557 565 Session().add(pull_request)
558 566 Session().commit()
559 567 pull_request_id = pull_request.pull_request_id
560 568
561 569 # source has ancestor - change - change-2
562 570 backend.pull_heads(source, heads=['change-2'])
563 571
564 572 # update PR
565 573 self.app.post(
566 574 url(controller='pullrequests', action='update',
567 575 repo_name=target.repo_name,
568 576 pull_request_id=str(pull_request_id)),
569 577 params={'update_commits': 'true', '_method': 'put',
570 578 'csrf_token': csrf_token})
571 579
572 580 # check that we have now both revisions
573 581 pull_request = PullRequest.get(pull_request_id)
574 582 assert pull_request.revisions == [
575 583 commit_ids['change-2'], commit_ids['change']]
576 584
577 585 # TODO: johbo: this should be a test on its own
578 586 response = self.app.get(url(
579 587 controller='pullrequests', action='index',
580 588 repo_name=target.repo_name))
581 589 assert response.status_int == 200
582 590 assert 'Pull request updated to' in response.body
583 591 assert 'with 1 added, 0 removed commits.' in response.body
584 592
585 593 def test_update_target_revision(self, backend, csrf_token):
586 594 commits = [
587 595 {'message': 'ancestor'},
588 596 {'message': 'change'},
589 597 {'message': 'ancestor-new', 'parents': ['ancestor']},
590 598 {'message': 'change-rebased'},
591 599 ]
592 600 commit_ids = backend.create_master_repo(commits)
593 601 target = backend.create_repo(heads=['ancestor'])
594 602 source = backend.create_repo(heads=['change'])
595 603
596 604 # create pr from a in source to A in target
597 605 pull_request = PullRequest()
598 606 pull_request.source_repo = source
599 607 # TODO: johbo: Make sure that we write the source ref this way!
600 608 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
601 609 branch=backend.default_branch_name, commit_id=commit_ids['change'])
602 610 pull_request.target_repo = target
603 611 # TODO: johbo: Target ref should be branch based, since tip can jump
604 612 # from branch to branch
605 613 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
606 614 branch=backend.default_branch_name,
607 615 commit_id=commit_ids['ancestor'])
608 616 pull_request.revisions = [commit_ids['change']]
609 617 pull_request.title = u"Test"
610 618 pull_request.description = u"Description"
611 619 pull_request.author = UserModel().get_by_username(
612 620 TEST_USER_ADMIN_LOGIN)
613 621 Session().add(pull_request)
614 622 Session().commit()
615 623 pull_request_id = pull_request.pull_request_id
616 624
617 625 # target has ancestor - ancestor-new
618 626 # source has ancestor - ancestor-new - change-rebased
619 627 backend.pull_heads(target, heads=['ancestor-new'])
620 628 backend.pull_heads(source, heads=['change-rebased'])
621 629
622 630 # update PR
623 631 self.app.post(
624 632 url(controller='pullrequests', action='update',
625 633 repo_name=target.repo_name,
626 634 pull_request_id=str(pull_request_id)),
627 635 params={'update_commits': 'true', '_method': 'put',
628 636 'csrf_token': csrf_token},
629 637 status=200)
630 638
631 639 # check that we have now both revisions
632 640 pull_request = PullRequest.get(pull_request_id)
633 641 assert pull_request.revisions == [commit_ids['change-rebased']]
634 642 assert pull_request.target_ref == 'branch:{branch}:{commit_id}'.format(
635 643 branch=backend.default_branch_name,
636 644 commit_id=commit_ids['ancestor-new'])
637 645
638 646 # TODO: johbo: This should be a test on its own
639 647 response = self.app.get(url(
640 648 controller='pullrequests', action='index',
641 649 repo_name=target.repo_name))
642 650 assert response.status_int == 200
643 651 assert 'Pull request updated to' in response.body
644 652 assert 'with 1 added, 1 removed commits.' in response.body
645 653
646 654 def test_update_of_ancestor_reference(self, backend, csrf_token):
647 655 commits = [
648 656 {'message': 'ancestor'},
649 657 {'message': 'change'},
650 658 {'message': 'change-2'},
651 659 {'message': 'ancestor-new', 'parents': ['ancestor']},
652 660 {'message': 'change-rebased'},
653 661 ]
654 662 commit_ids = backend.create_master_repo(commits)
655 663 target = backend.create_repo(heads=['ancestor'])
656 664 source = backend.create_repo(heads=['change'])
657 665
658 666 # create pr from a in source to A in target
659 667 pull_request = PullRequest()
660 668 pull_request.source_repo = source
661 669 # TODO: johbo: Make sure that we write the source ref this way!
662 670 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
663 671 branch=backend.default_branch_name,
664 672 commit_id=commit_ids['change'])
665 673 pull_request.target_repo = target
666 674 # TODO: johbo: Target ref should be branch based, since tip can jump
667 675 # from branch to branch
668 676 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
669 677 branch=backend.default_branch_name,
670 678 commit_id=commit_ids['ancestor'])
671 679 pull_request.revisions = [commit_ids['change']]
672 680 pull_request.title = u"Test"
673 681 pull_request.description = u"Description"
674 682 pull_request.author = UserModel().get_by_username(
675 683 TEST_USER_ADMIN_LOGIN)
676 684 Session().add(pull_request)
677 685 Session().commit()
678 686 pull_request_id = pull_request.pull_request_id
679 687
680 688 # target has ancestor - ancestor-new
681 689 # source has ancestor - ancestor-new - change-rebased
682 690 backend.pull_heads(target, heads=['ancestor-new'])
683 691 backend.pull_heads(source, heads=['change-rebased'])
684 692
685 693 # update PR
686 694 self.app.post(
687 695 url(controller='pullrequests', action='update',
688 696 repo_name=target.repo_name,
689 697 pull_request_id=str(pull_request_id)),
690 698 params={'update_commits': 'true', '_method': 'put',
691 699 'csrf_token': csrf_token},
692 700 status=200)
693 701
694 702 # Expect the target reference to be updated correctly
695 703 pull_request = PullRequest.get(pull_request_id)
696 704 assert pull_request.revisions == [commit_ids['change-rebased']]
697 705 expected_target_ref = 'branch:{branch}:{commit_id}'.format(
698 706 branch=backend.default_branch_name,
699 707 commit_id=commit_ids['ancestor-new'])
700 708 assert pull_request.target_ref == expected_target_ref
701 709
702 710 def test_remove_pull_request_branch(self, backend_git, csrf_token):
703 711 branch_name = 'development'
704 712 commits = [
705 713 {'message': 'initial-commit'},
706 714 {'message': 'old-feature'},
707 715 {'message': 'new-feature', 'branch': branch_name},
708 716 ]
709 717 repo = backend_git.create_repo(commits)
710 718 commit_ids = backend_git.commit_ids
711 719
712 720 pull_request = PullRequest()
713 721 pull_request.source_repo = repo
714 722 pull_request.target_repo = repo
715 723 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
716 724 branch=branch_name, commit_id=commit_ids['new-feature'])
717 725 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
718 726 branch=backend_git.default_branch_name,
719 727 commit_id=commit_ids['old-feature'])
720 728 pull_request.revisions = [commit_ids['new-feature']]
721 729 pull_request.title = u"Test"
722 730 pull_request.description = u"Description"
723 731 pull_request.author = UserModel().get_by_username(
724 732 TEST_USER_ADMIN_LOGIN)
725 733 Session().add(pull_request)
726 734 Session().commit()
727 735
728 736 vcs = repo.scm_instance()
729 737 vcs.remove_ref('refs/heads/{}'.format(branch_name))
730 738
731 739 response = self.app.get(url(
732 740 controller='pullrequests', action='show',
733 741 repo_name=repo.repo_name,
734 742 pull_request_id=str(pull_request.pull_request_id)))
735 743
736 744 assert response.status_int == 200
737 745 assert_response = AssertResponse(response)
738 746 assert_response.element_contains(
739 747 '#changeset_compare_view_content .alert strong',
740 748 'Missing commits')
741 749 assert_response.element_contains(
742 750 '#changeset_compare_view_content .alert',
743 751 'This pull request cannot be displayed, because one or more'
744 752 ' commits no longer exist in the source repository.')
745 753
746 754 def test_strip_commits_from_pull_request(
747 755 self, backend, pr_util, csrf_token):
748 756 commits = [
749 757 {'message': 'initial-commit'},
750 758 {'message': 'old-feature'},
751 759 {'message': 'new-feature', 'parents': ['initial-commit']},
752 760 ]
753 761 pull_request = pr_util.create_pull_request(
754 762 commits, target_head='initial-commit', source_head='new-feature',
755 763 revisions=['new-feature'])
756 764
757 765 vcs = pr_util.source_repository.scm_instance()
758 766 if backend.alias == 'git':
759 767 vcs.strip(pr_util.commit_ids['new-feature'], branch_name='master')
760 768 else:
761 769 vcs.strip(pr_util.commit_ids['new-feature'])
762 770
763 771 response = self.app.get(url(
764 772 controller='pullrequests', action='show',
765 773 repo_name=pr_util.target_repository.repo_name,
766 774 pull_request_id=str(pull_request.pull_request_id)))
767 775
768 776 assert response.status_int == 200
769 777 assert_response = AssertResponse(response)
770 778 assert_response.element_contains(
771 779 '#changeset_compare_view_content .alert strong',
772 780 'Missing commits')
773 781 assert_response.element_contains(
774 782 '#changeset_compare_view_content .alert',
775 783 'This pull request cannot be displayed, because one or more'
776 784 ' commits no longer exist in the source repository.')
777 785 assert_response.element_contains(
778 786 '#update_commits',
779 787 'Update commits')
780 788
781 789 def test_strip_commits_and_update(
782 790 self, backend, pr_util, csrf_token):
783 791 commits = [
784 792 {'message': 'initial-commit'},
785 793 {'message': 'old-feature'},
786 794 {'message': 'new-feature', 'parents': ['old-feature']},
787 795 ]
788 796 pull_request = pr_util.create_pull_request(
789 797 commits, target_head='old-feature', source_head='new-feature',
790 798 revisions=['new-feature'], mergeable=True)
791 799
792 800 vcs = pr_util.source_repository.scm_instance()
793 801 if backend.alias == 'git':
794 802 vcs.strip(pr_util.commit_ids['new-feature'], branch_name='master')
795 803 else:
796 804 vcs.strip(pr_util.commit_ids['new-feature'])
797 805
798 806 response = self.app.post(
799 807 url(controller='pullrequests', action='update',
800 808 repo_name=pull_request.target_repo.repo_name,
801 809 pull_request_id=str(pull_request.pull_request_id)),
802 810 params={'update_commits': 'true', '_method': 'put',
803 811 'csrf_token': csrf_token})
804 812
805 813 assert response.status_int == 200
806 814 assert response.body == 'true'
807 815
808 816 # Make sure that after update, it won't raise 500 errors
809 817 response = self.app.get(url(
810 818 controller='pullrequests', action='show',
811 819 repo_name=pr_util.target_repository.repo_name,
812 820 pull_request_id=str(pull_request.pull_request_id)))
813 821
814 822 assert response.status_int == 200
815 823 assert_response = AssertResponse(response)
816 824 assert_response.element_contains(
817 825 '#changeset_compare_view_content .alert strong',
818 826 'Missing commits')
819 827
820 828 def test_branch_is_a_link(self, pr_util):
821 829 pull_request = pr_util.create_pull_request()
822 830 pull_request.source_ref = 'branch:origin:1234567890abcdef'
823 831 pull_request.target_ref = 'branch:target:abcdef1234567890'
824 832 Session().add(pull_request)
825 833 Session().commit()
826 834
827 835 response = self.app.get(url(
828 836 controller='pullrequests', action='show',
829 837 repo_name=pull_request.target_repo.scm_instance().name,
830 838 pull_request_id=str(pull_request.pull_request_id)))
831 839 assert response.status_int == 200
832 840 assert_response = AssertResponse(response)
833 841
834 842 origin = assert_response.get_element('.pr-origininfo .tag')
835 843 origin_children = origin.getchildren()
836 844 assert len(origin_children) == 1
837 845 target = assert_response.get_element('.pr-targetinfo .tag')
838 846 target_children = target.getchildren()
839 847 assert len(target_children) == 1
840 848
841 849 expected_origin_link = url(
842 850 'changelog_home',
843 851 repo_name=pull_request.source_repo.scm_instance().name,
844 852 branch='origin')
845 853 expected_target_link = url(
846 854 'changelog_home',
847 855 repo_name=pull_request.target_repo.scm_instance().name,
848 856 branch='target')
849 857 assert origin_children[0].attrib['href'] == expected_origin_link
850 858 assert origin_children[0].text == 'branch: origin'
851 859 assert target_children[0].attrib['href'] == expected_target_link
852 860 assert target_children[0].text == 'branch: target'
853 861
854 862 def test_bookmark_is_not_a_link(self, pr_util):
855 863 pull_request = pr_util.create_pull_request()
856 864 pull_request.source_ref = 'bookmark:origin:1234567890abcdef'
857 865 pull_request.target_ref = 'bookmark:target:abcdef1234567890'
858 866 Session().add(pull_request)
859 867 Session().commit()
860 868
861 869 response = self.app.get(url(
862 870 controller='pullrequests', action='show',
863 871 repo_name=pull_request.target_repo.scm_instance().name,
864 872 pull_request_id=str(pull_request.pull_request_id)))
865 873 assert response.status_int == 200
866 874 assert_response = AssertResponse(response)
867 875
868 876 origin = assert_response.get_element('.pr-origininfo .tag')
869 877 assert origin.text.strip() == 'bookmark: origin'
870 878 assert origin.getchildren() == []
871 879
872 880 target = assert_response.get_element('.pr-targetinfo .tag')
873 881 assert target.text.strip() == 'bookmark: target'
874 882 assert target.getchildren() == []
875 883
876 884 def test_tag_is_not_a_link(self, pr_util):
877 885 pull_request = pr_util.create_pull_request()
878 886 pull_request.source_ref = 'tag:origin:1234567890abcdef'
879 887 pull_request.target_ref = 'tag:target:abcdef1234567890'
880 888 Session().add(pull_request)
881 889 Session().commit()
882 890
883 891 response = self.app.get(url(
884 892 controller='pullrequests', action='show',
885 893 repo_name=pull_request.target_repo.scm_instance().name,
886 894 pull_request_id=str(pull_request.pull_request_id)))
887 895 assert response.status_int == 200
888 896 assert_response = AssertResponse(response)
889 897
890 898 origin = assert_response.get_element('.pr-origininfo .tag')
891 899 assert origin.text.strip() == 'tag: origin'
892 900 assert origin.getchildren() == []
893 901
894 902 target = assert_response.get_element('.pr-targetinfo .tag')
895 903 assert target.text.strip() == 'tag: target'
896 904 assert target.getchildren() == []
897 905
898 906 def test_description_is_escaped_on_index_page(self, backend, pr_util):
899 907 xss_description = "<script>alert('Hi!')</script>"
900 908 pull_request = pr_util.create_pull_request(description=xss_description)
901 909 response = self.app.get(url(
902 910 controller='pullrequests', action='show_all',
903 911 repo_name=pull_request.target_repo.repo_name))
904 912 response.mustcontain(
905 913 "&lt;script&gt;alert(&#39;Hi!&#39;)&lt;/script&gt;")
906 914
907 915
908 916 def assert_pull_request_status(pull_request, expected_status):
909 917 status = ChangesetStatusModel().calculated_review_status(
910 918 pull_request=pull_request)
911 919 assert status == expected_status
912 920
913 921
914 922 @pytest.mark.parametrize('action', ['show_all', 'index', 'create'])
915 923 @pytest.mark.usefixtures("autologin_user")
916 924 def test_redirects_to_repo_summary_for_svn_repositories(
917 925 backend_svn, app, action):
918 926 denied_actions = ['show_all', 'index', 'create']
919 927 for action in denied_actions:
920 928 response = app.get(url(
921 929 controller='pullrequests', action=action,
922 930 repo_name=backend_svn.repo_name))
923 931 assert response.status_int == 302
924 932
925 933 # Not allowed, redirect to the summary
926 934 redirected = response.follow()
927 935 summary_url = url('summary_home', repo_name=backend_svn.repo_name)
928 936
929 937 # URL adds leading slash and path doesn't have it
930 938 assert redirected.req.path == summary_url
931 939
932 940
933 941 def test_delete_comment_returns_404_if_comment_does_not_exist(pylonsapp):
934 942 # TODO: johbo: Global import not possible because models.forms blows up
935 943 from rhodecode.controllers.pullrequests import PullrequestsController
936 944 controller = PullrequestsController()
937 945 patcher = mock.patch(
938 946 'rhodecode.model.db.BaseModel.get', return_value=None)
939 947 with pytest.raises(HTTPNotFound), patcher:
940 948 controller._delete_comment(1)
General Comments 0
You need to be logged in to leave comments. Login now