diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-19 12:08:42 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-19 12:08:42 +0300 |
commit | b76ae638462ab0f673e5915986070518dd3f9ad3 (patch) | |
tree | bdab0533383b52873be0ec0eb4d3c66598ff8b91 /spec/services/ci | |
parent | 434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff) |
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'spec/services/ci')
37 files changed, 700 insertions, 538 deletions
diff --git a/spec/services/ci/after_requeue_job_service_spec.rb b/spec/services/ci/after_requeue_job_service_spec.rb index f8c49060ce0..df5ddcafb37 100644 --- a/spec/services/ci/after_requeue_job_service_spec.rb +++ b/spec/services/ci/after_requeue_job_service_spec.rb @@ -8,37 +8,41 @@ RSpec.describe Ci::AfterRequeueJobService do let(:pipeline) { create(:ci_pipeline, project: project) } + let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 0, name: 'build') } let!(:test1) { create(:ci_build, :success, pipeline: pipeline, stage_idx: 1) } let!(:test2) { create(:ci_build, :skipped, pipeline: pipeline, stage_idx: 1) } - let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 0, name: 'build') } + let!(:test3) { create(:ci_build, :skipped, :dependent, pipeline: pipeline, stage_idx: 1, needed: build) } + let!(:deploy) { create(:ci_build, :skipped, :dependent, pipeline: pipeline, stage_idx: 2, needed: test3) } subject(:execute_service) { described_class.new(project, user).execute(build) } it 'marks subsequent skipped jobs as processable' do expect(test1.reload).to be_success expect(test2.reload).to be_skipped + expect(test3.reload).to be_skipped + expect(deploy.reload).to be_skipped execute_service expect(test1.reload).to be_success expect(test2.reload).to be_created + expect(test3.reload).to be_created + expect(deploy.reload).to be_created end context 'when there is a job need from the same stage' do - let!(:test3) do + let!(:test4) do create(:ci_build, :skipped, + :dependent, pipeline: pipeline, stage_idx: 0, - scheduling_type: :dag) - end - - before do - create(:ci_build_need, build: test3, name: 'build') + scheduling_type: :dag, + needed: build) end it 'marks subsequent skipped jobs as processable' do - expect { execute_service }.to change { test3.reload.status }.from('skipped').to('created') + expect { execute_service }.to change { test4.reload.status }.from('skipped').to('created') end context 'with ci_same_stage_job_needs FF disabled' do @@ -47,7 +51,7 @@ RSpec.describe Ci::AfterRequeueJobService do end it 'does nothing with the build' do - expect { execute_service }.not_to change { test3.reload.status } + expect { execute_service }.not_to change { test4.reload.status } end end end diff --git a/spec/services/ci/append_build_trace_service_spec.rb b/spec/services/ci/append_build_trace_service_spec.rb index b251f00158f..487dbacbe90 100644 --- a/spec/services/ci/append_build_trace_service_spec.rb +++ b/spec/services/ci/append_build_trace_service_spec.rb @@ -75,25 +75,5 @@ RSpec.describe Ci::AppendBuildTraceService do expect(build.reload).to be_failed expect(build.failure_reason).to eq 'trace_size_exceeded' end - - context 'when the feature flag is disabled' do - before do - stub_feature_flags(ci_jobs_trace_size_limit: false) - end - - it 'appends trace chunks' do - stream_size = 1.25.megabytes - body_data = 'x' * stream_size - content_range = "0-#{stream_size}" - - result = described_class - .new(build, content_range: content_range) - .execute(body_data) - - expect(result.status).to eq 202 - expect(result.stream_size).to eq stream_size - expect(build.trace_chunks.count).to eq 10 - end - end end end diff --git a/spec/services/ci/build_cancel_service_spec.rb b/spec/services/ci/build_cancel_service_spec.rb new file mode 100644 index 00000000000..fe036dc1368 --- /dev/null +++ b/spec/services/ci/build_cancel_service_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::BuildCancelService do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + + describe '#execute' do + subject(:execute) { described_class.new(build, user).execute } + + context 'when user is authorized to cancel the build' do + before do + project.add_maintainer(user) + end + + context 'when build is cancelable' do + let!(:build) { create(:ci_build, :cancelable, pipeline: pipeline) } + + it 'transits build to canceled', :aggregate_failures do + response = execute + + expect(response).to be_success + expect(response.payload.reload).to be_canceled + end + end + + context 'when build is not cancelable' do + let!(:build) { create(:ci_build, :canceled, pipeline: pipeline) } + + it 'responds with unprocessable entity', :aggregate_failures do + response = execute + + expect(response).to be_error + expect(response.http_status).to eq(:unprocessable_entity) + end + end + end + + context 'when user is not authorized to cancel the build' do + let!(:build) { create(:ci_build, :cancelable, pipeline: pipeline) } + + it 'responds with forbidden', :aggregate_failures do + response = execute + + expect(response).to be_error + expect(response.http_status).to eq(:forbidden) + end + end + end +end diff --git a/spec/services/ci/build_unschedule_service_spec.rb b/spec/services/ci/build_unschedule_service_spec.rb new file mode 100644 index 00000000000..d784d9a2754 --- /dev/null +++ b/spec/services/ci/build_unschedule_service_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::BuildUnscheduleService do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + + describe '#execute' do + subject(:execute) { described_class.new(build, user).execute } + + context 'when user is authorized to unschedule the build' do + before do + project.add_maintainer(user) + end + + context 'when build is scheduled' do + let!(:build) { create(:ci_build, :scheduled, pipeline: pipeline) } + + it 'transits build to manual' do + response = execute + + expect(response).to be_success + expect(response.payload.reload).to be_manual + end + end + + context 'when build is not scheduled' do + let!(:build) { create(:ci_build, pipeline: pipeline) } + + it 'responds with unprocessable entity', :aggregate_failures do + response = execute + + expect(response).to be_error + expect(response.http_status).to eq(:unprocessable_entity) + end + end + end + + context 'when user is not authorized to unschedule the build' do + let!(:build) { create(:ci_build, :scheduled, pipeline: pipeline) } + + it 'responds with forbidden', :aggregate_failures do + response = execute + + expect(response).to be_error + expect(response.http_status).to eq(:forbidden) + end + end + end +end diff --git a/spec/services/ci/create_downstream_pipeline_service_spec.rb b/spec/services/ci/create_downstream_pipeline_service_spec.rb index 18bd59a17f0..2237fd76d07 100644 --- a/spec/services/ci/create_downstream_pipeline_service_spec.rb +++ b/spec/services/ci/create_downstream_pipeline_service_spec.rb @@ -624,6 +624,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do let(:primary_pipeline) do Ci::CreatePipelineService.new(upstream_project, upstream_project.owner, { ref: 'master' }) .execute(:push, save_on_errors: false) + .payload end let(:bridge) { primary_pipeline.processables.find_by(name: 'bridge-job') } diff --git a/spec/services/ci/create_pipeline_service/cache_spec.rb b/spec/services/ci/create_pipeline_service/cache_spec.rb index f9767a794db..f5f162e4578 100644 --- a/spec/services/ci/create_pipeline_service/cache_spec.rb +++ b/spec/services/ci/create_pipeline_service/cache_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Ci::CreatePipelineService do let(:ref) { 'refs/heads/master' } let(:source) { :push } let(:service) { described_class.new(project, user, { ref: ref }) } - let(:pipeline) { service.execute(source) } + let(:pipeline) { service.execute(source).payload } let(:job) { pipeline.builds.find_by(name: 'job') } before do diff --git a/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb b/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb index a42770aae20..c69c91593ae 100644 --- a/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb +++ b/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Ci::CreatePipelineService do let(:ref) { 'refs/heads/master' } let(:source) { :push } let(:service) { described_class.new(project, user, { ref: ref }) } - let(:pipeline) { service.execute(source) } + let(:pipeline) { service.execute(source).payload } before do stub_ci_pipeline_yaml_file(config) diff --git a/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb b/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb index b3b8e34dd8e..e1d60ed57ef 100644 --- a/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb +++ b/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do let_it_be(:group) { create(:group, name: 'my-organization') } let(:upstream_project) { create(:project, :repository, name: 'upstream', group: group) } - let(:downstram_project) { create(:project, :repository, name: 'downstream', group: group) } + let(:downstream_project) { create(:project, :repository, name: 'downstream', group: group) } let(:user) { create(:user) } let(:service) do @@ -15,9 +15,9 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do before do upstream_project.add_developer(user) - downstram_project.add_developer(user) + downstream_project.add_developer(user) create_gitlab_ci_yml(upstream_project, upstream_config) - create_gitlab_ci_yml(downstram_project, downstream_config) + create_gitlab_ci_yml(downstream_project, downstream_config) end context 'with resource group', :aggregate_failures do @@ -79,7 +79,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do end def create_pipeline! - service.execute(:push) + service.execute(:push).payload end def create_gitlab_ci_yml(project, content) diff --git a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb index 42c3f52541b..f150a4f8b51 100644 --- a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb +++ b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb @@ -11,7 +11,7 @@ RSpec.describe Ci::CreatePipelineService do let(:upstream_pipeline) { create(:ci_pipeline, project: project) } let(:bridge) { create(:ci_bridge, pipeline: upstream_pipeline) } - subject { service.execute(:push, bridge: bridge) } + subject { service.execute(:push, bridge: bridge).payload } context 'custom config content' do let(:bridge) do diff --git a/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb b/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb index 5dceb9f57f0..026111d59f1 100644 --- a/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb +++ b/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Ci::CreatePipelineService do let(:ref) { 'refs/heads/master' } let(:source) { :push } let(:service) { described_class.new(project, user, { ref: ref }) } - let(:pipeline) { service.execute(source) } + let(:pipeline) { service.execute(source).payload } before do stub_ci_pipeline_yaml_file(config) diff --git a/spec/services/ci/create_pipeline_service/dry_run_spec.rb b/spec/services/ci/create_pipeline_service/dry_run_spec.rb index 01df7772eef..ae43c63b516 100644 --- a/spec/services/ci/create_pipeline_service/dry_run_spec.rb +++ b/spec/services/ci/create_pipeline_service/dry_run_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Ci::CreatePipelineService do let(:ref) { 'refs/heads/master' } let(:service) { described_class.new(project, user, { ref: ref }) } - subject { service.execute(:push, dry_run: true) } + subject { service.execute(:push, dry_run: true).payload } before do stub_ci_pipeline_yaml_file(config) diff --git a/spec/services/ci/create_pipeline_service/environment_spec.rb b/spec/services/ci/create_pipeline_service/environment_spec.rb index e77591298ad..43b5220334c 100644 --- a/spec/services/ci/create_pipeline_service/environment_spec.rb +++ b/spec/services/ci/create_pipeline_service/environment_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Ci::CreatePipelineService do end describe '#execute' do - subject { service.execute(:push) } + subject { service.execute(:push).payload } context 'with deployment tier' do before do diff --git a/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb b/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb index df881c1ac8f..9add096d782 100644 --- a/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb +++ b/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Ci::CreatePipelineService do let_it_be(:user) { create(:user) } let(:service) { described_class.new(project, user, ref: 'master') } - let(:pipeline) { service.execute(:push) } + let(:pipeline) { service.execute(:push).payload } let(:job) { pipeline.builds.find_by(name: 'job') } before do diff --git a/spec/services/ci/create_pipeline_service/include_spec.rb b/spec/services/ci/create_pipeline_service/include_spec.rb new file mode 100644 index 00000000000..46271ee36c0 --- /dev/null +++ b/spec/services/ci/create_pipeline_service/include_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::CreatePipelineService do + context 'include:' do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { project.owner } + + let(:ref) { 'refs/heads/master' } + let(:source) { :push } + let(:service) { described_class.new(project, user, { ref: ref }) } + let(:pipeline) { service.execute(source).payload } + + let(:file_location) { 'spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml' } + + before do + allow(project.repository) + .to receive(:blob_data_at).with(project.commit.id, '.gitlab-ci.yml') + .and_return(config) + + allow(project.repository) + .to receive(:blob_data_at).with(project.commit.id, file_location) + .and_return(File.read(Rails.root.join(file_location))) + end + + context 'with a local file' do + let(:config) do + <<~EOY + include: #{file_location} + job: + script: exit 0 + EOY + end + + it 'includes the job in the file' do + expect(pipeline).to be_created_successfully + expect(pipeline.processables.pluck(:name)).to contain_exactly('job', 'rspec') + end + end + + context 'with a local file with rules' do + let(:config) do + <<~EOY + include: + - local: #{file_location} + rules: + - if: $CI_PROJECT_ID == "#{project_id}" + job: + script: exit 0 + EOY + end + + context 'when the rules matches' do + let(:project_id) { project.id } + + it 'includes the job in the file' do + expect(pipeline).to be_created_successfully + expect(pipeline.processables.pluck(:name)).to contain_exactly('job', 'rspec') + end + + context 'when the FF ci_include_rules is disabled' do + before do + stub_feature_flags(ci_include_rules: false) + end + + it 'includes the job in the file' do + expect(pipeline).to be_created_successfully + expect(pipeline.processables.pluck(:name)).to contain_exactly('job', 'rspec') + end + end + end + + context 'when the rules does not match' do + let(:project_id) { non_existing_record_id } + + it 'does not include the job in the file' do + expect(pipeline).to be_created_successfully + expect(pipeline.processables.pluck(:name)).to contain_exactly('job') + end + + context 'when the FF ci_include_rules is disabled' do + before do + stub_feature_flags(ci_include_rules: false) + end + + it 'includes the job in the file' do + expect(pipeline).to be_created_successfully + expect(pipeline.processables.pluck(:name)).to contain_exactly('job', 'rspec') + end + end + end + end + end +end diff --git a/spec/services/ci/create_pipeline_service/merge_requests_spec.rb b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb index e5347faed6a..a1f85faa69f 100644 --- a/spec/services/ci/create_pipeline_service/merge_requests_spec.rb +++ b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Ci::CreatePipelineService do let(:ref) { 'refs/heads/feature' } let(:source) { :push } let(:service) { described_class.new(project, user, { ref: ref }) } - let(:pipeline) { service.execute(source) } + let(:pipeline) { service.execute(source).payload } before do stub_ci_pipeline_yaml_file <<-EOS diff --git a/spec/services/ci/create_pipeline_service/needs_spec.rb b/spec/services/ci/create_pipeline_service/needs_spec.rb index d096db10d0b..9070d86f7f6 100644 --- a/spec/services/ci/create_pipeline_service/needs_spec.rb +++ b/spec/services/ci/create_pipeline_service/needs_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Ci::CreatePipelineService do let(:ref) { 'refs/heads/master' } let(:source) { :push } let(:service) { described_class.new(project, user, { ref: ref }) } - let(:pipeline) { service.execute(source) } + let(:pipeline) { service.execute(source).payload } before do stub_ci_pipeline_yaml_file(config) diff --git a/spec/services/ci/create_pipeline_service/parallel_spec.rb b/spec/services/ci/create_pipeline_service/parallel_spec.rb index 5e34a67d376..6b455bf4874 100644 --- a/spec/services/ci/create_pipeline_service/parallel_spec.rb +++ b/spec/services/ci/create_pipeline_service/parallel_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Ci::CreatePipelineService do let_it_be(:user) { project.owner } let(:service) { described_class.new(project, user, { ref: 'master' }) } - let(:pipeline) { service.execute(:push) } + let(:pipeline) { service.execute(:push).payload } before do stub_ci_pipeline_yaml_file(config) diff --git a/spec/services/ci/create_pipeline_service/parameter_content_spec.rb b/spec/services/ci/create_pipeline_service/parameter_content_spec.rb index 94500a550c6..761504ffb58 100644 --- a/spec/services/ci/create_pipeline_service/parameter_content_spec.rb +++ b/spec/services/ci/create_pipeline_service/parameter_content_spec.rb @@ -30,7 +30,7 @@ RSpec.describe Ci::CreatePipelineService do describe '#execute' do context 'when source is a dangling build' do - subject { service.execute(:ondemand_dast_scan, content: content) } + subject { service.execute(:ondemand_dast_scan, content: content).payload } context 'parameter config content' do it 'creates a pipeline' do diff --git a/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb b/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb index 7a6535ed3fa..6eb1315fff4 100644 --- a/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb +++ b/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb @@ -369,6 +369,6 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do end def create_pipeline! - service.execute(:push) + service.execute(:push).payload end end diff --git a/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb b/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb index c84d9a53973..5e34eeb99db 100644 --- a/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb +++ b/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Ci::CreatePipelineService do let(:source) { :push } let(:service) { described_class.new(project, user, { ref: ref }) } - let(:pipeline) { service.execute(source) } + let(:pipeline) { service.execute(source).payload } let(:config) do <<~YAML diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb index acdf38bbc13..d0915f099de 100644 --- a/spec/services/ci/create_pipeline_service/rules_spec.rb +++ b/spec/services/ci/create_pipeline_service/rules_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Ci::CreatePipelineService do let(:ref) { 'refs/heads/master' } let(:source) { :push } let(:service) { described_class.new(project, user, { ref: ref }) } - let(:pipeline) { service.execute(source) } + let(:pipeline) { service.execute(source).payload } let(:build_names) { pipeline.builds.pluck(:name) } context 'job:rules' do diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 56bfeda3bff..2fdb0ed3c0d 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -19,7 +19,6 @@ RSpec.describe Ci::CreatePipelineService do def execute_service( source: :push, after: project.commit.id, - message: 'Message', ref: ref_name, trigger_request: nil, variables_attributes: nil, @@ -32,7 +31,6 @@ RSpec.describe Ci::CreatePipelineService do params = { ref: ref, before: '00000000', after: after, - commits: [{ message: message }], variables_attributes: variables_attributes, push_options: push_options, source_sha: source_sha, @@ -49,12 +47,16 @@ RSpec.describe Ci::CreatePipelineService do # rubocop:enable Metrics/ParameterLists context 'valid params' do - let(:pipeline) { execute_service } + let(:pipeline) { execute_service.payload } let(:pipeline_on_previous_commit) do execute_service( after: previous_commit_sha_from_ref('master') - ) + ).payload + end + + it 'responds with success' do + expect(execute_service).to be_success end it 'creates a pipeline' do @@ -128,7 +130,7 @@ RSpec.describe Ci::CreatePipelineService do merge_request_1 merge_request_2 - head_pipeline = execute_service(ref: 'feature', after: nil) + head_pipeline = execute_service(ref: 'feature', after: nil).payload expect(merge_request_1.reload.head_pipeline).to eq(head_pipeline) expect(merge_request_2.reload.head_pipeline).to eq(head_pipeline) @@ -157,7 +159,7 @@ RSpec.describe Ci::CreatePipelineService do target_branch: "branch_1", source_project: project) - head_pipeline = execute_service + head_pipeline = execute_service.payload expect(merge_request.reload.head_pipeline).not_to eq(head_pipeline) end @@ -178,7 +180,7 @@ RSpec.describe Ci::CreatePipelineService do source_project: project, target_project: target_project) - head_pipeline = execute_service(ref: 'feature', after: nil) + head_pipeline = execute_service(ref: 'feature', after: nil).payload expect(merge_request.reload.head_pipeline).to eq(head_pipeline) end @@ -209,7 +211,7 @@ RSpec.describe Ci::CreatePipelineService do target_branch: 'feature', source_project: project) - head_pipeline = execute_service + head_pipeline = execute_service.payload expect(head_pipeline).to be_persisted expect(head_pipeline.yaml_errors).to be_present @@ -230,7 +232,7 @@ RSpec.describe Ci::CreatePipelineService do target_branch: 'feature', source_project: project) - head_pipeline = execute_service + head_pipeline = execute_service.payload expect(head_pipeline).to be_skipped expect(head_pipeline).to be_persisted @@ -260,7 +262,7 @@ RSpec.describe Ci::CreatePipelineService do it 'cancels running outdated pipelines', :sidekiq_inline do pipeline_on_previous_commit.reload.run - head_pipeline = execute_service + head_pipeline = execute_service.payload expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'canceled', auto_canceled_by_id: head_pipeline.id) end @@ -276,7 +278,8 @@ RSpec.describe Ci::CreatePipelineService do new_pipeline = execute_service( ref: 'refs/heads/feature', after: previous_commit_sha_from_ref('feature') - ) + ).payload + pipeline expect(new_pipeline.reload).to have_attributes(status: 'created', auto_canceled_by_id: nil) @@ -290,7 +293,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'is cancelable' do - pipeline = execute_service + pipeline = execute_service.payload expect(pipeline.builds.find_by(name: 'rspec').interruptible).to be_nil end @@ -303,7 +306,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'is cancelable' do - pipeline = execute_service + pipeline = execute_service.payload expect(pipeline.builds.find_by(name: 'rspec').interruptible).to be_truthy end @@ -316,7 +319,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'is not cancelable' do - pipeline = execute_service + pipeline = execute_service.payload expect(pipeline.builds.find_by(name: 'rspec').interruptible).to be_falsy end @@ -476,21 +479,25 @@ RSpec.describe Ci::CreatePipelineService do context "skip tag if there is no build for it" do it "creates commit if there is appropriate job" do - expect(execute_service).to be_persisted + expect(execute_service.payload).to be_persisted end it "creates commit if there is no appropriate job but deploy job has right ref setting" do config = YAML.dump({ deploy: { script: "ls", only: ["master"] } }) stub_ci_pipeline_yaml_file(config) - expect(execute_service).to be_persisted + expect(execute_service.payload).to be_persisted end end - it 'skips creating pipeline for refs without .gitlab-ci.yml' do + it 'skips creating pipeline for refs without .gitlab-ci.yml', :aggregate_failures do stub_ci_pipeline_yaml_file(nil) - expect(execute_service).not_to be_persisted + response = execute_service + + expect(response).to be_error + expect(response.message).to eq('Missing CI config file') + expect(response.payload).not_to be_persisted expect(Ci::Pipeline.count).to eq(0) expect(Namespaces::OnboardingPipelineCreatedWorker).not_to receive(:perform_async) end @@ -499,7 +506,7 @@ RSpec.describe Ci::CreatePipelineService do it 'creates failed pipeline' do stub_ci_pipeline_yaml_file(ci_yaml) - pipeline = execute_service(message: message) + pipeline = execute_service.payload expect(pipeline).to be_persisted expect(pipeline.builds.any?).to be false @@ -516,7 +523,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'pull it from the repository' do - pipeline = execute_service + pipeline = execute_service.payload expect(pipeline).to be_repository_source expect(pipeline.builds.map(&:name)).to eq ['rspec'] end @@ -530,7 +537,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'pull it from Auto-DevOps' do - pipeline = execute_service + pipeline = execute_service.payload expect(pipeline).to be_auto_devops_source expect(pipeline.builds.map(&:name)).to match_array(%w[brakeman-sast build code_quality eslint-sast secret_detection semgrep-sast test]) end @@ -541,11 +548,12 @@ RSpec.describe Ci::CreatePipelineService do stub_ci_pipeline_yaml_file(nil) end - it 'attaches errors to the pipeline' do - pipeline = execute_service + it 'responds with error message', :aggregate_failures do + response = execute_service - expect(pipeline.errors.full_messages).to eq ['Missing CI config file'] - expect(pipeline).not_to be_persisted + expect(response).to be_error + expect(response.message).to eq('Missing CI config file') + expect(response.payload).not_to be_persisted end end @@ -556,7 +564,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'saves error in pipeline' do - pipeline = execute_service + pipeline = execute_service.payload expect(pipeline.yaml_errors).to include('Undefined error') end @@ -648,7 +656,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'saves error in pipeline' do - pipeline = execute_service + pipeline = execute_service.payload expect(pipeline.yaml_errors).to include('Undefined error') end @@ -661,26 +669,18 @@ RSpec.describe Ci::CreatePipelineService do end context 'when commit contains a [ci skip] directive' do - let(:message) { "some message[ci skip]" } - - ci_messages = [ - "some message[ci skip]", - "some message[skip ci]", - "some message[CI SKIP]", - "some message[SKIP CI]", - "some message[ci_skip]", - "some message[skip_ci]", - "some message[ci-skip]", - "some message[skip-ci]" - ] + shared_examples 'creating a pipeline' do + it 'does not skip pipeline creation' do + pipeline = execute_service.payload - before do - allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message } + expect(pipeline).to be_persisted + expect(pipeline.builds.first.name).to eq("rspec") + end end - ci_messages.each do |ci_message| - it "skips builds creation if the commit message is #{ci_message}" do - pipeline = execute_service(message: ci_message) + shared_examples 'skipping a pipeline' do + it 'skips pipeline creation' do + pipeline = execute_service.payload expect(pipeline).to be_persisted expect(pipeline.builds.any?).to be false @@ -688,14 +688,26 @@ RSpec.describe Ci::CreatePipelineService do end end - shared_examples 'creating a pipeline' do - it 'does not skip pipeline creation' do - allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { commit_message } + before do + allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { commit_message } + end + + skip_commit_messages = [ + "some message[ci skip]", + "some message[skip ci]", + "some message[CI SKIP]", + "some message[SKIP CI]", + "some message[ci_skip]", + "some message[skip_ci]", + "some message[ci-skip]", + "some message[skip-ci]" + ] - pipeline = execute_service(message: commit_message) + skip_commit_messages.each do |skip_commit_message| + context "when the commit message is #{skip_commit_message}" do + let(:commit_message) { skip_commit_message } - expect(pipeline).to be_persisted - expect(pipeline.builds.first.name).to eq("rspec") + it_behaves_like 'skipping a pipeline' end end @@ -712,9 +724,14 @@ RSpec.describe Ci::CreatePipelineService do end context 'when there is [ci skip] tag in commit message and yaml is invalid' do + let(:commit_message) { 'some message [ci skip]' } let(:ci_yaml) { 'invalid: file: fiile' } - it_behaves_like 'a failed pipeline' + before do + stub_ci_pipeline_yaml_file(ci_yaml) + end + + it_behaves_like 'skipping a pipeline' end end @@ -724,7 +741,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'creates a pipline in the skipped state' do - pipeline = execute_service(push_options: push_options) + pipeline = execute_service(push_options: push_options).payload # TODO: DRY these up with "skips builds creation if the commit message" expect(pipeline).to be_persisted @@ -739,10 +756,12 @@ RSpec.describe Ci::CreatePipelineService do stub_ci_pipeline_yaml_file(config) end - it 'does not create a new pipeline' do + it 'does not create a new pipeline', :aggregate_failures do result = execute_service - expect(result).not_to be_persisted + expect(result).to be_error + expect(result.message).to eq('No stages / jobs for this pipeline.') + expect(result.payload).not_to be_persisted expect(Ci::Build.all).to be_empty expect(Ci::Pipeline.count).to eq(0) end @@ -757,10 +776,11 @@ RSpec.describe Ci::CreatePipelineService do .and_call_original end - it 'rewinds iid' do + it 'rewinds iid', :aggregate_failures do result = execute_service - expect(result).not_to be_persisted + expect(result).to be_error + expect(result.payload).not_to be_persisted expect(internal_id.last_value).to eq(0) end end @@ -773,7 +793,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'does not create a new pipeline', :sidekiq_inline do - result = execute_service + result = execute_service.payload expect(result).to be_persisted expect(result.manual_actions).not_to be_empty @@ -793,7 +813,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'creates the environment with tags' do - result = execute_service + result = execute_service.payload expect(result).to be_persisted expect(Environment.find_by(name: "review/master")).to be_present @@ -815,7 +835,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'creates the environment with auto stop in' do - result = execute_service + result = execute_service.payload expect(result).to be_persisted expect(result.builds.first.options[:environment][:auto_stop_in]).to eq('1 day') @@ -835,7 +855,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'skipps persisted variables in environment name' do - result = execute_service + result = execute_service.payload expect(result).to be_persisted expect(Environment.find_by(name: "review/id1/id2")).to be_present @@ -860,7 +880,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'stores the requested namespace' do - result = execute_service + result = execute_service.payload build = result.builds.first expect(result).to be_persisted @@ -876,7 +896,7 @@ RSpec.describe Ci::CreatePipelineService do it 'does not create an environment' do expect do - result = execute_service + result = execute_service.payload expect(result).to be_persisted end.not_to change { Environment.count } @@ -896,7 +916,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'creates a pipeline with the environment' do - result = execute_service + result = execute_service.payload expect(result).to be_persisted expect(Environment.find_by(name: 'production')).to be_present @@ -906,7 +926,7 @@ RSpec.describe Ci::CreatePipelineService do end context 'when builds with auto-retries are configured' do - let(:pipeline) { execute_service } + let(:pipeline) { execute_service.payload } let(:rspec_job) { pipeline.builds.find_by(name: 'rspec') } before do @@ -946,7 +966,7 @@ RSpec.describe Ci::CreatePipelineService do let(:resource_group_key) { 'iOS' } it 'persists the association correctly' do - result = execute_service + result = execute_service.payload deploy_job = result.builds.find_by_name!(:test) resource_group = project.resource_groups.find_by_key!(resource_group_key) @@ -962,7 +982,7 @@ RSpec.describe Ci::CreatePipelineService do let(:resource_group_key) { '$CI_COMMIT_REF_NAME-$CI_JOB_NAME' } it 'interpolates the variables into the key correctly' do - result = execute_service + result = execute_service.payload expect(result).to be_persisted expect(project.resource_groups.exists?(key: 'master-test')).to eq(true) @@ -979,7 +999,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'correctly creates builds with custom timeout value configured' do - pipeline = execute_service + pipeline = execute_service.payload expect(pipeline).to be_persisted expect(pipeline.builds.find_by(name: 'rspec').options[:job_timeout]).to eq 123 @@ -994,7 +1014,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'is valid config' do - pipeline = execute_service + pipeline = execute_service.payload build = pipeline.builds.first expect(pipeline).to be_kind_of(Ci::Pipeline) expect(pipeline).to be_valid @@ -1059,14 +1079,14 @@ RSpec.describe Ci::CreatePipelineService do project.add_developer(user) end - it 'does not create a pipeline' do - expect(execute_service).not_to be_persisted + it 'does not create a pipeline', :aggregate_failures do + expect(execute_service.payload).not_to be_persisted expect(Ci::Pipeline.count).to eq(0) end end context 'when user is maintainer' do - let(:pipeline) { execute_service } + let(:pipeline) { execute_service.payload } before do project.add_maintainer(user) @@ -1083,9 +1103,11 @@ RSpec.describe Ci::CreatePipelineService do let(:user) {} let(:trigger_request) { create(:ci_trigger_request) } - it 'does not create a pipeline' do - expect(execute_service(trigger_request: trigger_request)) - .not_to be_persisted + it 'does not create a pipeline', :aggregate_failures do + response = execute_service(trigger_request: trigger_request) + + expect(response).to be_error + expect(response.payload).not_to be_persisted expect(Ci::Pipeline.count).to eq(0) end end @@ -1099,9 +1121,11 @@ RSpec.describe Ci::CreatePipelineService do project.add_developer(user) end - it 'does not create a pipeline' do - expect(execute_service(trigger_request: trigger_request)) - .not_to be_persisted + it 'does not create a pipeline', :aggregate_failures do + response = execute_service(trigger_request: trigger_request) + + expect(response).to be_error + expect(response.payload).not_to be_persisted expect(Ci::Pipeline.count).to eq(0) end end @@ -1116,7 +1140,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'creates a pipeline' do - expect(execute_service(trigger_request: trigger_request)) + expect(execute_service(trigger_request: trigger_request).payload) .to be_persisted expect(Ci::Pipeline.count).to eq(1) end @@ -1150,7 +1174,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'creates a tagged pipeline' do - pipeline = execute_service(ref: 'v1.0.0') + pipeline = execute_service(ref: 'v1.0.0').payload expect(pipeline.tag?).to be true end @@ -1161,7 +1185,7 @@ RSpec.describe Ci::CreatePipelineService do let(:ref_name) { 'refs/heads/nonexistant-branch' } - let(:pipeline) { execute_service } + let(:pipeline) { execute_service.payload } it 'does not create the pipeline' do expect(pipeline).not_to be_created_successfully @@ -1185,7 +1209,7 @@ RSpec.describe Ci::CreatePipelineService do # v1.1.0 is on the test repo as branch and tag let(:ref_name) { 'refs/heads/v1.1.0' } - let(:pipeline) { execute_service } + let(:pipeline) { execute_service.payload } it 'creates the pipeline for the branch' do expect(pipeline).to be_created_successfully @@ -1200,7 +1224,7 @@ RSpec.describe Ci::CreatePipelineService do # v1.1.0 is on the test repo as branch and tag let(:ref_name) { 'refs/tags/v1.1.0' } - let(:pipeline) { execute_service } + let(:pipeline) { execute_service.payload } it 'creates the pipeline for the tag' do expect(pipeline).to be_created_successfully @@ -1215,7 +1239,7 @@ RSpec.describe Ci::CreatePipelineService do # v1.1.0 is on the test repo as branch and tag let(:ref_name) { 'v1.1.0' } - let(:pipeline) { execute_service } + let(:pipeline) { execute_service.payload } it 'does not create the pipeline' do expect(pipeline).not_to be_created_successfully @@ -1229,16 +1253,16 @@ RSpec.describe Ci::CreatePipelineService do { key: 'second', secret_value: 'second_world' }] end - subject { execute_service(variables_attributes: variables_attributes) } + subject(:pipeline) { execute_service(variables_attributes: variables_attributes).payload } it 'creates a pipeline with specified variables' do - expect(subject.variables.map { |var| var.slice(:key, :secret_value) }) + expect(pipeline.variables.map { |var| var.slice(:key, :secret_value) }) .to eq variables_attributes.map(&:with_indifferent_access) end end context 'when pipeline has a job with environment' do - let(:pipeline) { execute_service } + let(:pipeline) { execute_service.payload } before do stub_ci_pipeline_yaml_file(YAML.dump(config)) @@ -1286,7 +1310,7 @@ RSpec.describe Ci::CreatePipelineService do end describe 'Pipeline for external pull requests' do - let(:pipeline) do + let(:response) do execute_service(source: source, external_pull_request: pull_request, ref: ref_name, @@ -1294,6 +1318,8 @@ RSpec.describe Ci::CreatePipelineService do target_sha: target_sha) end + let(:pipeline) { response.payload } + before do stub_ci_pipeline_yaml_file(YAML.dump(config)) end @@ -1342,9 +1368,11 @@ RSpec.describe Ci::CreatePipelineService do context 'when ref is tag' do let(:ref_name) { 'refs/tags/v1.1.0' } - it 'does not create an extrnal pull request pipeline' do + it 'does not create an extrnal pull request pipeline', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq('Tag is not included in the list and Failed to build the pipeline!') expect(pipeline).not_to be_persisted - expect(pipeline.errors[:tag]).to eq(["is not included in the list"]) + expect(pipeline.errors[:tag]).to eq(['is not included in the list']) end end @@ -1363,9 +1391,11 @@ RSpec.describe Ci::CreatePipelineService do } end - it 'does not create a detached merge request pipeline' do + it 'does not create a detached merge request pipeline', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq('No stages / jobs for this pipeline.') expect(pipeline).not_to be_persisted - expect(pipeline.errors[:base]).to eq(["No stages / jobs for this pipeline."]) + expect(pipeline.errors[:base]).to eq(['No stages / jobs for this pipeline.']) end end end @@ -1373,7 +1403,9 @@ RSpec.describe Ci::CreatePipelineService do context 'when external pull request is not specified' do let(:pull_request) { nil } - it 'does not create an external pull request pipeline' do + it 'does not create an external pull request pipeline', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq("External pull request can't be blank and Failed to build the pipeline!") expect(pipeline).not_to be_persisted expect(pipeline.errors[:external_pull_request]).to eq(["can't be blank"]) end @@ -1420,11 +1452,11 @@ RSpec.describe Ci::CreatePipelineService do context 'when external pull request is not specified' do let(:pull_request) { nil } - it 'does not create an external pull request pipeline' do + it 'does not create an external pull request pipeline', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq("External pull request can't be blank and Failed to build the pipeline!") expect(pipeline).not_to be_persisted - - expect(pipeline.errors[:base]) - .to eq(['Failed to build the pipeline!']) + expect(pipeline.errors[:base]).to eq(['Failed to build the pipeline!']) end end end @@ -1432,7 +1464,7 @@ RSpec.describe Ci::CreatePipelineService do end describe 'Pipelines for merge requests' do - let(:pipeline) do + let(:response) do execute_service(source: source, merge_request: merge_request, ref: ref_name, @@ -1440,6 +1472,8 @@ RSpec.describe Ci::CreatePipelineService do target_sha: target_sha) end + let(:pipeline) { response.payload } + before do stub_ci_pipeline_yaml_file(YAML.dump(config)) end @@ -1522,9 +1556,11 @@ RSpec.describe Ci::CreatePipelineService do context 'when ref is tag' do let(:ref_name) { 'refs/tags/v1.1.0' } - it 'does not create a merge request pipeline' do + it 'does not create a merge request pipeline', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq('Tag is not included in the list and Failed to build the pipeline!') expect(pipeline).not_to be_persisted - expect(pipeline.errors[:tag]).to eq(["is not included in the list"]) + expect(pipeline.errors[:tag]).to eq(['is not included in the list']) end end @@ -1564,9 +1600,10 @@ RSpec.describe Ci::CreatePipelineService do } end - it 'does not create a detached merge request pipeline' do + it 'does not create a detached merge request pipeline', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq('No stages / jobs for this pipeline.') expect(pipeline).not_to be_persisted - expect(pipeline.errors[:base]).to eq(["No stages / jobs for this pipeline."]) end end end @@ -1599,11 +1636,10 @@ RSpec.describe Ci::CreatePipelineService do target_branch: 'master') end - it 'does not create a detached merge request pipeline' do + it 'does not create a detached merge request pipeline', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq('No stages / jobs for this pipeline.') expect(pipeline).not_to be_persisted - - expect(pipeline.errors[:base]) - .to eq(['No stages / jobs for this pipeline.']) end end end @@ -1628,11 +1664,10 @@ RSpec.describe Ci::CreatePipelineService do target_branch: 'master') end - it 'does not create a detached merge request pipeline' do + it 'does not create a detached merge request pipeline', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq('No stages / jobs for this pipeline.') expect(pipeline).not_to be_persisted - - expect(pipeline.errors[:base]) - .to eq(['No stages / jobs for this pipeline.']) end end end @@ -1659,11 +1694,10 @@ RSpec.describe Ci::CreatePipelineService do target_branch: 'master') end - it 'does not create a detached merge request pipeline' do + it 'does not create a detached merge request pipeline', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq('No stages / jobs for this pipeline.') expect(pipeline).not_to be_persisted - - expect(pipeline.errors[:base]) - .to eq(['No stages / jobs for this pipeline.']) end end end @@ -1688,11 +1722,10 @@ RSpec.describe Ci::CreatePipelineService do target_branch: 'master') end - it 'does not create a detached merge request pipeline' do + it 'does not create a detached merge request pipeline', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq('No stages / jobs for this pipeline.') expect(pipeline).not_to be_persisted - - expect(pipeline.errors[:base]) - .to eq(['No stages / jobs for this pipeline.']) end end end @@ -1733,7 +1766,8 @@ RSpec.describe Ci::CreatePipelineService do end context 'when needs is used' do - let(:pipeline) { execute_service } + let(:response) { execute_service } + let(:pipeline) { response.payload } let(:config) do { @@ -1779,7 +1813,7 @@ RSpec.describe Ci::CreatePipelineService do let(:ref_name) { 'refs/heads/feature' } shared_examples 'has errors' do - it 'contains the expected errors' do + it 'contains the expected errors', :aggregate_failures do expect(pipeline.builds).to be_empty error_message = "'test_a' job needs 'build_a' job, but 'build_a' is not in any previous stage" @@ -1790,9 +1824,12 @@ RSpec.describe Ci::CreatePipelineService do end context 'when save_on_errors is enabled' do - let(:pipeline) { execute_service(save_on_errors: true) } + let(:response) { execute_service(save_on_errors: true) } + let(:pipeline) { response.payload } - it 'does create a pipeline as test_a depends on build_a' do + it 'does create a pipeline as test_a depends on build_a', :aggregate_failures do + expect(response).to be_error + expect(response.message).to eq("'test_a' job needs 'build_a' job, but 'build_a' is not in any previous stage") expect(pipeline).to be_persisted end @@ -1800,9 +1837,11 @@ RSpec.describe Ci::CreatePipelineService do end context 'when save_on_errors is disabled' do - let(:pipeline) { execute_service(save_on_errors: false) } + let(:response) { execute_service(save_on_errors: false) } + let(:pipeline) { response.payload } - it 'does not create a pipeline as test_a depends on build_a' do + it 'does not create a pipeline as test_a depends on build_a', :aggregate_failures do + expect(response).to be_error expect(pipeline).not_to be_persisted end @@ -1822,7 +1861,8 @@ RSpec.describe Ci::CreatePipelineService do context 'when rules are used' do let(:ref_name) { 'refs/heads/master' } - let(:pipeline) { execute_service } + let(:response) { execute_service } + let(:pipeline) { response.payload } let(:build_names) { pipeline.builds.pluck(:name) } let(:regular_job) { find_job('regular-job') } let(:rules_job) { find_job('rules-job') } @@ -2379,8 +2419,9 @@ RSpec.describe Ci::CreatePipelineService do end context 'when inside freeze period' do - it 'does not create the pipeline' do + it 'does not create the pipeline', :aggregate_failures do Timecop.freeze(2020, 4, 10, 23, 1) do + expect(response).to be_error expect(pipeline).not_to be_persisted end end @@ -2410,7 +2451,8 @@ RSpec.describe Ci::CreatePipelineService do context 'with no matches' do let(:ref_name) { 'refs/heads/feature' } - it 'does not create a pipeline' do + it 'does not create a pipeline', :aggregate_failures do + expect(response).to be_error expect(pipeline).not_to be_persisted end end @@ -2418,7 +2460,7 @@ RSpec.describe Ci::CreatePipelineService do context 'with workflow rules with pipeline variables' do let(:pipeline) do - execute_service(variables_attributes: variables_attributes) + execute_service(variables_attributes: variables_attributes).payload end let(:config) do @@ -2446,7 +2488,8 @@ RSpec.describe Ci::CreatePipelineService do context 'with no matches' do let(:variables_attributes) { {} } - it 'does not create a pipeline' do + it 'does not create a pipeline', :aggregate_failures do + expect(response).to be_error expect(pipeline).not_to be_persisted end end @@ -2456,7 +2499,7 @@ RSpec.describe Ci::CreatePipelineService do let(:pipeline) do execute_service do |pipeline| pipeline.variables.build(variables) - end + end.payload end let(:config) do @@ -2514,7 +2557,8 @@ RSpec.describe Ci::CreatePipelineService do context 'with no matches' do let(:variables) { {} } - it 'does not create a pipeline' do + it 'does not create a pipeline', :aggregate_failures do + expect(response).to be_error expect(pipeline).not_to be_persisted end @@ -2542,7 +2586,8 @@ RSpec.describe Ci::CreatePipelineService do EOY end - it 'does not create a pipeline' do + it 'does not create a pipeline', :aggregate_failures do + expect(response).to be_error expect(pipeline).not_to be_persisted end end diff --git a/spec/services/ci/daily_build_group_report_result_service_spec.rb b/spec/services/ci/daily_build_group_report_result_service_spec.rb index e58a5de26a1..32651247adb 100644 --- a/spec/services/ci/daily_build_group_report_result_service_spec.rb +++ b/spec/services/ci/daily_build_group_report_result_service_spec.rb @@ -41,6 +41,17 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do expect(Ci::DailyBuildGroupReportResult.find_by(group_name: 'extra')).to be_nil end + it 'creates a project_ci_feature_usage record for the pipeline project' do + described_class.new.execute(pipeline) + + expect(Projects::CiFeatureUsage.count).to eq(1) + expect(Projects::CiFeatureUsage.first).to have_attributes( + project_id: pipeline.project.id, + feature: 'code_coverage', + default_branch: false + ) + end + context 'when there are multiple builds with the same group name that report coverage' do let!(:test_job_1) { create(:ci_build, pipeline: pipeline, name: 'test 1/2', coverage: 70) } let!(:test_job_2) { create(:ci_build, pipeline: pipeline, name: 'test 2/2', coverage: 80) } @@ -99,6 +110,16 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do data: { 'coverage' => new_karma_job.coverage } ) end + + it 'does not create a new project_ci_feature_usage record for the pipeline project' do + expect { described_class.new.execute(pipeline) }.not_to change { Projects::CiFeatureUsage.count } + + expect(Projects::CiFeatureUsage.first).to have_attributes( + project_id: pipeline.project.id, + feature: 'code_coverage', + default_branch: false + ) + end end context 'when the ID of the pipeline is older than the last_pipeline_id' do @@ -161,6 +182,8 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do it 'does nothing' do expect { described_class.new.execute(new_pipeline) }.not_to raise_error + expect(Ci::DailyBuildGroupReportResult.count).to eq(0) + expect(Projects::CiFeatureUsage.count).to eq(0) end end @@ -178,6 +201,17 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do expect(coverage.default_branch).to be_truthy end end + + it 'creates a project_ci_feature_usage record for the pipeline project for default branch' do + described_class.new.execute(pipeline) + + expect(Projects::CiFeatureUsage.count).to eq(1) + expect(Projects::CiFeatureUsage.first).to have_attributes( + project_id: pipeline.project.id, + feature: 'code_coverage', + default_branch: true + ) + end end context 'when pipeline ref_path is not the project default branch' do diff --git a/spec/services/ci/destroy_pipeline_service_spec.rb b/spec/services/ci/destroy_pipeline_service_spec.rb index 588ff0b1762..6c1c02b2875 100644 --- a/spec/services/ci/destroy_pipeline_service_spec.rb +++ b/spec/services/ci/destroy_pipeline_service_spec.rb @@ -78,18 +78,6 @@ RSpec.describe ::Ci::DestroyPipelineService do subject end - - context 'when cancel_pipelines_prior_to_destroy is disabled' do - before do - stub_feature_flags(cancel_pipelines_prior_to_destroy: false) - end - - it "doesn't cancel the pipeline" do - expect(pipeline).not_to receive(:cancel_running) - - subject - end - end end end diff --git a/spec/services/ci/drop_pipeline_service_spec.rb b/spec/services/ci/drop_pipeline_service_spec.rb index 4adbb99b9e2..c6a118c6083 100644 --- a/spec/services/ci/drop_pipeline_service_spec.rb +++ b/spec/services/ci/drop_pipeline_service_spec.rb @@ -9,8 +9,10 @@ RSpec.describe Ci::DropPipelineService do let!(:cancelable_pipeline) { create(:ci_pipeline, :running, user: user) } let!(:running_build) { create(:ci_build, :running, pipeline: cancelable_pipeline) } + let!(:commit_status_running) { create(:commit_status, :running, pipeline: cancelable_pipeline) } let!(:success_pipeline) { create(:ci_pipeline, :success, user: user) } let!(:success_build) { create(:ci_build, :success, pipeline: success_pipeline) } + let!(:commit_status_success) { create(:commit_status, :success, pipeline: cancelable_pipeline) } describe '#execute_async_for_all' do subject { described_class.new.execute_async_for_all(user.pipelines, failure_reason, user) } diff --git a/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb b/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb index e25dd351bb3..2b310443b37 100644 --- a/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb +++ b/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb @@ -6,14 +6,13 @@ RSpec.describe Ci::ExternalPullRequests::CreatePipelineService do describe '#execute' do let_it_be(:project) { create(:project, :auto_devops, :repository) } let_it_be(:user) { create(:user) } - - let(:pull_request) { create(:external_pull_request, project: project) } + let_it_be_with_reload(:pull_request) { create(:external_pull_request, project: project) } before do project.add_maintainer(user) end - subject { described_class.new(project, user).execute(pull_request) } + subject(:response) { described_class.new(project, user).execute(pull_request) } context 'when pull request is open' do before do @@ -28,17 +27,20 @@ RSpec.describe Ci::ExternalPullRequests::CreatePipelineService do pull_request.update!(source_branch: source_branch.name, source_sha: source_branch.target) end - it 'creates a pipeline for external pull request' do - expect(subject).to be_valid - expect(subject).to be_persisted - expect(subject).to be_external_pull_request_event - expect(subject).to eq(project.ci_pipelines.last) - expect(subject.external_pull_request).to eq(pull_request) - expect(subject.user).to eq(user) - expect(subject.status).to eq('created') - expect(subject.ref).to eq(pull_request.source_branch) - expect(subject.sha).to eq(pull_request.source_sha) - expect(subject.source_sha).to eq(pull_request.source_sha) + it 'creates a pipeline for external pull request', :aggregate_failures do + pipeline = response.payload + + expect(response).to be_success + expect(pipeline).to be_valid + expect(pipeline).to be_persisted + expect(pipeline).to be_external_pull_request_event + expect(pipeline).to eq(project.ci_pipelines.last) + expect(pipeline.external_pull_request).to eq(pull_request) + expect(pipeline.user).to eq(user) + expect(pipeline.status).to eq('created') + expect(pipeline.ref).to eq(pull_request.source_branch) + expect(pipeline.sha).to eq(pull_request.source_sha) + expect(pipeline.source_sha).to eq(pull_request.source_sha) end end @@ -50,10 +52,12 @@ RSpec.describe Ci::ExternalPullRequests::CreatePipelineService do pull_request.update!(source_branch: source_branch.name, source_sha: commit.sha) end - it 'does nothing' do + it 'does nothing', :aggregate_failures do expect(Ci::CreatePipelineService).not_to receive(:new) - expect(subject).to be_nil + expect(response).to be_error + expect(response.message).to eq('The source sha is not the head of the source branch') + expect(response.payload).to be_nil end end end @@ -63,10 +67,12 @@ RSpec.describe Ci::ExternalPullRequests::CreatePipelineService do pull_request.update!(status: :closed) end - it 'does nothing' do + it 'does nothing', :aggregate_failures do expect(Ci::CreatePipelineService).not_to receive(:new) - expect(subject).to be_nil + expect(response).to be_error + expect(response.message).to eq('The pull request is not opened') + expect(response.payload).to be_nil end end end diff --git a/spec/services/ci/extract_sections_from_build_trace_service_spec.rb b/spec/services/ci/extract_sections_from_build_trace_service_spec.rb deleted file mode 100644 index c6ffcdcc6a8..00000000000 --- a/spec/services/ci/extract_sections_from_build_trace_service_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Ci::ExtractSectionsFromBuildTraceService, '#execute' do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:build) { create(:ci_build, project: project) } - - subject { described_class.new(project, user) } - - shared_examples 'build trace has sections markers' do - before do - build.trace.set(File.read(expand_fixture_path('trace/trace_with_sections'))) - end - - it 'saves the correct extracted sections' do - expect(build.trace_sections).to be_empty - expect(subject.execute(build)).to be(true) - expect(build.trace_sections).not_to be_empty - end - - it "fails if trace_sections isn't empty" do - expect(subject.execute(build)).to be(true) - expect(build.trace_sections).not_to be_empty - - expect(subject.execute(build)).to be(false) - expect(build.trace_sections).not_to be_empty - end - end - - shared_examples 'build trace has no sections markers' do - before do - build.trace.set('no markerts') - end - - it 'extracts no sections' do - expect(build.trace_sections).to be_empty - expect(subject.execute(build)).to be(true) - expect(build.trace_sections).to be_empty - end - end - - context 'when the build has no user' do - it_behaves_like 'build trace has sections markers' - it_behaves_like 'build trace has no sections markers' - end - - context 'when the build has a valid user' do - before do - build.user = user - end - - it_behaves_like 'build trace has sections markers' - it_behaves_like 'build trace has no sections markers' - end -end diff --git a/spec/services/ci/pipeline_processing/shared_processing_service.rb b/spec/services/ci/pipeline_processing/shared_processing_service.rb index 5089f8d5dba..a4bc8e68b2d 100644 --- a/spec/services/ci/pipeline_processing/shared_processing_service.rb +++ b/spec/services/ci/pipeline_processing/shared_processing_service.rb @@ -871,7 +871,7 @@ RSpec.shared_examples 'Pipeline Processing Service' do end let(:pipeline) do - Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push) + Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload end before do diff --git a/spec/services/ci/pipeline_processing/shared_processing_service_tests_with_yaml.rb b/spec/services/ci/pipeline_processing/shared_processing_service_tests_with_yaml.rb index 572808cd2db..b4ad2512593 100644 --- a/spec/services/ci/pipeline_processing/shared_processing_service_tests_with_yaml.rb +++ b/spec/services/ci/pipeline_processing/shared_processing_service_tests_with_yaml.rb @@ -10,7 +10,7 @@ RSpec.shared_context 'Pipeline Processing Service Tests With Yaml' do with_them do let(:test_file) { YAML.load_file(test_file_path) } - let(:pipeline) { Ci::CreatePipelineService.new(project, user, ref: 'master').execute(:pipeline) } + let(:pipeline) { Ci::CreatePipelineService.new(project, user, ref: 'master').execute(:pipeline).payload } before do stub_ci_pipeline_yaml_file(YAML.dump(test_file['config'])) diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_test_manual_same_and_different_stage_needs.yml b/spec/services/ci/pipeline_processing/test_cases/dag_test_manual_same_and_different_stage_needs.yml new file mode 100644 index 00000000000..115258c656e --- /dev/null +++ b/spec/services/ci/pipeline_processing/test_cases/dag_test_manual_same_and_different_stage_needs.yml @@ -0,0 +1,54 @@ +config: + stages: [first, second, third] + + job_a: + when: manual + stage: first + script: + - echo + + job_b: + when: manual + stage: second + script: + - echo + + job_c: + needs: ["job_b"] + stage: third + script: + - echo + + job_d: + needs: ["job_a"] + stage: third + script: + - echo + +init: + expect: + pipeline: skipped + stages: + first: skipped + second: skipped + third: skipped + jobs: + job_a: manual + job_b: manual + job_c: skipped + job_d: skipped + +transitions: + - event: play + jobs: [job_b] + expect: + pipeline: pending + stages: + first: skipped + second: pending + third: pending + jobs: + job_a: manual + job_b: pending + job_c: created + job_d: skipped diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_test_manual_same_stage_needs.yml b/spec/services/ci/pipeline_processing/test_cases/dag_test_manual_same_stage_needs.yml new file mode 100644 index 00000000000..fd15f7d1b57 --- /dev/null +++ b/spec/services/ci/pipeline_processing/test_cases/dag_test_manual_same_stage_needs.yml @@ -0,0 +1,70 @@ +config: + stages: [first, second, third, fourth] + + first_job: + stage: first + script: + - echo + + second_job: + stage: second + script: + - echo + when: manual + + third_job: + stage: third + needs: ["second_job"] + script: + - echo + + fourth_job: + stage: fourth + needs: ["third_job"] + script: + - echo + +init: + expect: + pipeline: pending + stages: + first: pending + second: created + third: created + fourth: created + jobs: + first_job: pending + second_job: created + third_job: created + fourth_job: created + +transitions: + - event: success + jobs: [first_job] + expect: + pipeline: success + stages: + first: success + second: skipped + third: skipped + fourth: skipped + jobs: + first_job: success + second_job: manual + third_job: skipped + fourth_job: skipped + + - event: play + jobs: [second_job] + expect: + pipeline: running + stages: + first: success + second: pending + third: skipped + fourth: skipped + jobs: + first_job: success + second_job: pending + third_job: created + fourth_job: created diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb index 080ca1cf0cd..2f93b1ecd3c 100644 --- a/spec/services/ci/pipeline_trigger_service_spec.rb +++ b/spec/services/ci/pipeline_trigger_service_spec.rb @@ -24,9 +24,11 @@ RSpec.describe Ci::PipelineTriggerService do context 'when the pipeline was not created successfully' do let(:fail_pipeline) do receive(:execute).and_wrap_original do |original, *args| - pipeline = original.call(*args) + response = original.call(*args) + pipeline = response.payload pipeline.update!(failure_reason: 'unknown_failure') - pipeline + + response end end diff --git a/spec/services/ci/pipelines/add_job_service_spec.rb b/spec/services/ci/pipelines/add_job_service_spec.rb index a72ffbfdc87..bdf7e577fa8 100644 --- a/spec/services/ci/pipelines/add_job_service_spec.rb +++ b/spec/services/ci/pipelines/add_job_service_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Ci::Pipelines::AddJobService do + include ExclusiveLeaseHelpers + let_it_be(:pipeline) { create(:ci_pipeline) } let(:job) { build(:ci_build) } @@ -68,5 +70,38 @@ RSpec.describe Ci::Pipelines::AddJobService do execute end end + + context 'exclusive lock' do + let(:lock_uuid) { 'test' } + let(:lock_key) { "ci:pipelines:#{pipeline.id}:add-job" } + let(:lock_timeout) { 1.minute } + + before do + # "Please stub a default value first if message might be received with other args as well." + allow(Gitlab::ExclusiveLease).to receive(:new).and_call_original + end + + it 'uses exclusive lock' do + lease = stub_exclusive_lease(lock_key, lock_uuid, timeout: lock_timeout) + expect(lease).to receive(:try_obtain) + expect(lease).to receive(:cancel) + + expect(execute).to be_success + expect(execute.payload[:job]).to eq(job) + end + + context 'when the FF ci_pipeline_add_job_with_lock is disabled' do + before do + stub_feature_flags(ci_pipeline_add_job_with_lock: false) + end + + it 'does not use exclusive lock' do + expect(Gitlab::ExclusiveLease).not_to receive(:new).with(lock_key, timeout: lock_timeout) + + expect(execute).to be_success + expect(execute.payload[:job]).to eq(job) + end + end + end end end diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index 6e5d7725a7a..2f37d0ea42d 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -5,8 +5,8 @@ require 'spec_helper' module Ci RSpec.describe RegisterJobService do let_it_be(:group) { create(:group) } - let_it_be(:project, reload: true) { create(:project, group: group, shared_runners_enabled: false, group_runners_enabled: false) } - let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let_it_be_with_reload(:project) { create(:project, group: group, shared_runners_enabled: false, group_runners_enabled: false) } + let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project) } let!(:shared_runner) { create(:ci_runner, :instance) } let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) } @@ -14,7 +14,7 @@ module Ci let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) } describe '#execute' do - context 'checks database loadbalancing stickiness' do + context 'checks database loadbalancing stickiness', :db_load_balancing do subject { described_class.new(shared_runner).execute } before do @@ -22,9 +22,6 @@ module Ci end it 'result is valid if replica did caught-up' do - allow(Gitlab::Database::LoadBalancing).to receive(:enable?) - .and_return(true) - expect(Gitlab::Database::LoadBalancing::Sticking).to receive(:all_caught_up?) .with(:runner, shared_runner.id) { true } @@ -32,9 +29,6 @@ module Ci end it 'result is invalid if replica did not caught-up' do - allow(Gitlab::Database::LoadBalancing).to receive(:enable?) - .and_return(true) - expect(Gitlab::Database::LoadBalancing::Sticking).to receive(:all_caught_up?) .with(:runner, shared_runner.id) { false } @@ -96,6 +90,9 @@ module Ci context 'allow shared runners' do before do project.update!(shared_runners_enabled: true) + pipeline.reload + pending_job.reload + pending_job.create_queuing_entry! end context 'for multiple builds' do @@ -470,13 +467,27 @@ module Ci context 'when depended job has not been completed yet' do let!(:pre_stage_job) { create(:ci_build, :pending, :queued, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) } - it { expect(subject).to eq(pending_job) } + it { is_expected.to eq(pending_job) } end context 'when artifacts of depended job has been expired' do let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) } - it_behaves_like 'not pick' + context 'when the pipeline is locked' do + before do + pipeline.artifacts_locked! + end + + it { is_expected.to eq(pending_job) } + end + + context 'when the pipeline is unlocked' do + before do + pipeline.unlocked! + end + + it_behaves_like 'not pick' + end end context 'when artifacts of depended job has been erased' do @@ -493,8 +504,12 @@ module Ci let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) } before do - allow_any_instance_of(Ci::Build).to receive(:drop!) - .and_raise(ActiveRecord::StaleObjectError.new(pending_job, :drop!)) + pipeline.unlocked! + + allow_next_instance_of(Ci::Build) do |build| + expect(build).to receive(:drop!) + .and_raise(ActiveRecord::StaleObjectError.new(pending_job, :drop!)) + end end it 'does not drop nor pick' do @@ -709,7 +724,21 @@ module Ci stub_feature_flags(ci_pending_builds_queue_source: true) end - include_examples 'handles runner assignment' + context 'with ci_queueing_denormalize_shared_runners_information enabled' do + before do + stub_feature_flags(ci_queueing_denormalize_shared_runners_information: true) + end + + include_examples 'handles runner assignment' + end + + context 'with ci_queueing_denormalize_shared_runners_information disabled' do + before do + stub_feature_flags(ci_queueing_denormalize_shared_runners_information: false) + end + + include_examples 'handles runner assignment' + end end context 'when not using pending builds table' do @@ -783,6 +812,11 @@ module Ci end context 'when shared runner is used' do + before do + pending_job.reload + pending_job.create_queuing_entry! + end + let(:runner) { create(:ci_runner, :instance, tag_list: %w(tag1 tag2)) } let(:expected_shared_runner) { true } let(:expected_shard) { ::Gitlab::Ci::Queue::Metrics::DEFAULT_METRICS_SHARD } diff --git a/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb b/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb index a741e3b49e7..53aa842bc28 100644 --- a/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb +++ b/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb @@ -51,14 +51,32 @@ RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupService do end context 'when there are no available resources' do + let!(:other_build) { create(:ci_build) } + before do - resource_group.assign_resource_to(create(:ci_build)) + resource_group.assign_resource_to(other_build) end it 'does not request resource' do expect_any_instance_of(Ci::Build).not_to receive(:enqueue_waiting_for_resource) subject + + expect(build.reload).to be_waiting_for_resource + end + + context 'when there is a stale build assigned to a resource' do + before do + other_build.doom! + other_build.update_column(:updated_at, 10.minutes.ago) + end + + it 'releases the resource from the stale build and assignes to the waiting build' do + subject + + expect(build.reload).to be_pending + expect(build.resource).to be_present + end end end end diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index 42d6e66b38b..ce2e6ba5e15 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -60,7 +60,7 @@ RSpec.describe Ci::RetryBuildService do artifacts_file artifacts_metadata artifacts_size commands resource resource_group_id processed security_scans author pipeline_id report_results pending_state pages_deployments - queuing_entry runtime_metadata].freeze + queuing_entry runtime_metadata trace_metadata].freeze shared_examples 'build duplication' do let_it_be(:another_pipeline) { create(:ci_empty_pipeline, project: project) } diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb deleted file mode 100644 index d5ef67c871c..00000000000 --- a/spec/services/ci/stop_environments_service_spec.rb +++ /dev/null @@ -1,253 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Ci::StopEnvironmentsService do - include CreateEnvironmentsHelpers - - let(:project) { create(:project, :private, :repository) } - let(:user) { create(:user) } - - let(:service) { described_class.new(project, user) } - - describe '#execute' do - context 'when environment with review app exists' do - before do - create(:environment, :with_review_app, project: project, - ref: 'feature') - end - - context 'when user has permission to stop environment' do - before do - project.add_developer(user) - end - - context 'when environment is associated with removed branch' do - it 'stops environment' do - expect_environment_stopped_on('feature') - end - end - - context 'when environment is associated with different branch' do - it 'does not stop environment' do - expect_environment_not_stopped_on('master') - end - end - - context 'when specified branch does not exist' do - it 'does not stop environment' do - expect_environment_not_stopped_on('non/existent/branch') - end - end - - context 'when no branch not specified' do - it 'does not stop environment' do - expect_environment_not_stopped_on(nil) - end - end - - context 'when environment is not stopped' do - before do - allow_any_instance_of(Environment) - .to receive(:state).and_return(:stopped) - end - - it 'does not stop environment' do - expect_environment_not_stopped_on('feature') - end - end - end - - context 'when user does not have permission to stop environment' do - context 'when user has no access to manage deployments' do - before do - project.add_guest(user) - end - - it 'does not stop environment' do - expect_environment_not_stopped_on('master') - end - end - end - - context 'when branch for stop action is protected' do - before do - project.add_developer(user) - create(:protected_branch, :no_one_can_push, - name: 'master', project: project) - end - - it 'does not stop environment' do - expect_environment_not_stopped_on('master') - end - end - end - - context 'when there is no environment associated with review app' do - before do - create(:environment, project: project) - end - - context 'when user has permission to stop environments' do - before do - project.add_maintainer(user) - end - - it 'does not stop environment' do - expect_environment_not_stopped_on('master') - end - end - end - - context 'when environment does not exist' do - it 'does not raise error' do - expect { service.execute('master') } - .not_to raise_error - end - end - end - - describe '#execute_for_merge_request' do - subject { service.execute_for_merge_request(merge_request) } - - let(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master') } - let(:project) { merge_request.project } - let(:user) { create(:user) } - - let(:pipeline) do - create(:ci_pipeline, - source: :merge_request_event, - merge_request: merge_request, - project: project, - sha: merge_request.diff_head_sha, - merge_requests_as_head_pipeline: [merge_request]) - end - - let!(:review_job) { create(:ci_build, :with_deployment, :start_review_app, pipeline: pipeline, project: project) } - let!(:stop_review_job) { create(:ci_build, :with_deployment, :stop_review_app, :manual, pipeline: pipeline, project: project) } - - before do - review_job.deployment.success! - end - - it 'has active environment at first' do - expect(pipeline.environments.first).to be_available - end - - context 'when user is a developer' do - before do - project.add_developer(user) - end - - it 'stops the active environment' do - subject - - expect(pipeline.environments.first).to be_stopped - end - end - - context 'when user is a reporter' do - before do - project.add_reporter(user) - end - - it 'does not stop the active environment' do - subject - - expect(pipeline.environments.first).to be_available - end - end - - context 'when pipeline is not associated with environments' do - let!(:job) { create(:ci_build, pipeline: pipeline, project: project) } - - it 'does not raise exception' do - expect { subject }.not_to raise_exception - end - end - - context 'when pipeline is not a pipeline for merge request' do - let(:pipeline) do - create(:ci_pipeline, - project: project, - ref: 'feature', - sha: merge_request.diff_head_sha, - merge_requests_as_head_pipeline: [merge_request]) - end - - it 'does not stop the active environment' do - subject - - expect(pipeline.environments.first).to be_available - end - end - end - - describe '.execute_in_batch' do - subject { described_class.execute_in_batch(environments) } - - let_it_be(:project) { create(:project, :repository) } - let_it_be(:user) { create(:user) } - - let(:environments) { Environment.available } - - before_all do - project.add_developer(user) - project.repository.add_branch(user, 'review/feature-1', 'master') - project.repository.add_branch(user, 'review/feature-2', 'master') - end - - before do - create_review_app(user, project, 'review/feature-1') - create_review_app(user, project, 'review/feature-2') - end - - it 'stops environments' do - expect { subject } - .to change { project.environments.all.map(&:state).uniq } - .from(['available']).to(['stopped']) - - expect(project.environments.all.map(&:auto_stop_at).uniq).to eq([nil]) - end - - it 'plays stop actions' do - expect { subject } - .to change { Ci::Build.where(name: 'stop_review_app').map(&:status).uniq } - .from(['manual']).to(['pending']) - end - - context 'when user does not have a permission to play the stop action' do - before do - project.team.truncate - end - - it 'tracks the exception' do - expect(Gitlab::ErrorTracking) - .to receive(:track_exception) - .with(Gitlab::Access::AccessDeniedError, anything) - .twice - .and_call_original - - subject - end - - after do - project.add_developer(user) - end - end - end - - def expect_environment_stopped_on(branch) - expect_any_instance_of(Environment) - .to receive(:stop!) - - service.execute(branch) - end - - def expect_environment_not_stopped_on(branch) - expect_any_instance_of(Environment) - .not_to receive(:stop!) - - service.execute(branch) - end -end |