gitlab-org--gitlab-foss/spec/services/ci/process_pipeline_service_sp...

424 lines
12 KiB
Ruby
Raw Normal View History

require 'spec_helper'
describe Ci::ProcessPipelineService, '#execute', :services do
let(:user) { create(:user) }
let(:project) { create(:empty_project) }
let(:pipeline) do
create(:ci_empty_pipeline, ref: 'master', project: project)
end
before do
project.add_developer(user)
end
context 'start queuing next builds' do
before do
create_build('linux', stage_idx: 0)
create_build('mac', stage_idx: 0)
create_build('rspec', stage_idx: 1)
create_build('rubocop', stage_idx: 1)
create_build('deploy', stage_idx: 2)
end
it 'processes a pipeline' do
expect(process_pipeline).to be_truthy
succeed_pending
expect(builds.success.count).to eq(2)
expect(process_pipeline).to be_truthy
succeed_pending
expect(builds.success.count).to eq(4)
expect(process_pipeline).to be_truthy
succeed_pending
expect(builds.success.count).to eq(5)
expect(process_pipeline).to be_falsey
end
it 'does not process pipeline if existing stage is running' do
expect(process_pipeline).to be_truthy
expect(builds.pending.count).to eq(2)
expect(process_pipeline).to be_falsey
expect(builds.pending.count).to eq(2)
end
end
context 'custom stage with first job allowed to fail' do
before do
create_build('clean_job', stage_idx: 0, allow_failure: true)
create_build('test_job', stage_idx: 1, allow_failure: true)
end
it 'automatically triggers a next stage when build finishes' do
expect(process_pipeline).to be_truthy
expect(builds_statuses).to eq ['pending']
fail_running_or_pending
expect(builds_statuses).to eq ['failed', 'pending']
end
end
context 'properly creates builds when optional actions are defined' do
before do
create_build('build', stage_idx: 0)
create_build('test', stage_idx: 1)
create_build('test_failure', stage_idx: 2, when: 'on_failure')
create_build('deploy', stage_idx: 3)
create_build('production', stage_idx: 3, when: 'manual', allow_failure: true)
create_build('cleanup', stage_idx: 4, when: 'always')
create_build('clear cache', stage_idx: 4, when: 'manual', allow_failure: true)
end
context 'when builds are successful' do
it 'properly creates builds' do
2016-09-29 19:41:22 +00:00
expect(process_pipeline).to be_truthy
expect(builds_names).to eq ['build']
expect(builds_statuses).to eq ['pending']
succeed_running_or_pending
expect(builds_names).to eq ['build', 'test']
expect(builds_statuses).to eq ['success', 'pending']
succeed_running_or_pending
expect(builds_names).to eq ['build', 'test', 'deploy']
expect(builds_statuses).to eq ['success', 'success', 'pending']
succeed_running_or_pending
expect(builds_names).to eq ['build', 'test', 'deploy', 'cleanup']
expect(builds_statuses).to eq ['success', 'success', 'success', 'pending']
succeed_running_or_pending
expect(builds_statuses).to eq ['success', 'success', 'success', 'success']
expect(pipeline.reload.status).to eq 'success'
end
end
context 'when test job fails' do
it 'properly creates builds' do
expect(process_pipeline).to be_truthy
expect(builds_names).to eq ['build']
expect(builds_statuses).to eq ['pending']
succeed_running_or_pending
expect(builds_names).to eq ['build', 'test']
expect(builds_statuses).to eq ['success', 'pending']
fail_running_or_pending
expect(builds_names).to eq ['build', 'test', 'test_failure']
expect(builds_statuses).to eq ['success', 'failed', 'pending']
succeed_running_or_pending
expect(builds_names).to eq ['build', 'test', 'test_failure', 'cleanup']
expect(builds_statuses).to eq ['success', 'failed', 'success', 'pending']
succeed_running_or_pending
expect(builds_statuses).to eq ['success', 'failed', 'success', 'success']
expect(pipeline.reload.status).to eq 'failed'
end
end
context 'when test and test_failure jobs fail' do
it 'properly creates builds' do
expect(process_pipeline).to be_truthy
expect(builds_names).to eq ['build']
expect(builds_statuses).to eq ['pending']
succeed_running_or_pending
expect(builds_names).to eq ['build', 'test']
expect(builds_statuses).to eq ['success', 'pending']
fail_running_or_pending
expect(builds_names).to eq ['build', 'test', 'test_failure']
expect(builds_statuses).to eq ['success', 'failed', 'pending']
fail_running_or_pending
expect(builds_names).to eq ['build', 'test', 'test_failure', 'cleanup']
expect(builds_statuses).to eq ['success', 'failed', 'failed', 'pending']
succeed_running_or_pending
expect(builds_names).to eq ['build', 'test', 'test_failure', 'cleanup']
expect(builds_statuses).to eq ['success', 'failed', 'failed', 'success']
expect(pipeline.reload.status).to eq('failed')
end
end
context 'when deploy job fails' do
it 'properly creates builds' do
expect(process_pipeline).to be_truthy
expect(builds_names).to eq ['build']
expect(builds_statuses).to eq ['pending']
succeed_running_or_pending
expect(builds_names).to eq ['build', 'test']
expect(builds_statuses).to eq ['success', 'pending']
succeed_running_or_pending
expect(builds_names).to eq ['build', 'test', 'deploy']
expect(builds_statuses).to eq ['success', 'success', 'pending']
fail_running_or_pending
expect(builds_names).to eq ['build', 'test', 'deploy', 'cleanup']
expect(builds_statuses).to eq ['success', 'success', 'failed', 'pending']
succeed_running_or_pending
expect(builds_statuses).to eq ['success', 'success', 'failed', 'success']
expect(pipeline.reload.status).to eq('failed')
end
end
context 'when build is canceled in the second stage' do
it 'does not schedule builds after build has been canceled' do
expect(process_pipeline).to be_truthy
expect(builds_names).to eq ['build']
expect(builds_statuses).to eq ['pending']
succeed_running_or_pending
expect(builds.running_or_pending).not_to be_empty
expect(builds_names).to eq ['build', 'test']
expect(builds_statuses).to eq ['success', 'pending']
cancel_running_or_pending
expect(builds.running_or_pending).to be_empty
expect(pipeline.reload.status).to eq 'canceled'
end
end
context 'when listing optional manual actions' do
it 'returns only for skipped builds' do
# currently all builds are created
expect(process_pipeline).to be_truthy
expect(manual_actions).to be_empty
# succeed stage build
succeed_running_or_pending
expect(manual_actions).to be_empty
# succeed stage test
succeed_running_or_pending
expect(manual_actions).to be_one # production
# succeed stage deploy
succeed_running_or_pending
expect(manual_actions).to be_many # production and clear cache
end
end
end
context 'when there are manual/on_failure jobs in earlier stages' do
context 'when first stage has only optional manual actions' do
before do
create_build('build', stage_idx: 0, when: 'manual', allow_failure: true)
create_build('check', stage_idx: 1)
create_build('test', stage_idx: 2)
process_pipeline
end
it 'starts from the second stage' do
expect(all_builds_statuses).to eq %w[skipped pending created]
end
end
context 'when second stage has only optional manual actions' do
before do
create_build('check', stage_idx: 0)
create_build('build', stage_idx: 1, when: 'manual', allow_failure: true)
create_build('test', stage_idx: 2)
process_pipeline
end
it 'skips second stage and continues on third stage' do
expect(all_builds_statuses).to eq(%w[pending created created])
builds.first.success
expect(all_builds_statuses).to eq(%w[success skipped pending])
end
end
context 'when second stage has only on_failure jobs' do
before do
create_build('check', stage_idx: 0)
create_build('build', stage_idx: 1, when: 'on_failure')
create_build('test', stage_idx: 2)
process_pipeline
end
it 'skips second stage and continues on third stage' do
expect(all_builds_statuses).to eq(%w[pending created created])
builds.first.success
expect(all_builds_statuses).to eq(%w[success skipped pending])
end
end
end
context 'when failed build in the middle stage is retried' do
context 'when failed build is the only unsuccessful build in the stage' do
before do
create_build('build:1', stage_idx: 0)
create_build('build:2', stage_idx: 0)
create_build('test:1', stage_idx: 1)
create_build('test:2', stage_idx: 1)
create_build('deploy:1', stage_idx: 2)
create_build('deploy:2', stage_idx: 2)
end
it 'does trigger builds in the next stage' do
expect(process_pipeline).to be_truthy
expect(builds_names).to eq ['build:1', 'build:2']
succeed_running_or_pending
expect(builds_names).to eq ['build:1', 'build:2', 'test:1', 'test:2']
pipeline.builds.find_by(name: 'test:1').success
pipeline.builds.find_by(name: 'test:2').drop
expect(builds_names).to eq ['build:1', 'build:2', 'test:1', 'test:2']
Ci::Build.retry(pipeline.builds.find_by(name: 'test:2'), user).success
expect(builds_names).to eq ['build:1', 'build:2', 'test:1', 'test:2',
'test:2', 'deploy:1', 'deploy:2']
end
end
end
context 'when there are builds that are not created yet' do
let(:pipeline) do
create(:ci_pipeline, config: config)
end
let(:config) do
{ rspec: { stage: 'test', script: 'rspec' },
deploy: { stage: 'deploy', script: 'rsync' } }
end
before do
create_build('linux', stage: 'build', stage_idx: 0)
create_build('mac', stage: 'build', stage_idx: 0)
end
it 'processes the pipeline' do
# Currently we have five builds with state created
#
expect(builds.count).to eq(0)
expect(all_builds.count).to eq(2)
# Process builds service will enqueue builds from the first stage.
#
process_pipeline
expect(builds.count).to eq(2)
expect(all_builds.count).to eq(2)
# When builds succeed we will enqueue remaining builds.
#
# We will have 2 succeeded, 1 pending (from stage test), total 4 (two
# additional build from `.gitlab-ci.yml`).
#
succeed_pending
process_pipeline
expect(builds.success.count).to eq(2)
expect(builds.pending.count).to eq(1)
expect(all_builds.count).to eq(4)
# When pending merge_when_pipeline_succeeds in stage test, we enqueue deploy stage.
#
succeed_pending
process_pipeline
expect(builds.pending.count).to eq(1)
expect(builds.success.count).to eq(3)
expect(all_builds.count).to eq(4)
# When the last one succeeds we have 4 successful builds.
#
succeed_pending
process_pipeline
expect(builds.success.count).to eq(4)
expect(all_builds.count).to eq(4)
end
end
def all_builds
pipeline.builds.order(:stage_idx, :id)
end
def builds
all_builds.where.not(status: [:created, :skipped])
end
def builds_names
builds.pluck(:name)
end
def builds_statuses
builds.pluck(:status)
end
def all_builds_statuses
all_builds.pluck(:status)
end
def process_pipeline
described_class.new(pipeline.project, user).execute(pipeline)
end
def succeed_pending
builds.pending.update_all(status: 'success')
end
def succeed_running_or_pending
pipeline.builds.running_or_pending.each(&:success)
end
def fail_running_or_pending
pipeline.builds.running_or_pending.each(&:drop)
end
def cancel_running_or_pending
pipeline.builds.running_or_pending.each(&:cancel)
end
2017-02-22 17:51:46 +00:00
delegate :manual_actions, to: :pipeline
def create_build(name, **opts)
create(:ci_build, :created, pipeline: pipeline, name: name, **opts)
end
end