def test_update_target_revision(self, backend, csrf_token):
        commits = [
            {'message': 'ancestor'},
            {'message': 'change'},
            {'message': 'ancestor-new', 'parents': ['ancestor']},
            {'message': 'change-rebased'},
        ]
        commit_ids = backend.create_master_repo(commits)
        target = backend.create_repo(heads=['ancestor'])
        source = backend.create_repo(heads=['change'])

        # create pr from a in source to A in target
        pull_request = PullRequest()
        pull_request.source_repo = source
        # TODO: johbo: Make sure that we write the source ref this way!
        pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
            branch=backend.default_branch_name, commit_id=commit_ids['change'])
        pull_request.target_repo = target
        # TODO: johbo: Target ref should be branch based, since tip can jump
        # from branch to branch
        pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
            branch=backend.default_branch_name,
            commit_id=commit_ids['ancestor'])
        pull_request.revisions = [commit_ids['change']]
        pull_request.title = u"Test"
        pull_request.description = u"Description"
        pull_request.author = UserModel().get_by_username(
            TEST_USER_ADMIN_LOGIN)
        Session().add(pull_request)
        Session().commit()
        pull_request_id = pull_request.pull_request_id

        # target has ancestor - ancestor-new
        # source has ancestor - ancestor-new - change-rebased
        backend.pull_heads(target, heads=['ancestor-new'])
        backend.pull_heads(source, heads=['change-rebased'])

        # update PR
        self.app.post(
            url(controller='pullrequests', action='update',
                repo_name=target.repo_name,
                pull_request_id=str(pull_request_id)),
            params={'update_commits': 'true', '_method': 'put',
                    'csrf_token': csrf_token},
            status=200)

        # check that we have now both revisions
        pull_request = PullRequest.get(pull_request_id)
        assert pull_request.revisions == [commit_ids['change-rebased']]
        assert pull_request.target_ref == 'branch:{branch}:{commit_id}'.format(
            branch=backend.default_branch_name,
            commit_id=commit_ids['ancestor-new'])

        # TODO: johbo: This should be a test on its own
        response = self.app.get(url(
            controller='pullrequests', action='index',
            repo_name=target.repo_name))
        assert response.status_int == 200
        assert 'Pull request updated to' in response.body
        assert 'with 1 added, 1 removed commits.' in response.body
    def test_merge_failed(self, pull_request, merge_extras):
        user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
        self.merge_mock.return_value = MergeResponse(
            False, False, '6126b7bfcc82ad2d3deaee22af926b082ce54cc6',
            MergeFailureReason.MERGE_FAILED)

        PullRequestModel().merge(pull_request,
                                 pull_request.author,
                                 extras=merge_extras)

        message = (
            u'Merge pull request #{pr_id} from {source_repo} {source_ref_name}'
            u'\n\n {pr_title}'.format(
                pr_id=pull_request.pull_request_id,
                source_repo=safe_unicode(
                    pull_request.source_repo.scm_instance().name),
                source_ref_name=pull_request.source_ref_parts.name,
                pr_title=safe_unicode(pull_request.title)))
        self.merge_mock.assert_called_once_with(
            pull_request.target_ref_parts,
            pull_request.source_repo.scm_instance(),
            pull_request.source_ref_parts,
            self.workspace_id,
            user_name=user.username,
            user_email=user.email,
            message=message)

        pull_request = PullRequest.get(pull_request.pull_request_id)
        assert self.invalidation_mock.called is False
        assert pull_request.merge_rev is None
    def test_comment_force_close_pull_request(self, pr_util, csrf_token):
        pull_request = pr_util.create_pull_request()
        pull_request_id = pull_request.pull_request_id
        reviewers_ids = [1, 2]
        PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
        author = pull_request.user_id
        repo = pull_request.target_repo.repo_id
        self.app.post(
            url(controller='pullrequests',
                action='comment',
                repo_name=pull_request.target_repo.scm_instance().name,
                pull_request_id=str(pull_request_id)),
            params={
                'changeset_status': 'forced_closed',
                'csrf_token': csrf_token},
            status=302)

        pull_request = PullRequest.get(pull_request_id)

        action = 'user_closed_pull_request:%d' % pull_request_id
        journal = UserLog.query().filter(
            UserLog.user_id == author,
            UserLog.repository_id == repo,
            UserLog.action == action).all()
        assert len(journal) == 1

        # check only the latest status, not the review status
        status = ChangesetStatusModel().get_status(
            pull_request.source_repo, pull_request=pull_request)
        assert status == ChangesetStatus.STATUS_REJECTED
    def test_reviewer_notifications(self, backend, csrf_token):
        # We have to use the app.post for this test so it will create the
        # notifications properly with the new PR
        commits = [
            {'message': 'ancestor',
             'added': [FileNode('file_A', content='content_of_ancestor')]},
            {'message': 'change',
             'added': [FileNode('file_a', content='content_of_change')]},
            {'message': 'change-child'},
            {'message': 'ancestor-child', 'parents': ['ancestor'],
             'added': [
                FileNode('file_B', content='content_of_ancestor_child')]},
            {'message': 'ancestor-child-2'},
        ]
        commit_ids = backend.create_master_repo(commits)
        target = backend.create_repo(heads=['ancestor-child'])
        source = backend.create_repo(heads=['change'])

        response = self.app.post(
            url(
                controller='pullrequests',
                action='create',
                repo_name=source.repo_name),
            params={
                'source_repo': source.repo_name,
                'source_ref': 'branch:default:' + commit_ids['change'],
                'target_repo': target.repo_name,
                'target_ref': 'branch:default:' + commit_ids['ancestor-child'],
                'pullrequest_desc': 'Description',
                'pullrequest_title': 'Title',
                'review_members': '2',
                'revisions': commit_ids['change'],
                'user': '',
                'csrf_token': csrf_token,
            },
            status=302)

        location = response.headers['Location']
        pull_request_id = int(location.rsplit('/', 1)[1])
        pull_request = PullRequest.get(pull_request_id)

        # Check that a notification was made
        notifications = Notification.query()\
            .filter(Notification.created_by == pull_request.author.user_id,
                    Notification.type_ == Notification.TYPE_PULL_REQUEST,
                    Notification.subject.contains("wants you to review "
                                                  "pull request #%d"
                                                  % pull_request_id))
        assert len(notifications.all()) == 1

        # Change reviewers and check that a notification was made
        PullRequestModel().update_reviewers(pull_request.pull_request_id, [1])
        assert len(notifications.all()) == 2
    def test_merge_pull_request_disabled(self, pr_util, csrf_token):
        pull_request = pr_util.create_pull_request(mergeable=False)
        pull_request_id = pull_request.pull_request_id
        pull_request = PullRequest.get(pull_request_id)

        response = self.app.post(
            url(controller='pullrequests',
                action='merge',
                repo_name=pull_request.target_repo.scm_instance().name,
                pull_request_id=str(pull_request.pull_request_id)),
            params={'csrf_token': csrf_token}).follow()

        assert response.status_int == 200
        assert 'Server-side pull request merging is disabled.' in response.body
    def test_create_pull_request_stores_ancestor_commit_id(self, backend,
                                                           csrf_token):
        commits = [
            {'message': 'ancestor',
             'added': [FileNode('file_A', content='content_of_ancestor')]},
            {'message': 'change',
             'added': [FileNode('file_a', content='content_of_change')]},
            {'message': 'change-child'},
            {'message': 'ancestor-child', 'parents': ['ancestor'],
             'added': [
                FileNode('file_B', content='content_of_ancestor_child')]},
            {'message': 'ancestor-child-2'},
        ]
        commit_ids = backend.create_master_repo(commits)
        target = backend.create_repo(heads=['ancestor-child'])
        source = backend.create_repo(heads=['change'])

        response = self.app.post(
            url(
                controller='pullrequests',
                action='create',
                repo_name=source.repo_name),
            params={
                'source_repo': source.repo_name,
                'source_ref': 'branch:default:' + commit_ids['change'],
                'target_repo': target.repo_name,
                'target_ref': 'branch:default:' + commit_ids['ancestor-child'],
                'pullrequest_desc': 'Description',
                'pullrequest_title': 'Title',
                'review_members': '1',
                'revisions': commit_ids['change'],
                'user': '',
                'csrf_token': csrf_token,
            },
            status=302)

        location = response.headers['Location']
        pull_request_id = int(location.rsplit('/', 1)[1])
        pull_request = PullRequest.get(pull_request_id)

        # target_ref has to point to the ancestor's commit_id in order to
        # show the correct diff
        expected_target_ref = 'branch:default:' + commit_ids['ancestor']
        assert pull_request.target_ref == expected_target_ref

        # Check generated diff contents
        response = response.follow()
        assert 'content_of_ancestor' not in response.body
        assert 'content_of_ancestor-child' not in response.body
        assert 'content_of_change' in response.body
    def test_merge_pull_request_not_approved(self, pr_util, csrf_token):
        pull_request = pr_util.create_pull_request(mergeable=True)
        pull_request_id = pull_request.pull_request_id
        repo_name = pull_request.target_repo.scm_instance().name,

        response = self.app.post(
            url(controller='pullrequests',
                action='merge',
                repo_name=str(repo_name[0]),
                pull_request_id=str(pull_request_id)),
            params={'csrf_token': csrf_token}).follow()

        pull_request = PullRequest.get(pull_request_id)

        assert response.status_int == 200
        assert ' Reviewer approval is pending.' in response.body
    def test_merge_pull_request_enabled(self, pr_util, csrf_token):
        # Clear any previous calls to rcextensions
        rhodecode.EXTENSIONS.calls.clear()

        pull_request = pr_util.create_pull_request(
            approved=True, mergeable=True)
        pull_request_id = pull_request.pull_request_id
        repo_name = pull_request.target_repo.scm_instance().name,

        response = self.app.post(
            url(controller='pullrequests',
                action='merge',
                repo_name=str(repo_name[0]),
                pull_request_id=str(pull_request_id)),
            params={'csrf_token': csrf_token}).follow()

        pull_request = PullRequest.get(pull_request_id)

        assert response.status_int == 200
        assert pull_request.is_closed()
        assert_pull_request_status(
            pull_request, ChangesetStatus.STATUS_APPROVED)

        # Check the relevant log entries were added
        user_logs = UserLog.query().order_by('-user_log_id').limit(4)
        actions = [log.action for log in user_logs]
        pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
        expected_actions = [
            u'user_closed_pull_request:%d' % pull_request_id,
            u'user_merged_pull_request:%d' % pull_request_id,
            # The action below reflect that the post push actions were executed
            u'user_commented_pull_request:%d' % pull_request_id,
            u'push:%s' % ','.join(pr_commit_ids),
        ]
        assert actions == expected_actions

        # Check post_push rcextension was really executed
        push_calls = rhodecode.EXTENSIONS.calls['post_push']
        assert len(push_calls) == 1
        unused_last_call_args, last_call_kwargs = push_calls[0]
        assert last_call_kwargs['action'] == 'push'
        assert last_call_kwargs['pushed_revs'] == pr_commit_ids
    def test_reject_and_close_pull_request(self, pr_util, csrf_token):
        pull_request = pr_util.create_pull_request()
        pull_request_id = pull_request.pull_request_id
        response = self.app.post(
            url(controller='pullrequests',
                action='update',
                repo_name=pull_request.target_repo.scm_instance().name,
                pull_request_id=str(pull_request.pull_request_id)),
            params={'close_pull_request': 'true', '_method': 'put',
                    'csrf_token': csrf_token})

        pull_request = PullRequest.get(pull_request_id)

        assert response.json is True
        assert pull_request.is_closed()

        # check only the latest status, not the review status
        status = ChangesetStatusModel().get_status(
            pull_request.source_repo, pull_request=pull_request)
        assert status == ChangesetStatus.STATUS_REJECTED
    def test_edit_title_description(self, pr_util, csrf_token):
        pull_request = pr_util.create_pull_request()
        pull_request_id = pull_request.pull_request_id

        response = self.app.post(
            url(controller='pullrequests', action='update',
                repo_name=pull_request.target_repo.repo_name,
                pull_request_id=str(pull_request_id)),
            params={
                'edit_pull_request': 'true',
                '_method': 'put',
                'title': 'New title',
                'description': 'New description',
                'csrf_token': csrf_token})

        assert_session_flash(
            response, u'Pull request title & description updated.',
            category='success')

        pull_request = PullRequest.get(pull_request_id)
        assert pull_request.title == 'New title'
        assert pull_request.description == 'New description'
    def test_create_pull_request(self, backend, csrf_token):
        commits = [
            {'message': 'ancestor'},
            {'message': 'change'},
        ]
        commit_ids = backend.create_master_repo(commits)
        target = backend.create_repo(heads=['ancestor'])
        source = backend.create_repo(heads=['change'])

        response = self.app.post(
            url(
                controller='pullrequests',
                action='create',
                repo_name=source.repo_name),
            params={
                'source_repo': source.repo_name,
                'source_ref': 'branch:default:' + commit_ids['change'],
                'target_repo': target.repo_name,
                'target_ref': 'branch:default:' + commit_ids['ancestor'],
                'pullrequest_desc': 'Description',
                'pullrequest_title': 'Title',
                'review_members': '1',
                'revisions': commit_ids['change'],
                'user': '',
                'csrf_token': csrf_token,
            },
            status=302)

        location = response.headers['Location']
        pull_request_id = int(location.rsplit('/', 1)[1])
        pull_request = PullRequest.get(pull_request_id)

        # check that we have now both revisions
        assert pull_request.revisions == [commit_ids['change']]
        assert pull_request.source_ref == 'branch:default:' + commit_ids['change']
        expected_target_ref = 'branch:default:' + commit_ids['ancestor']
        assert pull_request.target_ref == expected_target_ref