diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-20 12:55:51 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-20 12:55:51 +0300 |
commit | e8d2c2579383897a1dd7f9debd359abe8ae8373d (patch) | |
tree | c42be41678c2586d49a75cabce89322082698334 /spec/services/ci | |
parent | fc845b37ec3a90aaa719975f607740c22ba6a113 (diff) |
Add latest changes from gitlab-org/gitlab@14-1-stable-eev14.1.0-rc42
Diffstat (limited to 'spec/services/ci')
23 files changed, 538 insertions, 193 deletions
diff --git a/spec/services/ci/after_requeue_job_service_spec.rb b/spec/services/ci/after_requeue_job_service_spec.rb index a2147759dba..f8c49060ce0 100644 --- a/spec/services/ci/after_requeue_job_service_spec.rb +++ b/spec/services/ci/after_requeue_job_service_spec.rb @@ -8,9 +8,9 @@ RSpec.describe Ci::AfterRequeueJobService do let(:pipeline) { create(:ci_pipeline, project: project) } - let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 0) } 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') } subject(:execute_service) { described_class.new(project, user).execute(build) } @@ -24,6 +24,34 @@ RSpec.describe Ci::AfterRequeueJobService do expect(test2.reload).to be_created end + context 'when there is a job need from the same stage' do + let!(:test3) do + create(:ci_build, + :skipped, + pipeline: pipeline, + stage_idx: 0, + scheduling_type: :dag) + end + + before do + create(:ci_build_need, build: test3, name: 'build') + end + + it 'marks subsequent skipped jobs as processable' do + expect { execute_service }.to change { test3.reload.status }.from('skipped').to('created') + end + + context 'with ci_same_stage_job_needs FF disabled' do + before do + stub_feature_flags(ci_same_stage_job_needs: false) + end + + it 'does nothing with the build' do + expect { execute_service }.not_to change { test3.reload.status } + end + end + end + context 'when the pipeline is a downstream pipeline and the bridge is depended' do let!(:trigger_job) { create(:ci_bridge, :strategy_depend, status: 'success') } diff --git a/spec/services/ci/append_build_trace_service_spec.rb b/spec/services/ci/append_build_trace_service_spec.rb index 8812680b665..b251f00158f 100644 --- a/spec/services/ci/append_build_trace_service_spec.rb +++ b/spec/services/ci/append_build_trace_service_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Ci::AppendBuildTraceService do let_it_be(:project) { create(:project) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } - let_it_be(:build) { create(:ci_build, :running, pipeline: pipeline) } + let_it_be_with_reload(:build) { create(:ci_build, :running, pipeline: pipeline) } before do stub_feature_flags(ci_enable_live_trace: true) @@ -54,4 +54,46 @@ RSpec.describe Ci::AppendBuildTraceService do expect(result.stream_size).to eq 4 end end + + context 'when the trace size is exceeded' do + before do + project.actual_limits.update!(ci_jobs_trace_size_limit: 1) + end + + it 'returns 403 status code' 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 403 + expect(result.stream_size).to be_nil + expect(build.trace_chunks.count).to eq 0 + 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/archive_trace_service_spec.rb b/spec/services/ci/archive_trace_service_spec.rb index a4f498f17c3..12804efc28c 100644 --- a/spec/services/ci/archive_trace_service_spec.rb +++ b/spec/services/ci/archive_trace_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Ci::ArchiveTraceService, '#execute' do - subject { described_class.new.execute(job, worker_name: ArchiveTraceWorker.name) } + subject { described_class.new.execute(job, worker_name: Ci::ArchiveTraceWorker.name) } context 'when job is finished' do let(:job) { create(:ci_build, :success, :trace_live) } @@ -30,43 +30,17 @@ RSpec.describe Ci::ArchiveTraceService, '#execute' do create(:ci_build_trace_chunk, build: job) end - context 'when the feature flag `erase_traces_from_already_archived_jobs_when_archiving_again` is enabled' do - before do - stub_feature_flags(erase_traces_from_already_archived_jobs_when_archiving_again: true) - end - - it 'removes the trace chunks' do - expect { subject }.to change { job.trace_chunks.count }.to(0) - end - - context 'when associated data does not exist' do - before do - job.job_artifacts_trace.file.remove! - end - - it 'removes the trace artifact' do - expect { subject }.to change { job.reload.job_artifacts_trace }.to(nil) - end - end + it 'removes the trace chunks' do + expect { subject }.to change { job.trace_chunks.count }.to(0) end - context 'when the feature flag `erase_traces_from_already_archived_jobs_when_archiving_again` is disabled' do + context 'when associated data does not exist' do before do - stub_feature_flags(erase_traces_from_already_archived_jobs_when_archiving_again: false) + job.job_artifacts_trace.file.remove! end - it 'does not remove the trace chunks' do - expect { subject }.not_to change { job.trace_chunks.count } - end - - context 'when associated data does not exist' do - before do - job.job_artifacts_trace.file.remove! - end - - it 'does not remove the trace artifact' do - expect { subject }.not_to change { job.reload.job_artifacts_trace } - end + it 'removes the trace artifact' do + expect { subject }.to change { job.reload.job_artifacts_trace }.to(nil) end end end @@ -77,7 +51,7 @@ RSpec.describe Ci::ArchiveTraceService, '#execute' do it 'leaves a warning message in sidekiq log' do expect(Sidekiq.logger).to receive(:warn).with( - class: ArchiveTraceWorker.name, + class: Ci::ArchiveTraceWorker.name, message: 'The job does not have live trace but going to be archived.', job_id: job.id) @@ -94,7 +68,7 @@ RSpec.describe Ci::ArchiveTraceService, '#execute' do it 'leaves a warning message in sidekiq log' do expect(Sidekiq.logger).to receive(:warn).with( - class: ArchiveTraceWorker.name, + class: Ci::ArchiveTraceWorker.name, message: 'The job does not have archived trace after archiving.', job_id: job.id) @@ -114,7 +88,7 @@ RSpec.describe Ci::ArchiveTraceService, '#execute' do job_id: job.id).once expect(Sidekiq.logger).to receive(:warn).with( - class: ArchiveTraceWorker.name, + class: Ci::ArchiveTraceWorker.name, message: "Failed to archive trace. message: Job is not finished yet.", job_id: job.id).and_call_original diff --git a/spec/services/ci/create_pipeline_service/cache_spec.rb b/spec/services/ci/create_pipeline_service/cache_spec.rb index 5f74c2f1cef..f9767a794db 100644 --- a/spec/services/ci/create_pipeline_service/cache_spec.rb +++ b/spec/services/ci/create_pipeline_service/cache_spec.rb @@ -33,11 +33,11 @@ RSpec.describe Ci::CreatePipelineService do it 'uses the provided key' do expected = { - 'key' => 'a-key', - 'paths' => ['logs/', 'binaries/'], - 'policy' => 'pull-push', - 'untracked' => true, - 'when' => 'on_success' + key: 'a-key', + paths: ['logs/', 'binaries/'], + policy: 'pull-push', + untracked: true, + when: 'on_success' } expect(pipeline).to be_persisted @@ -66,10 +66,10 @@ RSpec.describe Ci::CreatePipelineService do it 'builds a cache key' do expected = { - 'key' => /[a-f0-9]{40}/, - 'paths' => ['logs/'], - 'policy' => 'pull-push', - 'when' => 'on_success' + key: /[a-f0-9]{40}/, + paths: ['logs/'], + policy: 'pull-push', + when: 'on_success' } expect(pipeline).to be_persisted @@ -82,10 +82,10 @@ RSpec.describe Ci::CreatePipelineService do it 'uses default cache key' do expected = { - 'key' => /default/, - 'paths' => ['logs/'], - 'policy' => 'pull-push', - 'when' => 'on_success' + key: /default/, + paths: ['logs/'], + policy: 'pull-push', + when: 'on_success' } expect(pipeline).to be_persisted @@ -115,10 +115,10 @@ RSpec.describe Ci::CreatePipelineService do it 'builds a cache key' do expected = { - 'key' => /\$ENV_VAR-[a-f0-9]{40}/, - 'paths' => ['logs/'], - 'policy' => 'pull-push', - 'when' => 'on_success' + key: /\$ENV_VAR-[a-f0-9]{40}/, + paths: ['logs/'], + policy: 'pull-push', + when: 'on_success' } expect(pipeline).to be_persisted @@ -131,10 +131,10 @@ RSpec.describe Ci::CreatePipelineService do it 'uses default cache key' do expected = { - 'key' => /\$ENV_VAR-default/, - 'paths' => ['logs/'], - 'policy' => 'pull-push', - 'when' => 'on_success' + key: /\$ENV_VAR-default/, + paths: ['logs/'], + policy: 'pull-push', + when: 'on_success' } expect(pipeline).to be_persisted 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 7193e5bd7d4..a42770aae20 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 @@ -69,7 +69,7 @@ RSpec.describe Ci::CreatePipelineService do end it 'contains both errors and warnings' do - error_message = 'build job: need test is not defined in prior stages' + error_message = 'build job: need test is not defined in current or prior stages' warning_message = /jobs:test may allow multiple pipelines to run/ expect(pipeline.yaml_errors).to eq(error_message) 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 4cf52223e38..5dceb9f57f0 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 @@ -39,8 +39,8 @@ RSpec.describe Ci::CreatePipelineService do it 'creates a pipeline' do expect(pipeline).to be_persisted expect(pipeline.builds.first.options).to match(a_hash_including({ - 'before_script' => ['ls'], - 'script' => [ + before_script: ['ls'], + script: [ 'echo doing my first step', 'echo doing step 1 of job 1', 'echo doing my last step' 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 0fb500f5729..01df7772eef 100644 --- a/spec/services/ci/create_pipeline_service/dry_run_spec.rb +++ b/spec/services/ci/create_pipeline_service/dry_run_spec.rb @@ -84,7 +84,7 @@ RSpec.describe Ci::CreatePipelineService do it_behaves_like 'returns a non persisted pipeline' it 'returns a pipeline with errors', :aggregate_failures do - error_message = 'build job: need test is not defined in prior stages' + error_message = 'build job: need test is not defined in current or prior stages' expect(subject.error_messages.map(&:content)).to eq([error_message]) expect(subject.errors).not_to be_empty @@ -109,7 +109,7 @@ RSpec.describe Ci::CreatePipelineService do it_behaves_like 'returns a non persisted pipeline' it 'returns a pipeline with errors', :aggregate_failures do - error_message = "'test' job needs 'build' job, but it was not added to the pipeline" + error_message = "'test' job needs 'build' job, but 'build' is not in any previous stage" expect(subject.error_messages.map(&:content)).to eq([error_message]) expect(subject.errors).not_to be_empty 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 new file mode 100644 index 00000000000..df881c1ac8f --- /dev/null +++ b/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::CreatePipelineService do + let_it_be(:group) { create(:group, :private) } + let_it_be(:group_variable) { create(:ci_group_variable, group: group, key: 'RUNNER_TAG', value: 'group')} + let_it_be(:project) { create(:project, :repository, group: group) } + let_it_be(:user) { create(:user) } + + let(:service) { described_class.new(project, user, ref: 'master') } + let(:pipeline) { service.execute(:push) } + let(:job) { pipeline.builds.find_by(name: 'job') } + + before do + project.add_developer(user) + stub_ci_pipeline_yaml_file config + end + + context 'when the variable is set' do + let(:config) do + <<~EOS + variables: + KUBERNETES_RUNNER: kubernetes + + job: + tags: + - docker + - $KUBERNETES_RUNNER + script: + - echo "Hello runner selector feature" + EOS + end + + it 'uses the evaluated variable' do + expect(pipeline).to be_created_successfully + expect(job.tags.pluck(:name)).to match_array(%w[docker kubernetes]) + end + end + + context 'when the tag is composed by two variables' do + let(:config) do + <<~EOS + variables: + CLOUD_PROVIDER: aws + KUBERNETES_RUNNER: kubernetes + ENVIRONMENT_NAME: prod + + job: + tags: + - docker + - $CLOUD_PROVIDER-$KUBERNETES_RUNNER-$ENVIRONMENT_NAME + script: + - echo "Hello runner selector feature" + EOS + end + + it 'uses the evaluated variables' do + expect(pipeline).to be_created_successfully + expect(job.tags.pluck(:name)).to match_array(%w[docker aws-kubernetes-prod]) + end + end + + context 'when the variable is not set' do + let(:config) do + <<~EOS + job: + tags: + - docker + - $KUBERNETES_RUNNER + script: + - echo "Hello runner selector feature" + EOS + end + + it 'uses the variable as a regular string' do + expect(pipeline).to be_created_successfully + expect(job.tags.pluck(:name)).to match_array(%w[docker $KUBERNETES_RUNNER]) + end + end + + context 'when the tag uses group variables' do + let(:config) do + <<~EOS + job: + tags: + - docker + - $RUNNER_TAG + script: + - echo "Hello runner selector feature" + EOS + end + + it 'uses the evaluated variables' do + expect(pipeline).to be_created_successfully + expect(job.tags.pluck(:name)).to match_array(%w[docker group]) + end + end + + context 'when the tag has the same variable name defined for both group and project' do + let_it_be(:project_variable) { create(:ci_variable, project: project, key: 'RUNNER_TAG', value: 'project') } + + let(:config) do + <<~EOS + variables: + RUNNER_TAG: pipeline + job: + tags: + - docker + - $RUNNER_TAG + script: + - echo "Hello runner selector feature" + EOS + end + + it 'uses the project variable instead of group due to variable precedence' do + expect(pipeline).to be_created_successfully + expect(job.tags.pluck(:name)).to match_array(%w[docker project]) + end + end + + context 'with parallel:matrix config' do + let(:tags) { pipeline.builds.map(&:tags).flatten.pluck(:name) } + + let(:config) do + <<~EOS + job: + parallel: + matrix: + - PROVIDER: [aws, gcp] + STACK: [monitoring, backup, app] + tags: + - ${PROVIDER}-${STACK} + script: + - echo "Hello runner selector feature" + EOS + end + + it 'uses the evaluated variables' do + expect(pipeline).to be_created_successfully + expect(tags).to match_array(%w[aws-monitoring aws-backup aws-app gcp-monitoring gcp-backup gcp-app]) + end + end +end diff --git a/spec/services/ci/create_pipeline_service/needs_spec.rb b/spec/services/ci/create_pipeline_service/needs_spec.rb index 3b4a6178b8f..d096db10d0b 100644 --- a/spec/services/ci/create_pipeline_service/needs_spec.rb +++ b/spec/services/ci/create_pipeline_service/needs_spec.rb @@ -104,7 +104,7 @@ RSpec.describe Ci::CreatePipelineService do it 'saves dependencies' do expect(test_a_build.options) - .to match(a_hash_including('dependencies' => ['build_a'])) + .to match(a_hash_including(dependencies: ['build_a'])) end it 'artifacts default to true' do @@ -257,7 +257,7 @@ RSpec.describe Ci::CreatePipelineService do it 'returns error' do expect(pipeline.yaml_errors) - .to eq("'test' job needs 'build' job, but it was not added to the pipeline") + .to eq("'test' job needs 'build' job, but 'build' is not in any previous stage") end context 'when need is optional' 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 512cf546e6a..7a6535ed3fa 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 @@ -69,9 +69,9 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do it_behaves_like 'successful creation' do let(:expected_bridge_options) do { - 'trigger' => { - 'include' => [ - { 'local' => 'path/to/child.yml' } + trigger: { + include: [ + { local: 'path/to/child.yml' } ] } } @@ -149,9 +149,9 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do it_behaves_like 'successful creation' do let(:expected_bridge_options) do { - 'trigger' => { - 'include' => [ - { 'local' => 'path/to/child.yml' } + trigger: { + include: [ + { local: 'path/to/child.yml' } ] } } @@ -175,8 +175,8 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do it_behaves_like 'successful creation' do let(:expected_bridge_options) do { - 'trigger' => { - 'include' => 'path/to/child.yml' + trigger: { + include: 'path/to/child.yml' } } end @@ -202,8 +202,8 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do it_behaves_like 'successful creation' do let(:expected_bridge_options) do { - 'trigger' => { - 'include' => ['path/to/child.yml', 'path/to/child2.yml'] + trigger: { + include: ['path/to/child.yml', 'path/to/child2.yml'] } } end @@ -252,7 +252,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do end it_behaves_like 'creation failure' do - let(:expected_error) { /test job: dependency generator is not defined in prior stages/ } + let(:expected_error) { /test job: dependency generator is not defined in current or prior stages/ } end end @@ -295,12 +295,12 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do it_behaves_like 'successful creation' do let(:expected_bridge_options) do { - 'trigger' => { - 'include' => [ + trigger: { + include: [ { - 'file' => 'path/to/child.yml', - 'project' => 'my-namespace/my-project', - 'ref' => 'master' + file: 'path/to/child.yml', + project: 'my-namespace/my-project', + ref: 'master' } ] } @@ -353,11 +353,11 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do it_behaves_like 'successful creation' do let(:expected_bridge_options) do { - 'trigger' => { - 'include' => [ + trigger: { + include: [ { - 'file' => ["path/to/child1.yml", "path/to/child2.yml"], - 'project' => 'my-namespace/my-project' + file: ["path/to/child1.yml", "path/to/child2.yml"], + project: 'my-namespace/my-project' } ] } diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb index 33ec6aacc44..acdf38bbc13 100644 --- a/spec/services/ci/create_pipeline_service/rules_spec.rb +++ b/spec/services/ci/create_pipeline_service/rules_spec.rb @@ -230,22 +230,6 @@ RSpec.describe Ci::CreatePipelineService do [nil, nil, nil, 'job var 4', nil, nil, 'overridden var 7'] ) end - - context 'when FF ci_workflow_rules_variables is disabled' do - before do - stub_feature_flags(ci_workflow_rules_variables: false) - end - - it 'does not affect workflow variables but job variables' do - expect(job1.scoped_variables.to_hash.values_at(*variable_keys)).to eq( - ['overridden var 1', 'job var 2', nil, 'workflow var 4', 'job var 5', nil, 'workflow var 7'] - ) - - expect(job2.scoped_variables.to_hash.values_at(*variable_keys)).to eq( - [nil, nil, nil, 'job var 4', nil, nil, 'overridden var 7'] - ) - end - end end context 'when matching to the second rule' do diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 3316f8c3d9b..64e8c6ac2df 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -1001,7 +1001,7 @@ RSpec.describe Ci::CreatePipelineService do expect(pipeline.yaml_errors).not_to be_present expect(pipeline).to be_persisted expect(build).to be_kind_of(Ci::Build) - expect(build.options).to eq(config[:release].except(:stage, :only).with_indifferent_access) + expect(build.options).to eq(config[:release].except(:stage, :only)) expect(build).to be_persisted end end @@ -1715,7 +1715,7 @@ RSpec.describe Ci::CreatePipelineService do it 'contains the expected errors' do expect(pipeline.builds).to be_empty - error_message = "'test_a' job needs 'build_a' job, but it was not added to the pipeline" + error_message = "'test_a' job needs 'build_a' job, but 'build_a' is not in any previous stage" expect(pipeline.yaml_errors).to eq(error_message) expect(pipeline.error_messages.map(&:content)).to contain_exactly(error_message) expect(pipeline.errors[:base]).to contain_exactly(error_message) diff --git a/spec/services/ci/destroy_pipeline_service_spec.rb b/spec/services/ci/destroy_pipeline_service_spec.rb index 302233cea5a..588ff0b1762 100644 --- a/spec/services/ci/destroy_pipeline_service_spec.rb +++ b/spec/services/ci/destroy_pipeline_service_spec.rb @@ -67,6 +67,30 @@ RSpec.describe ::Ci::DestroyPipelineService do end end end + + context 'when pipeline is in cancelable state' do + before do + allow(pipeline).to receive(:cancelable?).and_return(true) + end + + it 'cancels the pipeline' do + expect(pipeline).to receive(:cancel_running) + + 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 context 'user is not owner' do diff --git a/spec/services/ci/job_token_scope/add_project_service_spec.rb b/spec/services/ci/job_token_scope/add_project_service_spec.rb new file mode 100644 index 00000000000..ba889465fac --- /dev/null +++ b/spec/services/ci/job_token_scope/add_project_service_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Ci::JobTokenScope::AddProjectService do + let(:service) { described_class.new(project, current_user) } + + let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:target_project) { create(:project) } + let_it_be(:current_user) { create(:user) } + + describe '#execute' do + subject(:result) { service.execute(target_project) } + + it_behaves_like 'editable job token scope' do + context 'when user has permissions on source and target projects' do + before do + project.add_maintainer(current_user) + target_project.add_developer(current_user) + end + + it 'adds the project to the scope' do + expect do + expect(result).to be_success + end.to change { Ci::JobToken::ProjectScopeLink.count }.by(1) + end + end + + context 'when target project is same as the source project' do + before do + project.add_maintainer(current_user) + end + + let(:target_project) { project } + + it_behaves_like 'returns error', "Validation failed: Target project can't be the same as the source project" + end + end + end +end diff --git a/spec/services/ci/job_token_scope/remove_project_service_spec.rb b/spec/services/ci/job_token_scope/remove_project_service_spec.rb new file mode 100644 index 00000000000..238fc879f54 --- /dev/null +++ b/spec/services/ci/job_token_scope/remove_project_service_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Ci::JobTokenScope::RemoveProjectService do + let(:service) { described_class.new(project, current_user) } + + let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:target_project) { create(:project) } + let_it_be(:current_user) { create(:user) } + + let_it_be(:link) do + create(:ci_job_token_project_scope_link, + source_project: project, + target_project: target_project) + end + + describe '#execute' do + subject(:result) { service.execute(target_project) } + + it_behaves_like 'editable job token scope' do + context 'when user has permissions on source and target project' do + before do + project.add_maintainer(current_user) + target_project.add_developer(current_user) + end + + it 'removes the project from the scope' do + expect do + expect(result).to be_success + end.to change { Ci::JobToken::ProjectScopeLink.count }.by(-1) + end + end + + context 'when target project is same as the source project' do + before do + project.add_maintainer(current_user) + end + + let(:target_project) { project } + + it_behaves_like 'returns error', "Source project cannot be removed from the job token scope" + end + end + 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 13c924a3089..5089f8d5dba 100644 --- a/spec/services/ci/pipeline_processing/shared_processing_service.rb +++ b/spec/services/ci/pipeline_processing/shared_processing_service.rb @@ -842,20 +842,6 @@ RSpec.shared_examples 'Pipeline Processing Service' do expect(all_builds.manual).to contain_exactly(linux_build) expect(all_builds.skipped).to contain_exactly(deploy) end - - context 'when FF ci_fix_pipeline_status_for_dag_needs_manual is disabled' do - before do - stub_feature_flags(ci_fix_pipeline_status_for_dag_needs_manual: false) - end - - it 'makes deploy DAG to be waiting for optional manual to finish' do - expect(process_pipeline).to be_truthy - - expect(stages).to eq(%w(skipped created)) - expect(all_builds.manual).to contain_exactly(linux_build) - expect(all_builds.created).to contain_exactly(deploy) - end - end end context 'when a bridge job has parallel:matrix config', :sidekiq_inline do diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_same_stages.yml b/spec/services/ci/pipeline_processing/test_cases/dag_same_stages.yml new file mode 100644 index 00000000000..2a63daeb561 --- /dev/null +++ b/spec/services/ci/pipeline_processing/test_cases/dag_same_stages.yml @@ -0,0 +1,47 @@ +config: + build: + stage: test + script: exit 0 + + test: + stage: test + script: exit 0 + needs: [build] + + deploy: + stage: test + script: exit 0 + needs: [test] + +init: + expect: + pipeline: pending + stages: + test: pending + jobs: + build: pending + test: created + deploy: created + +transitions: + - event: success + jobs: [build] + expect: + pipeline: running + stages: + test: running + jobs: + build: success + test: pending + deploy: created + + - event: success + jobs: [test] + expect: + pipeline: running + stages: + test: running + jobs: + build: success + test: success + deploy: pending diff --git a/spec/services/ci/pipelines/add_job_service_spec.rb b/spec/services/ci/pipelines/add_job_service_spec.rb new file mode 100644 index 00000000000..a72ffbfdc87 --- /dev/null +++ b/spec/services/ci/pipelines/add_job_service_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::Pipelines::AddJobService do + let_it_be(:pipeline) { create(:ci_pipeline) } + + let(:job) { build(:ci_build) } + + subject(:service) { described_class.new(pipeline) } + + context 'when the pipeline is not persisted' do + let(:pipeline) { build(:ci_pipeline) } + + it 'raises error' do + expect { service }.to raise_error('Pipeline must be persisted for this service to be used') + end + end + + describe '#execute!' do + subject(:execute) do + service.execute!(job) do |job| + job.save! + end + end + + it 'assigns pipeline attributes to the job' do + expect do + execute + end.to change { job.slice(:pipeline, :project, :ref) }.to( + pipeline: pipeline, project: pipeline.project, ref: pipeline.ref + ) + end + + it 'returns a service response with the job as payload' do + expect(execute).to be_success + expect(execute.payload[:job]).to eq(job) + end + + it 'calls update_older_statuses_retried!' do + expect(job).to receive(:update_older_statuses_retried!) + + execute + end + + context 'when the block raises an error' do + subject(:execute) do + service.execute!(job) do |job| + raise "this is an error" + end + end + + it 'returns a service response with the error and the job as payload' do + expect(execute).to be_error + expect(execute.payload[:job]).to eq(job) + expect(execute.message).to eq('this is an error') + end + end + + context 'when the FF ci_fix_commit_status_retried is disabled' do + before do + stub_feature_flags(ci_fix_commit_status_retried: false) + end + + it 'does not call update_older_statuses_retried!' do + expect(job).not_to receive(:update_older_statuses_retried!) + + execute + end + end + end +end diff --git a/spec/services/ci/play_bridge_service_spec.rb b/spec/services/ci/play_bridge_service_spec.rb index d6130325b5a..3f97bfdf5ae 100644 --- a/spec/services/ci/play_bridge_service_spec.rb +++ b/spec/services/ci/play_bridge_service_spec.rb @@ -45,16 +45,6 @@ RSpec.describe Ci::PlayBridgeService, '#execute' do it 'marks the subsequent job as processable' do expect { execute_service }.to change { job.reload.status }.from('skipped').to('created') end - - context 'when the FF ci_fix_pipeline_status_for_dag_needs_manual is disabled' do - before do - stub_feature_flags(ci_fix_pipeline_status_for_dag_needs_manual: false) - end - - it 'does not change the subsequent job' do - expect { execute_service }.not_to change { job.reload.status }.from('skipped') - end - end end context 'when bridge is not playable' do diff --git a/spec/services/ci/play_build_service_spec.rb b/spec/services/ci/play_build_service_spec.rb index 78de91675f9..babd601e0cf 100644 --- a/spec/services/ci/play_build_service_spec.rb +++ b/spec/services/ci/play_build_service_spec.rb @@ -71,16 +71,6 @@ RSpec.describe Ci::PlayBuildService, '#execute' do it 'marks the subsequent job as processable' do expect { service.execute(build) }.to change { job.reload.status }.from('skipped').to('created') end - - context 'when the FF ci_fix_pipeline_status_for_dag_needs_manual is disabled' do - before do - stub_feature_flags(ci_fix_pipeline_status_for_dag_needs_manual: false) - end - - it 'does not change the subsequent job' do - expect { service.execute(build) }.not_to change { job.reload.status }.from('skipped') - end - end end context 'when variables are supplied' do diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index c4b1e2133ed..6e5d7725a7a 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -145,7 +145,7 @@ module Ci context 'when using DEFCON mode that disables fair scheduling' do before do - stub_feature_flags(ci_queueing_disaster_recovery: true) + stub_feature_flags(ci_queueing_disaster_recovery_disable_fair_scheduling: true) end context 'when all builds are pending' do @@ -269,51 +269,31 @@ module Ci let!(:unrelated_group_runner) { create(:ci_runner, :group, groups: [unrelated_group]) } it 'does not consider builds from other group runners' do - expect(described_class.new(group_runner).send(:builds_for_group_runner).count).to eq 6 + queue = ::Ci::Queue::BuildQueueService.new(group_runner) + + expect(queue.builds_for_group_runner.size).to eq 6 execute(group_runner) - expect(described_class.new(group_runner).send(:builds_for_group_runner).count).to eq 5 + expect(queue.builds_for_group_runner.size).to eq 5 execute(group_runner) - expect(described_class.new(group_runner).send(:builds_for_group_runner).count).to eq 4 + expect(queue.builds_for_group_runner.size).to eq 4 execute(group_runner) - expect(described_class.new(group_runner).send(:builds_for_group_runner).count).to eq 3 + expect(queue.builds_for_group_runner.size).to eq 3 execute(group_runner) - expect(described_class.new(group_runner).send(:builds_for_group_runner).count).to eq 2 + expect(queue.builds_for_group_runner.size).to eq 2 execute(group_runner) - expect(described_class.new(group_runner).send(:builds_for_group_runner).count).to eq 1 + expect(queue.builds_for_group_runner.size).to eq 1 execute(group_runner) - expect(described_class.new(group_runner).send(:builds_for_group_runner).count).to eq 0 + expect(queue.builds_for_group_runner.size).to eq 0 expect(execute(group_runner)).to be_nil end end - context 'when the use_distinct_in_register_job_object_hierarchy feature flag is enabled' do - before do - stub_feature_flags(use_distinct_in_register_job_object_hierarchy: true) - stub_feature_flags(use_distinct_for_all_object_hierarchy: true) - end - - it 'calls DISTINCT' do - expect(described_class.new(group_runner).send(:builds_for_group_runner).to_sql).to include("DISTINCT") - end - end - - context 'when the use_distinct_in_register_job_object_hierarchy feature flag is disabled' do - before do - stub_feature_flags(use_distinct_in_register_job_object_hierarchy: false) - stub_feature_flags(use_distinct_for_all_object_hierarchy: false) - end - - it 'does not call DISTINCT' do - expect(described_class.new(group_runner).send(:builds_for_group_runner).to_sql).not_to include("DISTINCT") - end - end - context 'group runner' do let(:build) { execute(group_runner) } @@ -349,8 +329,9 @@ module Ci let!(:other_build) { create(:ci_build, :pending, :queued, pipeline: pipeline) } before do - allow_any_instance_of(Ci::RegisterJobService).to receive(:builds_for_project_runner) - .and_return(Ci::Build.where(id: [pending_job, other_build])) + allow_any_instance_of(::Ci::Queue::BuildQueueService) + .to receive(:execute) + .and_return(Ci::Build.where(id: [pending_job, other_build]).pluck(:id)) end it "receives second build from the queue" do @@ -361,8 +342,9 @@ module Ci context 'when single build is in queue' do before do - allow_any_instance_of(Ci::RegisterJobService).to receive(:builds_for_project_runner) - .and_return(Ci::Build.where(id: pending_job)) + allow_any_instance_of(::Ci::Queue::BuildQueueService) + .to receive(:execute) + .and_return(Ci::Build.where(id: pending_job).pluck(:id)) end it "does not receive any valid result" do @@ -372,8 +354,9 @@ module Ci context 'when there is no build in queue' do before do - allow_any_instance_of(Ci::RegisterJobService).to receive(:builds_for_project_runner) - .and_return(Ci::Build.none) + allow_any_instance_of(::Ci::Queue::BuildQueueService) + .to receive(:execute) + .and_return([]) end it "does not receive builds but result is valid" do @@ -721,17 +704,17 @@ module Ci include_examples 'handles runner assignment' end - context 'when joining with pending builds table' do + context 'when using pending builds table' do before do - stub_feature_flags(ci_pending_builds_queue_join: true) + stub_feature_flags(ci_pending_builds_queue_source: true) end include_examples 'handles runner assignment' end - context 'when not joining with pending builds table' do + context 'when not using pending builds table' do before do - stub_feature_flags(ci_pending_builds_queue_join: false) + stub_feature_flags(ci_pending_builds_queue_source: false) end include_examples 'handles runner assignment' diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index c71bec31984..42d6e66b38b 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -30,7 +30,7 @@ RSpec.describe Ci::RetryBuildService do project.add_reporter(reporter) end - clone_accessors = described_class.clone_accessors + clone_accessors = described_class.clone_accessors.without(described_class.extra_accessors) reject_accessors = %i[id status user token token_encrypted coverage trace runner @@ -39,7 +39,7 @@ RSpec.describe Ci::RetryBuildService do erased_at auto_canceled_by job_artifacts job_artifacts_archive job_artifacts_metadata job_artifacts_trace job_artifacts_junit job_artifacts_sast job_artifacts_secret_detection job_artifacts_dependency_scanning - job_artifacts_container_scanning job_artifacts_dast + job_artifacts_container_scanning job_artifacts_cluster_image_scanning job_artifacts_dast job_artifacts_license_scanning job_artifacts_performance job_artifacts_browser_performance job_artifacts_load_performance job_artifacts_lsif job_artifacts_terraform job_artifacts_cluster_applications @@ -98,7 +98,7 @@ RSpec.describe Ci::RetryBuildService do end clone_accessors.each do |attribute| - it "clones #{attribute} build attribute" do + it "clones #{attribute} build attribute", :aggregate_failures do expect(attribute).not_to be_in(forbidden_associations), "association #{attribute} must be `belongs_to`" expect(build.send(attribute)).not_to be_nil expect(new_build.send(attribute)).not_to be_nil @@ -134,7 +134,7 @@ RSpec.describe Ci::RetryBuildService do end end - it 'has correct number of known attributes' do + it 'has correct number of known attributes', :aggregate_failures do processed_accessors = clone_accessors + reject_accessors known_accessors = processed_accessors + ignore_accessors @@ -146,9 +146,10 @@ RSpec.describe Ci::RetryBuildService do Ci::Build.attribute_names.map(&:to_sym) + Ci::Build.attribute_aliases.keys.map(&:to_sym) + Ci::Build.reflect_on_all_associations.map(&:name) + - [:tag_list, :needs_attributes] - - current_accessors << :secrets if Gitlab.ee? + [:tag_list, :needs_attributes] - + # ee-specific accessors should be tested in ee/spec/services/ci/retry_build_service_spec.rb instead + described_class.extra_accessors - + [:dast_site_profiles_build, :dast_scanner_profiles_build] # join tables current_accessors.uniq! diff --git a/spec/services/ci/update_build_queue_service_spec.rb b/spec/services/ci/update_build_queue_service_spec.rb index 44d7809b85f..2e2ef120f1b 100644 --- a/spec/services/ci/update_build_queue_service_spec.rb +++ b/spec/services/ci/update_build_queue_service_spec.rb @@ -45,7 +45,7 @@ RSpec.describe Ci::UpdateBuildQueueService do context 'when duplicate entry exists' do before do - ::Ci::PendingBuild.create!(build: build, project: project) + create(:ci_pending_build, build: build, project: build.project) end it 'does nothing and returns build id' do @@ -66,7 +66,7 @@ RSpec.describe Ci::UpdateBuildQueueService do context 'when pending build exists' do before do - Ci::PendingBuild.create!(build: build, project: project) + create(:ci_pending_build, build: build, project: build.project) end it 'removes pending build in a transaction' do @@ -146,9 +146,7 @@ RSpec.describe Ci::UpdateBuildQueueService do context 'when duplicate entry exists' do before do - ::Ci::RunningBuild.create!( - build: build, project: project, runner: runner, runner_type: runner.runner_type - ) + create(:ci_running_build, build: build, project: project, runner: runner) end it 'does nothing and returns build id' do @@ -169,9 +167,7 @@ RSpec.describe Ci::UpdateBuildQueueService do context 'when shared runner build tracking entry exists' do before do - Ci::RunningBuild.create!( - build: build, project: project, runner: runner, runner_type: runner.runner_type - ) + create(:ci_running_build, build: build, project: project, runner: runner) end it 'removes shared runner build' do |