예제 #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_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",
        ]
예제 #5
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 == []
예제 #6
0
 def _load(self, json):
     old_mock = self.api.call
     self.api.call = Mock(return_value=json)
     self.merge_request.refetch_info()
     self.api.call.assert_called_with(
         GET('/projects/1234/merge_requests/54'))
     self.api.call = old_mock
예제 #7
0
    def test_refetch_info(self):
        new_info = dict(INFO, state='closed')
        self.api.call = Mock(return_value=new_info)

        self.merge_request.refetch_info()
        self.api.call.assert_called_once_with(GET('/projects/1234/merge_requests/54'))
        assert self.merge_request.info == new_info
예제 #8
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'
예제 #9
0
 def test_last_on_branch(self):
     self.api.call.side_effect = lambda *_, **__: {'commit': INFO}
     Commit.last_on_branch(project_id=1234,
                           branch='foobar-branch',
                           api=self.api)
     self.api.call.assert_called_once_with(
         GET('/projects/1234/repository/branches/foobar-branch'))
예제 #10
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",
        ]
예제 #11
0
    def test_fetch_by_id(self):
        api = self.api
        api.call = Mock(return_value=INFO)

        project = Project.fetch_by_id(project_id=1234, api=api)

        api.call.assert_called_once_with(GET('/projects/1234'))
        assert project.info == INFO
예제 #12
0
    def test_fetch_by_username_exists(self):
        api = self.api
        api.call = Mock(return_value=INFO)

        user = User.fetch_by_username('john_smith', api)

        api.call.assert_called_once_with(GET('/users', {'username': '******'}, ANY))
        assert user and user.info == INFO
예제 #13
0
    def test_fetch_by_id(self):
        api = self.api
        api.call = Mock(return_value=INFO)

        user = User.fetch_by_id(user_id=1234, api=api)

        api.call.assert_called_once_with(GET('/users/1234'))
        assert user.info == INFO
예제 #14
0
    def test_fetch_by_iid(self):
        api = self.api
        api.call = Mock(return_value=INFO)

        merge_request = MergeRequest.fetch_by_iid(project_id=1234, merge_request_iid=54, api=api)

        api.call.assert_called_once_with(GET('/projects/1234/merge_requests/54'))
        assert merge_request.info == INFO
예제 #15
0
 def test_rebase_was_in_progress_no_error(self):
     expected = [
         (
             GET('/projects/1234/merge_requests/54'
                 ),  # refetch_info -> in progress
             dict(INFO, rebase_in_progress=True)),
         (
             GET('/projects/1234/merge_requests/54'
                 ),  # refetch_info -> in progress
             dict(INFO, rebase_in_progress=True)),
         (
             GET('/projects/1234/merge_requests/54'
                 ),  # refetch_info -> succeeded
             dict(INFO, rebase_in_progress=False)),
     ]
     self.api.call = Mock(side_effect=[resp for (req, resp) in expected])
     self.merge_request.rebase()
     self.api.call.assert_has_calls([call(req) for (req, resp) in expected])
예제 #16
0
 def test_fetch_all_opened_for_me(self):
     api = self.api
     mr1, mr_not_me, mr2 = INFO, dict(INFO, assignee={'id': _MARGE_ID+1}, id=679), dict(INFO, id=678)
     api.collect_all_pages = Mock(return_value=[mr1, mr_not_me, mr2])
     result = MergeRequest.fetch_all_open_for_user(1234, user_id=_MARGE_ID, api=api)
     api.collect_all_pages.assert_called_once_with(GET(
         '/projects/1234/merge_requests',
         {'state': 'opened', 'order_by': 'created_at', 'sort': 'asc'},
     ))
     assert [mr.info for mr in result] == [mr1, mr2]
예제 #17
0
    def test_fetch_by_id(self):
        api = self.api
        api.call = Mock(return_value=INFO)

        commit = Commit.fetch_by_id(project_id=1234, sha=INFO['id'], api=api)

        api.call.assert_called_once_with(
            GET('/projects/1234/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6'
                ))
        assert commit.info == INFO
예제 #18
0
    def test_pipelines_by_merge_request(self):
        api = self.api
        pl1, pl2 = INFO, dict(INFO, id=48)
        api.call = Mock(return_value=[pl1, pl2])

        result = Pipeline.pipelines_by_merge_request(project_id=1234,
                                                     merge_request_iid=1,
                                                     api=api)
        api.call.assert_called_once_with(
            GET('/projects/1234/merge_requests/1/pipelines', ))
        assert [pl.info for pl in result] == [pl2, pl1]
예제 #19
0
    def test_rebase_was_not_in_progress_error(self):
        expected = [
            (
                GET('/projects/1234/merge_requests/54'
                    ),  # refetch_info -> not in progress
                INFO),
            (PUT('/projects/1234/merge_requests/54/rebase'), True),
            (
                GET('/projects/1234/merge_requests/54'
                    ),  # refetch_info -> BOOM
                dict(INFO,
                     rebase_in_progress=False,
                     merge_error="Rebase failed. Please rebase locally")),
        ]

        self.api.call = Mock(side_effect=[resp for (req, resp) in expected])

        with pytest.raises(MergeRequestRebaseFailed):
            self.merge_request.rebase()
        self.api.call.assert_has_calls([call(req) for (req, resp) in expected])
예제 #20
0
    def test_fetch_by_path_exists(self):
        api = self.api
        prj1 = INFO
        prj2 = dict(INFO, id=1235, path_with_namespace='foo/bar')
        prj3 = dict(INFO, id=1240, path_with_namespace='foo/foo')
        api.collect_all_pages = Mock(return_value=[prj1, prj2, prj3])

        project = Project.fetch_by_path('foo/bar', api)

        api.collect_all_pages.assert_called_once_with(GET('/projects'))
        assert project and project.info == prj2
예제 #21
0
 def test_fetch_assigned_at(self):
     api = self.api
     dis1, dis2 = DISCUSSION, dict(DISCUSSION, id=679)
     mr1 = INFO
     user = marge.user.User(api=None, info=dict(USER_INFO, id=_MARGE_ID))
     api.collect_all_pages = Mock(return_value=[dis1, dis2])
     result = MergeRequest.fetch_assigned_at(user=user,
                                             api=api,
                                             merge_request=mr1)
     api.collect_all_pages.assert_called_once_with(
         GET('/projects/1234/merge_requests/54/discussions', ))
     assert result == 1597733578
예제 #22
0
    def test_fetch_from_merge_request(self):
        api = self.api
        api.call = Mock(return_value=INFO)

        merge_request = MergeRequest(api, {
            'id': 74,
            'iid': 6,
            'project_id': 1234
        })
        approvals = merge_request.fetch_approvals()

        api.call.assert_called_once_with(
            GET('/projects/1234/merge_requests/6/approvals'))
        assert approvals.info == INFO
예제 #23
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 == []
예제 #24
0
    def test_fetch_all_mine(self):
        prj1, prj2 = INFO, dict(INFO, id=678)

        api = self.api
        api.collect_all_pages = Mock(return_value=[prj1, prj2])

        result = Project.fetch_all_mine(api)
        api.collect_all_pages.assert_called_once_with(
            GET(
                '/projects',
                {
                    'membership': True,
                    'with_merge_requests_enabled': True
                },
            ))
        assert [prj.info for prj in result] == [prj1, prj2]
예제 #25
0
    def fetch_all_mine_with_permissions(self):
        prj1, prj2 = INFO, dict(INFO, id=678)

        api = self.api
        api.collect_all_pages = Mock(return_value=[prj1, prj2])
        api.version = Mock(return_value=Version.parse("11.0.0-ee"))

        result = Project.fetch_all_mine(api)
        api.collect_all_pages.assert_called_once_with(GET(
            '/projects',
            {
                'membership': True,
                'with_merge_requests_enabled': True,
            },
        ))
        assert [prj.info for prj in result] == [prj1, prj2]
        assert all(prj.access_level == AccessLevel.developer for prj in result)
예제 #26
0
    def test_pipelines_by_branch(self):
        api = self.api
        pl1, pl2 = INFO, dict(INFO, id=48)
        api.call = Mock(return_value=[pl1, pl2])

        result = Pipeline.pipelines_by_branch(project_id=1234,
                                              branch=INFO['ref'],
                                              api=api)
        api.call.assert_called_once_with(
            GET(
                '/projects/1234/pipelines',
                {
                    'ref': INFO['ref'],
                    'order_by': 'id',
                    'sort': 'desc'
                },
            ))
        assert [pl.info for pl in result] == [pl1, pl2]
예제 #27
0
    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'
예제 #28
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",
        ]
예제 #29
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 == []
예제 #30
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'