##// END OF EJS Templates
tests: Add test to check the link to the shadow repo of a PR.
Martin Bornhold -
r1047:f58888bc default
parent child Browse files
Show More
@@ -1,948 +1,977 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 291 {'message': 'change2'},
292 292 ]
293 293 commit_ids = backend.create_master_repo(commits)
294 294 target = backend.create_repo(heads=['ancestor'])
295 295 source = backend.create_repo(heads=['change2'])
296 296
297 297 response = self.app.post(
298 298 url(
299 299 controller='pullrequests',
300 300 action='create',
301 301 repo_name=source.repo_name
302 302 ),
303 303 [
304 304 ('source_repo', source.repo_name),
305 305 ('source_ref', 'branch:default:' + commit_ids['change2']),
306 306 ('target_repo', target.repo_name),
307 307 ('target_ref', 'branch:default:' + commit_ids['ancestor']),
308 308 ('pullrequest_desc', 'Description'),
309 309 ('pullrequest_title', 'Title'),
310 310 ('__start__', 'review_members:sequence'),
311 311 ('__start__', 'reviewer:mapping'),
312 312 ('user_id', '1'),
313 313 ('__start__', 'reasons:sequence'),
314 314 ('reason', 'Some reason'),
315 315 ('__end__', 'reasons:sequence'),
316 316 ('__end__', 'reviewer:mapping'),
317 317 ('__end__', 'review_members:sequence'),
318 318 ('__start__', 'revisions:sequence'),
319 319 ('revisions', commit_ids['change']),
320 320 ('revisions', commit_ids['change2']),
321 321 ('__end__', 'revisions:sequence'),
322 322 ('user', ''),
323 323 ('csrf_token', csrf_token),
324 324 ],
325 325 status=302)
326 326
327 327 location = response.headers['Location']
328 328 pull_request_id = int(location.rsplit('/', 1)[1])
329 329 pull_request = PullRequest.get(pull_request_id)
330 330
331 331 # check that we have now both revisions
332 332 assert pull_request.revisions == [commit_ids['change2'], commit_ids['change']]
333 333 assert pull_request.source_ref == 'branch:default:' + commit_ids['change2']
334 334 expected_target_ref = 'branch:default:' + commit_ids['ancestor']
335 335 assert pull_request.target_ref == expected_target_ref
336 336
337 337 def test_reviewer_notifications(self, backend, csrf_token):
338 338 # We have to use the app.post for this test so it will create the
339 339 # notifications properly with the new PR
340 340 commits = [
341 341 {'message': 'ancestor',
342 342 'added': [FileNode('file_A', content='content_of_ancestor')]},
343 343 {'message': 'change',
344 344 'added': [FileNode('file_a', content='content_of_change')]},
345 345 {'message': 'change-child'},
346 346 {'message': 'ancestor-child', 'parents': ['ancestor'],
347 347 'added': [
348 348 FileNode('file_B', content='content_of_ancestor_child')]},
349 349 {'message': 'ancestor-child-2'},
350 350 ]
351 351 commit_ids = backend.create_master_repo(commits)
352 352 target = backend.create_repo(heads=['ancestor-child'])
353 353 source = backend.create_repo(heads=['change'])
354 354
355 355 response = self.app.post(
356 356 url(
357 357 controller='pullrequests',
358 358 action='create',
359 359 repo_name=source.repo_name
360 360 ),
361 361 [
362 362 ('source_repo', source.repo_name),
363 363 ('source_ref', 'branch:default:' + commit_ids['change']),
364 364 ('target_repo', target.repo_name),
365 365 ('target_ref', 'branch:default:' + commit_ids['ancestor-child']),
366 366 ('pullrequest_desc', 'Description'),
367 367 ('pullrequest_title', 'Title'),
368 368 ('__start__', 'review_members:sequence'),
369 369 ('__start__', 'reviewer:mapping'),
370 370 ('user_id', '2'),
371 371 ('__start__', 'reasons:sequence'),
372 372 ('reason', 'Some reason'),
373 373 ('__end__', 'reasons:sequence'),
374 374 ('__end__', 'reviewer:mapping'),
375 375 ('__end__', 'review_members:sequence'),
376 376 ('__start__', 'revisions:sequence'),
377 377 ('revisions', commit_ids['change']),
378 378 ('__end__', 'revisions:sequence'),
379 379 ('user', ''),
380 380 ('csrf_token', csrf_token),
381 381 ],
382 382 status=302)
383 383
384 384 location = response.headers['Location']
385 385 pull_request_id = int(location.rsplit('/', 1)[1])
386 386 pull_request = PullRequest.get(pull_request_id)
387 387
388 388 # Check that a notification was made
389 389 notifications = Notification.query()\
390 390 .filter(Notification.created_by == pull_request.author.user_id,
391 391 Notification.type_ == Notification.TYPE_PULL_REQUEST,
392 392 Notification.subject.contains("wants you to review "
393 393 "pull request #%d"
394 394 % pull_request_id))
395 395 assert len(notifications.all()) == 1
396 396
397 397 # Change reviewers and check that a notification was made
398 398 PullRequestModel().update_reviewers(
399 399 pull_request.pull_request_id, [(1, [])])
400 400 assert len(notifications.all()) == 2
401 401
402 402 def test_create_pull_request_stores_ancestor_commit_id(self, backend,
403 403 csrf_token):
404 404 commits = [
405 405 {'message': 'ancestor',
406 406 'added': [FileNode('file_A', content='content_of_ancestor')]},
407 407 {'message': 'change',
408 408 'added': [FileNode('file_a', content='content_of_change')]},
409 409 {'message': 'change-child'},
410 410 {'message': 'ancestor-child', 'parents': ['ancestor'],
411 411 'added': [
412 412 FileNode('file_B', content='content_of_ancestor_child')]},
413 413 {'message': 'ancestor-child-2'},
414 414 ]
415 415 commit_ids = backend.create_master_repo(commits)
416 416 target = backend.create_repo(heads=['ancestor-child'])
417 417 source = backend.create_repo(heads=['change'])
418 418
419 419 response = self.app.post(
420 420 url(
421 421 controller='pullrequests',
422 422 action='create',
423 423 repo_name=source.repo_name
424 424 ),
425 425 [
426 426 ('source_repo', source.repo_name),
427 427 ('source_ref', 'branch:default:' + commit_ids['change']),
428 428 ('target_repo', target.repo_name),
429 429 ('target_ref', 'branch:default:' + commit_ids['ancestor-child']),
430 430 ('pullrequest_desc', 'Description'),
431 431 ('pullrequest_title', 'Title'),
432 432 ('__start__', 'review_members:sequence'),
433 433 ('__start__', 'reviewer:mapping'),
434 434 ('user_id', '1'),
435 435 ('__start__', 'reasons:sequence'),
436 436 ('reason', 'Some reason'),
437 437 ('__end__', 'reasons:sequence'),
438 438 ('__end__', 'reviewer:mapping'),
439 439 ('__end__', 'review_members:sequence'),
440 440 ('__start__', 'revisions:sequence'),
441 441 ('revisions', commit_ids['change']),
442 442 ('__end__', 'revisions:sequence'),
443 443 ('user', ''),
444 444 ('csrf_token', csrf_token),
445 445 ],
446 446 status=302)
447 447
448 448 location = response.headers['Location']
449 449 pull_request_id = int(location.rsplit('/', 1)[1])
450 450 pull_request = PullRequest.get(pull_request_id)
451 451
452 452 # target_ref has to point to the ancestor's commit_id in order to
453 453 # show the correct diff
454 454 expected_target_ref = 'branch:default:' + commit_ids['ancestor']
455 455 assert pull_request.target_ref == expected_target_ref
456 456
457 457 # Check generated diff contents
458 458 response = response.follow()
459 459 assert 'content_of_ancestor' not in response.body
460 460 assert 'content_of_ancestor-child' not in response.body
461 461 assert 'content_of_change' in response.body
462 462
463 463 def test_merge_pull_request_enabled(self, pr_util, csrf_token):
464 464 # Clear any previous calls to rcextensions
465 465 rhodecode.EXTENSIONS.calls.clear()
466 466
467 467 pull_request = pr_util.create_pull_request(
468 468 approved=True, mergeable=True)
469 469 pull_request_id = pull_request.pull_request_id
470 470 repo_name = pull_request.target_repo.scm_instance().name,
471 471
472 472 response = self.app.post(
473 473 url(controller='pullrequests',
474 474 action='merge',
475 475 repo_name=str(repo_name[0]),
476 476 pull_request_id=str(pull_request_id)),
477 477 params={'csrf_token': csrf_token}).follow()
478 478
479 479 pull_request = PullRequest.get(pull_request_id)
480 480
481 481 assert response.status_int == 200
482 482 assert pull_request.is_closed()
483 483 assert_pull_request_status(
484 484 pull_request, ChangesetStatus.STATUS_APPROVED)
485 485
486 486 # Check the relevant log entries were added
487 487 user_logs = UserLog.query().order_by('-user_log_id').limit(4)
488 488 actions = [log.action for log in user_logs]
489 489 pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
490 490 expected_actions = [
491 491 u'user_closed_pull_request:%d' % pull_request_id,
492 492 u'user_merged_pull_request:%d' % pull_request_id,
493 493 # The action below reflect that the post push actions were executed
494 494 u'user_commented_pull_request:%d' % pull_request_id,
495 495 u'push:%s' % ','.join(pr_commit_ids),
496 496 ]
497 497 assert actions == expected_actions
498 498
499 499 # Check post_push rcextension was really executed
500 500 push_calls = rhodecode.EXTENSIONS.calls['post_push']
501 501 assert len(push_calls) == 1
502 502 unused_last_call_args, last_call_kwargs = push_calls[0]
503 503 assert last_call_kwargs['action'] == 'push'
504 504 assert last_call_kwargs['pushed_revs'] == pr_commit_ids
505 505
506 506 def test_merge_pull_request_disabled(self, pr_util, csrf_token):
507 507 pull_request = pr_util.create_pull_request(mergeable=False)
508 508 pull_request_id = pull_request.pull_request_id
509 509 pull_request = PullRequest.get(pull_request_id)
510 510
511 511 response = self.app.post(
512 512 url(controller='pullrequests',
513 513 action='merge',
514 514 repo_name=pull_request.target_repo.scm_instance().name,
515 515 pull_request_id=str(pull_request.pull_request_id)),
516 516 params={'csrf_token': csrf_token}).follow()
517 517
518 518 assert response.status_int == 200
519 519 assert 'Server-side pull request merging is disabled.' in response.body
520 520
521 521 @pytest.mark.skip_backends('svn')
522 522 def test_merge_pull_request_not_approved(self, pr_util, csrf_token):
523 523 pull_request = pr_util.create_pull_request(mergeable=True)
524 524 pull_request_id = pull_request.pull_request_id
525 525 repo_name = pull_request.target_repo.scm_instance().name,
526 526
527 527 response = self.app.post(
528 528 url(controller='pullrequests',
529 529 action='merge',
530 530 repo_name=str(repo_name[0]),
531 531 pull_request_id=str(pull_request_id)),
532 532 params={'csrf_token': csrf_token}).follow()
533 533
534 534 pull_request = PullRequest.get(pull_request_id)
535 535
536 536 assert response.status_int == 200
537 537 assert ' Reviewer approval is pending.' in response.body
538 538
539 539 def test_update_source_revision(self, backend, csrf_token):
540 540 commits = [
541 541 {'message': 'ancestor'},
542 542 {'message': 'change'},
543 543 {'message': 'change-2'},
544 544 ]
545 545 commit_ids = backend.create_master_repo(commits)
546 546 target = backend.create_repo(heads=['ancestor'])
547 547 source = backend.create_repo(heads=['change'])
548 548
549 549 # create pr from a in source to A in target
550 550 pull_request = PullRequest()
551 551 pull_request.source_repo = source
552 552 # TODO: johbo: Make sure that we write the source ref this way!
553 553 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
554 554 branch=backend.default_branch_name, commit_id=commit_ids['change'])
555 555 pull_request.target_repo = target
556 556
557 557 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
558 558 branch=backend.default_branch_name,
559 559 commit_id=commit_ids['ancestor'])
560 560 pull_request.revisions = [commit_ids['change']]
561 561 pull_request.title = u"Test"
562 562 pull_request.description = u"Description"
563 563 pull_request.author = UserModel().get_by_username(
564 564 TEST_USER_ADMIN_LOGIN)
565 565 Session().add(pull_request)
566 566 Session().commit()
567 567 pull_request_id = pull_request.pull_request_id
568 568
569 569 # source has ancestor - change - change-2
570 570 backend.pull_heads(source, heads=['change-2'])
571 571
572 572 # update PR
573 573 self.app.post(
574 574 url(controller='pullrequests', action='update',
575 575 repo_name=target.repo_name,
576 576 pull_request_id=str(pull_request_id)),
577 577 params={'update_commits': 'true', '_method': 'put',
578 578 'csrf_token': csrf_token})
579 579
580 580 # check that we have now both revisions
581 581 pull_request = PullRequest.get(pull_request_id)
582 582 assert pull_request.revisions == [
583 583 commit_ids['change-2'], commit_ids['change']]
584 584
585 585 # TODO: johbo: this should be a test on its own
586 586 response = self.app.get(url(
587 587 controller='pullrequests', action='index',
588 588 repo_name=target.repo_name))
589 589 assert response.status_int == 200
590 590 assert 'Pull request updated to' in response.body
591 591 assert 'with 1 added, 0 removed commits.' in response.body
592 592
593 593 def test_update_target_revision(self, backend, csrf_token):
594 594 commits = [
595 595 {'message': 'ancestor'},
596 596 {'message': 'change'},
597 597 {'message': 'ancestor-new', 'parents': ['ancestor']},
598 598 {'message': 'change-rebased'},
599 599 ]
600 600 commit_ids = backend.create_master_repo(commits)
601 601 target = backend.create_repo(heads=['ancestor'])
602 602 source = backend.create_repo(heads=['change'])
603 603
604 604 # create pr from a in source to A in target
605 605 pull_request = PullRequest()
606 606 pull_request.source_repo = source
607 607 # TODO: johbo: Make sure that we write the source ref this way!
608 608 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
609 609 branch=backend.default_branch_name, commit_id=commit_ids['change'])
610 610 pull_request.target_repo = target
611 611 # TODO: johbo: Target ref should be branch based, since tip can jump
612 612 # from branch to branch
613 613 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
614 614 branch=backend.default_branch_name,
615 615 commit_id=commit_ids['ancestor'])
616 616 pull_request.revisions = [commit_ids['change']]
617 617 pull_request.title = u"Test"
618 618 pull_request.description = u"Description"
619 619 pull_request.author = UserModel().get_by_username(
620 620 TEST_USER_ADMIN_LOGIN)
621 621 Session().add(pull_request)
622 622 Session().commit()
623 623 pull_request_id = pull_request.pull_request_id
624 624
625 625 # target has ancestor - ancestor-new
626 626 # source has ancestor - ancestor-new - change-rebased
627 627 backend.pull_heads(target, heads=['ancestor-new'])
628 628 backend.pull_heads(source, heads=['change-rebased'])
629 629
630 630 # update PR
631 631 self.app.post(
632 632 url(controller='pullrequests', action='update',
633 633 repo_name=target.repo_name,
634 634 pull_request_id=str(pull_request_id)),
635 635 params={'update_commits': 'true', '_method': 'put',
636 636 'csrf_token': csrf_token},
637 637 status=200)
638 638
639 639 # check that we have now both revisions
640 640 pull_request = PullRequest.get(pull_request_id)
641 641 assert pull_request.revisions == [commit_ids['change-rebased']]
642 642 assert pull_request.target_ref == 'branch:{branch}:{commit_id}'.format(
643 643 branch=backend.default_branch_name,
644 644 commit_id=commit_ids['ancestor-new'])
645 645
646 646 # TODO: johbo: This should be a test on its own
647 647 response = self.app.get(url(
648 648 controller='pullrequests', action='index',
649 649 repo_name=target.repo_name))
650 650 assert response.status_int == 200
651 651 assert 'Pull request updated to' in response.body
652 652 assert 'with 1 added, 1 removed commits.' in response.body
653 653
654 654 def test_update_of_ancestor_reference(self, backend, csrf_token):
655 655 commits = [
656 656 {'message': 'ancestor'},
657 657 {'message': 'change'},
658 658 {'message': 'change-2'},
659 659 {'message': 'ancestor-new', 'parents': ['ancestor']},
660 660 {'message': 'change-rebased'},
661 661 ]
662 662 commit_ids = backend.create_master_repo(commits)
663 663 target = backend.create_repo(heads=['ancestor'])
664 664 source = backend.create_repo(heads=['change'])
665 665
666 666 # create pr from a in source to A in target
667 667 pull_request = PullRequest()
668 668 pull_request.source_repo = source
669 669 # TODO: johbo: Make sure that we write the source ref this way!
670 670 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
671 671 branch=backend.default_branch_name,
672 672 commit_id=commit_ids['change'])
673 673 pull_request.target_repo = target
674 674 # TODO: johbo: Target ref should be branch based, since tip can jump
675 675 # from branch to branch
676 676 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
677 677 branch=backend.default_branch_name,
678 678 commit_id=commit_ids['ancestor'])
679 679 pull_request.revisions = [commit_ids['change']]
680 680 pull_request.title = u"Test"
681 681 pull_request.description = u"Description"
682 682 pull_request.author = UserModel().get_by_username(
683 683 TEST_USER_ADMIN_LOGIN)
684 684 Session().add(pull_request)
685 685 Session().commit()
686 686 pull_request_id = pull_request.pull_request_id
687 687
688 688 # target has ancestor - ancestor-new
689 689 # source has ancestor - ancestor-new - change-rebased
690 690 backend.pull_heads(target, heads=['ancestor-new'])
691 691 backend.pull_heads(source, heads=['change-rebased'])
692 692
693 693 # update PR
694 694 self.app.post(
695 695 url(controller='pullrequests', action='update',
696 696 repo_name=target.repo_name,
697 697 pull_request_id=str(pull_request_id)),
698 698 params={'update_commits': 'true', '_method': 'put',
699 699 'csrf_token': csrf_token},
700 700 status=200)
701 701
702 702 # Expect the target reference to be updated correctly
703 703 pull_request = PullRequest.get(pull_request_id)
704 704 assert pull_request.revisions == [commit_ids['change-rebased']]
705 705 expected_target_ref = 'branch:{branch}:{commit_id}'.format(
706 706 branch=backend.default_branch_name,
707 707 commit_id=commit_ids['ancestor-new'])
708 708 assert pull_request.target_ref == expected_target_ref
709 709
710 710 def test_remove_pull_request_branch(self, backend_git, csrf_token):
711 711 branch_name = 'development'
712 712 commits = [
713 713 {'message': 'initial-commit'},
714 714 {'message': 'old-feature'},
715 715 {'message': 'new-feature', 'branch': branch_name},
716 716 ]
717 717 repo = backend_git.create_repo(commits)
718 718 commit_ids = backend_git.commit_ids
719 719
720 720 pull_request = PullRequest()
721 721 pull_request.source_repo = repo
722 722 pull_request.target_repo = repo
723 723 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
724 724 branch=branch_name, commit_id=commit_ids['new-feature'])
725 725 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
726 726 branch=backend_git.default_branch_name,
727 727 commit_id=commit_ids['old-feature'])
728 728 pull_request.revisions = [commit_ids['new-feature']]
729 729 pull_request.title = u"Test"
730 730 pull_request.description = u"Description"
731 731 pull_request.author = UserModel().get_by_username(
732 732 TEST_USER_ADMIN_LOGIN)
733 733 Session().add(pull_request)
734 734 Session().commit()
735 735
736 736 vcs = repo.scm_instance()
737 737 vcs.remove_ref('refs/heads/{}'.format(branch_name))
738 738
739 739 response = self.app.get(url(
740 740 controller='pullrequests', action='show',
741 741 repo_name=repo.repo_name,
742 742 pull_request_id=str(pull_request.pull_request_id)))
743 743
744 744 assert response.status_int == 200
745 745 assert_response = AssertResponse(response)
746 746 assert_response.element_contains(
747 747 '#changeset_compare_view_content .alert strong',
748 748 'Missing commits')
749 749 assert_response.element_contains(
750 750 '#changeset_compare_view_content .alert',
751 751 'This pull request cannot be displayed, because one or more'
752 752 ' commits no longer exist in the source repository.')
753 753
754 754 def test_strip_commits_from_pull_request(
755 755 self, backend, pr_util, csrf_token):
756 756 commits = [
757 757 {'message': 'initial-commit'},
758 758 {'message': 'old-feature'},
759 759 {'message': 'new-feature', 'parents': ['initial-commit']},
760 760 ]
761 761 pull_request = pr_util.create_pull_request(
762 762 commits, target_head='initial-commit', source_head='new-feature',
763 763 revisions=['new-feature'])
764 764
765 765 vcs = pr_util.source_repository.scm_instance()
766 766 if backend.alias == 'git':
767 767 vcs.strip(pr_util.commit_ids['new-feature'], branch_name='master')
768 768 else:
769 769 vcs.strip(pr_util.commit_ids['new-feature'])
770 770
771 771 response = self.app.get(url(
772 772 controller='pullrequests', action='show',
773 773 repo_name=pr_util.target_repository.repo_name,
774 774 pull_request_id=str(pull_request.pull_request_id)))
775 775
776 776 assert response.status_int == 200
777 777 assert_response = AssertResponse(response)
778 778 assert_response.element_contains(
779 779 '#changeset_compare_view_content .alert strong',
780 780 'Missing commits')
781 781 assert_response.element_contains(
782 782 '#changeset_compare_view_content .alert',
783 783 'This pull request cannot be displayed, because one or more'
784 784 ' commits no longer exist in the source repository.')
785 785 assert_response.element_contains(
786 786 '#update_commits',
787 787 'Update commits')
788 788
789 789 def test_strip_commits_and_update(
790 790 self, backend, pr_util, csrf_token):
791 791 commits = [
792 792 {'message': 'initial-commit'},
793 793 {'message': 'old-feature'},
794 794 {'message': 'new-feature', 'parents': ['old-feature']},
795 795 ]
796 796 pull_request = pr_util.create_pull_request(
797 797 commits, target_head='old-feature', source_head='new-feature',
798 798 revisions=['new-feature'], mergeable=True)
799 799
800 800 vcs = pr_util.source_repository.scm_instance()
801 801 if backend.alias == 'git':
802 802 vcs.strip(pr_util.commit_ids['new-feature'], branch_name='master')
803 803 else:
804 804 vcs.strip(pr_util.commit_ids['new-feature'])
805 805
806 806 response = self.app.post(
807 807 url(controller='pullrequests', action='update',
808 808 repo_name=pull_request.target_repo.repo_name,
809 809 pull_request_id=str(pull_request.pull_request_id)),
810 810 params={'update_commits': 'true', '_method': 'put',
811 811 'csrf_token': csrf_token})
812 812
813 813 assert response.status_int == 200
814 814 assert response.body == 'true'
815 815
816 816 # Make sure that after update, it won't raise 500 errors
817 817 response = self.app.get(url(
818 818 controller='pullrequests', action='show',
819 819 repo_name=pr_util.target_repository.repo_name,
820 820 pull_request_id=str(pull_request.pull_request_id)))
821 821
822 822 assert response.status_int == 200
823 823 assert_response = AssertResponse(response)
824 824 assert_response.element_contains(
825 825 '#changeset_compare_view_content .alert strong',
826 826 'Missing commits')
827 827
828 828 def test_branch_is_a_link(self, pr_util):
829 829 pull_request = pr_util.create_pull_request()
830 830 pull_request.source_ref = 'branch:origin:1234567890abcdef'
831 831 pull_request.target_ref = 'branch:target:abcdef1234567890'
832 832 Session().add(pull_request)
833 833 Session().commit()
834 834
835 835 response = self.app.get(url(
836 836 controller='pullrequests', action='show',
837 837 repo_name=pull_request.target_repo.scm_instance().name,
838 838 pull_request_id=str(pull_request.pull_request_id)))
839 839 assert response.status_int == 200
840 840 assert_response = AssertResponse(response)
841 841
842 842 origin = assert_response.get_element('.pr-origininfo .tag')
843 843 origin_children = origin.getchildren()
844 844 assert len(origin_children) == 1
845 845 target = assert_response.get_element('.pr-targetinfo .tag')
846 846 target_children = target.getchildren()
847 847 assert len(target_children) == 1
848 848
849 849 expected_origin_link = url(
850 850 'changelog_home',
851 851 repo_name=pull_request.source_repo.scm_instance().name,
852 852 branch='origin')
853 853 expected_target_link = url(
854 854 'changelog_home',
855 855 repo_name=pull_request.target_repo.scm_instance().name,
856 856 branch='target')
857 857 assert origin_children[0].attrib['href'] == expected_origin_link
858 858 assert origin_children[0].text == 'branch: origin'
859 859 assert target_children[0].attrib['href'] == expected_target_link
860 860 assert target_children[0].text == 'branch: target'
861 861
862 862 def test_bookmark_is_not_a_link(self, pr_util):
863 863 pull_request = pr_util.create_pull_request()
864 864 pull_request.source_ref = 'bookmark:origin:1234567890abcdef'
865 865 pull_request.target_ref = 'bookmark:target:abcdef1234567890'
866 866 Session().add(pull_request)
867 867 Session().commit()
868 868
869 869 response = self.app.get(url(
870 870 controller='pullrequests', action='show',
871 871 repo_name=pull_request.target_repo.scm_instance().name,
872 872 pull_request_id=str(pull_request.pull_request_id)))
873 873 assert response.status_int == 200
874 874 assert_response = AssertResponse(response)
875 875
876 876 origin = assert_response.get_element('.pr-origininfo .tag')
877 877 assert origin.text.strip() == 'bookmark: origin'
878 878 assert origin.getchildren() == []
879 879
880 880 target = assert_response.get_element('.pr-targetinfo .tag')
881 881 assert target.text.strip() == 'bookmark: target'
882 882 assert target.getchildren() == []
883 883
884 884 def test_tag_is_not_a_link(self, pr_util):
885 885 pull_request = pr_util.create_pull_request()
886 886 pull_request.source_ref = 'tag:origin:1234567890abcdef'
887 887 pull_request.target_ref = 'tag:target:abcdef1234567890'
888 888 Session().add(pull_request)
889 889 Session().commit()
890 890
891 891 response = self.app.get(url(
892 892 controller='pullrequests', action='show',
893 893 repo_name=pull_request.target_repo.scm_instance().name,
894 894 pull_request_id=str(pull_request.pull_request_id)))
895 895 assert response.status_int == 200
896 896 assert_response = AssertResponse(response)
897 897
898 898 origin = assert_response.get_element('.pr-origininfo .tag')
899 899 assert origin.text.strip() == 'tag: origin'
900 900 assert origin.getchildren() == []
901 901
902 902 target = assert_response.get_element('.pr-targetinfo .tag')
903 903 assert target.text.strip() == 'tag: target'
904 904 assert target.getchildren() == []
905 905
906 906 def test_description_is_escaped_on_index_page(self, backend, pr_util):
907 907 xss_description = "<script>alert('Hi!')</script>"
908 908 pull_request = pr_util.create_pull_request(description=xss_description)
909 909 response = self.app.get(url(
910 910 controller='pullrequests', action='show_all',
911 911 repo_name=pull_request.target_repo.repo_name))
912 912 response.mustcontain(
913 913 "&lt;script&gt;alert(&#39;Hi!&#39;)&lt;/script&gt;")
914 914
915 @pytest.mark.parametrize('mergeable', [True, False])
916 def test_shadow_repository_link(
917 self, mergeable, pr_util, http_host_stub):
918 """
919 Check that the pull request summary page displays a link to the shadow
920 repository if the pull request is mergeable. If it is not mergeable
921 the link should not be displayed.
922 """
923 pull_request = pr_util.create_pull_request(
924 mergeable=mergeable, enable_notifications=False)
925 target_repo = pull_request.target_repo.scm_instance()
926 pr_id = pull_request.pull_request_id
927 shadow_url = '{host}/{repo}/pull-request/{pr_id}/repository'.format(
928 host=http_host_stub, repo=target_repo.name, pr_id=pr_id)
929
930 response = self.app.get(url(
931 controller='pullrequests', action='show',
932 repo_name=target_repo.name,
933 pull_request_id=str(pr_id)))
934
935 assertr = AssertResponse(response)
936 if mergeable:
937 assertr.element_value_contains(
938 'div.pr-mergeinfo input', shadow_url)
939 assertr.element_value_contains(
940 'div.pr-mergeinfo input', 'pr-merge')
941 else:
942 assertr.no_element_exists('div.pr-mergeinfo')
943
915 944
916 945 def assert_pull_request_status(pull_request, expected_status):
917 946 status = ChangesetStatusModel().calculated_review_status(
918 947 pull_request=pull_request)
919 948 assert status == expected_status
920 949
921 950
922 951 @pytest.mark.parametrize('action', ['show_all', 'index', 'create'])
923 952 @pytest.mark.usefixtures("autologin_user")
924 953 def test_redirects_to_repo_summary_for_svn_repositories(
925 954 backend_svn, app, action):
926 955 denied_actions = ['show_all', 'index', 'create']
927 956 for action in denied_actions:
928 957 response = app.get(url(
929 958 controller='pullrequests', action=action,
930 959 repo_name=backend_svn.repo_name))
931 960 assert response.status_int == 302
932 961
933 962 # Not allowed, redirect to the summary
934 963 redirected = response.follow()
935 964 summary_url = url('summary_home', repo_name=backend_svn.repo_name)
936 965
937 966 # URL adds leading slash and path doesn't have it
938 967 assert redirected.req.path == summary_url
939 968
940 969
941 970 def test_delete_comment_returns_404_if_comment_does_not_exist(pylonsapp):
942 971 # TODO: johbo: Global import not possible because models.forms blows up
943 972 from rhodecode.controllers.pullrequests import PullrequestsController
944 973 controller = PullrequestsController()
945 974 patcher = mock.patch(
946 975 'rhodecode.model.db.BaseModel.get', return_value=None)
947 976 with pytest.raises(HTTPNotFound), patcher:
948 977 controller._delete_comment(1)
General Comments 0
You need to be logged in to leave comments. Login now