spec/lib/release_tools/security/merge_train_service_spec.rb (208 lines of code) (raw):
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ReleaseTools::Security::MergeTrainService do
let(:client) { ReleaseTools::GitlabOpsClient }
let(:project) { ReleaseTools::Project::GitlabEe }
subject(:service) { described_class.new }
def mirror_stub(overrides = {})
defaults = {
enabled: true,
last_error: nil,
update_status: 'finished',
url: 'https://*****:*****@gitlab.com/gitlab-org/security/gitlab'
}
double(defaults.merge(overrides))
end
before do
enable_feature(:internal_release_merge_train_stable_branches)
end
describe '#execute' do
context 'when all branches need to be synced using merge-train' do
before do
# Stub the methods that are called within execute
allow(service).to receive(:run_merge_train_default_branch)
allow(service).to receive(:run_merge_train_stable_branches)
end
it 'calls run_merge_train_default_branch for each project with correct schedule_id' do
without_dry_run do
service.execute
end
described_class::PROJECTS.each do |project, schedule_id|
expect(service).to have_received(:run_merge_train_default_branch)
.with(project, schedule_id)
end
end
it 'calls run_merge_train_stable_branches for each project' do
without_dry_run do
service.execute
end
described_class::PROJECTS_STABLE_BRANCHES.each_key do |project|
expect(service).to have_received(:run_merge_train_stable_branches)
.with(project, described_class::PROJECTS_STABLE_BRANCHES[project])
end
end
it 'processes all projects in PROJECTS' do
without_dry_run do
service.execute
end
expect(service).to have_received(:run_merge_train_default_branch)
.exactly(described_class::PROJECTS.count).times
expect(service).to have_received(:run_merge_train_stable_branches)
.exactly(described_class::PROJECTS_STABLE_BRANCHES.count).times
end
context 'when required and inactive' do
before do
allow(service).to receive(:run_merge_train_default_branch).and_call_original
allow(service).to receive_messages(
merge_train_required?: true,
merge_train_active?: false
)
end
it 'toggles on the merge-train pipeline schedule and notifies' do
described_class::PROJECTS.each_value do |schedule_id|
new_schedule = double
expect(client).to receive(:pipeline_schedule_take_ownership).with(
ReleaseTools::Project::MergeTrain,
schedule_id
)
expect(client).to receive(:edit_pipeline_schedule).with(
ReleaseTools::Project::MergeTrain.ops_path,
schedule_id,
active: true,
cron: described_class::CRON
).and_return(new_schedule)
expect(ReleaseTools::Slack::MergeTrainNotification)
.to receive(:toggled)
.with(new_schedule)
end
without_dry_run do
service.execute
end
end
end
context 'when not required and active' do
before do
allow(service).to receive(:run_merge_train_default_branch).and_call_original
allow(service).to receive_messages(
merge_train_required?: false,
merge_train_active?: true
)
end
it 'toggles off the merge-train pipeline schedule and notifies' do
described_class::PROJECTS.each_value do |schedule_id|
new_schedule = double
expect(client).to receive(:pipeline_schedule_take_ownership).with(
ReleaseTools::Project::MergeTrain,
schedule_id
)
expect(client).to receive(:edit_pipeline_schedule).with(
ReleaseTools::Project::MergeTrain.ops_path,
schedule_id,
active: false,
cron: described_class::CRON
).and_return(new_schedule)
expect(ReleaseTools::Slack::MergeTrainNotification)
.to receive(:toggled)
.with(new_schedule)
end
without_dry_run do
service.execute
end
end
end
context 'when required and active' do
before do
allow(service).to receive(:run_merge_train_default_branch).and_call_original
allow(service).to receive_messages(
merge_train_required?: true,
merge_train_active?: true
)
end
it 'does nothing' do
expect(client).not_to receive(:pipeline_schedule_take_ownership)
expect(client).not_to receive(:edit_pipeline_schedule)
expect(ReleaseTools::Slack::MergeTrainNotification).not_to receive(:toggled)
without_dry_run do
service.execute
end
end
end
context 'when not required and inactive' do
before do
allow(service).to receive(:run_merge_train_default_branch).and_call_original
allow(service).to receive_messages(
merge_train_required?: false,
merge_train_active?: false
)
end
it 'does nothing' do
expect(client).not_to receive(:pipeline_schedule_take_ownership)
expect(client).not_to receive(:edit_pipeline_schedule)
expect(ReleaseTools::Slack::MergeTrainNotification).not_to receive(:toggled)
without_dry_run do
service.execute
end
end
end
context 'when the feature flag is disabled' do
before do
disable_feature(:internal_release_merge_train_stable_branches)
end
it 'does not call run_merge_train_stable_branches for each project' do
without_dry_run do
service.execute
end
expect(service).to have_received(:run_merge_train_default_branch).exactly(3).times
expect(service).not_to have_received(:run_merge_train_stable_branches)
end
end
end
context 'when the default branches do not need to be synced using merge-train' do
before do
# Ignore the implementation of run_merge_train_stable_branches
allow(service).to receive(:run_merge_train_stable_branches)
described_class::PROJECTS.each_key do |project|
allow(service).to receive(:merge_train_required?).with(project).and_return(false)
end
allow(service).to receive(:merge_train_active?).and_return(false)
end
it 'does not enable any merge-train schedule' do
expect(service).not_to receive(:toggle_merge_train).with(anything, true)
without_dry_run do
service.execute
end
end
end
context 'when the stable branches do not need to be synced using merge-train' do
let(:branches) { ['15-6-stable-ee', '15-5-stable-ee'] }
let(:project) { ReleaseTools::Project::GitlabEe }
before do
# Ignore the implementation of run_merge_train_default_branch
allow(service).to receive(:run_merge_train_default_branch)
allow(service).to receive(:internal_release_stable_branches).and_return(branches)
branches.each do |branch|
allow(service).to receive(:merge_train_required?).with(project, branch: branch).and_return(false)
allow(service).to receive(:merge_train_active?).and_return(false)
end
end
it 'does not enable any merge-train schedule' do
expect(service).not_to receive(:toggle_merge_train).with(anything, true)
without_dry_run do
service.execute
end
end
end
context 'when an error happens with getting remote mirrors' do
let(:project) { ReleaseTools::Project::GitlabEe }
before do
allow(service).to receive(:run_merge_train_stable_branches)
described_class::PROJECTS.each_key do |project|
allow(service).to receive(:get_remote_mirrors).with(project).and_raise(Gitlab::Error::Error)
end
allow(service).to receive(:merge_train_active?).and_return(false)
end
it 'does not enable any merge-train schedule' do
expect(service).not_to receive(:toggle_merge_train).with(anything, true)
without_dry_run do
service.execute
end
end
end
end
end