245 lines
7.5 KiB
Ruby
245 lines
7.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
describe MergeRequests::MergeToRefService do
|
|
shared_examples_for 'MergeService for target ref' do
|
|
it 'target_ref has the same state of target branch' do
|
|
repo = merge_request.target_project.repository
|
|
|
|
process_merge_to_ref
|
|
merge_service.execute(merge_request)
|
|
|
|
ref_commits = repo.commits(merge_request.merge_ref_path, limit: 3)
|
|
target_branch_commits = repo.commits(merge_request.target_branch, limit: 3)
|
|
|
|
ref_commits.zip(target_branch_commits).each do |ref_commit, target_branch_commit|
|
|
expect(ref_commit.parents).to eq(target_branch_commit.parents)
|
|
end
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'successfully merges to ref with merge method' do
|
|
it 'writes commit to merge ref' do
|
|
repository = project.repository
|
|
|
|
expect(repository.ref_exists?(target_ref)).to be(false)
|
|
|
|
result = service.execute(merge_request)
|
|
|
|
ref_head = repository.commit(target_ref)
|
|
|
|
expect(result[:status]).to eq(:success)
|
|
expect(result[:commit_id]).to be_present
|
|
expect(result[:source_id]).to eq(merge_request.source_branch_sha)
|
|
expect(result[:target_id]).to eq(repository.commit(first_parent_ref).sha)
|
|
expect(repository.ref_exists?(target_ref)).to be(true)
|
|
expect(ref_head.id).to eq(result[:commit_id])
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'successfully evaluates pre-condition checks' do
|
|
it 'returns an error when the failing to process the merge' do
|
|
allow(project.repository).to receive(:merge_to_ref).and_return(nil)
|
|
|
|
result = service.execute(merge_request)
|
|
|
|
expect(result[:status]).to eq(:error)
|
|
expect(result[:message]).to eq('Conflicts detected during merge')
|
|
end
|
|
|
|
it 'does not send any mail' do
|
|
expect { process_merge_to_ref }.not_to change { ActionMailer::Base.deliveries.count }
|
|
end
|
|
|
|
it 'does not change the MR state' do
|
|
expect { process_merge_to_ref }.not_to change { merge_request.state }
|
|
end
|
|
|
|
it 'does not create notes' do
|
|
expect { process_merge_to_ref }.not_to change { merge_request.notes.count }
|
|
end
|
|
|
|
it 'does not delete the source branch' do
|
|
expect(DeleteBranchService).not_to receive(:new)
|
|
|
|
process_merge_to_ref
|
|
end
|
|
end
|
|
|
|
set(:user) { create(:user) }
|
|
let(:merge_request) { create(:merge_request, :simple) }
|
|
let(:project) { merge_request.project }
|
|
|
|
describe '#execute' do
|
|
let(:service) do
|
|
described_class.new(project, user, **params)
|
|
end
|
|
|
|
let(:params) { { commit_message: 'Awesome message', should_remove_source_branch: true } }
|
|
|
|
def process_merge_to_ref
|
|
perform_enqueued_jobs do
|
|
service.execute(merge_request)
|
|
end
|
|
end
|
|
|
|
it_behaves_like 'successfully merges to ref with merge method' do
|
|
let(:first_parent_ref) { 'refs/heads/master' }
|
|
let(:target_ref) { merge_request.merge_ref_path }
|
|
end
|
|
|
|
it_behaves_like 'successfully evaluates pre-condition checks'
|
|
|
|
context 'commit history comparison with regular MergeService' do
|
|
before do
|
|
# The merge service needs an authorized user while merge-to-ref
|
|
# doesn't.
|
|
project.add_maintainer(user)
|
|
end
|
|
|
|
let(:merge_ref_service) do
|
|
described_class.new(project, user, {})
|
|
end
|
|
|
|
let(:merge_service) do
|
|
MergeRequests::MergeService.new(project, user, {})
|
|
end
|
|
|
|
context 'when merge commit' do
|
|
it_behaves_like 'MergeService for target ref'
|
|
end
|
|
|
|
context 'when merge commit with squash' do
|
|
before do
|
|
merge_request.update!(squash: true)
|
|
end
|
|
|
|
it_behaves_like 'MergeService for target ref'
|
|
|
|
it 'does not squash before merging' do
|
|
expect(MergeRequests::SquashService).not_to receive(:new)
|
|
|
|
process_merge_to_ref
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'merge pre-condition checks' do
|
|
before do
|
|
merge_request.project.update!(merge_method: merge_method)
|
|
end
|
|
|
|
context 'when semi-linear merge method' do
|
|
let(:merge_method) { :rebase_merge }
|
|
|
|
it_behaves_like 'successfully merges to ref with merge method' do
|
|
let(:first_parent_ref) { 'refs/heads/master' }
|
|
let(:target_ref) { merge_request.merge_ref_path }
|
|
end
|
|
|
|
it_behaves_like 'successfully evaluates pre-condition checks'
|
|
end
|
|
|
|
context 'when fast-forward merge method' do
|
|
let(:merge_method) { :ff }
|
|
|
|
it_behaves_like 'successfully merges to ref with merge method' do
|
|
let(:first_parent_ref) { 'refs/heads/master' }
|
|
let(:target_ref) { merge_request.merge_ref_path }
|
|
end
|
|
|
|
it_behaves_like 'successfully evaluates pre-condition checks'
|
|
end
|
|
|
|
context 'when MR is not mergeable to ref' do
|
|
let(:merge_method) { :merge }
|
|
|
|
it 'returns error' do
|
|
allow(project).to receive_message_chain(:repository, :merge_to_ref) { nil }
|
|
|
|
error_message = 'Conflicts detected during merge'
|
|
|
|
result = service.execute(merge_request)
|
|
|
|
expect(result[:status]).to eq(:error)
|
|
expect(result[:message]).to eq(error_message)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'does not close related todos' do
|
|
let(:merge_request) { create(:merge_request, assignees: [user], author: user) }
|
|
let(:project) { merge_request.project }
|
|
let!(:todo) do
|
|
create(:todo, :assigned,
|
|
project: project,
|
|
author: user,
|
|
user: user,
|
|
target: merge_request)
|
|
end
|
|
|
|
before do
|
|
allow(service).to receive(:execute_hooks)
|
|
|
|
perform_enqueued_jobs do
|
|
service.execute(merge_request)
|
|
todo.reload
|
|
end
|
|
end
|
|
|
|
it { expect(todo).not_to be_done }
|
|
end
|
|
|
|
context 'when source is missing' do
|
|
it 'returns error' do
|
|
allow(merge_request).to receive(:diff_head_sha) { nil }
|
|
|
|
error_message = 'No source for merge'
|
|
|
|
result = service.execute(merge_request)
|
|
|
|
expect(result[:status]).to eq(:error)
|
|
expect(result[:message]).to eq(error_message)
|
|
end
|
|
end
|
|
|
|
context 'when target ref is passed as a parameter' do
|
|
let(:params) { { commit_message: 'merge train', target_ref: target_ref } }
|
|
|
|
it_behaves_like 'successfully merges to ref with merge method' do
|
|
let(:first_parent_ref) { 'refs/heads/master' }
|
|
let(:target_ref) { 'refs/merge-requests/1/train' }
|
|
end
|
|
end
|
|
|
|
describe 'cascading merge refs' do
|
|
set(:project) { create(:project, :repository) }
|
|
let(:params) { { commit_message: 'Cascading merge', first_parent_ref: first_parent_ref, target_ref: target_ref } }
|
|
|
|
context 'when first merge happens' do
|
|
let(:merge_request) do
|
|
create(:merge_request, source_project: project, source_branch: 'feature',
|
|
target_project: project, target_branch: 'master')
|
|
end
|
|
|
|
it_behaves_like 'successfully merges to ref with merge method' do
|
|
let(:first_parent_ref) { 'refs/heads/master' }
|
|
let(:target_ref) { 'refs/merge-requests/1/train' }
|
|
end
|
|
|
|
context 'when second merge happens' do
|
|
let(:merge_request) do
|
|
create(:merge_request, source_project: project, source_branch: 'improve/awesome',
|
|
target_project: project, target_branch: 'master')
|
|
end
|
|
|
|
it_behaves_like 'successfully merges to ref with merge method' do
|
|
let(:first_parent_ref) { 'refs/merge-requests/1/train' }
|
|
let(:target_ref) { 'refs/merge-requests/2/train' }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|