debian-mirror-gitlab/spec/services/merge_requests/refresh_service_spec.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1028 lines
36 KiB
Ruby
Raw Permalink Normal View History

2019-07-31 22:56:46 +05:30
# frozen_string_literal: true
2015-04-26 12:48:37 +05:30
require 'spec_helper'
2023-03-17 16:20:25 +05:30
RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_workflow do
2018-03-17 18:26:18 +05:30
include ProjectForksHelper
2022-11-25 23:54:43 +05:30
include UserHelpers
2018-03-17 18:26:18 +05:30
2017-08-17 22:00:37 +05:30
let(:project) { create(:project, :repository) }
2015-04-26 12:48:37 +05:30
let(:user) { create(:user) }
2017-09-10 17:25:29 +05:30
let(:service) { described_class }
2015-04-26 12:48:37 +05:30
2016-08-24 12:49:21 +05:30
describe '#execute' do
2015-04-26 12:48:37 +05:30
before do
@user = create(:user)
group = create(:group)
group.add_owner(@user)
2017-08-17 22:00:37 +05:30
@project = create(:project, :repository, namespace: group)
2018-03-17 18:26:18 +05:30
@fork_project = fork_project(@project, @user, repository: true)
2023-07-09 08:55:56 +05:30
@merge_request = create(
:merge_request,
source_project: @project,
source_branch: 'master',
target_branch: 'feature',
target_project: @project,
auto_merge_enabled: true,
auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS,
merge_user: @user
)
@another_merge_request = create(
:merge_request,
source_project: @project,
source_branch: 'master',
target_branch: 'test',
target_project: @project,
auto_merge_enabled: true,
auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS,
merge_user: @user
)
@fork_merge_request = create(
:merge_request,
source_project: @fork_project,
source_branch: 'master',
target_branch: 'feature',
target_project: @project
)
@build_failed_todo = create(
:todo,
:build_failed,
user: @user,
project: @project,
target: @merge_request,
author: @user
)
@fork_build_failed_todo = create(
:todo,
:build_failed,
user: @user,
project: @project,
target: @merge_request,
author: @user
)
2016-06-02 11:05:42 +05:30
2015-04-26 12:48:37 +05:30
@commits = @merge_request.commits
@oldrev = @commits.last.id
@newrev = @commits.first.id
end
context 'push to origin repo source branch' do
2021-06-08 01:23:25 +05:30
let(:refresh_service) { service.new(project: @project, current_user: @user) }
2018-05-09 12:01:36 +05:30
let(:notification_service) { spy('notification_service') }
2017-08-17 22:00:37 +05:30
2015-04-26 12:48:37 +05:30
before do
2015-09-11 14:41:01 +05:30
allow(refresh_service).to receive(:execute_hooks)
2018-05-09 12:01:36 +05:30
allow(NotificationService).to receive(:new) { notification_service }
2015-04-26 12:48:37 +05:30
end
2021-04-17 20:07:23 +05:30
context 'query count' do
it 'does not execute a lot of queries' do
# Hardcoded the query limit since the queries can also be reduced even
# if there are the same number of merge requests (e.g. by preloading
# associations). This should also fail in case additional queries are
# added elsewhere that affected this service.
#
# The limit is based on the number of queries executed at the current
# state of the service. As we reduce the number of queries executed in
# this service, the limit should be reduced as well.
expect { refresh_service.execute(@oldrev, @newrev, 'refs/heads/master') }
.not_to exceed_query_limit(260)
end
end
2016-09-13 17:45:13 +05:30
it 'executes hooks with update action' do
2018-03-17 18:26:18 +05:30
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
reload_mrs
2017-09-10 17:25:29 +05:30
expect(refresh_service).to have_received(:execute_hooks)
2018-03-17 18:26:18 +05:30
.with(@merge_request, 'update', old_rev: @oldrev)
2017-08-17 22:00:37 +05:30
2018-05-09 12:01:36 +05:30
expect(notification_service).to have_received(:push_to_merge_request)
.with(@merge_request, @user, new_commits: anything, existing_commits: anything)
expect(notification_service).to have_received(:push_to_merge_request)
.with(@another_merge_request, @user, new_commits: anything, existing_commits: anything)
2017-08-17 22:00:37 +05:30
expect(@merge_request.notes).not_to be_empty
expect(@merge_request).to be_open
2019-09-04 21:01:54 +05:30
expect(@merge_request.auto_merge_enabled).to be_falsey
2017-08-17 22:00:37 +05:30
expect(@merge_request.diff_head_sha).to eq(@newrev)
expect(@fork_merge_request).to be_open
expect(@fork_merge_request.notes).to be_empty
expect(@build_failed_todo).to be_done
expect(@fork_build_failed_todo).to be_done
end
2018-03-17 18:26:18 +05:30
2023-05-27 22:25:52 +05:30
it 'triggers mergeRequestMergeStatusUpdated GraphQL subscription conditionally' do
expect(GraphqlTriggers).to receive(:merge_request_merge_status_updated).with(@merge_request)
expect(GraphqlTriggers).to receive(:merge_request_merge_status_updated).with(@another_merge_request)
expect(GraphqlTriggers).not_to receive(:merge_request_merge_status_updated).with(@fork_merge_request)
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
end
2020-05-24 23:13:21 +05:30
context 'when a merge error exists' do
let(:error_message) { 'This is a merge error' }
before do
@merge_request = create(:merge_request,
source_project: @project,
source_branch: 'feature',
target_branch: 'master',
target_project: @project,
merge_error: error_message)
end
it 'clears merge errors when pushing to the source branch' do
expect { refresh_service.execute(@oldrev, @newrev, 'refs/heads/feature') }
.to change { @merge_request.reload.merge_error }
.from(error_message)
.to(nil)
end
it 'does not clear merge errors when pushing to the target branch' do
expect { refresh_service.execute(@oldrev, @newrev, 'refs/heads/master') }
.not_to change { @merge_request.reload.merge_error }
end
end
2018-03-17 18:26:18 +05:30
it 'reloads source branch MRs memoization' do
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
expect { refresh_service.execute(@oldrev, @newrev, 'refs/heads/master') }.to change {
2023-03-17 16:20:25 +05:30
refresh_service.instance_variable_get(:@source_merge_requests).first.merge_request_diff
2018-03-17 18:26:18 +05:30
}
end
2019-07-07 11:18:12 +05:30
it 'outdates MR suggestions' do
expect_next_instance_of(Suggestions::OutdateService) do |service|
expect(service).to receive(:execute).with(@merge_request).and_call_original
expect(service).to receive(:execute).with(@another_merge_request).and_call_original
end
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
end
2018-03-17 18:26:18 +05:30
context 'when source branch ref does not exists' do
before do
2020-01-01 13:55:28 +05:30
::Branches::DeleteService.new(@project, @user).execute(@merge_request.source_branch)
2018-03-17 18:26:18 +05:30
end
it 'closes MRs without source branch ref' do
expect { refresh_service.execute(@oldrev, @newrev, 'refs/heads/master') }
.to change { @merge_request.reload.state }
.from('opened')
.to('closed')
expect(@fork_merge_request.reload).to be_open
end
it 'does not change the merge request diff' do
expect { refresh_service.execute(@oldrev, @newrev, 'refs/heads/master') }
.not_to change { @merge_request.reload.merge_request_diff }
end
end
2021-04-17 20:07:23 +05:30
it 'calls the merge request activity counter' do
expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
.to receive(:track_mr_including_ci_config)
.with(user: @merge_request.author, merge_request: @merge_request)
expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
.to receive(:track_mr_including_ci_config)
.with(user: @another_merge_request.author, merge_request: @another_merge_request)
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
end
2018-03-17 18:26:18 +05:30
end
context 'when pipeline exists for the source branch' do
2022-08-27 11:52:29 +05:30
let!(:pipeline) { create(:ci_empty_pipeline, ref: @merge_request.source_branch, project: @project, sha: @commits.first.sha) }
2018-03-17 18:26:18 +05:30
2021-06-08 01:23:25 +05:30
subject { service.new(project: @project, current_user: @user).execute(@oldrev, @newrev, 'refs/heads/master') }
2018-03-17 18:26:18 +05:30
2022-11-25 23:54:43 +05:30
it 'updates the head_pipeline_id for @merge_request', :sidekiq_inline do
2018-03-17 18:26:18 +05:30
expect { subject }.to change { @merge_request.reload.head_pipeline_id }.from(nil).to(pipeline.id)
end
it 'does not update the head_pipeline_id for @fork_merge_request' do
expect { subject }.not_to change { @fork_merge_request.reload.head_pipeline_id }
end
2017-08-17 22:00:37 +05:30
end
2021-06-08 01:23:25 +05:30
context 'Pipelines for merge requests', :sidekiq_inline do
2019-02-15 15:39:39 +05:30
before do
2020-04-08 14:13:33 +05:30
stub_ci_pipeline_yaml_file(config)
2019-02-15 15:39:39 +05:30
end
2021-06-08 01:23:25 +05:30
subject { service.new(project: project, current_user: @user).execute(@oldrev, @newrev, ref) }
2019-07-07 11:18:12 +05:30
let(:ref) { 'refs/heads/master' }
let(:project) { @project }
2019-02-15 15:39:39 +05:30
context "when .gitlab-ci.yml has merge_requests keywords" do
let(:config) do
2020-04-08 14:13:33 +05:30
YAML.dump({
2019-02-15 15:39:39 +05:30
test: {
stage: 'test',
script: 'echo',
only: ['merge_requests']
}
2020-04-08 14:13:33 +05:30
})
2019-02-15 15:39:39 +05:30
end
2019-07-07 11:18:12 +05:30
it 'create detached merge request pipeline with commits' do
2019-02-15 15:39:39 +05:30
expect { subject }
2019-07-31 22:56:46 +05:30
.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
.and change { @another_merge_request.pipelines_for_merge_request.count }.by(0)
2019-03-02 22:35:43 +05:30
expect(@merge_request.has_commits?).to be_truthy
expect(@another_merge_request.has_commits?).to be_falsy
2019-02-15 15:39:39 +05:30
end
2022-07-23 23:45:48 +05:30
context 'when "push_options: nil" is passed' do
let(:service_instance) { service.new(project: project, current_user: @user, params: { push_options: nil }) }
subject { service_instance.execute(@oldrev, @newrev, ref) }
it 'creates a detached merge request pipeline with commits' do
expect { subject }
.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
.and change { @another_merge_request.pipelines_for_merge_request.count }.by(0)
expect(@merge_request.has_commits?).to be_truthy
expect(@another_merge_request.has_commits?).to be_falsy
end
end
2022-08-13 15:12:31 +05:30
context 'when ci.skip push_options are passed' do
let(:params) { { push_options: { ci: { skip: true } } } }
let(:service_instance) { service.new(project: project, current_user: @user, params: params) }
subject { service_instance.execute(@oldrev, @newrev, ref) }
it 'creates a skipped detached merge request pipeline with commits' do
expect { subject }
.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
.and change { @another_merge_request.pipelines_for_merge_request.count }.by(0)
expect(@merge_request.has_commits?).to be_truthy
expect(@another_merge_request.has_commits?).to be_falsy
pipeline = @merge_request.pipelines_for_merge_request.last
expect(pipeline).to be_skipped
end
end
2019-07-07 11:18:12 +05:30
it 'does not create detached merge request pipeline for forked project' do
expect { subject }
2019-07-31 22:56:46 +05:30
.not_to change { @fork_merge_request.pipelines_for_merge_request.count }
2019-07-07 11:18:12 +05:30
end
it 'create detached merge request pipeline for non-fork merge request' do
subject
2019-07-31 22:56:46 +05:30
expect(@merge_request.pipelines_for_merge_request.first)
2019-07-07 11:18:12 +05:30
.to be_detached_merge_request_pipeline
end
context 'when service is hooked by target branch' do
let(:ref) { 'refs/heads/feature' }
it 'does not create detached merge request pipeline' do
expect { subject }
2019-07-31 22:56:46 +05:30
.not_to change { @merge_request.pipelines_for_merge_request.count }
2019-07-07 11:18:12 +05:30
end
end
context 'when service runs on forked project' do
let(:project) { @fork_project }
2021-04-29 21:17:54 +05:30
it 'creates detached merge request pipeline for fork merge request' do
2019-07-07 11:18:12 +05:30
expect { subject }
2019-07-31 22:56:46 +05:30
.to change { @fork_merge_request.pipelines_for_merge_request.count }.by(1)
2019-07-07 11:18:12 +05:30
2020-07-28 23:09:34 +05:30
merge_request_pipeline = @fork_merge_request.pipelines_for_merge_request.first
expect(merge_request_pipeline).to be_detached_merge_request_pipeline
expect(merge_request_pipeline.project).to eq(@project)
2019-07-07 11:18:12 +05:30
end
end
context "when branch pipeline was created before a detaced merge request pipeline has been created" do
2019-02-15 15:39:39 +05:30
before do
2023-07-09 08:55:56 +05:30
create(
:ci_pipeline,
project: @merge_request.source_project,
sha: @merge_request.diff_head_sha,
ref: @merge_request.source_branch,
tag: false
)
2019-02-15 15:39:39 +05:30
subject
end
2022-11-25 23:54:43 +05:30
it 'sets the latest detached merge request pipeline as a head pipeline' do
2019-02-15 15:39:39 +05:30
@merge_request.reload
2019-07-07 11:18:12 +05:30
expect(@merge_request.actual_head_pipeline).to be_merge_request_event
2019-02-15 15:39:39 +05:30
end
it 'returns pipelines in correct order' do
@merge_request.reload
2019-07-07 11:18:12 +05:30
expect(@merge_request.all_pipelines.first).to be_merge_request_event
2019-02-15 15:39:39 +05:30
expect(@merge_request.all_pipelines.second).to be_push
end
end
context "when MergeRequestUpdateWorker is retried by an exception" do
2019-07-07 11:18:12 +05:30
it 'does not re-create a duplicate detached merge request pipeline' do
2019-02-15 15:39:39 +05:30
expect do
2021-06-08 01:23:25 +05:30
service.new(project: @project, current_user: @user).execute(@oldrev, @newrev, 'refs/heads/master')
2019-07-31 22:56:46 +05:30
end.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
2019-02-15 15:39:39 +05:30
expect do
2021-06-08 01:23:25 +05:30
service.new(project: @project, current_user: @user).execute(@oldrev, @newrev, 'refs/heads/master')
2019-07-31 22:56:46 +05:30
end.not_to change { @merge_request.pipelines_for_merge_request.count }
2019-02-15 15:39:39 +05:30
end
end
2020-04-08 14:13:33 +05:30
context 'when the pipeline should be skipped' do
it 'saves a skipped detached merge request pipeline' do
2023-07-09 08:55:56 +05:30
project.repository.create_file(
@user, 'new-file.txt', 'A new file',
message: '[skip ci] This is a test',
branch_name: 'master'
)
2020-04-08 14:13:33 +05:30
expect { subject }
.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
expect(@merge_request.pipelines_for_merge_request.last).to be_skipped
end
end
2019-02-15 15:39:39 +05:30
end
context "when .gitlab-ci.yml does not have merge_requests keywords" do
let(:config) do
2020-04-08 14:13:33 +05:30
YAML.dump({
2019-02-15 15:39:39 +05:30
test: {
stage: 'test',
script: 'echo'
}
2020-04-08 14:13:33 +05:30
})
2019-02-15 15:39:39 +05:30
end
2019-07-07 11:18:12 +05:30
it 'does not create a detached merge request pipeline' do
2019-02-15 15:39:39 +05:30
expect { subject }
2019-07-31 22:56:46 +05:30
.not_to change { @merge_request.pipelines_for_merge_request.count }
2019-02-15 15:39:39 +05:30
end
end
2020-04-08 14:13:33 +05:30
context 'when .gitlab-ci.yml is invalid' do
let(:config) { 'invalid yaml file' }
it 'persists a pipeline with config error' do
expect { subject }
.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
expect(@merge_request.pipelines_for_merge_request.last).to be_failed
expect(@merge_request.pipelines_for_merge_request.last).to be_config_error
end
end
context 'when .gitlab-ci.yml file is valid but has a logical error' do
let(:config) do
YAML.dump({
build: {
script: 'echo "Valid yaml syntax, but..."',
only: ['master']
},
test: {
script: 'echo "... I depend on build, which does not run."',
only: ['merge_request'],
needs: ['build']
}
})
end
it 'persists a pipeline with config error' do
expect { subject }
.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
expect(@merge_request.pipelines_for_merge_request.last).to be_failed
expect(@merge_request.pipelines_for_merge_request.last).to be_config_error
end
end
2019-02-15 15:39:39 +05:30
end
2019-07-31 22:56:46 +05:30
context 'push to origin repo source branch' do
2021-06-08 01:23:25 +05:30
let(:refresh_service) { service.new(project: @project, current_user: @user) }
2018-05-09 12:01:36 +05:30
let(:notification_service) { spy('notification_service') }
2017-08-17 22:00:37 +05:30
before do
allow(refresh_service).to receive(:execute_hooks)
2018-05-09 12:01:36 +05:30
allow(NotificationService).to receive(:new) { notification_service }
2017-08-17 22:00:37 +05:30
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
reload_mrs
2015-09-11 14:41:01 +05:30
end
2017-08-17 22:00:37 +05:30
it 'executes hooks with update action' do
2017-09-10 17:25:29 +05:30
expect(refresh_service).to have_received(:execute_hooks)
2018-03-17 18:26:18 +05:30
.with(@merge_request, 'update', old_rev: @oldrev)
2018-05-09 12:01:36 +05:30
expect(notification_service).to have_received(:push_to_merge_request)
.with(@merge_request, @user, new_commits: anything, existing_commits: anything)
expect(notification_service).to have_received(:push_to_merge_request)
.with(@another_merge_request, @user, new_commits: anything, existing_commits: anything)
2017-08-17 22:00:37 +05:30
expect(@merge_request.notes).not_to be_empty
expect(@merge_request).to be_open
2019-09-04 21:01:54 +05:30
expect(@merge_request.auto_merge_enabled).to be_falsey
2017-08-17 22:00:37 +05:30
expect(@merge_request.diff_head_sha).to eq(@newrev)
expect(@fork_merge_request).to be_open
expect(@fork_merge_request.notes).to be_empty
expect(@build_failed_todo).to be_done
expect(@fork_build_failed_todo).to be_done
end
2015-04-26 12:48:37 +05:30
end
2022-11-25 23:54:43 +05:30
context 'push to origin repo target branch' do
2021-01-03 14:25:43 +05:30
context 'when all MRs to the target branch had diffs' do
2017-09-10 17:25:29 +05:30
before do
2021-06-08 01:23:25 +05:30
service.new(project: @project, current_user: @user).execute(@oldrev, @newrev, 'refs/heads/feature')
2021-01-03 14:25:43 +05:30
reload_mrs
2017-09-10 17:25:29 +05:30
end
2021-01-03 14:25:43 +05:30
it 'updates the merge state' do
expect(@merge_request).to be_merged
expect(@fork_merge_request).to be_merged
expect(@build_failed_todo).to be_done
expect(@fork_build_failed_todo).to be_done
2020-06-23 00:09:42 +05:30
2021-01-03 14:25:43 +05:30
expect(@merge_request.resource_state_events.last.state).to eq('merged')
expect(@fork_merge_request.resource_state_events.last.state).to eq('merged')
2017-09-10 17:25:29 +05:30
end
2021-01-03 14:25:43 +05:30
end
2015-04-26 12:48:37 +05:30
2021-01-03 14:25:43 +05:30
context 'when an MR to be closed was empty already' do
let!(:empty_fork_merge_request) do
2023-07-09 08:55:56 +05:30
create(
:merge_request,
source_project: @fork_project,
source_branch: 'master',
target_branch: 'master',
target_project: @project
)
2021-01-03 14:25:43 +05:30
end
2020-06-23 00:09:42 +05:30
2021-01-03 14:25:43 +05:30
before do
# This spec already has a fake push, so pretend that we were targeting
# feature all along.
empty_fork_merge_request.update_columns(target_branch: 'feature')
2020-06-23 00:09:42 +05:30
2021-06-08 01:23:25 +05:30
service.new(project: @project, current_user: @user).execute(@oldrev, @newrev, 'refs/heads/feature')
2021-01-03 14:25:43 +05:30
reload_mrs
empty_fork_merge_request.reload
end
2020-06-23 00:09:42 +05:30
2021-01-03 14:25:43 +05:30
it 'only updates the non-empty MRs' do
expect(@merge_request).to be_merged
expect(@fork_merge_request).to be_merged
expect(empty_fork_merge_request).to be_open
expect(empty_fork_merge_request.merge_request_diff.state).to eq('empty')
expect(empty_fork_merge_request.notes).to be_empty
expect(@merge_request.resource_state_events.last.state).to eq('merged')
expect(@fork_merge_request.resource_state_events.last.state).to eq('merged')
2017-09-10 17:25:29 +05:30
end
2020-06-23 00:09:42 +05:30
end
2017-09-10 17:25:29 +05:30
2022-11-25 23:54:43 +05:30
context 'manual merge of source branch' do
2017-09-10 17:25:29 +05:30
before do
2020-06-23 00:09:42 +05:30
# Merge master -> feature branch
@project.repository.merge(@user, @merge_request.diff_head_sha, @merge_request, 'Test message')
commit = @project.repository.commit('feature')
2021-06-08 01:23:25 +05:30
service.new(project: @project, current_user: @user).execute(@oldrev, commit.id, 'refs/heads/feature')
2017-09-10 17:25:29 +05:30
reload_mrs
end
2020-06-23 00:09:42 +05:30
it 'updates the merge state' do
2021-01-03 14:25:43 +05:30
expect(@merge_request.resource_state_events.last.state).to eq('merged')
expect(@fork_merge_request.resource_state_events.last.state).to eq('merged')
2017-09-10 17:25:29 +05:30
2020-06-23 00:09:42 +05:30
expect(@merge_request).to be_merged
expect(@merge_request.diffs.size).to be > 0
2017-09-10 17:25:29 +05:30
expect(@fork_merge_request).to be_merged
2020-06-23 00:09:42 +05:30
expect(@build_failed_todo).to be_done
expect(@fork_build_failed_todo).to be_done
2017-09-10 17:25:29 +05:30
end
2017-08-17 22:00:37 +05:30
end
2015-04-26 12:48:37 +05:30
end
2022-11-25 23:54:43 +05:30
context 'push to fork repo source branch' do
2021-06-08 01:23:25 +05:30
let(:refresh_service) { service.new(project: @fork_project, current_user: @user) }
2015-04-26 12:48:37 +05:30
2019-07-07 11:18:12 +05:30
def refresh
allow(refresh_service).to receive(:execute_hooks)
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
reload_mrs
end
2019-05-18 00:54:41 +05:30
2019-07-07 11:18:12 +05:30
context 'open fork merge request' do
2020-03-13 15:44:24 +05:30
it 'calls MergeRequests::LinkLfsObjectsService#execute' do
expect_next_instance_of(MergeRequests::LinkLfsObjectsService) do |svc|
expect(svc).to receive(:execute).with(@fork_merge_request, oldrev: @oldrev, newrev: @newrev)
end
refresh
end
2019-05-30 16:15:17 +05:30
it 'executes hooks with update action' do
2019-07-07 11:18:12 +05:30
refresh
2017-09-10 17:25:29 +05:30
expect(refresh_service).to have_received(:execute_hooks)
2018-03-17 18:26:18 +05:30
.with(@fork_merge_request, 'update', old_rev: @oldrev)
2017-08-17 22:00:37 +05:30
expect(@merge_request.notes).to be_empty
expect(@merge_request).to be_open
expect(@fork_merge_request.notes.last.note).to include('added 28 commits')
expect(@fork_merge_request).to be_open
expect(@build_failed_todo).to be_pending
expect(@fork_build_failed_todo).to be_pending
end
2019-07-07 11:18:12 +05:30
it 'outdates opened forked MR suggestions' do
expect_next_instance_of(Suggestions::OutdateService) do |service|
expect(service).to receive(:execute).with(@fork_merge_request).and_call_original
end
refresh
end
2015-09-11 14:41:01 +05:30
end
2017-08-17 22:00:37 +05:30
context 'closed fork merge request' do
before do
@fork_merge_request.close!
end
it 'do not execute hooks with update action' do
2019-07-07 11:18:12 +05:30
refresh
2017-08-17 22:00:37 +05:30
expect(refresh_service).not_to have_received(:execute_hooks)
end
it 'updates merge request to closed state' do
2019-07-07 11:18:12 +05:30
refresh
2017-08-17 22:00:37 +05:30
expect(@merge_request.notes).to be_empty
expect(@merge_request).to be_open
expect(@fork_merge_request.notes).to be_empty
expect(@fork_merge_request).to be_closed
expect(@build_failed_todo).to be_pending
expect(@fork_build_failed_todo).to be_pending
end
end
2015-04-26 12:48:37 +05:30
end
2022-11-25 23:54:43 +05:30
context 'push to fork repo target branch' do
2017-08-17 22:00:37 +05:30
describe 'changes to merge requests' do
before do
2021-06-08 01:23:25 +05:30
service.new(project: @fork_project, current_user: @user).execute(@oldrev, @newrev, 'refs/heads/feature')
2017-08-17 22:00:37 +05:30
reload_mrs
end
it 'updates the merge request state' do
expect(@merge_request.notes).to be_empty
expect(@merge_request).to be_open
expect(@fork_merge_request.notes).to be_empty
expect(@fork_merge_request).to be_open
expect(@build_failed_todo).to be_pending
expect(@fork_build_failed_todo).to be_pending
end
2015-04-26 12:48:37 +05:30
end
2017-08-17 22:00:37 +05:30
describe 'merge request diff' do
it 'does not reload the diff of the merge request made from fork' do
expect do
2021-06-08 01:23:25 +05:30
service.new(project: @fork_project, current_user: @user).execute(@oldrev, @newrev, 'refs/heads/feature')
2017-08-17 22:00:37 +05:30
end.not_to change { @fork_merge_request.reload.merge_request_diff }
end
end
2015-04-26 12:48:37 +05:30
end
2022-11-25 23:54:43 +05:30
context 'forked projects with the same source branch name as target branch' do
2018-12-13 13:39:08 +05:30
let!(:first_commit) do
2023-07-09 08:55:56 +05:30
@fork_project.repository.create_file(
@user,
'test1.txt',
'Test data',
message: 'Test commit',
branch_name: 'master'
)
2018-12-13 13:39:08 +05:30
end
2020-10-24 23:57:45 +05:30
2018-12-13 13:39:08 +05:30
let!(:second_commit) do
2023-07-09 08:55:56 +05:30
@fork_project.repository.create_file(
@user,
'test2.txt',
'More test data',
message: 'Second test commit',
branch_name: 'master'
)
2018-12-13 13:39:08 +05:30
end
2020-10-24 23:57:45 +05:30
2018-12-13 13:39:08 +05:30
let!(:forked_master_mr) do
2023-07-09 08:55:56 +05:30
create(
:merge_request,
source_project: @fork_project,
source_branch: 'master',
target_branch: 'master',
target_project: @project
)
2018-12-13 13:39:08 +05:30
end
2020-10-24 23:57:45 +05:30
2018-12-13 13:39:08 +05:30
let(:force_push_commit) { @project.commit('feature').id }
2019-07-07 11:18:12 +05:30
it 'reloads a new diff for a push to the forked project' do
2018-12-13 13:39:08 +05:30
expect do
2021-06-08 01:23:25 +05:30
service.new(project: @fork_project, current_user: @user).execute(@oldrev, first_commit, 'refs/heads/master')
2018-12-13 13:39:08 +05:30
reload_mrs
end.to change { forked_master_mr.merge_request_diffs.count }.by(1)
end
2019-07-07 11:18:12 +05:30
it 'reloads a new diff for a force push to the source branch' do
2018-12-13 13:39:08 +05:30
expect do
2021-06-08 01:23:25 +05:30
service.new(project: @fork_project, current_user: @user).execute(@oldrev, force_push_commit, 'refs/heads/master')
2018-12-13 13:39:08 +05:30
reload_mrs
end.to change { forked_master_mr.merge_request_diffs.count }.by(1)
end
2019-07-07 11:18:12 +05:30
it 'reloads a new diff for a force push to the target branch' do
2018-12-13 13:39:08 +05:30
expect do
2021-06-08 01:23:25 +05:30
service.new(project: @project, current_user: @user).execute(@oldrev, force_push_commit, 'refs/heads/master')
2018-12-13 13:39:08 +05:30
reload_mrs
end.to change { forked_master_mr.merge_request_diffs.count }.by(1)
end
2019-07-07 11:18:12 +05:30
it 'reloads a new diff for a push to the target project that contains a commit in the MR' do
2018-12-13 13:39:08 +05:30
expect do
2021-06-08 01:23:25 +05:30
service.new(project: @project, current_user: @user).execute(@oldrev, first_commit, 'refs/heads/master')
2018-12-13 13:39:08 +05:30
reload_mrs
end.to change { forked_master_mr.merge_request_diffs.count }.by(1)
end
2019-07-07 11:18:12 +05:30
it 'does not increase the diff count for a new push to target branch' do
2023-07-09 08:55:56 +05:30
new_commit = @project.repository.create_file(
@user,
'new-file.txt',
'A new file',
message: 'This is a test',
branch_name: 'master'
)
2018-12-13 13:39:08 +05:30
expect do
2021-06-08 01:23:25 +05:30
service.new(project: @project, current_user: @user).execute(@newrev, new_commit, 'refs/heads/master')
2018-12-13 13:39:08 +05:30
reload_mrs
end.not_to change { forked_master_mr.merge_request_diffs.count }
end
end
2021-01-03 14:25:43 +05:30
context 'push to origin repo target branch after fork project was removed' do
before do
@fork_project.destroy!
2021-06-08 01:23:25 +05:30
service.new(project: @project, current_user: @user).execute(@oldrev, @newrev, 'refs/heads/feature')
2021-01-03 14:25:43 +05:30
reload_mrs
end
2020-06-23 00:09:42 +05:30
2021-01-03 14:25:43 +05:30
it 'updates the merge request state' do
expect(@merge_request.resource_state_events.last.state).to eq('merged')
2020-06-23 00:09:42 +05:30
2021-01-03 14:25:43 +05:30
expect(@merge_request).to be_merged
expect(@fork_merge_request).to be_open
expect(@fork_merge_request.notes).to be_empty
expect(@build_failed_todo).to be_done
expect(@fork_build_failed_todo).to be_done
2017-08-17 22:00:37 +05:30
end
2015-04-26 12:48:37 +05:30
end
2015-10-24 18:46:33 +05:30
context 'push new branch that exists in a merge request' do
2021-06-08 01:23:25 +05:30
let(:refresh_service) { service.new(project: @fork_project, current_user: @user) }
2015-10-24 18:46:33 +05:30
2022-11-25 23:54:43 +05:30
it 'refreshes the merge request' do
2017-09-10 17:25:29 +05:30
expect(refresh_service).to receive(:execute_hooks)
2018-03-17 18:26:18 +05:30
.with(@fork_merge_request, 'update', old_rev: Gitlab::Git::BLANK_SHA)
2015-10-24 18:46:33 +05:30
allow_any_instance_of(Repository).to receive(:merge_base).and_return(@oldrev)
refresh_service.execute(Gitlab::Git::BLANK_SHA, @newrev, 'refs/heads/master')
reload_mrs
expect(@merge_request.notes).to be_empty
expect(@merge_request).to be_open
notes = @fork_merge_request.notes.reorder(:created_at).map(&:note)
2017-08-17 22:00:37 +05:30
expect(notes[0]).to include('restored source branch `master`')
expect(notes[1]).to include('added 28 commits')
2015-10-24 18:46:33 +05:30
expect(@fork_merge_request).to be_open
end
end
2016-09-29 09:46:39 +05:30
context 'merge request metrics' do
2021-03-11 19:13:27 +05:30
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:issue) { create(:issue, project: project) }
2016-09-29 09:46:39 +05:30
let(:commit) { project.commit }
before do
2018-03-17 18:26:18 +05:30
project.add_developer(user)
2016-09-29 09:46:39 +05:30
allow(commit).to receive_messages(
safe_message: "Closes #{issue.to_reference}",
references: [issue],
2021-03-11 19:13:27 +05:30
author_name: user.name,
author_email: user.email,
2020-05-24 23:13:21 +05:30
committed_date: Time.current
2016-09-29 09:46:39 +05:30
)
end
context 'when the merge request is sourced from the same project' do
it 'creates a `MergeRequestsClosingIssues` record for each issue closed by a commit' do
2021-03-11 19:13:27 +05:30
allow_any_instance_of(MergeRequest).to receive(:commits).and_return(
CommitCollection.new(project, [commit], 'close-by-commit')
)
2023-07-09 08:55:56 +05:30
merge_request = create(
:merge_request,
target_branch: 'master',
source_branch: 'close-by-commit',
source_project: project
)
2021-03-11 19:13:27 +05:30
2021-06-08 01:23:25 +05:30
refresh_service = service.new(project: project, current_user: user)
2016-09-29 09:46:39 +05:30
allow(refresh_service).to receive(:execute_hooks)
2021-03-11 19:13:27 +05:30
refresh_service.execute(@oldrev, @newrev, 'refs/heads/close-by-commit')
2016-09-29 09:46:39 +05:30
issue_ids = MergeRequestsClosingIssues.where(merge_request: merge_request).pluck(:issue_id)
expect(issue_ids).to eq([issue.id])
end
end
context 'when the merge request is sourced from a different project' do
it 'creates a `MergeRequestsClosingIssues` record for each issue closed by a commit' do
2021-03-11 19:13:27 +05:30
forked_project = fork_project(project, user, repository: true)
allow_any_instance_of(MergeRequest).to receive(:commits).and_return(
CommitCollection.new(forked_project, [commit], 'close-by-commit')
)
2016-09-29 09:46:39 +05:30
2023-07-09 08:55:56 +05:30
merge_request = create(
:merge_request,
target_branch: 'master',
target_project: project,
source_branch: 'close-by-commit',
source_project: forked_project
)
2021-03-11 19:13:27 +05:30
2021-06-08 01:23:25 +05:30
refresh_service = service.new(project: forked_project, current_user: user)
2016-09-29 09:46:39 +05:30
allow(refresh_service).to receive(:execute_hooks)
2021-03-11 19:13:27 +05:30
refresh_service.execute(@oldrev, @newrev, 'refs/heads/close-by-commit')
2016-09-29 09:46:39 +05:30
issue_ids = MergeRequestsClosingIssues.where(merge_request: merge_request).pluck(:issue_id)
expect(issue_ids).to eq([issue.id])
end
end
end
2021-01-29 00:20:46 +05:30
context 'marking the merge request as draft' do
2021-06-08 01:23:25 +05:30
let(:refresh_service) { service.new(project: @project, current_user: @user) }
2020-01-01 13:55:28 +05:30
2017-08-17 22:00:37 +05:30
before do
allow(refresh_service).to receive(:execute_hooks)
end
2021-01-29 00:20:46 +05:30
it 'marks the merge request as draft from fixup commits' do
2023-07-09 08:55:56 +05:30
fixup_merge_request = create(
:merge_request,
source_project: @project,
source_branch: 'wip',
target_branch: 'master',
target_project: @project
)
2017-08-17 22:00:37 +05:30
commits = fixup_merge_request.commits
oldrev = commits.last.id
newrev = commits.first.id
refresh_service.execute(oldrev, newrev, 'refs/heads/wip')
fixup_merge_request.reload
2022-07-23 23:45:48 +05:30
expect(fixup_merge_request.draft?).to eq(true)
2017-08-17 22:00:37 +05:30
expect(fixup_merge_request.notes.last.note).to match(
2021-01-29 00:20:46 +05:30
/marked this merge request as \*\*draft\*\* from #{Commit.reference_pattern}/
2017-08-17 22:00:37 +05:30
)
end
2021-01-29 00:20:46 +05:30
it 'references the commit that caused the draft status' do
2023-07-09 08:55:56 +05:30
draft_merge_request = create(
:merge_request,
source_project: @project,
source_branch: 'wip',
target_branch: 'master',
target_project: @project
)
2018-03-17 18:26:18 +05:30
2022-07-23 23:45:48 +05:30
commits = draft_merge_request.commits
2018-03-17 18:26:18 +05:30
oldrev = commits.last.id
newrev = commits.first.id
2022-07-23 23:45:48 +05:30
draft_commit = draft_merge_request.commits.find(&:draft?)
2018-03-17 18:26:18 +05:30
refresh_service.execute(oldrev, newrev, 'refs/heads/wip')
2022-07-23 23:45:48 +05:30
expect(draft_merge_request.reload.notes.last.note).to eq(
"marked this merge request as **draft** from #{draft_commit.id}"
2017-08-17 22:00:37 +05:30
)
end
2021-01-29 00:20:46 +05:30
it 'does not mark as draft based on commits that do not belong to an MR' do
2017-08-17 22:00:37 +05:30
allow(refresh_service).to receive(:find_new_commits)
2022-07-23 23:45:48 +05:30
2023-03-17 16:20:25 +05:30
refresh_service.instance_variable_set(:@commits,
2022-11-25 23:54:43 +05:30
[
double(
id: 'aaaaaaa',
sha: 'aaaaaaa',
short_id: 'aaaaaaa',
title: 'Fix issue',
draft?: false
),
double(
id: 'bbbbbbb',
sha: 'bbbbbbbb',
short_id: 'bbbbbbb',
title: 'fixup! Fix issue',
draft?: true,
to_reference: 'bbbbbbb'
)
])
2017-08-17 22:00:37 +05:30
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
reload_mrs
2022-07-23 23:45:48 +05:30
expect(@merge_request.draft?).to be_falsey
2017-08-17 22:00:37 +05:30
end
end
2015-04-26 12:48:37 +05:30
def reload_mrs
@merge_request.reload
@fork_merge_request.reload
2016-06-02 11:05:42 +05:30
@build_failed_todo.reload
@fork_build_failed_todo.reload
2015-04-26 12:48:37 +05:30
end
end
2019-02-15 15:39:39 +05:30
describe 'updating merge_commit' do
2021-06-08 01:23:25 +05:30
let(:service) { described_class.new(project: project, current_user: user) }
2019-02-15 15:39:39 +05:30
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:oldrev) { TestEnv::BRANCH_SHA['merge-commit-analyze-before'] }
let(:newrev) { TestEnv::BRANCH_SHA['merge-commit-analyze-after'] } # Pretend branch is now updated
let!(:merge_request) do
create(
:merge_request,
source_project: project,
source_branch: 'merge-commit-analyze-after',
target_branch: 'merge-commit-analyze-before',
target_project: project,
merge_user: user
)
end
let!(:merge_request_side_branch) do
create(
:merge_request,
source_project: project,
source_branch: 'merge-commit-analyze-side-branch',
target_branch: 'merge-commit-analyze-before',
target_project: project,
merge_user: user
)
end
subject { service.execute(oldrev, newrev, 'refs/heads/merge-commit-analyze-before') }
context 'feature enabled' do
it "updates merge requests' merge_commits" do
expect(Gitlab::BranchPushMergeCommitAnalyzer).to receive(:new).and_wrap_original do |original_method, commits|
expect(commits.map(&:id)).to eq(%w{646ece5cfed840eca0a4feb21bcd6a81bb19bda3 29284d9bcc350bcae005872d0be6edd016e2efb5 5f82584f0a907f3b30cfce5bb8df371454a90051 8a994512e8c8f0dfcf22bb16df6e876be7a61036 689600b91aabec706e657e38ea706ece1ee8268f db46a1c5a5e474aa169b6cdb7a522d891bc4c5f9})
original_method.call(commits)
end
subject
merge_request.reload
merge_request_side_branch.reload
expect(merge_request.merge_commit.id).to eq('646ece5cfed840eca0a4feb21bcd6a81bb19bda3')
expect(merge_request_side_branch.merge_commit.id).to eq('29284d9bcc350bcae005872d0be6edd016e2efb5')
end
end
end
2019-12-21 20:55:43 +05:30
describe '#abort_ff_merge_requests_with_when_pipeline_succeeds' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:source_project) { project }
let_it_be(:target_project) { project }
let_it_be(:author) { create_user_from_membership(target_project, :developer) }
let_it_be(:user) { create(:user) }
let_it_be(:forked_project) do
fork_project(target_project, author, repository: true)
end
2019-12-26 22:10:19 +05:30
let_it_be(:merge_request, refind: true) do
2023-07-09 08:55:56 +05:30
create(
:merge_request,
author: author,
source_project: source_project,
source_branch: 'feature',
target_branch: 'master',
target_project: target_project,
auto_merge_enabled: true,
auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS,
merge_user: user
)
2019-12-21 20:55:43 +05:30
end
let_it_be(:newrev) do
2023-07-09 08:55:56 +05:30
target_project.repository.create_file(
user, 'test1.txt', 'Test data', message: 'Test commit', branch_name: 'master'
)
2019-12-21 20:55:43 +05:30
end
let_it_be(:oldrev) do
target_project
.repository
.commit(newrev)
.parent_id
end
2019-12-26 22:10:19 +05:30
let(:auto_merge_strategy) { AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS }
2021-06-08 01:23:25 +05:30
let(:refresh_service) { service.new(project: project, current_user: user) }
2019-12-21 20:55:43 +05:30
before do
target_project.merge_method = merge_method
target_project.save!
2019-12-26 22:10:19 +05:30
merge_request.auto_merge_strategy = auto_merge_strategy
merge_request.save!
2019-12-21 20:55:43 +05:30
refresh_service.execute(oldrev, newrev, 'refs/heads/master')
merge_request.reload
end
context 'when Project#merge_method is set to FF' do
let(:merge_method) { :ff }
2019-12-26 22:10:19 +05:30
it_behaves_like 'aborted merge requests for MWPS'
2019-12-21 20:55:43 +05:30
context 'with forked project' do
let(:source_project) { forked_project }
2019-12-26 22:10:19 +05:30
it_behaves_like 'aborted merge requests for MWPS'
end
context 'with bogus auto merge strategy' do
let(:auto_merge_strategy) { 'bogus' }
it_behaves_like 'maintained merge requests for MWPS'
2019-12-21 20:55:43 +05:30
end
end
context 'when Project#merge_method is set to rebase_merge' do
let(:merge_method) { :rebase_merge }
2019-12-26 22:10:19 +05:30
it_behaves_like 'aborted merge requests for MWPS'
2019-12-21 20:55:43 +05:30
context 'with forked project' do
let(:source_project) { forked_project }
2019-12-26 22:10:19 +05:30
it_behaves_like 'aborted merge requests for MWPS'
2019-12-21 20:55:43 +05:30
end
end
context 'when Project#merge_method is set to merge' do
let(:merge_method) { :merge }
2019-12-26 22:10:19 +05:30
it_behaves_like 'maintained merge requests for MWPS'
2019-12-21 20:55:43 +05:30
context 'with forked project' do
let(:source_project) { forked_project }
2019-12-26 22:10:19 +05:30
it_behaves_like 'maintained merge requests for MWPS'
2019-12-21 20:55:43 +05:30
end
end
end
2015-04-26 12:48:37 +05:30
end