diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 14:22:11 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 14:22:11 +0000 |
commit | 0c872e02b2c822e3397515ec324051ff540f0cd5 (patch) | |
tree | ce2fb6ce7030e4dad0f4118d21ab6453e5938cdd /spec/services/ci | |
parent | f7e05a6853b12f02911494c4b3fe53d9540d74fc (diff) | |
download | gitlab-ce-0c872e02b2c822e3397515ec324051ff540f0cd5.tar.gz |
Add latest changes from gitlab-org/gitlab@15-7-stable-eev15.7.0-rc42
Diffstat (limited to 'spec/services/ci')
33 files changed, 768 insertions, 294 deletions
diff --git a/spec/services/ci/append_build_trace_service_spec.rb b/spec/services/ci/append_build_trace_service_spec.rb index 487dbacbe90..20f7967d1f1 100644 --- a/spec/services/ci/append_build_trace_service_spec.rb +++ b/spec/services/ci/append_build_trace_service_spec.rb @@ -76,4 +76,36 @@ RSpec.describe Ci::AppendBuildTraceService do expect(build.failure_reason).to eq 'trace_size_exceeded' end end + + context 'when debug_trace param is provided' do + let(:metadata) { Ci::BuildMetadata.find_by(build_id: build) } + let(:stream_size) { 192.kilobytes } + let(:body_data) { 'x' * stream_size } + let(:content_range) { "#{body_start}-#{stream_size}" } + + context 'when sending the first trace' do + let(:body_start) { 0 } + + it 'updates build metadata debug_trace_enabled' do + described_class + .new(build, content_range: content_range, debug_trace: true) + .execute(body_data) + + expect(metadata.debug_trace_enabled).to be(true) + end + end + + context 'when sending the second trace' do + let(:body_start) { 1 } + + it 'does not update build metadata debug_trace_enabled', :aggregate_failures do + query_recorder = ActiveRecord::QueryRecorder.new do + described_class.new(build, content_range: content_range, debug_trace: true).execute(body_data) + end + + expect(metadata.debug_trace_enabled).to be(false) + expect(query_recorder.log).not_to include(/p_ci_builds_metadata/) + 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 9c02c5218f1..bcdb2b4f796 100644 --- a/spec/services/ci/create_downstream_pipeline_service_spec.rb +++ b/spec/services/ci/create_downstream_pipeline_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do +RSpec.describe Ci::CreateDownstreamPipelineService, '#execute', feature_category: :continuous_integration do include Ci::SourcePipelineHelpers # Using let_it_be on user and projects for these specs can cause @@ -13,7 +13,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do let(:downstream_project) { create(:project, :repository) } let!(:upstream_pipeline) do - create(:ci_pipeline, :running, project: upstream_project) + create(:ci_pipeline, :created, project: upstream_project) end let(:trigger) do @@ -33,6 +33,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do end let(:service) { described_class.new(upstream_project, user) } + let(:pipeline) { subject.payload } before do upstream_project.add_developer(user) @@ -40,6 +41,12 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do subject { service.execute(bridge) } + shared_context 'when ci_bridge_remove_sourced_pipelines is disabled' do + before do + stub_feature_flags(ci_bridge_remove_sourced_pipelines: false) + end + end + context 'when downstream project has not been found' do let(:trigger) do { trigger: { project: 'unknown/project' } } @@ -48,6 +55,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'does not create a pipeline' do expect { subject } .not_to change { Ci::Pipeline.count } + expect(subject).to be_error + expect(subject.message).to eq("Pre-conditions not met") end it 'changes pipeline bridge job status to failed' do @@ -63,9 +72,11 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'does not create a new pipeline' do expect { subject } .not_to change { Ci::Pipeline.count } + expect(subject).to be_error + expect(subject.message).to eq("Pre-conditions not met") end - it 'changes status of the bridge build' do + it 'changes status of the bridge build to failed' do subject expect(bridge.reload).to be_failed @@ -82,9 +93,11 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'does not create a new pipeline' do expect { subject } .not_to change { Ci::Pipeline.count } + expect(subject).to be_error + expect(subject.message).to eq("Pre-conditions not met") end - it 'changes status of the bridge build' do + it 'changes status of the bridge build to failed' do subject expect(bridge.reload).to be_failed @@ -103,35 +116,51 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates only one new pipeline' do expect { subject } .to change { Ci::Pipeline.count }.by(1) + expect(subject).to be_success end it 'creates a new pipeline in a downstream project' do - pipeline = subject - expect(pipeline.user).to eq bridge.user expect(pipeline.project).to eq downstream_project - expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline + expect(bridge.reload.sourced_pipeline.pipeline).to eq pipeline expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline expect(pipeline.source_bridge).to eq bridge expect(pipeline.source_bridge).to be_a ::Ci::Bridge end + context 'when ci_bridge_remove_sourced_pipelines is disabled' do + include_context 'when ci_bridge_remove_sourced_pipelines is disabled' + + it 'creates a new pipeline in a downstream project' do + expect(pipeline.user).to eq bridge.user + expect(pipeline.project).to eq downstream_project + expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline + expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline + expect(pipeline.source_bridge).to eq bridge + expect(pipeline.source_bridge).to be_a ::Ci::Bridge + end + end + it_behaves_like 'logs downstream pipeline creation' do + let(:downstream_pipeline) { pipeline } let(:expected_root_pipeline) { upstream_pipeline } let(:expected_hierarchy_size) { 2 } let(:expected_downstream_relationship) { :multi_project } end it 'updates bridge status when downstream pipeline gets processed' do - pipeline = subject - expect(pipeline.reload).to be_created expect(bridge.reload).to be_success end - context 'when bridge job has already any downstream pipelines' do + it 'triggers the upstream pipeline duration calculation', :sidekiq_inline do + expect { subject } + .to change { upstream_pipeline.reload.duration }.from(nil).to(an_instance_of(Integer)) + end + + context 'when bridge job has already any downstream pipeline' do before do - bridge.sourced_pipelines.create!( + bridge.create_sourced_pipeline!( source_pipeline: bridge.pipeline, source_project: bridge.project, project: bridge.project, @@ -147,7 +176,33 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do bridge_id: bridge.id, project_id: bridge.project.id) .and_call_original expect(Ci::CreatePipelineService).not_to receive(:new) - expect(subject).to eq({ message: "Already has a downstream pipeline", status: :error }) + expect(subject).to be_error + expect(subject.message).to eq("Already has a downstream pipeline") + end + + context 'when ci_bridge_remove_sourced_pipelines is disabled' do + include_context 'when ci_bridge_remove_sourced_pipelines is disabled' + + before do + bridge.sourced_pipelines.create!( + source_pipeline: bridge.pipeline, + source_project: bridge.project, + project: bridge.project, + pipeline: create(:ci_pipeline, project: bridge.project) + ) + end + + it 'logs an error and exits' do + expect(Gitlab::ErrorTracking) + .to receive(:track_exception) + .with( + instance_of(described_class::DuplicateDownstreamPipelineError), + bridge_id: bridge.id, project_id: bridge.project.id) + .and_call_original + expect(Ci::CreatePipelineService).not_to receive(:new) + expect(subject).to be_error + expect(subject.message).to eq("Already has a downstream pipeline") + end end end @@ -157,8 +212,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do end it 'is using default branch name' do - pipeline = subject - expect(pipeline.ref).to eq 'master' end end @@ -171,22 +224,33 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates only one new pipeline' do expect { subject } .to change { Ci::Pipeline.count }.by(1) + expect(subject).to be_error + expect(subject.message).to match_array(["jobs job config should implement a script: or a trigger: keyword"]) end it 'creates a new pipeline in a downstream project' do - pipeline = subject - expect(pipeline.user).to eq bridge.user expect(pipeline.project).to eq downstream_project - expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline + expect(bridge.reload.sourced_pipeline.pipeline).to eq pipeline expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline expect(pipeline.source_bridge).to eq bridge expect(pipeline.source_bridge).to be_a ::Ci::Bridge end - it 'updates the bridge status when downstream pipeline gets processed' do - pipeline = subject + context 'when ci_bridge_remove_sourced_pipelines is disabled' do + include_context 'when ci_bridge_remove_sourced_pipelines is disabled' + + it 'creates a new pipeline in a downstream project' do + expect(pipeline.user).to eq bridge.user + expect(pipeline.project).to eq downstream_project + expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline + expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline + expect(pipeline.source_bridge).to eq bridge + expect(pipeline.source_bridge).to be_a ::Ci::Bridge + end + end + it 'updates the bridge status when downstream pipeline gets processed' do expect(pipeline.reload).to be_failed expect(bridge.reload).to be_failed end @@ -201,6 +265,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'does not create a new pipeline' do expect { subject } .not_to change { Ci::Pipeline.count } + expect(subject).to be_error + expect(subject.message).to eq("Pre-conditions not met") end it 'changes status of the bridge build' do @@ -222,32 +288,39 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates only one new pipeline' do expect { subject } .to change { Ci::Pipeline.count }.by(1) + expect(subject).to be_success end it 'creates a child pipeline in the same project' do - pipeline = subject - pipeline.reload - expect(pipeline.builds.map(&:name)).to match_array(%w[rspec echo]) expect(pipeline.user).to eq bridge.user expect(pipeline.project).to eq bridge.project - expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline + expect(bridge.reload.sourced_pipeline.pipeline).to eq pipeline expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline expect(pipeline.source_bridge).to eq bridge expect(pipeline.source_bridge).to be_a ::Ci::Bridge end - it 'updates bridge status when downstream pipeline gets processed' do - pipeline = subject + context 'when ci_bridge_remove_sourced_pipelines is disabled' do + include_context 'when ci_bridge_remove_sourced_pipelines is disabled' + + it 'creates a child pipeline in the same project' do + expect(pipeline.builds.map(&:name)).to match_array(%w[rspec echo]) + expect(pipeline.user).to eq bridge.user + expect(pipeline.project).to eq bridge.project + expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline + expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline + expect(pipeline.source_bridge).to eq bridge + expect(pipeline.source_bridge).to be_a ::Ci::Bridge + end + end + it 'updates bridge status when downstream pipeline gets processed' do expect(pipeline.reload).to be_created expect(bridge.reload).to be_success end it 'propagates parent pipeline settings to the child pipeline' do - pipeline = subject - pipeline.reload - expect(pipeline.ref).to eq(upstream_pipeline.ref) expect(pipeline.sha).to eq(upstream_pipeline.sha) expect(pipeline.source_sha).to eq(upstream_pipeline.source_sha) @@ -276,6 +349,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it_behaves_like 'creates a child pipeline' it_behaves_like 'logs downstream pipeline creation' do + let(:downstream_pipeline) { pipeline } let(:expected_root_pipeline) { upstream_pipeline } let(:expected_hierarchy_size) { 2 } let(:expected_downstream_relationship) { :parent_child } @@ -283,6 +357,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'updates the bridge job to success' do expect { subject }.to change { bridge.status }.to 'success' + expect(subject).to be_success end context 'when bridge uses "depend" strategy' do @@ -292,8 +367,9 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do } end - it 'does not update the bridge job status' do - expect { subject }.not_to change { bridge.status } + it 'update the bridge job to running status' do + expect { subject }.to change { bridge.status }.from('pending').to('running') + expect(subject).to be_success end end @@ -323,8 +399,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it_behaves_like 'creates a child pipeline' it 'propagates the merge request to the child pipeline' do - pipeline = subject - expect(pipeline.merge_request).to eq(merge_request) expect(pipeline).to be_merge_request end @@ -341,11 +415,13 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates the pipeline' do expect { subject } .to change { Ci::Pipeline.count }.by(1) + expect(subject).to be_success expect(bridge.reload).to be_success end it_behaves_like 'logs downstream pipeline creation' do + let(:downstream_pipeline) { pipeline } let(:expected_root_pipeline) { upstream_pipeline.parent_pipeline } let(:expected_hierarchy_size) { 3 } let(:expected_downstream_relationship) { :parent_child } @@ -394,6 +470,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'create the pipeline' do expect { subject }.to change { Ci::Pipeline.count }.by(1) + expect(subject).to be_success end end @@ -406,11 +483,10 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates a new pipeline allowing variables to be passed downstream' do expect { subject }.to change { Ci::Pipeline.count }.by(1) + expect(subject).to be_success end it 'passes variables downstream from the bridge' do - pipeline = subject - pipeline.variables.map(&:key).tap do |variables| expect(variables).to include 'BRIDGE' end @@ -466,6 +542,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'does not create a new pipeline' do expect { subject } .not_to change { Ci::Pipeline.count } + expect(subject).to be_error + expect(subject.message).to eq("Pre-conditions not met") end it 'changes status of the bridge build' do @@ -480,6 +558,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates a new pipeline' do expect { subject } .to change { Ci::Pipeline.count } + expect(subject).to be_success end it 'expect bridge build not to be failed' do @@ -559,18 +638,16 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates only one new pipeline' do expect { subject } .to change { Ci::Pipeline.count }.by(1) + expect(subject).to be_error + expect(subject.message).to match_array(["jobs invalid config should implement a script: or a trigger: keyword"]) end it 'creates a new pipeline in the downstream project' do - pipeline = subject - expect(pipeline.user).to eq bridge.user expect(pipeline.project).to eq downstream_project end it 'drops the bridge' do - pipeline = subject - expect(pipeline.reload).to be_failed expect(bridge.reload).to be_failed expect(bridge.failure_reason).to eq('downstream_pipeline_creation_failed') @@ -585,15 +662,10 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do bridge.drop! end - it 'tracks the exception' do - expect(Gitlab::ErrorTracking) - .to receive(:track_exception) - .with( - instance_of(Ci::Bridge::InvalidTransitionError), - bridge_id: bridge.id, - downstream_pipeline_id: kind_of(Numeric)) - - subject + it 'returns the error' do + expect { subject }.not_to change(downstream_project.ci_pipelines, :count) + expect(subject).to be_error + expect(subject.message).to eq('Can not run the bridge') end end @@ -603,8 +675,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do end it 'passes bridge variables to downstream pipeline' do - pipeline = subject - expect(pipeline.variables.first) .to have_attributes(key: 'BRIDGE', value: 'var') end @@ -616,8 +686,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do end it 'does not pass pipeline variables directly downstream' do - pipeline = subject - pipeline.variables.map(&:key).tap do |variables| expect(variables).not_to include 'PIPELINE_VARIABLE' end @@ -629,8 +697,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do end it 'makes it possible to pass pipeline variable downstream' do - pipeline = subject - pipeline.variables.find_by(key: 'BRIDGE').tap do |variable| expect(variable.value).to eq 'my-value-var' end @@ -644,11 +710,11 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'does not create a new pipeline' do expect { subject } .not_to change { Ci::Pipeline.count } + expect(subject).to be_error + expect(subject.message).to match_array(["Insufficient permissions to set pipeline variables"]) end it 'ignores variables passed downstream from the bridge' do - pipeline = subject - pipeline.variables.map(&:key).tap do |variables| expect(variables).not_to include 'BRIDGE' end @@ -668,7 +734,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do # TODO: Move this context into a feature spec that uses # multiple pipeline processing services. Location TBD in: # https://gitlab.com/gitlab-org/gitlab/issues/36216 - context 'when configured with bridge job rules' do + context 'when configured with bridge job rules', :sidekiq_inline do before do stub_ci_pipeline_yaml_file(config) downstream_project.add_maintainer(upstream_project.first_owner) @@ -701,6 +767,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates the downstream pipeline' do expect { subject } .to change(downstream_project.ci_pipelines, :count).by(1) + expect(subject).to be_error + expect(subject.message).to eq("Already has a downstream pipeline") end end end @@ -731,6 +799,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'does not create a pipeline and drops the bridge' do expect { subject }.not_to change(downstream_project.ci_pipelines, :count) + expect(subject).to be_error + expect(subject.message).to match_array(["Reference not found"]) expect(bridge.reload).to be_failed expect(bridge.failure_reason).to eq('downstream_pipeline_creation_failed') @@ -754,6 +824,8 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'does not create a pipeline and drops the bridge' do expect { subject }.not_to change(downstream_project.ci_pipelines, :count) + expect(subject).to be_error + expect(subject.message).to match_array(["No stages / jobs for this pipeline."]) expect(bridge.reload).to be_failed expect(bridge.failure_reason).to eq('downstream_pipeline_creation_failed') @@ -776,6 +848,10 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates the pipeline but drops the bridge' do expect { subject }.to change(downstream_project.ci_pipelines, :count).by(1) + expect(subject).to be_error + expect(subject.message).to eq( + ["test job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post"] + ) expect(bridge.reload).to be_failed expect(bridge.failure_reason).to eq('downstream_pipeline_creation_failed') @@ -808,6 +884,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates the pipeline' do expect { subject }.to change(downstream_project.ci_pipelines, :count).by(1) + expect(subject).to be_success expect(bridge.reload).to be_success end @@ -822,6 +899,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do context 'when a downstream pipeline has sibling pipelines' do it_behaves_like 'logs downstream pipeline creation' do + let(:downstream_pipeline) { pipeline } let(:expected_root_pipeline) { upstream_pipeline } let(:expected_downstream_relationship) { :multi_project } @@ -839,23 +917,47 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do let_it_be(:child) { create(:ci_pipeline, child_of: parent) } let_it_be(:sibling) { create(:ci_pipeline, child_of: parent) } - before do - stub_const("#{described_class}::MAX_HIERARCHY_SIZE", 3) - end - + let(:project) { build(:project, :repository) } let(:bridge) do - create(:ci_bridge, status: :pending, user: user, options: trigger, pipeline: child) + create(:ci_bridge, status: :pending, user: user, options: trigger, pipeline: child, project: project) end - it 'does not create a new pipeline' do - expect { subject }.not_to change { Ci::Pipeline.count } + context 'when limit was specified by admin' do + before do + project.actual_limits.update!(pipeline_hierarchy_size: 3) + end + + it 'does not create a new pipeline' do + expect { subject }.not_to change { Ci::Pipeline.count } + end + + it 'drops the trigger job with an explanatory reason' do + subject + + expect(bridge.reload).to be_failed + expect(bridge.failure_reason).to eq('reached_max_pipeline_hierarchy_size') + end end - it 'drops the trigger job with an explanatory reason' do - subject + context 'when there was no limit specified by admin' do + before do + allow(bridge.pipeline).to receive(:complete_hierarchy_count).and_return(1000) + end - expect(bridge.reload).to be_failed - expect(bridge.failure_reason).to eq('reached_max_pipeline_hierarchy_size') + context 'when pipeline count reaches the default limit of 1000' do + it 'does not create a new pipeline' do + expect { subject }.not_to change { Ci::Pipeline.count } + expect(subject).to be_error + expect(subject.message).to eq("Pre-conditions not met") + end + + it 'drops the trigger job with an explanatory reason' do + subject + + expect(bridge.reload).to be_failed + expect(bridge.failure_reason).to eq('reached_max_pipeline_hierarchy_size') + end + end end context 'with :ci_limit_complete_hierarchy_size disabled' do @@ -865,6 +967,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do it 'creates a new pipeline' do expect { subject }.to change { Ci::Pipeline.count }.by(1) + expect(subject).to be_success end it 'marks the bridge job as successful' do 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 74d3534eb45..0d5017a763f 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 @@ -57,7 +57,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_fl pipeline = create_pipeline! test = pipeline.statuses.find_by(name: 'instrumentation_test') - expect(test).to be_pending + expect(test).to be_running expect(pipeline.triggered_pipelines.count).to eq(1) end diff --git a/spec/services/ci/create_pipeline_service/environment_spec.rb b/spec/services/ci/create_pipeline_service/environment_spec.rb index 438cb6ac895..b713cad2cad 100644 --- a/spec/services/ci/create_pipeline_service/environment_spec.rb +++ b/spec/services/ci/create_pipeline_service/environment_spec.rb @@ -46,6 +46,24 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes end end + context 'when branch pipeline creates a dynamic environment' do + before do + config = YAML.dump( + review_app: { + script: 'echo', + environment: { name: "review/$CI_COMMIT_REF_NAME" } + }) + + stub_ci_pipeline_yaml_file(config) + end + + it 'does not associate merge request with the environment' do + is_expected.to be_created_successfully + + expect(Environment.find_by_name('review/master').merge_request).to be_nil + end + end + context 'when variables are dependent on stage name' do let(:config) do <<~YAML diff --git a/spec/services/ci/create_pipeline_service/logger_spec.rb b/spec/services/ci/create_pipeline_service/logger_spec.rb index 3045f8e92b1..ccb15bfa684 100644 --- a/spec/services/ci/create_pipeline_service/logger_spec.rb +++ b/spec/services/ci/create_pipeline_service/logger_spec.rb @@ -2,8 +2,10 @@ require 'spec_helper' -RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do - context 'pipeline logger' do +RSpec.describe Ci::CreatePipelineService, # rubocop: disable RSpec/FilePath + :yaml_processor_feature_flag_corectness, + feature_category: :continuous_integration do + describe 'pipeline logger' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } @@ -12,17 +14,11 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes let(:pipeline) { service.execute(:push).payload } let(:file_location) { 'spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml' } - before do - stub_ci_pipeline_yaml_file(gitlab_ci_yaml) - end - let(:counters) do { 'count' => a_kind_of(Numeric), - 'avg' => a_kind_of(Numeric), - 'sum' => a_kind_of(Numeric), 'max' => a_kind_of(Numeric), - 'min' => a_kind_of(Numeric) + 'sum' => a_kind_of(Numeric) } end @@ -34,15 +30,22 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes 'pipeline_persisted' => true, 'project_id' => project.id, 'pipeline_creation_service_duration_s' => a_kind_of(Numeric), - 'pipeline_creation_duration_s' => counters, - 'pipeline_size_count' => counters, - 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => counters, + 'pipeline_creation_duration_s' => a_kind_of(Numeric), + 'pipeline_size_count' => a_kind_of(Numeric), + 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => a_kind_of(Numeric), 'pipeline_seed_build_inclusion_duration_s' => counters, + 'pipeline_seed_build_errors_duration_s' => counters, + 'pipeline_seed_build_to_resource_duration_s' => counters, + 'pipeline_seed_stage_seeds_duration_s' => counters, 'pipeline_builds_tags_count' => a_kind_of(Numeric), 'pipeline_builds_distinct_tags_count' => a_kind_of(Numeric) } end + before do + stub_ci_pipeline_yaml_file(gitlab_ci_yaml) + end + context 'when the duration is under the threshold' do it 'does not create a log entry but it collects the data' do expect(Gitlab::AppJsonLogger).not_to receive(:info) @@ -51,9 +54,9 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes expect(service.logger.observations_hash) .to match( a_hash_including( - 'pipeline_creation_duration_s' => counters, - 'pipeline_size_count' => counters, - 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => counters + 'pipeline_creation_duration_s' => a_kind_of(Numeric), + 'pipeline_size_count' => a_kind_of(Numeric), + 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => a_kind_of(Numeric) ) ) end @@ -62,7 +65,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes context 'when the durations exceeds the threshold' do let(:timer) do proc do - @timer = @timer.to_i + 30 + @timer = @timer.to_i + 30 # rubocop: disable RSpec/InstanceVariable end end @@ -88,17 +91,15 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes 'pipeline_persisted' => false, 'project_id' => project.id, 'pipeline_creation_service_duration_s' => a_kind_of(Numeric), - 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => counters + 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => a_kind_of(Numeric) } end - before do + it 'creates a log entry' do allow_next_instance_of(Ci::Pipeline) do |pipeline| expect(pipeline).to receive(:save!).and_raise { RuntimeError } end - end - it 'creates a log entry' do expect(Gitlab::AppJsonLogger) .to receive(:info) .with(a_hash_including(loggable_data)) @@ -125,7 +126,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes context 'when the size exceeds the threshold' do before do allow_next_instance_of(Ci::Pipeline) do |pipeline| - allow(pipeline).to receive(:total_size) { 5000 } + allow(pipeline).to receive(:total_size).and_return(5000) end end 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 513cbbed6cd..eb17935967c 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 @@ -108,7 +108,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_fl pipeline = create_pipeline! test = pipeline.statuses.find_by(name: 'instrumentation_test') - expect(test).to be_pending + expect(test).to be_running expect(pipeline.triggered_pipelines.count).to eq(1) end diff --git a/spec/services/ci/create_pipeline_service/partitioning_spec.rb b/spec/services/ci/create_pipeline_service/partitioning_spec.rb index f34d103d965..a87135cefdd 100644 --- a/spec/services/ci/create_pipeline_service/partitioning_spec.rb +++ b/spec/services/ci/create_pipeline_service/partitioning_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, :aggregate_failures, -:ci_partitionable do +:ci_partitionable, feature_category: :continuous_integration do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } @@ -15,8 +15,13 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes - test - deploy + needs:build: + stage: build + script: echo "needs..." + build: stage: build + needs: ["needs:build"] script: make build test: @@ -95,6 +100,12 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes expect(pipeline.variables.size).to eq(2) expect(variables_partition_ids).to eq([current_partition_id]) end + + it 'assigns partition_id to needs' do + needs = find_need('build') + + expect(needs.partition_id).to eq(current_partition_id) + end end context 'with parent child pipelines' do @@ -144,4 +155,12 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes .find { |job| job.name == name } .metadata end + + def find_need(name) + pipeline + .processables + .find { |job| job.name == name } + .needs + .first + end end diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb index 5fdefb2b306..b866293393b 100644 --- a/spec/services/ci/create_pipeline_service/rules_spec.rb +++ b/spec/services/ci/create_pipeline_service/rules_spec.rb @@ -912,7 +912,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes context 'when outside freeze period' do it 'creates two jobs' do - Timecop.freeze(2020, 4, 10, 22, 59) do + travel_to(Time.utc(2020, 4, 10, 22, 59)) do expect(pipeline).to be_persisted expect(build_names).to contain_exactly('test-job', 'deploy-job') end @@ -921,7 +921,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes context 'when inside freeze period' do it 'creates one job' do - Timecop.freeze(2020, 4, 10, 23, 1) do + travel_to(Time.utc(2020, 4, 10, 23, 1)) do expect(pipeline).to be_persisted expect(build_names).to contain_exactly('test-job') end @@ -946,7 +946,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes context 'when outside freeze period' do it 'creates two jobs' do - Timecop.freeze(2020, 4, 10, 22, 59) do + travel_to(Time.utc(2020, 4, 10, 22, 59)) do expect(pipeline).to be_persisted expect(build_names).to contain_exactly('deploy-job') end @@ -955,7 +955,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes context 'when inside freeze period' do it 'does not create the pipeline', :aggregate_failures do - Timecop.freeze(2020, 4, 10, 23, 1) do + travel_to(Time.utc(2020, 4, 10, 23, 1)) do expect(response).to be_error expect(pipeline).not_to be_persisted end diff --git a/spec/services/ci/create_pipeline_service/scripts_spec.rb b/spec/services/ci/create_pipeline_service/scripts_spec.rb new file mode 100644 index 00000000000..50b558e505a --- /dev/null +++ b/spec/services/ci/create_pipeline_service/scripts_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { project.first_owner } + + let(:service) { described_class.new(project, user, { ref: 'master' }) } + let(:pipeline) { service.execute(:push).payload } + + before do + stub_ci_pipeline_yaml_file(config) + end + + context 'when job has script and nested before_script and after_script' do + let(:config) do + <<-CI_CONFIG + default: + before_script: echo 'hello default before_script' + after_script: echo 'hello default after_script' + + job: + before_script: echo 'hello job before_script' + after_script: echo 'hello job after_script' + script: echo 'hello job script' + CI_CONFIG + end + + it 'creates a job with script data' do + expect(pipeline).to be_created_successfully + expect(pipeline.builds.first).to have_attributes( + name: 'job', + stage: 'test', + options: { script: ["echo 'hello job script'"], + before_script: ["echo 'hello job before_script'"], + after_script: ["echo 'hello job after_script'"] } + ) + end + end + + context 'when job has hooks and default hooks' do + let(:config) do + <<-CI_CONFIG + default: + hooks: + pre_get_sources_script: + - echo 'hello default pre_get_sources_script' + + job1: + hooks: + pre_get_sources_script: + - echo 'hello job1 pre_get_sources_script' + script: echo 'hello job1 script' + + job2: + script: echo 'hello job2 script' + + job3: + inherit: + default: false + script: echo 'hello job3 script' + CI_CONFIG + end + + it 'creates jobs with hook data' do + expect(pipeline).to be_created_successfully + expect(pipeline.builds.find_by(name: 'job1')).to have_attributes( + name: 'job1', + stage: 'test', + options: { script: ["echo 'hello job1 script'"], + hooks: { pre_get_sources_script: ["echo 'hello job1 pre_get_sources_script'"] } } + ) + expect(pipeline.builds.find_by(name: 'job2')).to have_attributes( + name: 'job2', + stage: 'test', + options: { script: ["echo 'hello job2 script'"], + hooks: { pre_get_sources_script: ["echo 'hello default pre_get_sources_script'"] } } + ) + expect(pipeline.builds.find_by(name: 'job3')).to have_attributes( + name: 'job3', + stage: 'test', + options: { script: ["echo 'hello job3 script'"] } + ) + end + + context 'when the FF ci_hooks_pre_get_sources_script is disabled' do + before do + stub_feature_flags(ci_hooks_pre_get_sources_script: false) + end + + it 'creates jobs without hook data' do + expect(pipeline).to be_created_successfully + expect(pipeline.builds.find_by(name: 'job1')).to have_attributes( + name: 'job1', + stage: 'test', + options: { script: ["echo 'hello job1 script'"] } + ) + expect(pipeline.builds.find_by(name: 'job2')).to have_attributes( + name: 'job2', + stage: 'test', + options: { script: ["echo 'hello job2 script'"] } + ) + expect(pipeline.builds.find_by(name: 'job3')).to have_attributes( + name: 'job3', + stage: 'test', + options: { script: ["echo 'hello job3 script'"] } + ) + end + end + end +end diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 67c13649c6f..8628e95ba80 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -79,11 +79,11 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes let(:accepted_n_plus_ones) do 1 + # SELECT "ci_instance_variables" - 1 + # INSERT INTO "ci_stages" - 1 + # SELECT "ci_builds".* FROM "ci_builds" - 1 + # INSERT INTO "ci_builds" - 1 + # INSERT INTO "ci_builds_metadata" - 1 # SELECT "taggings".* FROM "taggings" + 1 + # INSERT INTO "ci_stages" + 1 + # SELECT "ci_builds".* FROM "ci_builds" + 1 + # INSERT INTO "ci_builds" + 1 + # INSERT INTO "ci_builds_metadata" + 1 # SELECT "taggings".* FROM "taggings" end end end @@ -710,6 +710,29 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes end end + context 'when the configuration includes ID tokens' do + it 'creates variables for the ID tokens' do + config = YAML.dump({ + job_with_id_tokens: { + script: 'ls', + id_tokens: { + 'TEST_ID_TOKEN' => { + aud: 'https://gitlab.com' + } + } + } + }) + stub_ci_pipeline_yaml_file(config) + + result = execute_service.payload + + expect(result).to be_persisted + expect(result.builds.first.id_tokens).to eq({ + 'TEST_ID_TOKEN' => { 'aud' => 'https://gitlab.com' } + }) + end + end + context 'with manual actions' do before do config = YAML.dump({ deploy: { script: 'ls', when: 'manual' } }) diff --git a/spec/services/ci/enqueue_job_service_spec.rb b/spec/services/ci/enqueue_job_service_spec.rb new file mode 100644 index 00000000000..c2bb0bb2bb5 --- /dev/null +++ b/spec/services/ci/enqueue_job_service_spec.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::EnqueueJobService, '#execute', feature_category: :continuous_integration do + let_it_be(:project) { create(:project) } + let(:user) { create(:user, developer_projects: [project]) } + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:build) { create(:ci_build, :manual, pipeline: pipeline) } + + let(:service) do + described_class.new(build, current_user: user) + end + + subject(:execute) { service.execute } + + it 'assigns the user to the job' do + expect { execute }.to change { build.reload.user }.to(user) + end + + it 'calls enqueue!' do + expect(build).to receive(:enqueue!) + execute + end + + it 'calls Ci::ResetSkippedJobsService' do + expect_next_instance_of(Ci::ResetSkippedJobsService) do |service| + expect(service).to receive(:execute).with(build) + end + + execute + end + + it 'returns the job' do + expect(execute).to eq(build) + end + + context 'when variables are supplied' do + let(:job_variables) do + [{ key: 'first', secret_value: 'first' }, + { key: 'second', secret_value: 'second' }] + end + + let(:service) do + described_class.new(build, current_user: user, variables: job_variables) + end + + it 'assigns the variables to the job' do + execute + expect(build.reload.job_variables.map(&:key)).to contain_exactly('first', 'second') + end + end + + context 'when the job transition is invalid' do + let(:bridge) { create(:ci_bridge, :failed, pipeline: pipeline, project: project) } + + let(:service) do + described_class.new(bridge, current_user: user) + end + + it 'raises StateMachines::InvalidTransition' do + expect { execute }.to raise_error StateMachines::InvalidTransition + end + end + + context 'when a transition block is supplied' do + let(:bridge) { create(:ci_bridge, :playable, pipeline: pipeline) } + + let(:service) do + described_class.new(bridge, current_user: user) + end + + subject(:execute) { service.execute(&:pending!) } + + it 'calls the transition block instead of enqueue!' do + expect(bridge).to receive(:pending!) + expect(bridge).not_to receive(:enqueue!) + execute + end + end +end diff --git a/spec/services/ci/generate_kubeconfig_service_spec.rb b/spec/services/ci/generate_kubeconfig_service_spec.rb index bfde39780dd..c0858b0f0c9 100644 --- a/spec/services/ci/generate_kubeconfig_service_spec.rb +++ b/spec/services/ci/generate_kubeconfig_service_spec.rb @@ -4,52 +4,98 @@ require 'spec_helper' RSpec.describe Ci::GenerateKubeconfigService do describe '#execute' do - let(:project) { create(:project) } - let(:build) { create(:ci_build, project: project) } - let(:pipeline) { build.pipeline } - let(:agent1) { create(:cluster_agent, project: project) } - let(:agent2) { create(:cluster_agent) } - let(:authorization1) { create(:agent_project_authorization, agent: agent1) } - let(:authorization2) { create(:agent_project_authorization, agent: agent2) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:pipeline) { create(:ci_empty_pipeline, project: project) } + let_it_be(:build) { create(:ci_build, project: project, pipeline: pipeline) } - let(:template) { instance_double(Gitlab::Kubernetes::Kubeconfig::Template) } + let_it_be(:agent_project) { create(:project, group: group, name: 'project-containing-agent-config') } - subject { described_class.new(pipeline, token: build.token).execute } + let_it_be(:project_agent_authorization) do + agent = create(:cluster_agent, project: agent_project) + create(:agent_project_authorization, agent: agent, project: project) + end + + let_it_be(:group_agent_authorization) do + agent = create(:cluster_agent, project: agent_project) + create(:agent_group_authorization, agent: agent, group: group) + end + + let(:template) do + instance_double( + Gitlab::Kubernetes::Kubeconfig::Template, + add_cluster: nil, + add_user: nil, + add_context: nil + ) + end + + let(:agent_authorizations) { [project_agent_authorization, group_agent_authorization] } + let(:filter_service) do + instance_double( + ::Clusters::Agents::FilterAuthorizationsService, + execute: agent_authorizations + ) + end + + subject(:execute) { described_class.new(pipeline, token: build.token, environment: nil).execute } before do - expect(Gitlab::Kubernetes::Kubeconfig::Template).to receive(:new).and_return(template) - expect(pipeline).to receive(:cluster_agent_authorizations).and_return([authorization1, authorization2]) + allow(Gitlab::Kubernetes::Kubeconfig::Template).to receive(:new).and_return(template) + allow(::Clusters::Agents::FilterAuthorizationsService).to receive(:new).and_return(filter_service) + end + + it 'returns a Kubeconfig Template' do + expect(execute).to eq(template) end - it 'adds a cluster, and a user and context for each available agent' do + it 'adds a cluster' do expect(template).to receive(:add_cluster).with( name: 'gitlab', url: Gitlab::Kas.tunnel_url ).once - expect(template).to receive(:add_user).with( - name: "agent:#{agent1.id}", - token: "ci:#{agent1.id}:#{build.token}" - ) - expect(template).to receive(:add_user).with( - name: "agent:#{agent2.id}", - token: "ci:#{agent2.id}:#{build.token}" - ) + execute + end - expect(template).to receive(:add_context).with( - name: "#{project.full_path}:#{agent1.name}", - namespace: 'production', - cluster: 'gitlab', - user: "agent:#{agent1.id}" - ) - expect(template).to receive(:add_context).with( - name: "#{agent2.project.full_path}:#{agent2.name}", - namespace: 'production', - cluster: 'gitlab', - user: "agent:#{agent2.id}" + it "filters the pipeline's agents by `nil` environment" do + expect(::Clusters::Agents::FilterAuthorizationsService).to receive(:new).with( + pipeline.cluster_agent_authorizations, + environment: nil ) - expect(subject).to eq(template) + execute + end + + it 'adds user and context for all eligible agents', :aggregate_failures do + agent_authorizations.each do |authorization| + expect(template).to receive(:add_user).with( + name: "agent:#{authorization.agent.id}", + token: "ci:#{authorization.agent.id}:#{build.token}" + ) + + expect(template).to receive(:add_context).with( + name: "#{agent_project.full_path}:#{authorization.agent.name}", + namespace: 'production', + cluster: 'gitlab', + user: "agent:#{authorization.agent.id}" + ) + end + + execute + end + + context 'when environment is specified' do + subject(:execute) { described_class.new(pipeline, token: build.token, environment: 'production').execute } + + it "filters the pipeline's agents by the specified environment" do + expect(::Clusters::Agents::FilterAuthorizationsService).to receive(:new).with( + pipeline.cluster_agent_authorizations, + environment: 'production' + ) + + execute + end end end end diff --git a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb index 1fbefc1fa22..2f2af9f6c85 100644 --- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb +++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Ci::PipelineProcessing::AtomicProcessingService do +RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category: :continuous_integration do describe 'Pipeline Processing Service Tests With Yaml' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } diff --git a/spec/services/ci/pipeline_schedules/calculate_next_run_service_spec.rb b/spec/services/ci/pipeline_schedules/calculate_next_run_service_spec.rb new file mode 100644 index 00000000000..182c5bebbc1 --- /dev/null +++ b/spec/services/ci/pipeline_schedules/calculate_next_run_service_spec.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true +# rubocop:disable Layout/LineLength +require 'spec_helper' + +RSpec.describe Ci::PipelineSchedules::CalculateNextRunService, feature_category: :continuous_integration do + let_it_be(:project) { create(:project, :public, :repository) } + + describe '#execute' do + using RSpec::Parameterized::TableSyntax + + let(:run_service) do + described_class.new(project).execute(pipeline_schedule, + fallback_method: pipeline_schedule.method(:calculate_next_run_at)) + end + + let(:pipeline_schedule) { create(:ci_pipeline_schedule, cron: schedule_cron) } + let(:daily_limit_of_144_runs) { 1.day / 10.minutes } + let(:daily_limit_of_24_runs) { 1.day / 1.hour } + + before do + allow(Settings).to receive(:cron_jobs) { { 'pipeline_schedule_worker' => { 'cron' => worker_cron } } } + create(:plan_limits, :default_plan, ci_daily_pipeline_schedule_triggers: plan_limit) if plan_limit + end + + context "when there is invalid or no plan limits" do + where(:worker_cron, :schedule_cron, :plan_limit, :now, :expected_result) do + '0 1 2 3 *' | '0 1 * * *' | nil | Time.zone.local(2021, 3, 2, 1, 0) | Time.zone.local(2022, 3, 2, 1, 0) + '*/5 * * * *' | '*/1 * * * *' | nil | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 5) + '*/5 * * * *' | '0 * * * *' | nil | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 5) + # 1.day / 2.hours => 12 times a day and it is invalid because there is a minimum for plan limits. + # See: https://docs.gitlab.com/ee/administration/instance_limits.html#limit-the-number-of-pipelines-created-by-a-pipeline-schedule-per-day + '*/5 * * * *' | '0 * * * *' | 1.day / 2.hours | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 5) + end + + with_them do + it 'calls fallback method to get next_run_at' do + travel_to(now) do + expect(pipeline_schedule).to receive(:calculate_next_run_at).and_call_original + + result = run_service + + expect(result).to eq(expected_result) + end + end + end + end + + context "when the workers next run matches schedule's earliest run" do + where(:worker_cron, :schedule_cron, :plan_limit, :now, :expected_result) do + '*/5 * * * *' | '0 * * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0) + '*/5 * * * *' | '*/5 * * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10) + '*/5 * * * *' | '0 1 * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0) + '*/5 * * * *' | '0 2 * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 27, 2, 0) + '*/5 * * * *' | '0 3 * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 27, 3, 0) + '*/5 * * * *' | '0 1 1 * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 1, 1, 0) | Time.zone.local(2021, 6, 1, 1, 0) + '*/9 * * * *' | '0 1 1 * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 1, 1, 9) | Time.zone.local(2021, 6, 1, 1, 0) + '*/5 * * * *' | '45 21 1 2 *' | daily_limit_of_144_runs | Time.zone.local(2021, 2, 1, 21, 45) | Time.zone.local(2022, 2, 1, 21, 45) + end + + with_them do + it 'calculates the next_run_at to be earliest point of match' do + travel_to(now) do + result = run_service + + expect(result).to eq(expected_result) + end + end + end + end + + context "when next_run_at is restricted by plan limit" do + where(:worker_cron, :schedule_cron, :plan_limit, :now, :expected_result) do + '*/5 * * * *' | '59 14 * * *' | daily_limit_of_24_runs | Time.zone.local(2021, 5, 1, 15, 0) | Time.zone.local(2021, 5, 2, 15, 0) + '*/5 * * * *' | '*/1 * * * *' | daily_limit_of_24_runs | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0) + '*/5 * * * *' | '*/1 * * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10) + '*/5 * * * *' | '*/1 * * * *' | (1.day / 7.minutes).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10) + end + + with_them do + it 'calculates the next_run_at based on next available limit' do + travel_to(now) do + result = run_service + + expect(result).to eq(expected_result) + end + end + end + end + + context "when next_run_at is restricted by worker's availability" do + where(:worker_cron, :schedule_cron, :plan_limit, :now, :expected_result) do + '0 1 2 3 *' | '0 1 * * *' | daily_limit_of_144_runs | Time.zone.local(2021, 3, 2, 1, 0) | Time.zone.local(2022, 3, 2, 1, 0) + end + + with_them do + it 'calculates the next_run_at using worker_cron' do + travel_to(now) do + result = run_service + + expect(result).to eq(expected_result) + end + end + end + end + end +end +# rubocop:enable Layout/LineLength diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb index 4b3e774ff3c..4946367380e 100644 --- a/spec/services/ci/pipeline_trigger_service_spec.rb +++ b/spec/services/ci/pipeline_trigger_service_spec.rb @@ -197,8 +197,7 @@ RSpec.describe Ci::PipelineTriggerService do end it_behaves_like 'logs downstream pipeline creation' do - subject { result[:pipeline] } - + let(:downstream_pipeline) { result[:pipeline] } let(:expected_root_pipeline) { pipeline } let(:expected_hierarchy_size) { 2 } let(:expected_downstream_relationship) { :multi_project } diff --git a/spec/services/ci/pipelines/add_job_service_spec.rb b/spec/services/ci/pipelines/add_job_service_spec.rb index e735b2752d9..c62aa9506bd 100644 --- a/spec/services/ci/pipelines/add_job_service_spec.rb +++ b/spec/services/ci/pipelines/add_job_service_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Ci::Pipelines::AddJobService do include ExclusiveLeaseHelpers - let_it_be(:pipeline) { create(:ci_pipeline) } + let_it_be_with_reload(:pipeline) { create(:ci_pipeline) } let(:job) { build(:ci_build) } @@ -35,7 +35,7 @@ RSpec.describe Ci::Pipelines::AddJobService do end it 'assigns partition_id to job and metadata' do - pipeline.partition_id = 123 + pipeline.partition_id = ci_testing_partition_id expect { execute } .to change(job, :partition_id).to(pipeline.partition_id) diff --git a/spec/services/ci/process_build_service_spec.rb b/spec/services/ci/process_build_service_spec.rb index 9301098b083..de308bb1a87 100644 --- a/spec/services/ci/process_build_service_spec.rb +++ b/spec/services/ci/process_build_service_spec.rb @@ -19,31 +19,25 @@ RSpec.describe Ci::ProcessBuildService, '#execute' do end end - shared_context 'with ci_retry_job_fix disabled' do - before do - stub_feature_flags(ci_retry_job_fix: false) - end - end - context 'for single build' do let!(:build) { create(:ci_build, *[trait].compact, :created, **conditions, pipeline: pipeline) } - where(:trait, :conditions, :current_status, :after_status, :retry_after_status, :retry_disabled_after_status) do - nil | { when: :on_success } | 'success' | 'pending' | 'pending' | 'pending' - nil | { when: :on_success } | 'skipped' | 'pending' | 'pending' | 'pending' - nil | { when: :on_success } | 'failed' | 'skipped' | 'skipped' | 'skipped' - nil | { when: :on_failure } | 'success' | 'skipped' | 'skipped' | 'skipped' - nil | { when: :on_failure } | 'skipped' | 'skipped' | 'skipped' | 'skipped' - nil | { when: :on_failure } | 'failed' | 'pending' | 'pending' | 'pending' - nil | { when: :always } | 'success' | 'pending' | 'pending' | 'pending' - nil | { when: :always } | 'skipped' | 'pending' | 'pending' | 'pending' - nil | { when: :always } | 'failed' | 'pending' | 'pending' | 'pending' - :actionable | { when: :manual } | 'success' | 'manual' | 'pending' | 'manual' - :actionable | { when: :manual } | 'skipped' | 'manual' | 'pending' | 'manual' - :actionable | { when: :manual } | 'failed' | 'skipped' | 'skipped' | 'skipped' - :schedulable | { when: :delayed } | 'success' | 'scheduled' | 'pending' | 'scheduled' - :schedulable | { when: :delayed } | 'skipped' | 'scheduled' | 'pending' | 'scheduled' - :schedulable | { when: :delayed } | 'failed' | 'skipped' | 'skipped' | 'skipped' + where(:trait, :conditions, :current_status, :after_status, :retry_after_status) do + nil | { when: :on_success } | 'success' | 'pending' | 'pending' + nil | { when: :on_success } | 'skipped' | 'pending' | 'pending' + nil | { when: :on_success } | 'failed' | 'skipped' | 'skipped' + nil | { when: :on_failure } | 'success' | 'skipped' | 'skipped' + nil | { when: :on_failure } | 'skipped' | 'skipped' | 'skipped' + nil | { when: :on_failure } | 'failed' | 'pending' | 'pending' + nil | { when: :always } | 'success' | 'pending' | 'pending' + nil | { when: :always } | 'skipped' | 'pending' | 'pending' + nil | { when: :always } | 'failed' | 'pending' | 'pending' + :actionable | { when: :manual } | 'success' | 'manual' | 'pending' + :actionable | { when: :manual } | 'skipped' | 'manual' | 'pending' + :actionable | { when: :manual } | 'failed' | 'skipped' | 'skipped' + :schedulable | { when: :delayed } | 'success' | 'scheduled' | 'pending' + :schedulable | { when: :delayed } | 'skipped' | 'scheduled' | 'pending' + :schedulable | { when: :delayed } | 'failed' | 'skipped' | 'skipped' end with_them do @@ -57,14 +51,6 @@ RSpec.describe Ci::ProcessBuildService, '#execute' do it 'updates the job status to retry_after_status' do expect { subject }.to change { build.status }.to(retry_after_status) end - - context 'when feature flag ci_retry_job_fix is disabled' do - include_context 'with ci_retry_job_fix disabled' - - it "updates the job status to retry_disabled_after_status" do - expect { subject }.to change { build.status }.to(retry_disabled_after_status) - end - end end end end @@ -84,15 +70,15 @@ RSpec.describe Ci::ProcessBuildService, '#execute' do let!(:other_build) { create(:ci_build, :created, when: :on_success, pipeline: pipeline) } - where(:trait, :build_when, :current_status, :after_status, :retry_after_status, :retry_disabled_after_status) do - nil | :on_success | 'success' | 'pending' | 'pending' | 'pending' - nil | :on_success | 'skipped' | 'skipped' | 'skipped' | 'skipped' - nil | :manual | 'success' | 'manual' | 'pending' | 'manual' - nil | :manual | 'skipped' | 'skipped' | 'skipped' | 'skipped' - nil | :delayed | 'success' | 'manual' | 'pending' | 'manual' - nil | :delayed | 'skipped' | 'skipped' | 'skipped' | 'skipped' - :schedulable | :delayed | 'success' | 'scheduled' | 'pending' | 'scheduled' - :schedulable | :delayed | 'skipped' | 'skipped' | 'skipped' | 'skipped' + where(:trait, :build_when, :current_status, :after_status, :retry_after_status) do + nil | :on_success | 'success' | 'pending' | 'pending' + nil | :on_success | 'skipped' | 'skipped' | 'skipped' + nil | :manual | 'success' | 'manual' | 'pending' + nil | :manual | 'skipped' | 'skipped' | 'skipped' + nil | :delayed | 'success' | 'manual' | 'pending' + nil | :delayed | 'skipped' | 'skipped' | 'skipped' + :schedulable | :delayed | 'success' | 'scheduled' | 'pending' + :schedulable | :delayed | 'skipped' | 'skipped' | 'skipped' end with_them do @@ -106,14 +92,6 @@ RSpec.describe Ci::ProcessBuildService, '#execute' do it 'updates the job status to retry_after_status' do expect { subject }.to change { build.status }.to(retry_after_status) end - - context 'when feature flag ci_retry_job_fix is disabled' do - include_context 'with ci_retry_job_fix disabled' - - it "updates the job status to retry_disabled_after_status" do - expect { subject }.to change { build.status }.to(retry_disabled_after_status) - end - end end end end diff --git a/spec/services/ci/after_requeue_job_service_spec.rb b/spec/services/ci/reset_skipped_jobs_service_spec.rb index e6f46fb9ebe..712a21e665b 100644 --- a/spec/services/ci/after_requeue_job_service_spec.rb +++ b/spec/services/ci/reset_skipped_jobs_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do +RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :continuous_integration do let_it_be(:project) { create(:project, :empty_repo) } let_it_be(:user) { project.first_owner } @@ -12,9 +12,9 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do subject(:service) { described_class.new(project, user) } - context 'stage-dag mixed pipeline' do + context 'with a stage-dag mixed pipeline' do let(:config) do - <<-EOY + <<-YAML stages: [a, b, c] a1: @@ -49,7 +49,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do c2: stage: c script: exit 0 - EOY + YAML end let(:pipeline) do @@ -150,9 +150,9 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do end end - context 'stage-dag mixed pipeline with some same-stage needs' do + context 'with stage-dag mixed pipeline with some same-stage needs' do let(:config) do - <<-EOY + <<-YAML stages: [a, b, c] a1: @@ -181,7 +181,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do c2: stage: c script: exit 0 - EOY + YAML end let(:pipeline) do @@ -239,7 +239,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do context 'with same-stage needs' do let(:config) do - <<-EOY + <<-YAML a: script: exit $(($RANDOM % 2)) @@ -250,7 +250,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do c: script: exit 0 needs: [b] - EOY + YAML end let(:pipeline) do diff --git a/spec/services/ci/retry_job_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb index 540e700efa6..c3d80f2cb56 100644 --- a/spec/services/ci/retry_job_service_spec.rb +++ b/spec/services/ci/retry_job_service_spec.rb @@ -48,12 +48,6 @@ RSpec.describe Ci::RetryJobService do end end - shared_context 'with ci_retry_job_fix disabled' do - before do - stub_feature_flags(ci_retry_job_fix: false) - end - end - shared_examples_for 'clones the job' do let(:job) { job_to_clone } @@ -284,14 +278,6 @@ RSpec.describe Ci::RetryJobService do with_them do it_behaves_like 'checks enqueue_immediately?' - - context 'when feature flag is disabled' do - include_context 'with ci_retry_job_fix disabled' - - it_behaves_like 'checks enqueue_immediately?' do - let(:enqueue_immediately) { false } - end - end end end end @@ -384,15 +370,6 @@ RSpec.describe Ci::RetryJobService do expect(subject).to be_success expect(new_job.status).to eq after_status end - - context 'when feature flag is disabled' do - include_context 'with ci_retry_job_fix disabled' - - it 'enqueues the new job' do - expect(subject).to be_success - expect(new_job).to be_pending - end - end end end @@ -435,15 +412,6 @@ RSpec.describe Ci::RetryJobService do expect(subject).to be_success expect(new_job.status).to eq after_status end - - context 'when feature flag is disabled' do - include_context 'with ci_retry_job_fix disabled' - - it 'enqueues the new job' do - expect(subject).to be_success - expect(new_job).to be_pending - end - end end end @@ -487,19 +455,6 @@ RSpec.describe Ci::RetryJobService do end it_behaves_like 'checks enqueue_immediately?' - - context 'when feature flag is disabled' do - include_context 'with ci_retry_job_fix disabled' - - it 'enqueues the new job' do - expect(subject).to be_success - expect(new_job).to be_pending - end - - it_behaves_like 'checks enqueue_immediately?' do - let(:enqueue_immediately) { false } - end - end end end end diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index 77345096537..07518c35fab 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -349,7 +349,10 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do it 'marks source bridge as pending' do expect { service.execute(pipeline) }.to change { bridge.reload.status }.to('pending') - .and not_change { bridge.reload.user } + end + + it 'assigns the current user to the source bridge' do + expect { service.execute(pipeline) }.to change { bridge.reload.user }.to(user) end end end diff --git a/spec/services/ci/runners/assign_runner_service_spec.rb b/spec/services/ci/runners/assign_runner_service_spec.rb index 08bb99830fb..92f6db2bdfb 100644 --- a/spec/services/ci/runners/assign_runner_service_spec.rb +++ b/spec/services/ci/runners/assign_runner_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::Ci::Runners::AssignRunnerService, '#execute' do +RSpec.describe ::Ci::Runners::AssignRunnerService, '#execute', feature_category: :runner_fleet do subject(:execute) { described_class.new(runner, project, user).execute } let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) } diff --git a/spec/services/ci/runners/bulk_delete_runners_service_spec.rb b/spec/services/ci/runners/bulk_delete_runners_service_spec.rb index fa8af1100df..5e697565972 100644 --- a/spec/services/ci/runners/bulk_delete_runners_service_spec.rb +++ b/spec/services/ci/runners/bulk_delete_runners_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::Ci::Runners::BulkDeleteRunnersService, '#execute' do +RSpec.describe ::Ci::Runners::BulkDeleteRunnersService, '#execute', feature_category: :runner_fleet do subject(:execute) { described_class.new(**service_args).execute } let_it_be(:admin_user) { create(:user, :admin) } diff --git a/spec/services/ci/runners/process_runner_version_update_service_spec.rb b/spec/services/ci/runners/process_runner_version_update_service_spec.rb index b885138fc7a..d2a7e87b2d5 100644 --- a/spec/services/ci/runners/process_runner_version_update_service_spec.rb +++ b/spec/services/ci/runners/process_runner_version_update_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Ci::Runners::ProcessRunnerVersionUpdateService do +RSpec.describe Ci::Runners::ProcessRunnerVersionUpdateService, feature_category: :runner_fleet do subject(:service) { described_class.new(version) } let(:version) { '1.0.0' } diff --git a/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb b/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb index 1690190320a..39082b5c0f4 100644 --- a/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb +++ b/spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute' do +RSpec.describe ::Ci::Runners::ReconcileExistingRunnerVersionsService, '#execute', feature_category: :runner_fleet do include RunnerReleasesHelper subject(:execute) { described_class.new.execute } diff --git a/spec/services/ci/runners/register_runner_service_spec.rb b/spec/services/ci/runners/register_runner_service_spec.rb index 2d1b109072f..47d399cb19a 100644 --- a/spec/services/ci/runners/register_runner_service_spec.rb +++ b/spec/services/ci/runners/register_runner_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute' do +RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute', feature_category: :runner_fleet do let(:registration_token) { 'abcdefg123456' } let(:token) {} let(:args) { {} } diff --git a/spec/services/ci/runners/reset_registration_token_service_spec.rb b/spec/services/ci/runners/reset_registration_token_service_spec.rb index 79059712032..c8115236034 100644 --- a/spec/services/ci/runners/reset_registration_token_service_spec.rb +++ b/spec/services/ci/runners/reset_registration_token_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::Ci::Runners::ResetRegistrationTokenService, '#execute' do +RSpec.describe ::Ci::Runners::ResetRegistrationTokenService, '#execute', feature_category: :runner_fleet do subject(:execute) { described_class.new(scope, current_user).execute } let_it_be(:user) { build(:user) } diff --git a/spec/services/ci/runners/set_runner_associated_projects_service_spec.rb b/spec/services/ci/runners/set_runner_associated_projects_service_spec.rb index e5cba80d567..9921f9322bd 100644 --- a/spec/services/ci/runners/set_runner_associated_projects_service_spec.rb +++ b/spec/services/ci/runners/set_runner_associated_projects_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::Ci::Runners::SetRunnerAssociatedProjectsService, '#execute' do +RSpec.describe ::Ci::Runners::SetRunnerAssociatedProjectsService, '#execute', feature_category: :runner_fleet do subject(:execute) { described_class.new(runner: runner, current_user: user, project_ids: project_ids).execute } let_it_be(:owner_project) { create(:project) } diff --git a/spec/services/ci/runners/unassign_runner_service_spec.rb b/spec/services/ci/runners/unassign_runner_service_spec.rb index cf710cf6893..e91d4249473 100644 --- a/spec/services/ci/runners/unassign_runner_service_spec.rb +++ b/spec/services/ci/runners/unassign_runner_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::Ci::Runners::UnassignRunnerService, '#execute' do +RSpec.describe ::Ci::Runners::UnassignRunnerService, '#execute', feature_category: :runner_fleet do let_it_be(:project) { create(:project) } let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) } diff --git a/spec/services/ci/runners/unregister_runner_service_spec.rb b/spec/services/ci/runners/unregister_runner_service_spec.rb index 77fc299e4e1..fb779e1a673 100644 --- a/spec/services/ci/runners/unregister_runner_service_spec.rb +++ b/spec/services/ci/runners/unregister_runner_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::Ci::Runners::UnregisterRunnerService, '#execute' do +RSpec.describe ::Ci::Runners::UnregisterRunnerService, '#execute', feature_category: :runner_fleet do subject(:execute) { described_class.new(runner, 'some_token').execute } let(:runner) { create(:ci_runner) } diff --git a/spec/services/ci/runners/update_runner_service_spec.rb b/spec/services/ci/runners/update_runner_service_spec.rb index 1f953ac4cbb..86875df70a2 100644 --- a/spec/services/ci/runners/update_runner_service_spec.rb +++ b/spec/services/ci/runners/update_runner_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Ci::Runners::UpdateRunnerService, '#execute' do +RSpec.describe Ci::Runners::UpdateRunnerService, '#execute', feature_category: :runner_fleet do subject(:execute) { described_class.new(runner).execute(params) } let(:runner) { create(:ci_runner) } diff --git a/spec/services/ci/test_failure_history_service_spec.rb b/spec/services/ci/test_failure_history_service_spec.rb index c19df6e217b..10f6c6f5007 100644 --- a/spec/services/ci/test_failure_history_service_spec.rb +++ b/spec/services/ci/test_failure_history_service_spec.rb @@ -3,16 +3,19 @@ require 'spec_helper' RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do - describe '#execute' do - let(:project) { create(:project) } - let(:pipeline) { create(:ci_empty_pipeline, status: :created, project: project) } + let_it_be(:project) { create(:project, :repository) } + + let_it_be_with_reload(:pipeline) do + create(:ci_pipeline, status: :created, project: project, ref: project.default_branch) + end + describe '#execute' do subject(:execute_service) { described_class.new(pipeline).execute } context 'when pipeline has failed builds with test reports' do - before do + let_it_be(:job) do # The test report has 2 unit test failures - create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project) + create(:ci_build, :failed, :test_reports, pipeline: pipeline) end it 'creates unit test failures records' do @@ -22,6 +25,14 @@ RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do expect(Ci::UnitTestFailure.count).to eq(2) end + it 'assigns partition_id to Ci::UnitTestFailure' do + execute_service + + unit_test_failure_partition_ids = Ci::UnitTestFailure.distinct.pluck(:partition_id) + + expect(unit_test_failure_partition_ids).to match_array([job.partition_id]) + end + context 'when pipeline is not for the default branch' do before do pipeline.update_column(:ref, 'new-feature') @@ -67,7 +78,7 @@ RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do # This other test report has 1 unique unit test failure which brings us to 3 total failures across all builds # thus exceeding the limit of 2 for MAX_TRACKABLE_FAILURES - create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline, project: project) + create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline) end it 'does not persist data' do @@ -82,7 +93,7 @@ RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do context 'when test failure data have duplicates within the same payload (happens when the JUnit report has duplicate unit test names but have different failures)' do before do # The test report has 2 unit test failures but with the same unit test keys - create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline, project: project) + create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline) end it 'does not fail but does not persist duplicate data' do @@ -95,8 +106,8 @@ RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do context 'when pipeline has no failed builds with test reports' do before do - create(:ci_build, :test_reports, pipeline: pipeline, project: project) - create(:ci_build, :failed, pipeline: pipeline, project: project) + create(:ci_build, :test_reports, pipeline: pipeline) + create(:ci_build, :failed, pipeline: pipeline) end it 'does not persist data' do @@ -109,14 +120,10 @@ RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do end describe '#should_track_failures?' do - let(:project) { create(:project, :repository) } - let(:pipeline) { create(:ci_empty_pipeline, status: :created, project: project, ref: project.default_branch) } - subject { described_class.new(pipeline).should_track_failures? } - before do - create(:ci_build, :test_reports, :failed, pipeline: pipeline, project: project) - create(:ci_build, :test_reports, :failed, pipeline: pipeline, project: project) + let_it_be(:jobs) do + create_list(:ci_build, 2, :test_reports, :failed, pipeline: pipeline) end context 'when feature flag is enabled and pipeline ref is the default branch' do diff --git a/spec/services/ci/track_failed_build_service_spec.rb b/spec/services/ci/track_failed_build_service_spec.rb index d83e56f0669..676769d2fc7 100644 --- a/spec/services/ci/track_failed_build_service_spec.rb +++ b/spec/services/ci/track_failed_build_service_spec.rb @@ -21,19 +21,22 @@ RSpec.describe Ci::TrackFailedBuildService do expect(response.success?).to be true + context = { + schema: described_class::SCHEMA_URL, + data: { + build_id: build.id, + build_name: build.name, + build_artifact_types: ["sast"], + exit_code: exit_code, + failure_reason: failure_reason, + project: project.id + } + } + expect_snowplow_event( category: 'ci::build', action: 'failed', - context: [{ - schema: described_class::SCHEMA_URL, - data: { - build_id: build.id, - build_name: build.name, - build_artifact_types: ["sast"], - exit_code: exit_code, - failure_reason: failure_reason - } - }], + context: [context], user: user, project: project.id) end diff --git a/spec/services/ci/unlock_artifacts_service_spec.rb b/spec/services/ci/unlock_artifacts_service_spec.rb index f21afc7fe9e..c15e1cb2b5d 100644 --- a/spec/services/ci/unlock_artifacts_service_spec.rb +++ b/spec/services/ci/unlock_artifacts_service_spec.rb @@ -5,11 +5,11 @@ require 'spec_helper' RSpec.describe Ci::UnlockArtifactsService do using RSpec::Parameterized::TableSyntax - where(:tag, :ci_update_unlocked_job_artifacts) do - false | false - false | true - true | false - true | true + where(:tag) do + [ + [false], + [true] + ] end with_them do @@ -31,7 +31,6 @@ RSpec.describe Ci::UnlockArtifactsService do before do stub_const("#{described_class}::BATCH_SIZE", 1) - stub_feature_flags(ci_update_unlocked_job_artifacts: ci_update_unlocked_job_artifacts) end describe '#execute' do @@ -69,17 +68,11 @@ RSpec.describe Ci::UnlockArtifactsService do end it 'unlocks job artifact records' do - pending unless ci_update_unlocked_job_artifacts - expect { execute }.to change { ::Ci::JobArtifact.artifact_unlocked.count }.from(0).to(2) end it 'unlocks pipeline artifact records' do - if ci_update_unlocked_job_artifacts - expect { execute }.to change { ::Ci::PipelineArtifact.artifact_unlocked.count }.from(0).to(1) - else - expect { execute }.not_to change { ::Ci::PipelineArtifact.artifact_unlocked.count } - end + expect { execute }.to change { ::Ci::PipelineArtifact.artifact_unlocked.count }.from(0).to(1) end end @@ -111,17 +104,11 @@ RSpec.describe Ci::UnlockArtifactsService do end it 'unlocks job artifact records' do - pending unless ci_update_unlocked_job_artifacts - expect { execute }.to change { ::Ci::JobArtifact.artifact_unlocked.count }.from(0).to(8) end it 'unlocks pipeline artifact records' do - if ci_update_unlocked_job_artifacts - expect { execute }.to change { ::Ci::PipelineArtifact.artifact_unlocked.count }.from(0).to(1) - else - expect { execute }.not_to change { ::Ci::PipelineArtifact.artifact_unlocked.count } - end + expect { execute }.to change { ::Ci::PipelineArtifact.artifact_unlocked.count }.from(0).to(1) end end end |