def test_merge_status(self, pull_request):
        self.merge_mock.return_value = MergeResponse(True, False, None,
                                                     MergeFailureReason.NONE)

        assert pull_request._last_merge_source_rev is None
        assert pull_request._last_merge_target_rev is None
        assert pull_request._last_merge_status is None

        status, msg = PullRequestModel().merge_status(pull_request)
        assert status is True
        assert msg.eval() == 'This pull request can be automatically merged.'
        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,
            dry_run=True)

        assert pull_request._last_merge_source_rev == self.source_commit
        assert pull_request._last_merge_target_rev == self.target_commit
        assert pull_request._last_merge_status is MergeFailureReason.NONE

        self.merge_mock.reset_mock()
        status, msg = PullRequestModel().merge_status(pull_request)
        assert status is True
        assert msg.eval() == 'This pull request can be automatically merged.'
        assert self.merge_mock.called is False
def test_update_skips_new_version_if_unchanged(pr_util):
    pull_request = pr_util.create_pull_request()
    model = PullRequestModel()
    model.update_commits(pull_request)

    # Expect that it still has no versions
    assert len(model.get_versions(pull_request)) == 0
    def test_merge_status_known_failure(self, pull_request):
        self.merge_mock.return_value = MergeResponse(
            False, False, None, MergeFailureReason.MERGE_FAILED)

        assert pull_request._last_merge_source_rev is None
        assert pull_request._last_merge_target_rev is None
        assert pull_request._last_merge_status is None

        status, msg = PullRequestModel().merge_status(pull_request)
        assert status is False
        assert (msg.eval() ==
                'This pull request cannot be merged because of conflicts.')
        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,
            dry_run=True)

        assert pull_request._last_merge_source_rev == self.source_commit
        assert pull_request._last_merge_target_rev == self.target_commit
        assert (pull_request._last_merge_status is
                MergeFailureReason.MERGE_FAILED)

        self.merge_mock.reset_mock()
        status, msg = PullRequestModel().merge_status(pull_request)
        assert status is False
        assert (msg.eval() ==
                'This pull request cannot be merged because of conflicts.')
        assert self.merge_mock.called is False
    def test_merge_status_unknown_failure(self, pull_request):
        self.merge_mock.return_value = MergeResponse(
            False, False, None, MergeFailureReason.UNKNOWN)

        assert pull_request._last_merge_source_rev is None
        assert pull_request._last_merge_target_rev is None
        assert pull_request._last_merge_status is None

        status, msg = PullRequestModel().merge_status(pull_request)
        assert status is False
        assert msg.eval() == (
            'This pull request cannot be merged because of an unhandled'
            ' exception.')
        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,
            dry_run=True)

        assert pull_request._last_merge_source_rev is None
        assert pull_request._last_merge_target_rev is None
        assert pull_request._last_merge_status is None

        self.merge_mock.reset_mock()
        status, msg = PullRequestModel().merge_status(pull_request)
        assert status is False
        assert msg.eval() == (
            'This pull request cannot be merged because of an unhandled'
            ' exception.')
        assert self.merge_mock.called is True
def test_update_adds_a_comment_to_the_pull_request_about_the_change(pr_util):
    model = PullRequestModel()
    pull_request = pr_util.create_pull_request()
    pr_util.update_source_repository()
    pr_util.update_source_repository()

    model.update_commits(pull_request)

    # Expect to find a new comment about the change
    expected_message = textwrap.dedent("""\
        Auto status change to |under_review|

        .. role:: added
        .. role:: removed
        .. parsed-literal::

          Changed commits:
            * :added:`1 added`
            * :removed:`0 removed`

          Changed files:
            * `A file_2 <#a_c--92ed3b5f07b4>`_

        .. |under_review| replace:: *"Under Review"*""")
    pull_request_comments = sorted(pull_request.comments,
                                   key=lambda c: c.modified_at)
    update_comment = pull_request_comments[-1]
    assert update_comment.text == expected_message
示例#6
0
 def _update_commits(self, pull_request):
     try:
         if PullRequestModel().has_valid_update_type(pull_request):
             updated_version, changes = PullRequestModel().update_commits(
                 pull_request)
             if updated_version:
                 msg = _(
                     u'Pull request updated to "{source_commit_id}" with '
                     u'{count_added} added, {count_removed} removed '
                     u'commits.').format(source_commit_id=pull_request.
                                         source_ref_parts.commit_id,
                                         count_added=len(changes.added),
                                         count_removed=len(changes.removed))
                 h.flash(msg, category='success')
             else:
                 h.flash(_("Nothing changed in pull request."),
                         category='warning')
         else:
             msg = _(u"Skipping update of pull request due to reference "
                     u"type: {reference_type}").format(
                         reference_type=pull_request.source_ref_parts.type)
             h.flash(msg, category='warning')
     except CommitDoesNotExistError:
         h.flash(_(u'Update failed due to missing commits.'),
                 category='error')
 def test_get_awaiting_my_review(self, pull_request):
     PullRequestModel().update_reviewers(pull_request,
                                         [pull_request.author])
     prs = PullRequestModel().get_awaiting_my_review(
         pull_request.target_repo, user_id=pull_request.author.user_id)
     assert isinstance(prs, list)
     assert len(prs) == 1
def close_pull_request(request,
                       apiuser,
                       repoid,
                       pullrequestid,
                       userid=Optional(OAttr('apiuser'))):
    """
    Close the pull request specified by `pullrequestid`.

    :param apiuser: This is filled automatically from the |authtoken|.
    :type apiuser: AuthUser
    :param repoid: Repository name or repository ID to which the pull
        request belongs.
    :type repoid: str or int
    :param pullrequestid: ID of the pull request to be closed.
    :type pullrequestid: int
    :param userid: Close the pull request as this user.
    :type userid: Optional(str or int)

    Example output:

    .. code-block:: bash

      "id": <id_given_in_input>,
      "result":
        {
            "pull_request_id":  "<int>",
            "closed":           "<bool>"
        },
      "error": null

    """
    repo = get_repo_or_error(repoid)
    if not isinstance(userid, Optional):
        if (has_superadmin_permission(apiuser)
                or HasRepoPermissionAnyApi('repository.admin')(
                    user=apiuser, repo_name=repo.repo_name)):
            apiuser = get_user_or_error(userid)
        else:
            raise JSONRPCError('userid is not the same as your user')

    pull_request = get_pull_request_or_error(pullrequestid)
    if not PullRequestModel().check_user_update(
            pull_request, apiuser, api=True):
        raise JSONRPCError(
            'pull request `%s` close failed, no permission to close.' %
            (pullrequestid, ))
    if pull_request.is_closed():
        raise JSONRPCError('pull request `%s` is already closed' %
                           (pullrequestid, ))

    PullRequestModel().close_pull_request(pull_request.pull_request_id,
                                          apiuser)
    Session.commit()
    data = {
        'pull_request_id': pull_request.pull_request_id,
        'closed': True,
    }
    return data
def test_update_writes_snapshot_into_pull_request_version(pr_util):
    model = PullRequestModel()
    pull_request = pr_util.create_pull_request()
    pr_util.update_source_repository()

    model.update_commits(pull_request)

    # Expect that it has a version entry now
    assert len(model.get_versions(pull_request)) == 1
def test_update_assigns_comments_to_the_new_version(pr_util):
    model = PullRequestModel()
    pull_request = pr_util.create_pull_request()
    comment = pr_util.create_comment()
    pr_util.update_source_repository()

    model.update_commits(pull_request)

    # Expect that the comment is linked to the pr version now
    assert comment.pull_request_version == model.get_versions(pull_request)[0]
示例#11
0
    def index(self):
        source_repo = c.rhodecode_db_repo

        try:
            source_repo.scm_instance().get_commit()
        except EmptyRepositoryError:
            h.flash(h.literal(_('There are no commits yet')),
                    category='warning')
            redirect(url('summary_home', repo_name=source_repo.repo_name))

        commit_id = request.GET.get('commit')
        branch_ref = request.GET.get('branch')
        bookmark_ref = request.GET.get('bookmark')

        try:
            source_repo_data = PullRequestModel().generate_repo_data(
                source_repo,
                commit_id=commit_id,
                branch=branch_ref,
                bookmark=bookmark_ref)
        except CommitDoesNotExistError as e:
            log.exception(e)
            h.flash(_('Commit does not exist'), 'error')
            redirect(url('pullrequest_home', repo_name=source_repo.repo_name))

        default_target_repo = source_repo
        if (source_repo.parent
                and not source_repo.parent.scm_instance().is_empty()):
            # change default if we have a parent repo
            default_target_repo = source_repo.parent

        target_repo_data = PullRequestModel().generate_repo_data(
            default_target_repo)

        selected_source_ref = source_repo_data['refs']['selected_ref']

        title_source_ref = selected_source_ref.split(':', 2)[1]
        c.default_title = PullRequestModel().generate_pullrequest_title(
            source=source_repo.repo_name,
            source_ref=title_source_ref,
            target=default_target_repo.repo_name)

        c.default_repo_data = {
            'source_repo_name': source_repo.repo_name,
            'source_refs_json': json.dumps(source_repo_data),
            'target_repo_name': default_target_repo.repo_name,
            'target_refs_json': json.dumps(target_repo_data),
        }
        c.default_source_ref = selected_source_ref

        return render('/pullrequests/pullrequest.html')
    def test_get_commit_ids(self, pull_request):
        # The PR has been not merget yet, so expect an exception
        with pytest.raises(ValueError):
            PullRequestModel()._get_commit_ids(pull_request)

        # Merge revision is in the revisions list
        pull_request.merge_rev = pull_request.revisions[0]
        commit_ids = PullRequestModel()._get_commit_ids(pull_request)
        assert commit_ids == pull_request.revisions

        # Merge revision is not in the revisions list
        pull_request.merge_rev = 'f000' * 10
        commit_ids = PullRequestModel()._get_commit_ids(pull_request)
        assert commit_ids == pull_request.revisions + [pull_request.merge_rev]
示例#13
0
    def _reject_close(self, pull_request):
        if pull_request.is_closed():
            raise HTTPForbidden()

        PullRequestModel().close_pull_request_with_comment(
            pull_request, c.rhodecode_user, c.rhodecode_db_repo)
        Session().commit()
    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
示例#15
0
    def test_works_for_missing_git_references_during_update(self, pr_util):
        pull_request = self._prepare_pull_request(pr_util)
        removed_commit_id = pr_util.remove_one_commit()
        self.assert_commit_cannot_be_accessed(removed_commit_id, pull_request)

        pr_version = PullRequestModel().get_versions(pull_request)[0]
        self.assert_diff_can_be_fetched(pr_version)
    def test_close_calls_cleanup_and_hook(self, pull_request):
        PullRequestModel().close_pull_request(pull_request,
                                              pull_request.author)

        self.workspace_remove_mock.assert_called_once_with(self.workspace_id)
        self.hook_mock.assert_called_with(self.pull_request,
                                          self.pull_request.author, 'close')
示例#17
0
 def _update_reviewers(self, pull_request_id):
     reviewers_ids = map(
         int,
         filter(lambda v: v not in [None, ''],
                request.POST.get('reviewers_ids', '').split(',')))
     PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
     Session().commit()
def test_create_version_from_snapshot_updates_attributes(pr_util):
    pull_request = pr_util.create_pull_request()

    # Avoiding default values
    pull_request.status = PullRequest.STATUS_CLOSED
    pull_request._last_merge_source_rev = "0" * 40
    pull_request._last_merge_target_rev = "1" * 40
    pull_request._last_merge_status = 1
    pull_request.merge_rev = "2" * 40

    # Remember automatic values
    created_on = pull_request.created_on
    updated_on = pull_request.updated_on

    # Create a new version of the pull request
    version = PullRequestModel()._create_version_from_snapshot(pull_request)

    # Check attributes
    assert version.title == pr_util.create_parameters['title']
    assert version.description == pr_util.create_parameters['description']
    assert version.status == PullRequest.STATUS_CLOSED
    assert version.created_on == created_on
    assert version.updated_on == updated_on
    assert version.user_id == pull_request.user_id
    assert version.revisions == pr_util.create_parameters['revisions']
    assert version.source_repo == pr_util.source_repository
    assert version.source_ref == pr_util.create_parameters['source_ref']
    assert version.target_repo == pr_util.target_repository
    assert version.target_ref == pr_util.create_parameters['target_ref']
    assert version._last_merge_source_rev == pull_request._last_merge_source_rev
    assert version._last_merge_target_rev == pull_request._last_merge_target_rev
    assert version._last_merge_status == pull_request._last_merge_status
    assert version.merge_rev == pull_request.merge_rev
    assert version.pull_request == pull_request
    def test_api_comment_pull_request(self, pr_util, no_notifications):
        pull_request = pr_util.create_pull_request()
        pull_request_id = pull_request.pull_request_id
        author = pull_request.user_id
        repo = pull_request.target_repo.repo_id
        id_, params = build_data(self.apikey,
                                 'comment_pull_request',
                                 repoid=pull_request.target_repo.repo_name,
                                 pullrequestid=pull_request.pull_request_id,
                                 message='test message')
        response = api_call(self.app, params)
        pull_request = PullRequestModel().get(pull_request.pull_request_id)

        comments = ChangesetCommentsModel().get_comments(
            pull_request.target_repo.repo_id, pull_request=pull_request)

        expected = {
            'pull_request_id': pull_request.pull_request_id,
            'comment_id': comments[-1].comment_id,
            'status': None
        }
        assert_ok(id_, expected, response.body)

        action = 'user_commented_pull_request:%d' % pull_request_id
        journal = UserLog.query()\
            .filter(UserLog.user_id == author)\
            .filter(UserLog.repository_id == repo)\
            .filter(UserLog.action == action)\
            .all()
        assert len(journal) == 2
    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_merge_status_when_target_is_locked(self, pull_request):
     pull_request.target_repo.locked = [1, u'12345.50', 'lock_web']
     status, msg = PullRequestModel().merge_status(pull_request)
     assert status is False
     assert msg.eval() == (
         'This pull request cannot be merged because the target repository'
         ' is locked.')
示例#22
0
    def _meets_merge_pre_conditions(self, pull_request, user):
        if not PullRequestModel().check_user_merge(pull_request, user):
            raise HTTPForbidden()

        merge_status, msg = PullRequestModel().merge_status(pull_request)
        if not merge_status:
            log.debug("Cannot merge, not mergeable.")
            h.flash(msg, category='error')
            return False

        if (pull_request.calculated_review_status()
                is not ChangesetStatus.STATUS_APPROVED):
            log.debug("Cannot merge, approval is pending.")
            msg = _('Pull request reviewer approval is pending.')
            h.flash(msg, category='error')
            return False
        return True
def assert_pr_file_changes(pull_request,
                           added=None,
                           modified=None,
                           removed=None):
    pr_versions = PullRequestModel().get_versions(pull_request)
    # always use first version, ie original PR to calculate changes
    pull_request_version = pr_versions[0]
    old_diff_data, new_diff_data = PullRequestModel()._generate_update_diffs(
        pull_request, pull_request_version)
    file_changes = PullRequestModel()._calculate_file_changes(
        old_diff_data, new_diff_data)

    assert added == file_changes.added, \
        'expected added:%s vs value:%s' % (added, file_changes.added)
    assert modified == file_changes.modified, \
        'expected modified:%s vs value:%s' % (modified, file_changes.modified)
    assert removed == file_changes.removed, \
        'expected removed:%s vs value:%s' % (removed, file_changes.removed)
示例#24
0
    def _merge_pull_request(self, pull_request, user, extras):
        merge_resp = PullRequestModel().merge(pull_request,
                                              user,
                                              extras=extras)

        if merge_resp.executed:
            log.debug("The merge was successful, closing the pull request.")
            PullRequestModel().close_pull_request(pull_request.pull_request_id,
                                                  user)
            Session().commit()
            msg = _('Pull request was successfully merged and closed.')
            h.flash(msg, category='success')
        else:
            log.debug("The merge was not successful. Merge response: %s",
                      merge_resp)
            msg = PullRequestModel().merge_status_message(
                merge_resp.failure_reason)
            h.flash(msg, category='error')
def test_pull_request_stays_if_update_without_change(pr_util, voted_status):
    pull_request = pr_util.create_pull_request()
    pr_util.create_status_votes(voted_status, *pull_request.reviewers)

    # Update, without change
    PullRequestModel().update_commits(pull_request)

    # Expect that review status is the voted_status
    expected_review_status = voted_status
    assert pull_request.calculated_review_status() == expected_review_status
示例#26
0
 def delete(self, repo_name, pull_request_id):
     pull_request = PullRequest.get_or_404(pull_request_id)
     #only owner can delete it !
     if pull_request.author.user_id == c.rhodecode_user.user_id:
         PullRequestModel().delete(pull_request)
         Session().commit()
         h.flash(_('Successfully deleted pull request'), category='success')
         return redirect(
             url('admin_settings_my_account', anchor='pullrequests'))
     raise HTTPForbidden()
def test_pull_request_under_review_if_update(pr_util, voted_status):
    pull_request = pr_util.create_pull_request()
    pr_util.create_status_votes(voted_status, *pull_request.reviewers)

    # Update, with change
    pr_util.update_source_repository()
    PullRequestModel().update_commits(pull_request)

    # Expect that review status is the voted_status
    expected_review_status = db.ChangesetStatus.STATUS_UNDER_REVIEW
    assert pull_request.calculated_review_status() == expected_review_status
    def test_merge_status_requirements_check_source(self, pull_request):
        def has_largefiles(self, repo):
            return repo == pull_request.target_repo

        patcher = mock.patch.object(PullRequestModel, '_has_largefiles',
                                    has_largefiles)
        with patcher:
            status, msg = PullRequestModel().merge_status(pull_request)

        assert status is False
        assert msg == 'Source repository large files support is disabled.'
def test_commit_keeps_status_if_unchanged_after_update_of_pull_request(
        pr_util, voted_status):
    pull_request = pr_util.create_pull_request()
    commit_id = pull_request.revisions[-1]
    pr_util.create_status_votes(voted_status, pull_request.reviewers[0])
    pr_util.update_source_repository()
    PullRequestModel().update_commits(pull_request)
    assert pull_request.revisions[-1] == commit_id

    status = ChangesetStatusModel().get_status(repo=pr_util.source_repository,
                                               revision=commit_id)
    assert status == voted_status
    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