diff options
Diffstat (limited to 'spec/services/merge_requests/create_ref_service_spec.rb')
-rw-r--r-- | spec/services/merge_requests/create_ref_service_spec.rb | 183 |
1 files changed, 144 insertions, 39 deletions
diff --git a/spec/services/merge_requests/create_ref_service_spec.rb b/spec/services/merge_requests/create_ref_service_spec.rb index 85ac651c1fa..5f7b9430416 100644 --- a/spec/services/merge_requests/create_ref_service_spec.rb +++ b/spec/services/merge_requests/create_ref_service_spec.rb @@ -6,13 +6,14 @@ RSpec.describe MergeRequests::CreateRefService, feature_category: :merge_trains using RSpec::Parameterized::TableSyntax describe '#execute' do - let_it_be(:project) { create(:project, :empty_repo) } + let_it_be_with_reload(:project) { create(:project, :empty_repo) } let_it_be(:user) { project.creator } let_it_be(:first_parent_ref) { project.default_branch_or_main } let_it_be(:source_branch) { 'branch' } let(:target_ref) { "refs/merge-requests/#{merge_request.iid}/train" } let(:source_sha) { project.commit(source_branch).sha } let(:squash) { false } + let(:default_commit_message) { merge_request.default_merge_commit_message(user: user) } let(:merge_request) do create( @@ -84,36 +85,40 @@ RSpec.describe MergeRequests::CreateRefService, feature_category: :merge_trains ) end - it 'writes the merged result into target_ref', :aggregate_failures do - expect(result[:status]).to eq :success - expect(result[:commit_sha]).to eq(project.repository.commit(target_ref).sha) - expect(result[:source_sha]).to eq(project.repository.commit(target_ref).parents[1].sha) - expect(result[:target_sha]).to eq(project.repository.commit(first_parent_ref).sha) - expect(project.repository.commits(target_ref, limit: 10, order: 'topo').map(&:message)).to( - match( - [ - a_string_matching(/Merge branch '#{source_branch}' into '#{first_parent_ref}'/), - 'Feature branch commit 2', - 'Feature branch commit 1', - 'Base parent commit 2', - 'Base parent commit 1' - ] + shared_examples_for 'writing with a merge commit' do + it 'merges with a merge commit', :aggregate_failures do + expect(result[:status]).to eq :success + expect(result[:commit_sha]).to eq(project.repository.commit(target_ref).sha) + expect(result[:source_sha]).to eq(project.repository.commit(source_branch).sha) + expect(result[:target_sha]).to eq(project.repository.commit(first_parent_ref).sha) + expect(result[:merge_commit_sha]).to be_present + expect(result[:squash_commit_sha]).not_to be_present + expect(project.repository.commits(target_ref, limit: 10, order: 'topo').map(&:message)).to( + match( + [ + expected_merge_commit, + 'Feature branch commit 2', + 'Feature branch commit 1', + 'Base parent commit 2', + 'Base parent commit 1' + ] + ) ) - ) + end end - context 'when squash is requested' do - let(:squash) { true } - + shared_examples_for 'writing with a squash and merge commit' do it 'writes the squashed result', :aggregate_failures do expect(result[:status]).to eq :success expect(result[:commit_sha]).to eq(project.repository.commit(target_ref).sha) - expect(result[:source_sha]).to eq(project.repository.commit(target_ref).parents[1].sha) + expect(result[:source_sha]).to eq(project.repository.commit(source_branch).sha) expect(result[:target_sha]).to eq(project.repository.commit(first_parent_ref).sha) + expect(result[:merge_commit_sha]).to be_present + expect(result[:squash_commit_sha]).to be_present expect(project.repository.commits(target_ref, limit: 10, order: 'topo').map(&:message)).to( match( [ - a_string_matching(/Merge branch '#{source_branch}' into '#{first_parent_ref}'/), + expected_merge_commit, "#{merge_request.title}\n", 'Base parent commit 2', 'Base parent commit 1' @@ -123,23 +128,18 @@ RSpec.describe MergeRequests::CreateRefService, feature_category: :merge_trains end end - context 'when semi-linear merges are enabled' do - before do - project.merge_method = :rebase_merge - project.save! - end - - it 'writes the semi-linear merged result', :aggregate_failures do + shared_examples_for 'writing with a squash and no merge commit' do + it 'writes the squashed result without a merge commit', :aggregate_failures do expect(result[:status]).to eq :success expect(result[:commit_sha]).to eq(project.repository.commit(target_ref).sha) - expect(result[:source_sha]).to eq(project.repository.commit(target_ref).parents[1].sha) + expect(result[:source_sha]).to eq(project.repository.commit(source_branch).sha) expect(result[:target_sha]).to eq(project.repository.commit(first_parent_ref).sha) + expect(result[:merge_commit_sha]).not_to be_present + expect(result[:squash_commit_sha]).to be_present expect(project.repository.commits(target_ref, limit: 10, order: 'topo').map(&:message)).to( match( [ - a_string_matching(/Merge branch '#{source_branch}' into '#{first_parent_ref}'/), - 'Feature branch commit 2', - 'Feature branch commit 1', + "#{merge_request.title}\n", 'Base parent commit 2', 'Base parent commit 1' ] @@ -148,17 +148,14 @@ RSpec.describe MergeRequests::CreateRefService, feature_category: :merge_trains end end - context 'when fast-forward merges are enabled' do - before do - project.merge_method = :ff - project.save! - end - + shared_examples_for 'writing without a merge commit' do it 'writes the rebased merged result', :aggregate_failures do expect(result[:status]).to eq :success expect(result[:commit_sha]).to eq(project.repository.commit(target_ref).sha) - expect(result[:source_sha]).to eq(project.repository.commit(target_ref).sha) + expect(result[:source_sha]).to eq(project.repository.commit(source_branch).sha) expect(result[:target_sha]).to eq(project.repository.commit(first_parent_ref).sha) + expect(result[:merge_commit_sha]).not_to be_present + expect(result[:squash_commit_sha]).not_to be_present expect(project.repository.commits(target_ref, limit: 10, order: 'topo').map(&:message)).to( eq( [ @@ -171,6 +168,114 @@ RSpec.describe MergeRequests::CreateRefService, feature_category: :merge_trains ) end end + + shared_examples 'merge commits without squash' do + context 'with a custom template' do + let(:expected_merge_commit) { 'This is the merge commit' } # could also be default_commit_message + + before do + project.project_setting.update!(merge_commit_template: expected_merge_commit) + end + + it_behaves_like 'writing with a merge commit' + end + + context 'with no custom template' do + let(:expected_merge_commit) { default_commit_message } + + before do + project.project_setting.update!(merge_commit_template: nil) + end + + it_behaves_like 'writing with a merge commit' + end + end + + shared_examples 'merge commits with squash' do + context 'when squash set' do + let(:squash) { true } + let(:expected_merge_commit) { merge_request.default_merge_commit_message(user: user) } + + before do + project.project_setting.update!(merge_commit_template: 'This is the merge commit') + end + + it_behaves_like 'writing with a squash and merge commit' + end + end + + context 'when the merge commit message is provided at time of merge' do + let(:expected_merge_commit) { 'something custom' } + + before do + merge_request.merge_params['commit_message'] = expected_merge_commit + end + + it 'writes the merged result', :aggregate_failures do + expect(result[:status]).to eq :success + expect(project.repository.commits(target_ref, limit: 1, order: 'topo').map(&:message)).to( + match([expected_merge_commit]) + ) + end + + context 'when squash set' do + let(:squash) { true } + + it_behaves_like 'writing with a squash and merge commit' + end + end + + context 'when merged commit strategy' do + include_examples 'merge commits without squash' + include_examples 'merge commits with squash' + end + + context 'when semi-linear merge strategy' do + before do + project.merge_method = :rebase_merge + project.save! + end + + include_examples 'merge commits without squash' + include_examples 'merge commits with squash' + + context 'when the target ref changes between rebase and merge' do + # this tests internal handling of expected_old_oid + + it 'returns an error', :aggregate_failures do + expect_next_instance_of(described_class) do |instance| + original = instance.method(:maybe_merge!) + + expect(instance).to receive(:maybe_merge!) do |*args| + # Corrupt target_ref before the merge, simulating a race with + # another instance of the service for the same MR. source_sha is + # just an arbitrary valid commit that differs from what was just + # written. + project.repository.write_ref(target_ref, source_sha) + original.call(*args) + end + end + + expect(result[:status]).to eq :error + expect(result[:message]).to eq "9:Could not update #{target_ref}. Please refresh and try again." + end + end + end + + context 'when fast-forward merge strategy' do + before do + project.merge_method = :ff + project.save! + end + + it_behaves_like 'writing without a merge commit' + + context 'when squash set' do + let(:squash) { true } + + it_behaves_like 'writing with a squash and no merge commit' + end + end end end end |