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*", ]
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:" )
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:")
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", ]
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 == []
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
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
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'
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'))
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", ]
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
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
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
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
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])
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]
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
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]
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])
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
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
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
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_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]
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)
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]
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'
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", ]
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 == []
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'