Exemple #1
0
 def test_github_comment_after_sync_no_data_in_db(
     self, m_set_status, m_get_statuses, m_sha, m_get_perms, m_author,
     m_body, m_head, m_commits
 ):
     m_get_statuses.return_value = (
         CommitStatus(Status.FAILED, 'Terrible issues',
                      'review/gitmate/manual', 'https://gitmate.io'),
         CommitStatus(Status.SUCCESS, 'No issues',
                      'review/somewhere/else', 'https://some/url'))
     m_sha.return_value = 'f6d2b7c66372236a090a2a74df2e47f42a54456b'
     m_get_perms.return_value = AccessLevel.CAN_WRITE
     m_author.return_value = GitHubUser(self.gh_token, self.user.username)
     m_body.return_value = 'unack f6d2b7c'
     m_head.return_value = self.gh_commit
     m_commits.return_value = tuple([self.gh_commit])
     response = self.simulate_github_webhook_call('issue_comment',
                                                  self.gh_comment_data)
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     args = sum([list(args) for args, _ in m_set_status.call_args_list], [])
     # 3 calls to be made as follows
     # Status.FAILED review/gitmate/manual/pr
     # Status.FAILED review/gitmate/manual
     # Status.FAILED review/gitmate/manual/pr
     self.assertEqual(m_set_status.call_count, 3)
     self.assertEqual([(arg.status, arg.context) for arg in args],
                      [(Status.FAILED, 'review/gitmate/manual/pr'),
                       (Status.FAILED, 'review/gitmate/manual'),
                       (Status.FAILED, 'review/gitmate/manual/pr')])
Exemple #2
0
 def test_gitlab_ack(
         self, m_set_status, m_get_statuses, m_sha, m_get_perms, m_author,
         m_body, m_commits
 ):
     m_get_statuses.return_value = (
         CommitStatus(Status.SUCCESS, 'No issues',
                      'review/gitmate/manual', 'https://gitmate.io'),
         CommitStatus(Status.SUCCESS, 'No issues',
                      'review/somewhere/else', 'https://some/url'))
     m_sha.return_value = 'f6d2b7c66372236a090a2a74df2e47f42a54456b'
     m_body.return_value = 'ack f6d2b7c'
     m_get_perms.return_value = AccessLevel.CAN_WRITE
     m_author.return_value = GitLabUser(self.gl_token, 0)
     m_commits.return_value = tuple([self.gl_commit])
     response = self.simulate_gitlab_webhook_call('Merge Request Hook',
                                                  self.gl_pr_data)
     response = self.simulate_gitlab_webhook_call('Note Hook',
                                                  self.gl_comment_data)
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     args = sum([list(args) for args, _ in m_set_status.call_args_list], [])
     # 3 calls to be made as follows
     # Status.SUCCESS review/gitmate/manual/pr
     # Status.SUCCESS review/gitmate/manual
     # Status.SUCCESS review/gitmate/manual/pr
     self.assertEqual(m_set_status.call_count, 3)
     self.assertEqual([(arg.status, arg.context) for arg in args],
                      [(Status.SUCCESS, 'review/gitmate/manual/pr'),
                       (Status.SUCCESS, 'review/gitmate/manual'),
                       (Status.SUCCESS, 'review/gitmate/manual/pr')])
Exemple #3
0
 def test_set_status(self):
     commit = GitLabCommit(self.token, 'gitmate-test-user/test',
                           '3fc4b860e0a2c17819934d678decacd914271e5c')
     status = CommitStatus(Status.FAILED, 'Theres a problem',
                           'gitmate/test')
     self.commit.set_status(status)
     self.assertEqual(commit.get_statuses().pop().description,
                      'Theres a problem')
Exemple #4
0
    def ack_state(self):
        state = CommitStatus(Status.SUCCESS, 'This PR is reviewed. :)',
                             'review/gitmate/manual/pr', 'https://gitmate.io')
        for acked in dict(self.acks).values():
            if acked['status'] in [
                    Status.FAILED.value, Status.ERROR.value,
                    Status.CANCELED.value
            ]:
                return CommitStatus(Status.FAILED, 'This PR needs work. :(',
                                    'review/gitmate/manual/pr',
                                    'https://gitmate.io')
            if acked['status'] == Status.PENDING.value:
                state = CommitStatus(Status.PENDING, 'This PR needs review.',
                                     'review/gitmate/manual/pr',
                                     'https://gitmate.io')

        return state
Exemple #5
0
def pending(commit: Commit):
    for status in commit.get_statuses():
        if status.context == 'review/gitmate/manual':
            return status

    state = CommitStatus(Status.PENDING, 'This commit needs review.',
                         'review/gitmate/manual', 'https://gitmate.io')
    commit.set_status(state)
    return state
Exemple #6
0
 def test_gitlab_ack_without_minimum_access_level(
         self, _, m_get_statuses, m_get_perms, m_username, m_body, m_author,
         m_sha, m_add_comment, m_commits
 ):
     m_get_statuses.return_value = (
         CommitStatus(Status.SUCCESS, 'No issues',
                      'review/gitmate/manual', 'https://gitmate.io'),
         CommitStatus(Status.SUCCESS, 'No issues',
                      'review/somewhere/else', 'https://some/url'))
     m_sha.return_value = 'f6d2b7c66372236a090a2a74df2e47f42a54456b'
     m_get_perms.return_value = AccessLevel.CAN_VIEW
     m_username.return_value = self.user.username
     m_body.return_value = 'unack f6d2b7c'
     m_author.return_value = GitLabUser(self.gl_token, 0)
     m_commits.return_value = tuple([self.gl_commit])
     _ = self.simulate_gitlab_webhook_call('Merge Request Hook',
                                           self.gl_pr_data)
     response = self.simulate_gitlab_webhook_call('Note Hook',
                                                  self.gl_comment_data)
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     m_add_comment.assert_called_once_with(
         'Sorry @{}, you do not have the necessary permission levels to '
         'perform the action.'.format(self.user.username))
Exemple #7
0
    def test_github_ack_with_special_chars(
        self, m_set_status, m_get_statuses, m_sha, m_get_perms, m_author,
        m_body, m_head, m_commits
    ):
        self.repo.settings = [{
            'name': 'ack',
            'settings': {
                'ack_strs': r'bot\ack, bot\accept'
            }
        }]

        m_get_statuses.return_value = (
            CommitStatus(Status.SUCCESS, 'No issues',
                         'review/gitmate/manual', 'https://gitmate.io'),
            CommitStatus(Status.SUCCESS, 'No issues',
                         'review/somewhere/else', 'https://some/url'))
        m_sha.return_value = 'f6d2b7c66372236a090a2a74df2e47f42a54456b'
        m_body.return_value = r'bot\accept f6d2b7c'
        m_get_perms.return_value = AccessLevel.CAN_WRITE
        m_author.return_value = GitHubUser(self.gh_token, self.user.username)
        m_head.return_value = self.gh_commit
        m_commits.return_value = tuple([self.gh_commit])
        response = self.simulate_github_webhook_call('pull_request',
                                                     self.gh_pr_data)
        response = self.simulate_github_webhook_call('issue_comment',
                                                     self.gh_comment_data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        args = sum([list(args) for args, _ in m_set_status.call_args_list], [])
        # 3 calls to be made as follows
        # Status.SUCCESS review/gitmate/manual/pr
        # Status.SUCCESS review/gitmate/manual
        # Status.SUCCESS review/gitmate/manual/pr
        self.assertEqual(m_set_status.call_count, 3)
        self.assertEqual([(arg.status, arg.context) for arg in args],
                         [(Status.SUCCESS, 'review/gitmate/manual/pr'),
                          (Status.SUCCESS, 'review/gitmate/manual'),
                          (Status.SUCCESS, 'review/gitmate/manual/pr')])
Exemple #8
0
    def ack(self):
        """
        Acknowledges the commit by setting the manual review GitMate status to
        success.

        >>> CommitMock = type('CommitMock', (Commit,),
        ...                   {'set_status': lambda self, s: print(s.status)})
        >>> CommitMock().ack()
        Status.SUCCESS

        :raises RuntimeError: If something goes wrong (network, auth...).
        """
        status = CommitStatus(Status.SUCCESS, 'This commit was acknowledged.',
                              'review/gitmate/manual', 'http://gitmate.io/')
        self.set_status(status)
Exemple #9
0
    def unack(self):
        """
        Unacknowledges the commit by setting the manual review GitMate status to
        failed.

        >>> CommitMock = type('CommitMock', (Commit,),
        ...                   {'set_status': lambda self, s: print(s.status)})
        >>> CommitMock().unack()
        Status.FAILED

        :raises RuntimeError: If something goes wrong (network, auth...).
        """
        status = CommitStatus(Status.FAILED, 'This commit needs work.',
                              'review/gitmate/manual', 'http://gitmate.io/')
        self.set_status(status)
Exemple #10
0
    def pending(self):
        """
        Sets the commit to a pending manual review state if there is no manual
        review state yet.

        Given a commit with an unrelated status:

        >>> CommitMock = type(
        ...     'CommitMock', (Commit,),
        ...     {'set_status': lambda self, s: self.statuses.append(s),
        ...      'get_statuses': lambda self: self.statuses,
        ...      'statuses': []})
        >>> commit = CommitMock()
        >>> commit.set_status(CommitStatus(Status.FAILED, context='unrelated'))
        >>> len(commit.get_statuses())
        1

        The invocation of pending will now add a pending status:

        >>> commit.pending()
        >>> len(commit.get_statuses())
        2
        >>> commit.get_statuses()[1].context
        'review/gitmate/manual'

        However, if there is already a manual review state, the invocation of
        pending won't affect the status:

        >>> commit.get_statuses().clear()
        >>> commit.ack()
        >>> commit.pending()  # Won't do anything
        >>> len(commit.get_statuses())
        1
        >>> commit.get_statuses()[0].status
        <Status.SUCCESS: 1>

        :raises RuntimeError: If something goes wrong (network, auth...).
        """
        for status in self.get_statuses():
            if status.context == 'review/gitmate/manual':
                return

        status = CommitStatus(Status.PENDING, 'This commit needs review.',
                              'review/gitmate/manual', 'http://gitmate.io')
        self.set_status(status)
Exemple #11
0
 def test_github_pending_pr_open_event(
         self, m_set_status, m_get_statuses, m_sha, m_head, m_commits
 ):
     m_get_statuses.return_value = (
         CommitStatus(Status.FAILED, 'Terrible issues',
                      'some/other/review', 'https://some/other/ci'),)
     m_sha.return_value = 'f6d2b7c66372236a090a2a74df2e47f42a54456b'
     m_head.return_value = self.gh_commit
     m_commits.return_value = tuple([self.gh_commit])
     response = self.simulate_github_webhook_call('pull_request',
                                                  self.gh_pr_data)
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     args = sum([list(args) for args, _ in m_set_status.call_args_list], [])
     # 2 calls to be made as follows
     # Status.PENDING review/gitmate/manual
     # Status.PENDING review/gitmate/manual/pr
     self.assertEqual(m_set_status.call_count, 2)
     self.assertEqual([(arg.status, arg.context) for arg in args],
                      [(Status.PENDING, 'review/gitmate/manual'),
                       (Status.PENDING, 'review/gitmate/manual/pr')])
Exemple #12
0
    def get_statuses(self) -> Set[CommitStatus]:
        """
        Retrieves the all commit statuses.

        :return: A (frozen)set of CommitStatus objects.
        :raises RuntimeError: If something goes wrong (network, auth...).
        """
        url = self._url + '/statuses'
        statuses = get(self._token, url)

        # Only the first of each context is the one we want
        result = set()
        contexts = set()
        for status in statuses:
            if status['context'] not in contexts:
                result.add(
                    CommitStatus(INV_GH_STATE_TRANSLATION[status['state']],
                                 status['description'], status['context'],
                                 status['target_url']))
                contexts.add(status['context'])

        return result
Exemple #13
0
def add_review_status(pr: MergeRequest):
    """
    A responder to add pending/acknowledged/rejected status on commits on
    MergeRequest SYNCHRONIZED and OPENED events based on their generated
    commit hashes (generated from the unified diff and commit message).
    """
    db_pr, _ = MergeRequestModel.objects.get_or_create(
        repo=Repository.from_igitt_repo(pr.repository), number=pr.number)

    head = pr.head

    hashes = []
    for commit in pr.commits:
        commit_hash = _get_commit_hash(commit)
        hashes.append(commit_hash)

        # This commit was head of the PR before, deletion of the PR state is
        # not possible so we make it green to clean it up.
        if commit.sha == db_pr.last_head != head.sha:
            commit.set_status(
                CommitStatus(Status.SUCCESS,
                             'Outdated. Check ' + head.sha[:7] + ' instead.',
                             'review/gitmate/manual/pr', 'https://gitmate.io'))

        # copying status from unmodified commits in the same merge request
        if commit_hash in db_pr.acks:
            commit.set_status(_dict_to_status(db_pr.acks[commit_hash]))
        else:
            db_pr.acks[commit_hash] = _status_to_dict(pending(commit))

    for chash in dict(db_pr.acks).keys():
        if not chash in hashes:
            del db_pr.acks[chash]

    db_pr.last_head = head.sha
    db_pr.save()
    pr.head.set_status(db_pr.ack_state)
Exemple #14
0
    def test_gitlab_unmodified_commit(
        self, m_set_status, m_get_statuses, m_sha, m_message, m_diff, m_commits
    ):
        m_get_statuses.return_value = (
            CommitStatus(Status.SUCCESS, 'Good to go!',
                         'review/gitmate/manual', 'https://gitmate.io'),)
        m_diff.return_value = ('--- a/README.md\n'
                               '+++ b/README.md\n'
                               '@@ -1,2 +1,4 @@\n'
                               ' # test\n'
                               ' a test repo\n'
                               '+\n'
                               '+a commiit that can one acknowledge')
        m_message.return_value = 'Update README.md'
        m_commits.return_value = tuple([self.gl_commit])
        m_sha.return_value = 'f6d2b7c66372236a090a2a74df2e47f42a54456b'
        response = self.simulate_gitlab_webhook_call('Merge Request Hook',
                                                     self.gl_pr_data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # resyncing merge request with a new unmodified commit
        m_sha.return_value = '9ba5b704f5866e468ec2e639fa893ae4c129f2ad'
        m_commits.return_value = tuple([GitLabCommit(
            self.gl_token, self.gl_repo.full_name, m_sha.return_value)])
        response = self.simulate_gitlab_webhook_call('Merge Request Hook',
                                                     self.gl_pr_data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        args = sum([list(args) for args, _ in m_set_status.call_args_list], [])
        # 3 calls to be made as follows
        # Status.SUCCESS review/gitmate/manual/pr
        # Status.SUCCESS review/gitmate/manual
        # Status.SUCCESS review/gitmate/manual/pr
        self.assertEqual([(arg.status, arg.context) for arg in args],
                         [(Status.SUCCESS, 'review/gitmate/manual/pr'),
                          (Status.SUCCESS, 'review/gitmate/manual'),
                          (Status.SUCCESS, 'review/gitmate/manual/pr')])
Exemple #15
0
    def get_statuses(self) -> Set[CommitStatus]:
        """
        Retrieves the all commit statuses.

        :return: A (frozen)set of CommitStatus objects.
        :raises RuntimeError: If something goes wrong (network, auth...).
        """
        # rebuild the url with full sha because gitlab doesn't work that way
        url = '/projects/{repo}/repository/commits/{sha}/statuses'.format(
            repo=quote_plus(self._repository), sha=self.sha)
        statuses = get(self._token, url)

        # Only the first of each context is the one we want
        result = set()
        contexts = set()
        for status in statuses:
            if status['name'] not in contexts:
                result.add(CommitStatus(
                    INV_GL_STATE_TRANSLATION[status['status']],
                    status['description'], status['name'],
                    status['target_url']))
                contexts.add(status['name'])

        return result
Exemple #16
0
def ack(commit: Commit):
    state = CommitStatus(Status.SUCCESS, 'This commit was acknowledged. :)',
                         'review/gitmate/manual', 'https://gitmate.io')
    commit.set_status(state)
    return state
Exemple #17
0
def unack(commit: Commit):
    state = CommitStatus(Status.FAILED, 'This commit needs work. :(',
                         'review/gitmate/manual', 'https://gitmate.io')
    commit.set_status(state)
    return state
Exemple #18
0
def _dict_to_status(state: dict):
    return CommitStatus(**{**state, 'status': Status(state['status'])})