summaryrefslogtreecommitdiff
path: root/spec/services/ci
diff options
context:
space:
mode:
Diffstat (limited to 'spec/services/ci')
-rw-r--r--spec/services/ci/after_requeue_job_service_spec.rb30
-rw-r--r--spec/services/ci/append_build_trace_service_spec.rb44
-rw-r--r--spec/services/ci/archive_trace_service_spec.rb46
-rw-r--r--spec/services/ci/create_pipeline_service/cache_spec.rb42
-rw-r--r--spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb4
-rw-r--r--spec/services/ci/create_pipeline_service/dry_run_spec.rb4
-rw-r--r--spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb144
-rw-r--r--spec/services/ci/create_pipeline_service/needs_spec.rb4
-rw-r--r--spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb40
-rw-r--r--spec/services/ci/create_pipeline_service/rules_spec.rb16
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb4
-rw-r--r--spec/services/ci/destroy_pipeline_service_spec.rb24
-rw-r--r--spec/services/ci/job_token_scope/add_project_service_spec.rb39
-rw-r--r--spec/services/ci/job_token_scope/remove_project_service_spec.rb45
-rw-r--r--spec/services/ci/pipeline_processing/shared_processing_service.rb14
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/dag_same_stages.yml47
-rw-r--r--spec/services/ci/pipelines/add_job_service_spec.rb72
-rw-r--r--spec/services/ci/play_bridge_service_spec.rb10
-rw-r--r--spec/services/ci/play_build_service_spec.rb10
-rw-r--r--spec/services/ci/register_job_service_spec.rb63
-rw-r--r--spec/services/ci/retry_build_service_spec.rb15
-rw-r--r--spec/services/ci/update_build_queue_service_spec.rb12
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