示例#1
0
    def test_succeeds_second_time_if_master_moved(self, unused_time_sleep):
        api, mocklab = self.api, self.mocklab
        moved_master_sha = 'fafafa'
        first_rewritten_sha = '1o1'
        api.add_pipelines(
            mocklab.project_info['id'],
            _pipeline(sha1=first_rewritten_sha, status='success'),
            from_state=['pushed_but_master_moved', 'merged_rejected'],
        )
        api.add_transition(
            PUT(
                '/projects/1234/merge_requests/54/merge',
                dict(
                    sha=first_rewritten_sha,
                    should_remove_source_branch=True,
                    merge_when_pipeline_succeeds=True,
                ),
            ),
            Error(marge.gitlab.NotAcceptable()),
            from_state='pushed_but_master_moved',
            to_state='merge_rejected',
        )
        api.add_transition(
            GET('/projects/1234/repository/branches/useless_new_feature'),
            Ok({
                'commit':
                _commit(commit_id=first_rewritten_sha, status='success')
            }),
            from_state='pushed_but_master_moved')
        api.add_transition(GET('/projects/1234/repository/branches/master'),
                           Ok({
                               'commit':
                               _commit(commit_id=moved_master_sha,
                                       status='success')
                           }),
                           from_state='merge_rejected')

        def push_effects():
            assert api.state == 'initial'
            api.state = 'pushed_but_master_moved'
            yield mocklab.initial_master_sha, 'f00ba4', first_rewritten_sha

            assert api.state == 'merge_rejected'
            api.state = 'pushed'
            yield moved_master_sha, 'deadbeef', mocklab.rewritten_sha

        with patch('marge.job.update_from_target_branch_and_push',
                   side_effect=push_effects()):
            job = self.make_job(
                marge.job.MergeJobOptions.default(add_tested=True,
                                                  add_reviewers=False))
            job.execute()

        assert api.state == 'merged'
        assert api.notes == [
            "My job would be easier if people didn't jump the queue and push directly... *sigh*",
        ]
示例#2
0
 def __init__(self, gitlab_url=None, fork=False, merge_request_options=None):
     super().__init__(gitlab_url, fork=fork, merge_request_options=merge_request_options)
     api = self.api
     self.rewritten_sha = rewritten_sha = 'af7a'
     api.add_pipelines(
         self.merge_request_info['source_project_id'],
         _pipeline(sha1=rewritten_sha, status='running', ref=self.merge_request_info['source_branch']),
         from_state='pushed', to_state='passed',
     )
     api.add_pipelines(
         self.merge_request_info['source_project_id'],
         _pipeline(sha1=rewritten_sha, status='success', ref=self.merge_request_info['source_branch']),
         from_state=['passed', 'merged'],
     )
     source_project_id = self.merge_request_info['source_project_id']
     api.add_transition(
         GET(
             '/projects/{}/repository/branches/{}'.format(
                 source_project_id, self.merge_request_info['source_branch'],
             ),
         ),
         Ok({'commit': _commit(commit_id=rewritten_sha, status='running')}),
         from_state='pushed',
     )
     api.add_transition(
         GET(
             '/projects/{}/repository/branches/{}'.format(
                 source_project_id, self.merge_request_info['source_branch'],
             ),
         ),
         Ok({'commit': _commit(commit_id=rewritten_sha, status='success')}),
         from_state='passed'
     )
     api.add_transition(
         PUT(
             '/projects/1234/merge_requests/{iid}/merge'.format(iid=self.merge_request_info['iid']),
             dict(sha=rewritten_sha, should_remove_source_branch=True, merge_when_pipeline_succeeds=True),
         ),
         Ok({}),
         from_state=['passed', 'skipped'], to_state='merged',
     )
     api.add_merge_request(dict(self.merge_request_info, state='merged'), from_state='merged')
     api.add_transition(
         GET('/projects/1234/repository/branches/{}'.format(self.merge_request_info['target_branch'])),
         Ok({'commit': {'id': self.rewritten_sha}}),
         from_state='merged'
     )
     api.expected_note(
         self.merge_request_info,
         "My job would be easier if people didn't jump the queue and push directly... *sigh*",
         from_state=['pushed_but_master_moved', 'merge_rejected'],
     )
     api.expected_note(
         self.merge_request_info,
         "I'm broken on the inside, please somebody fix me... :cry:"
     )
示例#3
0
 def __init__(self, gitlab_url=None):
     super().__init__(gitlab_url)
     api = self.api
     self.rewritten_sha = rewritten_sha = 'af7a'
     api.add_pipelines(
         self.project_info['id'],
         _pipeline(sha1=rewritten_sha, status='running'),
         from_state='pushed',
         to_state='passed',
     )
     api.add_pipelines(
         self.project_info['id'],
         _pipeline(sha1=rewritten_sha, status='success'),
         from_state=['passed', 'merged'],
     )
     api.add_transition(
         GET('/projects/1234/repository/branches/useless_new_feature'),
         Ok({'commit': _commit(commit_id=rewritten_sha, status='running')}),
         from_state='pushed',
     )
     api.add_transition(
         GET('/projects/1234/repository/branches/useless_new_feature'),
         Ok({'commit': _commit(commit_id=rewritten_sha, status='success')}),
         from_state='passed')
     api.add_transition(
         PUT(
             '/projects/1234/merge_requests/54/merge',
             dict(sha=rewritten_sha,
                  should_remove_source_branch=True,
                  merge_when_pipeline_succeeds=True),
         ),
         Ok({}),
         from_state='passed',
         to_state='merged',
     )
     api.add_merge_request(dict(self.merge_request_info, state='merged'),
                           from_state='merged')
     api.add_transition(GET('/projects/1234/repository/branches/master'),
                        Ok({'commit': {
                            'id': self.rewritten_sha
                        }}),
                        from_state='merged')
     api.expected_note(
         self.merge_request_info,
         "My job would be easier if people didn't jump the queue and push directly... *sigh*",
         from_state=['pushed_but_master_moved', 'merge_rejected'],
     )
     api.expected_note(
         self.merge_request_info,
         "I'm broken on the inside, please somebody fix me... :cry:")
示例#4
0
    def test_fails_if_branch_is_protected(self, mocks_factory, fusion):
        def reject_push(*_args, **_kwargs):
            raise marge.git.GitError()

        mocklab, api, job = mocks_factory(on_push=reject_push)
        api.add_transition(
            GET(
                '/projects/{source_project_id}/repository/branches/useless_new_feature'.format(
                    source_project_id=mocklab.merge_request_info['source_project_id'],
                ),
            ),
            Ok(_branch('useless_new_feature', protected=True)),
            from_state='initial', to_state='protected'
        )

        if fusion is Fusion.gitlab_rebase:
            api.add_transition(
                PUT(
                    '/projects/{project_id}/merge_requests/{iid}/rebase'.format(
                        project_id=mocklab.merge_request_info['project_id'],
                        iid=mocklab.merge_request_info['iid'],
                    ),
                ),
                Error(marge.gitlab.MethodNotAllowed(405, {'message': '405 Method Not Allowed'})),
                from_state='initial',
            )

        with mocklab.expected_failure("Sorry, I can't modify protected branches!"):
            job.execute()

        assert api.state == 'protected'
示例#5
0
    def test_fails_on_not_acceptable_if_master_did_not_move(
            self, unused_time_sleep):
        api, mocklab = self.api, self.mocklab
        new_branch_head_sha = '99ba110035'
        api.add_transition(
            GET('/projects/1234/repository/branches/useless_new_feature'),
            Ok({
                'commit':
                _commit(commit_id=new_branch_head_sha, status='success')
            }),
            from_state='pushed',
            to_state='pushed_but_head_changed')
        with patch('marge.job.update_from_target_branch_and_push',
                   side_effect=mocklab.push_updated):
            with mocklab.expected_failure(
                    "Someone pushed to branch while we were trying to merge"):
                job = self.make_job(
                    marge.job.MergeJobOptions.default(add_tested=True,
                                                      add_reviewers=False))
                job.execute()

        assert api.state == 'pushed_but_head_changed'
        assert api.notes == [
            "I couldn't merge this branch: Someone pushed to branch while we were trying to merge",
        ]
示例#6
0
    def test_fails_on_not_acceptable_if_master_did_not_move(
            self, api, mocklab, test_params):
        new_branch_head_sha = '99ba110035'
        api.add_transition(GET(
            '/projects/{source_project_id}/repository/branches/useless_new_feature'
            .format(source_project_id=test_params.source_project_id, ), ),
                           Ok({
                               'commit':
                               _commit(commit_id=new_branch_head_sha,
                                       status='success')
                           }),
                           from_state='pushed',
                           to_state='pushed_but_head_changed')
        with mocklab.branch_update():
            with mocklab.expected_failure(
                    "Someone pushed to branch while we were trying to merge"):
                job = self.make_job(
                    api,
                    mocklab,
                    options=marge.job.MergeJobOptions.default(
                        add_tested=True, add_reviewers=False),
                )
                job.execute()

        assert api.state == 'pushed_but_head_changed'
        assert api.notes == [
            "I couldn't merge this branch: Someone pushed to branch while we were trying to merge",
        ]
示例#7
0
    def test_succeeds_if_source_is_master(self, mocklab_factory):
        mocklab = mocklab_factory(merge_request_options={
            'source_branch': 'master',
            'target_branch': 'production'
        }, )
        api = mocklab.api
        api.add_transition(
            GET(
                '/projects/1234/repository/branches/{source}'.format(
                    source=mocklab.merge_request_info['source_branch'], ), ),
            Ok({'commit': {
                'id': mocklab.rewritten_sha
            }}),
            from_state='initial',
            to_state='pushed',
        )
        with patch.object(
                marge.single_merge_job.SingleMergeJob,
                'add_trailers',
                side_effect=lambda *_: mocklab.push_updated()[2],
                autospec=True,
        ):
            job = self.make_job(
                api,
                mocklab,
                options=marge.job.MergeJobOptions.default(add_tested=True,
                                                          add_reviewers=False),
            )
            job.execute()

        assert api.state == 'merged'
        assert api.notes == []
示例#8
0
    def test_succeeds_with_updated_branch(self, mocks):
        mocklab, api, job = mocks
        api.add_transition(
            GET(
                '/projects/1234/repository/branches/{source}'.format(
                    source=mocklab.merge_request_info['source_branch'],
                ),
            ),
            Ok({'commit': {'id': mocklab.rewritten_sha}}),
            from_state='initial', to_state='pushed',
        )
        job.execute()

        assert api.state == 'merged'
        assert api.notes == []
    def test_fuse_mr_when_source_branch_was_moved(self):
        api, mocklab = self.api, self.mocklab
        batch_merge_job = self.get_batch_merge_job()
        merge_request = Mock(
            source_project_id=batch_merge_job._project.id,
            target_branch='master',
            source_branch=self.mocklab.merge_request_info['source_branch'],
        )

        api.add_transition(
            GET('/projects/1234/repository/branches/useless_new_feature'),
            Ok({'commit': commit(commit_id='abc', status='running')}),
        )

        with pytest.raises(CannotMerge) as exc_info:
            batch_merge_job.accept_mr(merge_request, mocklab.initial_master_sha)

        assert str(exc_info.value) == 'Someone pushed to branch while we were trying to merge'
示例#10
0
    def test_fails_on_not_acceptable_if_master_did_not_move(self, mocks):
        mocklab, api, job = mocks
        new_branch_head_sha = '99ba110035'
        api.add_transition(
            GET(
                '/projects/{source_project_id}/repository/branches/useless_new_feature'.format(
                    source_project_id=mocklab.merge_request_info['source_project_id'],
                ),
            ),
            Ok({'commit': _commit(commit_id=new_branch_head_sha, status='success')}),
            from_state='pushed', to_state='pushed_but_head_changed'
        )
        with mocklab.expected_failure("Someone pushed to branch while we were trying to merge"):
            job.execute()

        assert api.state == 'pushed_but_head_changed'
        assert api.notes == [
            "I couldn't merge this branch: Someone pushed to branch while we were trying to merge",
        ]
示例#11
0
    def test_succeeds_if_source_is_master(self, mocks_factory):
        mocklab, api, job = mocks_factory(
            extra_mocklab_opts=dict(merge_request_options={
                'source_branch': 'master',
                'target_branch': 'production',
            }),
        )
        api.add_transition(
            GET(
                '/projects/1234/repository/branches/{source}'.format(
                    source=mocklab.merge_request_info['source_branch'],
                ),
            ),
            Ok({'commit': {'id': mocklab.rewritten_sha}}),
            from_state='initial', to_state='pushed',
        )
        job.execute()

        assert api.state == 'merged'
        assert api.notes == []
示例#12
0
    def test_fails_if_branch_is_protected(self, api, mocklab, test_params):
        api.add_transition(GET(
            '/projects/{source_project_id}/repository/branches/useless_new_feature'
            .format(source_project_id=test_params.source_project_id, ), ),
                           Ok(_branch('useless_new_feature', protected=True)),
                           from_state='initial',
                           to_state='protected')
        with mocklab.expected_failure(
                "Sorry, I can't push rewritten changes to protected branches!"
        ):
            job = self.make_job(
                api,
                mocklab,
                options=marge.job.MergeJobOptions.default(add_tested=True,
                                                          add_reviewers=False),
            )
            job.repo.push.side_effect = marge.git.GitError()
            job.execute()

        assert api.state == 'protected'
示例#13
0
 def test_calculates_merge_when_pipeline_succeeds_correctly(
         self, mocks, only_allow_merge_if_pipeline_succeeds):
     mocklab, api, job = mocks
     rewritten_sha = mocklab.rewritten_sha
     project_info = dict(TEST_PROJECT_INFO)
     project_info[
         "only_allow_merge_if_pipeline_succeeds"] = only_allow_merge_if_pipeline_succeeds
     api.add_project(project_info)
     api.add_transition(
         PUT(
             '/projects/{pid}/merge_requests/{iid}/merge'.format(
                 iid=mocklab.merge_request_info['iid'],
                 pid=project_info["id"]),
             dict(sha=rewritten_sha,
                  should_remove_source_branch=True,
                  merge_when_pipeline_succeeds=
                  only_allow_merge_if_pipeline_succeeds),
         ),
         Ok(True),
         to_state='merged',
     )
     job.execute()
     assert api.state == 'merged'
示例#14
0
    def test_second_time_if_master_moved(self, mocks_factory, fusion, update_sha, rewrite_sha):
        initial_master_sha = 'eaeaea9e9e'
        moved_master_sha = 'fafafa'
        first_rewritten_sha = rewrite_sha(update_sha(INITIAL_MR_SHA, initial_master_sha))
        second_rewritten_sha = rewrite_sha(update_sha(first_rewritten_sha, moved_master_sha))

        # pylint: disable=unused-argument
        def push_effects(remote_url, remote_branch, old_sha, new_sha):
            nonlocal mocklab, target_branch, remote_target_repo

            if api.state == 'initial':
                assert old_sha == INITIAL_MR_SHA
                assert new_sha == first_rewritten_sha
                api.state = 'pushed_but_master_moved'
                remote_target_repo.set_ref(target_branch, moved_master_sha)
            elif api.state == 'merge_rejected':
                assert new_sha == second_rewritten_sha
                api.state = 'pushed'

        mocklab, api, job = mocks_factory(
            initial_master_sha=initial_master_sha,
            rewritten_sha=second_rewritten_sha,
            on_push=push_effects,
        )

        source_project_info = mocklab.forked_project_info or mocklab.project_info
        target_project_info = mocklab.project_info

        source_project_url = source_project_info['ssh_url_to_repo']
        target_project_url = target_project_info['ssh_url_to_repo']

        source_branch = mocklab.merge_request_info['source_branch']
        target_branch = mocklab.merge_request_info['target_branch']

        remote_source_repo = job.repo.mock_impl.remote_repos[source_project_url]
        remote_target_repo = job.repo.mock_impl.remote_repos[target_project_url]

        api.add_merge_request(
            dict(
                mocklab.merge_request_info,
                sha=first_rewritten_sha,
            ),
            from_state=['pushed_but_master_moved', 'merge_rejected'],
        )
        api.add_pipelines(
            mocklab.merge_request_info['source_project_id'],
            _pipeline(sha1=first_rewritten_sha, status='success'),
            from_state=['pushed_but_master_moved', 'merge_rejected'],
        )
        api.add_transition(
            PUT(
                '/projects/1234/merge_requests/{iid}/merge'.format(iid=mocklab.merge_request_info['iid']),
                dict(
                    sha=first_rewritten_sha,
                    should_remove_source_branch=True,
                ),
            ),
            Error(marge.gitlab.NotAcceptable()),
            from_state='pushed_but_master_moved', to_state='merge_rejected',
        )
        api.add_transition(
            GET(
                '/projects/{source_project_id}/repository/branches/useless_new_feature'.format(
                    source_project_id=mocklab.merge_request_info['source_project_id'],
                ),
            ),
            Ok({'commit': _commit(commit_id=first_rewritten_sha, status='success')}),
            from_state='pushed_but_master_moved'
        )
        api.add_transition(
            GET('/projects/1234/repository/branches/master'),
            Ok({'commit': _commit(commit_id=moved_master_sha, status='success')}),
            from_state='merge_rejected'
        )
        if fusion is Fusion.gitlab_rebase:
            rebase_url = '/projects/{project_id}/merge_requests/{iid}/rebase'.format(
                project_id=mocklab.merge_request_info['project_id'],
                iid=mocklab.merge_request_info['iid'],
            )

            api.add_transition(
                PUT(rebase_url), Ok(True),
                from_state='initial', to_state='pushed_but_master_moved',
                side_effect=lambda: (
                    remote_source_repo.set_ref(source_branch, first_rewritten_sha),
                    remote_target_repo.set_ref(target_branch, moved_master_sha)
                )
            )
            api.add_transition(
                PUT(rebase_url), Ok(True),
                from_state='merge_rejected', to_state='rebase-in-progress',
                side_effect=lambda: remote_source_repo.set_ref(source_branch, second_rewritten_sha)
            )

        job.execute()
        assert api.state == 'merged'
        assert api.notes == [
            "My job would be easier if people didn't jump the queue and push directly... *sigh*",
        ]
示例#15
0
    def __init__(self, gitlab_url=None):
        self.gitlab_url = gitlab_url = gitlab_url or 'http://git.example.com'
        self.api = api = ApiMock(gitlab_url=gitlab_url,
                                 auth_token='no-token',
                                 initial_state='initial')

        api.add_transition(GET('/version'), Ok({'version': '9.2.3-ee'}))

        self.user_info = dict(test_user.INFO)
        self.user_id = self.user_info['id']
        api.add_user(self.user_info, is_current=True)

        self.project_info = dict(test_project.INFO)
        api.add_project(self.project_info)

        self.commit_info = dict(test_commit.INFO)
        api.add_commit(self.project_info['id'], self.commit_info)

        self.author_id = 234234
        self.merge_request_info = {
            'id': 53,
            'iid': 54,
            'title': 'a title',
            'project_id': 1234,
            'author': {
                'id': self.author_id
            },
            'assignee': {
                'id': self.user_id
            },
            'approved_by': [],
            'state': 'opened',
            'sha': self.commit_info['id'],
            'source_project_id': 1234,
            'target_project_id': 1234,
            'source_branch': 'useless_new_feature',
            'target_branch': 'master',
            'work_in_progress': False,
            'web_url':
            'http://git.example.com/group/project/merge_request/666',
        }
        api.add_merge_request(self.merge_request_info)

        self.initial_master_sha = '505e'
        self.rewritten_sha = rewritten_sha = 'af7a'
        api.add_pipelines(
            self.project_info['id'],
            _pipeline(sha1=rewritten_sha, status='running'),
            from_state='pushed',
            to_state='passed',
        )
        api.add_pipelines(
            self.project_info['id'],
            _pipeline(sha1=rewritten_sha, status='success'),
            from_state=['passed', 'merged'],
        )
        api.add_transition(
            GET('/projects/1234/repository/branches/useless_new_feature'),
            Ok({'commit': _commit(commit_id=rewritten_sha, status='running')}),
            from_state='pushed',
        )
        api.add_transition(
            GET('/projects/1234/repository/branches/useless_new_feature'),
            Ok({'commit': _commit(commit_id=rewritten_sha, status='success')}),
            from_state='passed')
        api.add_transition(
            PUT(
                '/projects/1234/merge_requests/54/merge',
                dict(sha=rewritten_sha,
                     should_remove_source_branch=True,
                     merge_when_pipeline_succeeds=True),
            ),
            Ok({}),
            from_state='passed',
            to_state='merged',
        )
        api.add_merge_request(dict(self.merge_request_info, state='merged'),
                              from_state='merged')
        self.approvals_info = dict(
            test_approvals.INFO,
            id=self.merge_request_info['id'],
            iid=self.merge_request_info['iid'],
            project_id=self.merge_request_info['project_id'],
            approvals_left=0,
        )
        api.add_approvals(self.approvals_info)
        api.add_transition(
            GET('/projects/1234/repository/branches/master'),
            Ok({'commit': {
                'id': self.initial_master_sha
            }}),
        )
        api.add_transition(GET('/projects/1234/repository/branches/master'),
                           Ok({'commit': {
                               'id': self.rewritten_sha
                           }}),
                           from_state='merged')
        api.expected_note(
            self.merge_request_info,
            "My job would be easier if people didn't jump the queue and push directly... *sigh*",
            from_state=['pushed_but_master_moved', 'merge_rejected'],
        )
        api.expected_note(
            self.merge_request_info,
            "I'm broken on the inside, please somebody fix me... :cry:")