Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/ci')
-rw-r--r--spec/lib/gitlab/ci/ansi2html_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/ansi2json_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/artifacts/logger_spec.rb60
-rw-r--r--spec/lib/gitlab/ci/artifacts/metrics_spec.rb19
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb86
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/releaser_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb98
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/config/entry/image_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/config/entry/imageable_spec.rb81
-rw-r--r--spec/lib/gitlab/ci/config/entry/processable_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/release_spec.rb38
-rw-r--r--spec/lib/gitlab/ci/config/entry/reports_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/config/entry/service_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/config/entry/tags_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/external/file/base_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config/external/file/local_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/normalizer_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb17
-rw-r--r--spec/lib/gitlab/ci/cron_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb88
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb135
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb40
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb132
-rw-r--r--spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb599
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/command_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb90
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/component_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/report_spec.rb54
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/reports_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/source_spec.rb29
-rw-r--r--spec/lib/gitlab/ci/reports/security/reports_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/test_suite_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/runner_releases_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/runner_upgrade_check_spec.rb45
-rw-r--r--spec/lib/gitlab/ci/status/bridge/common_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/status/build/canceled_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/created_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/manual_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/pending_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/skipped_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/processable/waiting_for_resource_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/trace/remote_checksum_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/variables/collection_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/variables/helpers_spec.rb113
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/result_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb72
66 files changed, 1517 insertions, 616 deletions
diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb
index 27c2b005a93..30359a7170f 100644
--- a/spec/lib/gitlab/ci/ansi2html_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2html_spec.rb
@@ -210,8 +210,8 @@ RSpec.describe Gitlab::Ci::Ansi2html do
let(:section_start_time) { Time.new(2017, 9, 20).utc }
let(:section_duration) { 3.seconds }
let(:section_end_time) { section_start_time + section_duration }
- let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K"}
- let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K"}
+ let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K" }
+ let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K" }
let(:section_start_html) do
'<div class="section-start"' \
" data-timestamp=\"#{section_start_time.to_i}\" data-section=\"#{class_name(section_name)}\"" \
@@ -258,13 +258,13 @@ RSpec.describe Gitlab::Ci::Ansi2html do
it_behaves_like 'a legit section'
context 'section name includes $' do
- let(:section_name) { 'my_$ection'}
+ let(:section_name) { 'my_$ection' }
it_behaves_like 'forbidden char in section_name'
end
context 'section name includes <' do
- let(:section_name) { '<a_tag>'}
+ let(:section_name) { '<a_tag>' }
it_behaves_like 'forbidden char in section_name'
end
diff --git a/spec/lib/gitlab/ci/ansi2json_spec.rb b/spec/lib/gitlab/ci/ansi2json_spec.rb
index f9d23ff97bc..4b3b049176f 100644
--- a/spec/lib/gitlab/ci/ansi2json_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json_spec.rb
@@ -78,8 +78,8 @@ RSpec.describe Gitlab::Ci::Ansi2json do
let(:section_duration) { 63.seconds }
let(:section_start_time) { Time.new(2019, 9, 17).utc }
let(:section_end_time) { section_start_time + section_duration }
- let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K"}
- let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K"}
+ let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K" }
+ let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K" }
it 'marks the first line of the section as header' do
expect(convert_json("Hello#{section_start}world!")).to eq([
@@ -258,8 +258,8 @@ RSpec.describe Gitlab::Ci::Ansi2json do
let(:nested_section_duration) { 2.seconds }
let(:nested_section_start_time) { Time.new(2019, 9, 17).utc }
let(:nested_section_end_time) { nested_section_start_time + nested_section_duration }
- let(:nested_section_start) { "section_start:#{nested_section_start_time.to_i}:#{nested_section_name}\r\033[0K"}
- let(:nested_section_end) { "section_end:#{nested_section_end_time.to_i}:#{nested_section_name}\r\033[0K"}
+ let(:nested_section_start) { "section_start:#{nested_section_start_time.to_i}:#{nested_section_name}\r\033[0K" }
+ let(:nested_section_end) { "section_end:#{nested_section_end_time.to_i}:#{nested_section_name}\r\033[0K" }
it 'adds multiple sections to the lines inside the nested section' do
trace = "Hello#{section_start}foo#{nested_section_start}bar#{nested_section_end}baz#{section_end}world"
@@ -342,7 +342,7 @@ RSpec.describe Gitlab::Ci::Ansi2json do
end
context 'with section options' do
- let(:option_section_start) { "section_start:#{section_start_time.to_i}:#{section_name}[collapsed=true,unused_option=123]\r\033[0K"}
+ let(:option_section_start) { "section_start:#{section_start_time.to_i}:#{section_name}[collapsed=true,unused_option=123]\r\033[0K" }
it 'provides section options when set' do
trace = "#{option_section_start}hello#{section_end}"
@@ -463,8 +463,8 @@ RSpec.describe Gitlab::Ci::Ansi2json do
let(:section_duration) { 63.seconds }
let(:section_start_time) { Time.new(2019, 9, 17).utc }
let(:section_end_time) { section_start_time + section_duration }
- let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K"}
- let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K"}
+ let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K" }
+ let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K" }
context 'with split section body' do
let(:pre_text) { "#{section_start}this is a header\nand " }
diff --git a/spec/lib/gitlab/ci/artifacts/logger_spec.rb b/spec/lib/gitlab/ci/artifacts/logger_spec.rb
new file mode 100644
index 00000000000..7753cb0d25e
--- /dev/null
+++ b/spec/lib/gitlab/ci/artifacts/logger_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Artifacts::Logger do
+ before do
+ Gitlab::ApplicationContext.push(feature_category: 'test', caller_id: 'caller')
+ end
+
+ describe '.log_created' do
+ it 'logs information about created artifact' do
+ artifact = create(:ci_job_artifact, :archive)
+
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ hash_including(
+ message: 'Artifact created',
+ job_artifact_id: artifact.id,
+ size: artifact.size,
+ type: artifact.file_type,
+ build_id: artifact.job_id,
+ project_id: artifact.project_id,
+ 'correlation_id' => an_instance_of(String),
+ 'meta.feature_category' => 'test',
+ 'meta.caller_id' => 'caller'
+ )
+ )
+
+ described_class.log_created(artifact)
+ end
+ end
+
+ describe '.log_deleted' do
+ it 'logs information about deleted artifacts' do
+ artifact_1 = create(:ci_job_artifact, :archive, :expired)
+ artifact_2 = create(:ci_job_artifact, :archive)
+ artifacts = [artifact_1, artifact_2]
+ method = 'Foo#method'
+
+ artifacts.each do |artifact|
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ hash_including(
+ message: 'Artifact deleted',
+ job_artifact_id: artifact.id,
+ expire_at: artifact.expire_at,
+ size: artifact.size,
+ type: artifact.file_type,
+ build_id: artifact.job_id,
+ project_id: artifact.project_id,
+ method: method,
+ 'correlation_id' => an_instance_of(String),
+ 'meta.feature_category' => 'test',
+ 'meta.caller_id' => 'caller'
+ )
+ )
+ end
+
+ described_class.log_deleted(artifacts, method)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/artifacts/metrics_spec.rb b/spec/lib/gitlab/ci/artifacts/metrics_spec.rb
index 0ce76285b03..39e440f09e1 100644
--- a/spec/lib/gitlab/ci/artifacts/metrics_spec.rb
+++ b/spec/lib/gitlab/ci/artifacts/metrics_spec.rb
@@ -5,6 +5,25 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Artifacts::Metrics, :prometheus do
let(:metrics) { described_class.new }
+ describe '.build_completed_report_type_counter' do
+ context 'when incrementing by more than one' do
+ let(:sast_counter) { described_class.send(:build_completed_report_type_counter, :sast) }
+ let(:dast_counter) { described_class.send(:build_completed_report_type_counter, :dast) }
+
+ it 'increments a single counter' do
+ [dast_counter, sast_counter].each do |counter|
+ counter.increment(status: 'success')
+ counter.increment(status: 'success')
+ counter.increment(status: 'failed')
+
+ expect(counter.get(status: 'success')).to eq 2.0
+ expect(counter.get(status: 'failed')).to eq 1.0
+ expect(counter.values.count).to eq 2
+ end
+ end
+ end
+ end
+
describe '#increment_destroyed_artifacts' do
context 'when incrementing by more than one' do
let(:counter) { metrics.send(:destroyed_artifacts_counter) }
diff --git a/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb b/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb
new file mode 100644
index 00000000000..2c236ba3726
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Build::Artifacts::Adapters::ZipStream do
+ let(:file_name) { 'single_file.zip' }
+ let(:fixture_path) { "lib/gitlab/ci/build/artifacts/adapters/zip_stream/#{file_name}" }
+ let(:stream) { File.open(expand_fixture_path(fixture_path), 'rb') }
+
+ describe '#initialize' do
+ it 'initializes when stream is passed' do
+ expect { described_class.new(stream) }.not_to raise_error
+ end
+
+ context 'when stream is not passed' do
+ let(:stream) { nil }
+
+ it 'raises an error' do
+ expect { described_class.new(stream) }.to raise_error(described_class::InvalidStreamError)
+ end
+ end
+ end
+
+ describe '#each_blob' do
+ let(:adapter) { described_class.new(stream) }
+
+ context 'when stream is a zip file' do
+ it 'iterates file content when zip file contains one file' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_with_args("file 1 content\n")
+ end
+
+ context 'when zip file contains multiple files' do
+ let(:file_name) { 'multiple_files.zip' }
+
+ it 'iterates content of all files' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_successive_args("file 1 content\n", "file 2 content\n")
+ end
+ end
+
+ context 'when zip file includes files in a directory' do
+ let(:file_name) { 'with_directory.zip' }
+
+ it 'iterates contents from files only' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_successive_args("file 1 content\n", "file 2 content\n")
+ end
+ end
+
+ context 'when zip contains a file which decompresses beyond the size limit' do
+ let(:file_name) { '200_mb_decompressed.zip' }
+
+ it 'does not read the file' do
+ expect { |b| adapter.each_blob(&b) }.not_to yield_control
+ end
+ end
+
+ context 'when the zip contains too many files' do
+ let(:file_name) { '100_files.zip' }
+
+ it 'stops processing when the limit is reached' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_control.exactly(described_class::MAX_FILES_PROCESSED).times
+ end
+ end
+
+ context 'when stream is a zipbomb' do
+ let(:file_name) { 'zipbomb.zip' }
+
+ it 'does not read the file' do
+ expect { |b| adapter.each_blob(&b) }.not_to yield_control
+ end
+ end
+ end
+
+ context 'when stream is not a zip file' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
+
+ it 'does not yield any data' do
+ expect { |b| adapter.each_blob(&b) }.not_to yield_control
+ expect { adapter.each_blob { |b| b } }.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
index c8ace28108b..7b35c9ba483 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
@@ -67,6 +67,7 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do
subject { |example| path(example).children }
it { is_expected.to all(be_an_instance_of(described_class)) }
+
it do
is_expected.to contain_exactly entry('path/dir_1/file_1'),
entry('path/dir_1/file_b'),
@@ -79,6 +80,7 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do
it { is_expected.to all(be_file) }
it { is_expected.to all(be_an_instance_of(described_class)) }
+
it do
is_expected.to contain_exactly entry('path/dir_1/file_1'),
entry('path/dir_1/file_b')
@@ -99,6 +101,7 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do
it { is_expected.to all(be_directory) }
it { is_expected.to all(be_an_instance_of(described_class)) }
+
it do
is_expected.to contain_exactly entry('path/dir_1/subdir/'),
entry('path/')
diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
index 94c14cfa479..baabab73ea2 100644
--- a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
+++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
@@ -74,7 +74,7 @@ RSpec.describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
end
context 'kubernetes namespace does not exist' do
- let(:namespace_builder) { double(execute: kubernetes_namespace)}
+ let(:namespace_builder) { double(execute: kubernetes_namespace) }
before do
allow(Clusters::KubernetesNamespaceFinder).to receive(:new)
diff --git a/spec/lib/gitlab/ci/build/releaser_spec.rb b/spec/lib/gitlab/ci/build/releaser_spec.rb
index 435f70e9ac5..ffa7073818a 100644
--- a/spec/lib/gitlab/ci/build/releaser_spec.rb
+++ b/spec/lib/gitlab/ci/build/releaser_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe Gitlab::Ci::Build::Releaser do
name: 'Release $CI_COMMIT_SHA',
description: 'Created using the release-cli $EXTRA_DESCRIPTION',
tag_name: 'release-$CI_COMMIT_SHA',
+ tag_message: 'Annotated tag message',
ref: '$CI_COMMIT_SHA',
milestones: %w[m1 m2 m3],
released_at: '2020-07-15T08:00:00Z',
@@ -27,7 +28,7 @@ RSpec.describe Gitlab::Ci::Build::Releaser do
end
it 'generates the script' do
- expect(subject).to eq(['release-cli create --name "Release $CI_COMMIT_SHA" --description "Created using the release-cli $EXTRA_DESCRIPTION" --tag-name "release-$CI_COMMIT_SHA" --ref "$CI_COMMIT_SHA" --released-at "2020-07-15T08:00:00Z" --milestone "m1" --milestone "m2" --milestone "m3" --assets-link "{\"name\":\"asset1\",\"url\":\"https://example.com/assets/1\",\"link_type\":\"other\",\"filepath\":\"/pretty/asset/1\"}" --assets-link "{\"name\":\"asset2\",\"url\":\"https://example.com/assets/2\"}"'])
+ expect(subject).to eq(['release-cli create --name "Release $CI_COMMIT_SHA" --description "Created using the release-cli $EXTRA_DESCRIPTION" --tag-name "release-$CI_COMMIT_SHA" --tag-message "Annotated tag message" --ref "$CI_COMMIT_SHA" --released-at "2020-07-15T08:00:00Z" --milestone "m1" --milestone "m2" --milestone "m3" --assets-link "{\"name\":\"asset1\",\"url\":\"https://example.com/assets/1\",\"link_type\":\"other\",\"filepath\":\"/pretty/asset/1\"}" --assets-link "{\"name\":\"asset2\",\"url\":\"https://example.com/assets/2\"}"'])
end
end
@@ -39,6 +40,7 @@ RSpec.describe Gitlab::Ci::Build::Releaser do
:name | 'Release $CI_COMMIT_SHA' | 'release-cli create --name "Release $CI_COMMIT_SHA"'
:description | 'Release-cli $EXTRA_DESCRIPTION' | 'release-cli create --description "Release-cli $EXTRA_DESCRIPTION"'
:tag_name | 'release-$CI_COMMIT_SHA' | 'release-cli create --tag-name "release-$CI_COMMIT_SHA"'
+ :tag_message | 'Annotated tag message' | 'release-cli create --tag-message "Annotated tag message"'
:ref | '$CI_COMMIT_SHA' | 'release-cli create --ref "$CI_COMMIT_SHA"'
:milestones | %w[m1 m2 m3] | 'release-cli create --milestone "m1" --milestone "m2" --milestone "m3"'
:released_at | '2020-07-15T08:00:00Z' | 'release-cli create --released-at "2020-07-15T08:00:00Z"'
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
index 3892b88598a..234ba68d627 100644
--- a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
@@ -4,7 +4,9 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
describe '#satisfied_by?' do
- subject { described_class.new(globs).satisfied_by?(pipeline, context) }
+ let(:context) { instance_double(Gitlab::Ci::Build::Context::Base) }
+
+ subject(:satisfied_by) { described_class.new(globs).satisfied_by?(pipeline, context) }
context 'a glob matching rule' do
using RSpec::Parameterized::TableSyntax
@@ -18,11 +20,9 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
# rubocop:disable Layout/LineLength
where(:case_name, :globs, :files, :satisfied) do
- 'exact top-level match' | ['Dockerfile'] | { 'Dockerfile' => '', 'Gemfile' => '' } | true
'exact top-level match' | { paths: ['Dockerfile'] } | { 'Dockerfile' => '', 'Gemfile' => '' } | true
'exact top-level no match' | { paths: ['Dockerfile'] } | { 'Gemfile' => '' } | false
'pattern top-level match' | { paths: ['Docker*'] } | { 'Dockerfile' => '', 'Gemfile' => '' } | true
- 'pattern top-level no match' | ['Docker*'] | { 'Gemfile' => '' } | false
'pattern top-level no match' | { paths: ['Docker*'] } | { 'Gemfile' => '' } | false
'exact nested match' | { paths: ['project/build.properties'] } | { 'project/build.properties' => '' } | true
'exact nested no match' | { paths: ['project/build.properties'] } | { 'project/README.md' => '' } | false
@@ -92,5 +92,97 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
it { is_expected.to be_truthy }
end
end
+
+ context 'when using compare_to' do
+ let_it_be(:project) do
+ create(:project, :custom_repo,
+ files: { 'README.md' => 'readme' })
+ end
+
+ let_it_be(:user) { project.owner }
+
+ before_all do
+ project.repository.add_branch(user, 'feature_1', 'master')
+
+ project.repository.create_file(
+ user, 'file1.txt', 'file 1', message: 'Create file1.txt', branch_name: 'feature_1'
+ )
+ project.repository.add_tag(user, 'tag_1', 'feature_1')
+
+ project.repository.create_file(
+ user, 'file2.txt', 'file 2', message: 'Create file2.txt', branch_name: 'feature_1'
+ )
+ project.repository.add_branch(user, 'feature_2', 'feature_1')
+
+ project.repository.update_file(
+ user, 'file2.txt', 'file 2 updated', message: 'Update file2.txt', branch_name: 'feature_2'
+ )
+ end
+
+ context 'when compare_to is branch or tag' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:pipeline_ref, :compare_to, :paths, :ff, :result) do
+ 'feature_1' | 'master' | ['file1.txt'] | true | true
+ 'feature_1' | 'master' | ['README.md'] | true | false
+ 'feature_1' | 'master' | ['xyz.md'] | true | false
+ 'feature_2' | 'master' | ['file1.txt'] | true | true
+ 'feature_2' | 'master' | ['file2.txt'] | true | true
+ 'feature_2' | 'feature_1' | ['file1.txt'] | true | false
+ 'feature_2' | 'feature_1' | ['file1.txt'] | false | true
+ 'feature_2' | 'feature_1' | ['file2.txt'] | true | true
+ 'feature_1' | 'tag_1' | ['file1.txt'] | true | false
+ 'feature_1' | 'tag_1' | ['file1.txt'] | false | true
+ 'feature_1' | 'tag_1' | ['file2.txt'] | true | true
+ 'feature_2' | 'tag_1' | ['file2.txt'] | true | true
+ end
+
+ with_them do
+ let(:globs) { { paths: paths, compare_to: compare_to } }
+
+ let(:pipeline) do
+ build(:ci_pipeline, project: project, ref: pipeline_ref, sha: project.commit(pipeline_ref).sha)
+ end
+
+ before do
+ stub_feature_flags(ci_rules_changes_compare: ff)
+ end
+
+ it { is_expected.to eq(result) }
+ end
+ end
+
+ context 'when compare_to is a sha' do
+ let(:globs) { { paths: ['file2.txt'], compare_to: project.commit('tag_1').sha } }
+
+ let(:pipeline) do
+ build(:ci_pipeline, project: project, ref: 'feature_2', sha: project.commit('feature_2').sha)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when compare_to is not a valid ref' do
+ let(:globs) { { paths: ['file1.txt'], compare_to: 'xyz' } }
+
+ let(:pipeline) do
+ build(:ci_pipeline, project: project, ref: 'feature_2', sha: project.commit('feature_2').sha)
+ end
+
+ it 'raises ParseError' do
+ expect { satisfied_by }.to raise_error(
+ ::Gitlab::Ci::Build::Rules::Rule::Clause::ParseError, 'rules:changes:compare_to is not a valid ref'
+ )
+ end
+
+ context 'when the FF ci_rules_changes_compare is disabled' do
+ before do
+ stub_feature_flags(ci_rules_changes_compare: false)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb
index 81bce989833..31c7437cfe0 100644
--- a/spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb
@@ -51,14 +51,6 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::If do
end
it { is_expected.to eq(true) }
-
- context 'when the FF ci_fix_rules_if_comparison_with_regexp_variable is disabled' do
- before do
- stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: false)
- end
-
- it { is_expected.to eq(false) }
- end
end
context 'when comparison is false' do
diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb
index 0fa6d4f8804..6121c28070f 100644
--- a/spec/lib/gitlab/ci/config/entry/image_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb
@@ -1,12 +1,8 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
-require 'support/helpers/stubbed_feature'
-require 'support/helpers/stub_feature_flags'
+require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Image do
- include StubFeatureFlags
-
before do
stub_feature_flags(ci_docker_image_pull_policy: true)
diff --git a/spec/lib/gitlab/ci/config/entry/imageable_spec.rb b/spec/lib/gitlab/ci/config/entry/imageable_spec.rb
new file mode 100644
index 00000000000..88f8e260611
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/imageable_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Entry::Imageable do
+ let(:node_class) do
+ Class.new(::Gitlab::Config::Entry::Node) do
+ include ::Gitlab::Ci::Config::Entry::Imageable
+
+ validations do
+ validates :config, allowed_keys: ::Gitlab::Ci::Config::Entry::Imageable::IMAGEABLE_ALLOWED_KEYS
+ end
+
+ def self.name
+ 'node'
+ end
+
+ def value
+ if string?
+ { name: @config }
+ elsif hash?
+ {
+ name: @config[:name]
+ }.compact
+ else
+ {}
+ end
+ end
+ end
+ end
+
+ subject(:entry) { node_class.new(config) }
+
+ before do
+ entry.compose!
+ end
+
+ context 'when entry value is correct' do
+ let(:config) { 'image:1.0' }
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ context 'when entry value is not correct' do
+ let(:config) { ['image:1.0'] }
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors.first)
+ .to match /config should be a hash or a string/
+ end
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+ end
+
+ context 'when unexpected key is specified' do
+ let(:config) { { name: 'image:1.0', non_existing: 'test' } }
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors.first)
+ .to match /config contains unknown keys: non_existing/
+ end
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/processable_spec.rb b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
index 5b9337ede34..714b0a3b6aa 100644
--- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
@@ -212,7 +212,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do
let(:unspecified) { double('unspecified', 'specified?' => false) }
let(:default) { double('default', '[]' => unspecified) }
let(:workflow) { double('workflow', 'has_rules?' => false) }
- let(:variables) { }
+ let(:variables) {}
let(:deps) do
double('deps',
diff --git a/spec/lib/gitlab/ci/config/entry/release_spec.rb b/spec/lib/gitlab/ci/config/entry/release_spec.rb
index e5155f91be4..7b6b31ca748 100644
--- a/spec/lib/gitlab/ci/config/entry/release_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/release_spec.rb
@@ -128,25 +128,25 @@ RSpec.describe Gitlab::Ci::Config::Entry::Release do
end
context "when 'ref' is a short commit SHA" do
- let(:ref) { 'b3235930'}
+ let(:ref) { 'b3235930' }
it_behaves_like 'a valid entry'
end
context "when 'ref' is a branch name" do
- let(:ref) { 'fix/123-branch-name'}
+ let(:ref) { 'fix/123-branch-name' }
it_behaves_like 'a valid entry'
end
context "when 'ref' is a semantic versioning tag" do
- let(:ref) { 'v1.2.3'}
+ let(:ref) { 'v1.2.3' }
it_behaves_like 'a valid entry'
end
context "when 'ref' is a semantic versioning tag rc" do
- let(:ref) { 'v1.2.3-rc'}
+ let(:ref) { 'v1.2.3-rc' }
it_behaves_like 'a valid entry'
end
@@ -188,6 +188,30 @@ RSpec.describe Gitlab::Ci::Config::Entry::Release do
end
end
+ context "when value includes 'tag_message' keyword" do
+ let(:config) do
+ {
+ tag_name: 'v0.06',
+ description: "./release_changelog.txt",
+ tag_message: "Annotated tag message"
+ }
+ end
+
+ it_behaves_like 'a valid entry'
+ end
+
+ context "when 'tag_message' is nil" do
+ let(:config) do
+ {
+ tag_name: 'v0.06',
+ description: "./release_changelog.txt",
+ tag_message: nil
+ }
+ end
+
+ it_behaves_like 'a valid entry'
+ end
+
context 'when entry value is not correct' do
describe '#errors' do
context 'when value of attribute is invalid' do
@@ -231,6 +255,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Release do
it_behaves_like 'reports error', 'release milestones should be an array of strings or a string'
end
+
+ context 'when `tag_message` is not a string' do
+ let(:config) { { tag_message: 100 } }
+
+ it_behaves_like 'reports error', 'release tag message should be a string'
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/reports_spec.rb b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
index 051cccb4833..45aa859a356 100644
--- a/spec/lib/gitlab/ci/config/entry/reports_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
@@ -47,6 +47,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Reports do
:dotenv | 'build.dotenv'
:terraform | 'tfplan.json'
:accessibility | 'gl-accessibility.json'
+ :cyclonedx | 'gl-sbom.cdx.zip'
end
with_them do
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 55ad119ea21..1f8543227c9 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -155,7 +155,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }],
only: { refs: %w(branches tags) },
- job_variables: { 'VAR' => 'job' },
+ job_variables: { 'VAR' => { value: 'job' } },
root_variables_inheritance: true,
after_script: [],
ignore: false,
@@ -215,7 +215,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
- job_variables: { 'VAR' => 'job' },
+ job_variables: { 'VAR' => { value: 'job' } },
root_variables_inheritance: true,
ignore: false,
after_script: ['make clean'],
diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb
index 295561b3c4d..64f0a64074c 100644
--- a/spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule::Changes do
let(:factory) do
@@ -119,6 +119,23 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule::Changes do
end
end
end
+
+ context 'with paths and compare_to' do
+ let(:config) { { paths: %w[app/ lib/], compare_to: 'branch1' } }
+
+ it { is_expected.to be_valid }
+
+ context 'when compare_to is not a string' do
+ let(:config) { { paths: %w[app/ lib/], compare_to: 1 } }
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns information about errors' do
+ expect(entry.errors)
+ .to include(/should be a string/)
+ end
+ end
+ end
end
describe '#value' do
@@ -137,5 +154,13 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule::Changes do
it { is_expected.to eq(config) }
end
+
+ context 'with paths and compare_to' do
+ let(:config) do
+ { paths: ['app/', 'lib/'], compare_to: 'branch1' }
+ end
+
+ it { is_expected.to eq(config) }
+ end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
index 93f4a66bfb6..c85fe366da6 100644
--- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
require 'gitlab_chronic_duration'
-require 'support/helpers/stub_feature_flags'
require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
@@ -418,6 +417,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
it { is_expected.to eq(config) }
end
+
+ context 'when using changes with paths and compare_to' do
+ let(:config) { { changes: { paths: %w[app/ lib/ spec/ other/* paths/**/*.rb], compare_to: 'branch1' } } }
+
+ it { is_expected.to eq(config) }
+ end
end
context 'when default value has been provided' do
diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb
index 3c000fd09ed..821ab442d61 100644
--- a/spec/lib/gitlab/ci/config/entry/service_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb
@@ -1,12 +1,8 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
-require 'support/helpers/stubbed_feature'
-require 'support/helpers/stub_feature_flags'
+require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Service do
- include StubFeatureFlags
-
before do
stub_feature_flags(ci_docker_image_pull_policy: true)
entry.compose!
diff --git a/spec/lib/gitlab/ci/config/entry/tags_spec.rb b/spec/lib/gitlab/ci/config/entry/tags_spec.rb
index e05d4ae52b2..24efd08c855 100644
--- a/spec/lib/gitlab/ci/config/entry/tags_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/tags_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Tags do
end
context 'when tags limit is reached' do
- let(:config) { Array.new(50) {|i| "tag-#{i}" } }
+ let(:config) { Array.new(50) { |i| "tag-#{i}" } }
it 'reports error' do
expect(entry.errors)
diff --git a/spec/lib/gitlab/ci/config/external/file/base_spec.rb b/spec/lib/gitlab/ci/config/external/file/base_spec.rb
index 280bebe1a7c..1306d61d99c 100644
--- a/spec/lib/gitlab/ci/config/external/file/base_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::External::File::Base do
- let(:variables) { }
+ let(:variables) {}
let(:context_params) { { sha: 'HEAD', variables: variables } }
let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) }
@@ -100,7 +100,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base do
describe '#to_hash' do
context 'with includes' do
let(:location) { 'some/file/config.yml' }
- let(:content) { 'include: { template: Bash.gitlab-ci.yml }'}
+ let(:content) { 'include: { template: Bash.gitlab-ci.yml }' }
before do
allow_any_instance_of(test_class)
diff --git a/spec/lib/gitlab/ci/config/external/file/local_spec.rb b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
index 0e78498c98e..f5b36ebfa45 100644
--- a/spec/lib/gitlab/ci/config/external/file/local_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
@@ -167,7 +167,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do
describe '#to_hash' do
context 'properly includes another local file in the same repository' do
let(:location) { 'some/file/config.yml' }
- let(:content) { 'include: { local: another-config.yml }'}
+ let(:content) { 'include: { local: another-config.yml }' }
let(:another_location) { 'another-config.yml' }
let(:another_content) { 'rspec: JOB' }
diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
index 3e1c4df4e32..45dfea636f3 100644
--- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::External::File::Remote do
include StubRequests
- let(:variables) {Gitlab::Ci::Variables::Collection.new([{ 'key' => 'GITLAB_TOKEN', 'value' => 'secret_file', 'masked' => true }]) }
+ let(:variables) { Gitlab::Ci::Variables::Collection.new([{ 'key' => 'GITLAB_TOKEN', 'value' => 'secret_file', 'masked' => true }]) }
let(:context_params) { { sha: '12345', variables: variables } }
let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) }
let(:params) { { remote: location } }
diff --git a/spec/lib/gitlab/ci/config/normalizer_spec.rb b/spec/lib/gitlab/ci/config/normalizer_spec.rb
index 354392eb42e..96ca5d98a6e 100644
--- a/spec/lib/gitlab/ci/config/normalizer_spec.rb
+++ b/spec/lib/gitlab/ci/config/normalizer_spec.rb
@@ -232,7 +232,7 @@ RSpec.describe Gitlab::Ci::Config::Normalizer do
context 'when parallel config does not matches a factory' do
let(:variables_config) { {} }
- let(:parallel_config) { }
+ let(:parallel_config) {}
it 'does not alter the job config' do
is_expected.to match(config)
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 5eb04d969eb..055114769ea 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -872,4 +872,21 @@ RSpec.describe Gitlab::Ci::Config do
end
end
end
+
+ describe '#workflow_rules' do
+ subject(:workflow_rules) { config.workflow_rules }
+
+ let(:yml) do
+ <<-EOS
+ workflow:
+ rules:
+ - if: $CI_COMMIT_REF_NAME == "master"
+
+ rspec:
+ script: exit 0
+ EOS
+ end
+
+ it { is_expected.to eq([{ if: '$CI_COMMIT_REF_NAME == "master"' }]) }
+ end
end
diff --git a/spec/lib/gitlab/ci/cron_parser_spec.rb b/spec/lib/gitlab/ci/cron_parser_spec.rb
index 4017accb462..33474865a93 100644
--- a/spec/lib/gitlab/ci/cron_parser_spec.rb
+++ b/spec/lib/gitlab/ci/cron_parser_spec.rb
@@ -178,7 +178,7 @@ RSpec.describe Gitlab::Ci::CronParser do
end
context 'when time crosses a Daylight Savings boundary' do
- let(:cron) { '* 0 1 12 *'}
+ let(:cron) { '* 0 1 12 *' }
# Note this previously only failed if the time zone is set
# to a zone that observes Daylight Savings
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
new file mode 100644
index 00000000000..c99cfa94aa6
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties do
+ subject(:parse_source) { described_class.parse_source(properties) }
+
+ context 'when properties are nil' do
+ let(:properties) { nil }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when report does not have gitlab properties' do
+ let(:properties) { ['name' => 'foo', 'value' => 'bar'] }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when schema_version is missing' do
+ let(:properties) do
+ [
+ { 'name' => 'gitlab:dependency_scanning:dependency_file', 'value' => 'package-lock.json' },
+ { 'name' => 'gitlab:dependency_scanning:package_manager_name', 'value' => 'npm' },
+ { 'name' => 'gitlab:dependency_scanning:language', 'value' => 'JavaScript' }
+ ]
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when schema version is unsupported' do
+ let(:properties) do
+ [
+ { 'name' => 'gitlab:meta:schema_version', 'value' => '2' },
+ { 'name' => 'gitlab:dependency_scanning:dependency_file', 'value' => 'package-lock.json' },
+ { 'name' => 'gitlab:dependency_scanning:package_manager_name', 'value' => 'npm' },
+ { 'name' => 'gitlab:dependency_scanning:language', 'value' => 'JavaScript' }
+ ]
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when no dependency_scanning properties are present' do
+ let(:properties) do
+ [
+ { 'name' => 'gitlab:meta:schema_version', 'value' => '1' }
+ ]
+ end
+
+ it 'does not call dependency_scanning parser' do
+ expect(Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning).not_to receive(:parse_source)
+
+ parse_source
+ end
+ end
+
+ context 'when dependency_scanning properties are present' do
+ let(:properties) do
+ [
+ { 'name' => 'gitlab:meta:schema_version', 'value' => '1' },
+ { 'name' => 'gitlab:dependency_scanning:category', 'value' => 'development' },
+ { 'name' => 'gitlab:dependency_scanning:input_file:path', 'value' => 'package-lock.json' },
+ { 'name' => 'gitlab:dependency_scanning:source_file:path', 'value' => 'package.json' },
+ { 'name' => 'gitlab:dependency_scanning:package_manager:name', 'value' => 'npm' },
+ { 'name' => 'gitlab:dependency_scanning:language:name', 'value' => 'JavaScript' },
+ { 'name' => 'gitlab:dependency_scanning:unsupported_property', 'value' => 'Should be ignored' }
+ ]
+ end
+
+ let(:expected_input) do
+ {
+ 'category' => 'development',
+ 'input_file' => { 'path' => 'package-lock.json' },
+ 'source_file' => { 'path' => 'package.json' },
+ 'package_manager' => { 'name' => 'npm' },
+ 'language' => { 'name' => 'JavaScript' }
+ }
+ end
+
+ it 'passes only supported properties to the dependency scanning parser' do
+ expect(Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning).to receive(:source).with(expected_input)
+
+ parse_source
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
new file mode 100644
index 00000000000..431fe9f3591
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
@@ -0,0 +1,135 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx do
+ let(:report) { instance_double('Gitlab::Ci::Reports::Sbom::Report') }
+ let(:report_data) { base_report_data }
+ let(:raw_report_data) { report_data.to_json }
+ let(:report_valid?) { true }
+ let(:validator_errors) { [] }
+ let(:properties_parser) { class_double('Gitlab::Ci::Parsers::Sbom::CyclonedxProperties') }
+
+ let(:base_report_data) do
+ {
+ 'bomFormat' => 'CycloneDX',
+ 'specVersion' => '1.4',
+ 'version' => 1
+ }
+ end
+
+ subject(:parse!) { described_class.new.parse!(raw_report_data, report) }
+
+ before do
+ allow_next_instance_of(Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator) do |validator|
+ allow(validator).to receive(:valid?).and_return(report_valid?)
+ allow(validator).to receive(:errors).and_return(validator_errors)
+ end
+
+ allow(properties_parser).to receive(:parse_source)
+ stub_const('Gitlab::Ci::Parsers::Sbom::CyclonedxProperties', properties_parser)
+ end
+
+ context 'when report JSON is invalid' do
+ let(:raw_report_data) { '{ ' }
+
+ it 'handles errors and adds them to the report' do
+ expect(report).to receive(:add_error).with(a_string_including("Report JSON is invalid:"))
+
+ expect { parse! }.not_to raise_error
+ end
+ end
+
+ context 'when report uses an unsupported spec version' do
+ let(:report_data) { base_report_data.merge({ 'specVersion' => '1.3' }) }
+
+ it 'reports unsupported version as an error' do
+ expect(report).to receive(:add_error).with("Unsupported CycloneDX spec version. Must be one of: 1.4")
+
+ parse!
+ end
+ end
+
+ context 'when report does not conform to the CycloneDX schema' do
+ let(:report_valid?) { false }
+ let(:validator_errors) { %w[error1 error2] }
+
+ it 'reports all errors returned by the validator' do
+ expect(report).to receive(:add_error).with("error1")
+ expect(report).to receive(:add_error).with("error2")
+
+ parse!
+ end
+ end
+
+ context 'when cyclonedx report has no components' do
+ it 'skips component processing' do
+ expect(report).not_to receive(:add_component)
+
+ parse!
+ end
+ end
+
+ context 'when report has components' do
+ let(:report_data) { base_report_data.merge({ 'components' => components }) }
+ let(:components) do
+ [
+ {
+ "name" => "activesupport",
+ "version" => "5.1.4",
+ "purl" => "pkg:gem/activesupport@5.1.4",
+ "type" => "library",
+ "bom-ref" => "pkg:gem/activesupport@5.1.4"
+ },
+ {
+ "name" => "byebug",
+ "version" => "10.0.0",
+ "purl" => "pkg:gem/byebug@10.0.0",
+ "type" => "library",
+ "bom-ref" => "pkg:gem/byebug@10.0.0"
+ },
+ {
+ "name" => "minimal-component",
+ "type" => "library"
+ },
+ {
+ # Should be skipped
+ "name" => "unrecognized-type",
+ "type" => "unknown"
+ }
+ ]
+ end
+
+ it 'adds each component, ignoring unused attributes' do
+ expect(report).to receive(:add_component)
+ .with({ "name" => "activesupport", "version" => "5.1.4", "type" => "library" })
+ expect(report).to receive(:add_component)
+ .with({ "name" => "byebug", "version" => "10.0.0", "type" => "library" })
+ expect(report).to receive(:add_component)
+ .with({ "name" => "minimal-component", "type" => "library" })
+
+ parse!
+ end
+ end
+
+ context 'when report has metadata properties' do
+ let(:report_data) { base_report_data.merge({ 'metadata' => { 'properties' => properties } }) }
+
+ let(:properties) do
+ [
+ { 'name' => 'gitlab:meta:schema_version', 'value' => '1' },
+ { 'name' => 'gitlab:dependency_scanning:category', 'value' => 'development' },
+ { 'name' => 'gitlab:dependency_scanning:input_file:path', 'value' => 'package-lock.json' },
+ { 'name' => 'gitlab:dependency_scanning:source_file:path', 'value' => 'package.json' },
+ { 'name' => 'gitlab:dependency_scanning:package_manager:name', 'value' => 'npm' },
+ { 'name' => 'gitlab:dependency_scanning:language:name', 'value' => 'JavaScript' }
+ ]
+ end
+
+ it 'passes them to the properties parser' do
+ expect(properties_parser).to receive(:parse_source).with(properties)
+
+ parse!
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
new file mode 100644
index 00000000000..30114b17cac
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning do
+ subject { described_class.source(property_data) }
+
+ context 'when all property data is present' do
+ let(:property_data) do
+ {
+ 'category' => 'development',
+ 'input_file' => { 'path' => 'package-lock.json' },
+ 'source_file' => { 'path' => 'package.json' },
+ 'package_manager' => { 'name' => 'npm' },
+ 'language' => { 'name' => 'JavaScript' }
+ }
+ end
+
+ it 'returns expected source data' do
+ is_expected.to eq({
+ 'type' => :dependency_scanning,
+ 'data' => property_data,
+ 'fingerprint' => '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
+ })
+ end
+ end
+
+ context 'when required properties are missing' do
+ let(:property_data) do
+ {
+ 'category' => 'development',
+ 'source_file' => { 'path' => 'package.json' },
+ 'package_manager' => { 'name' => 'npm' },
+ 'language' => { 'name' => 'JavaScript' }
+ }
+ end
+
+ it { is_expected.to be_nil }
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb
new file mode 100644
index 00000000000..c54a3268bbe
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb
@@ -0,0 +1,132 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator do
+ # Reports should be valid or invalid according to the specification at
+ # https://cyclonedx.org/docs/1.4/json/
+
+ subject(:validator) { described_class.new(report_data) }
+
+ let_it_be(:required_attributes) do
+ {
+ "bomFormat" => "CycloneDX",
+ "specVersion" => "1.4",
+ "version" => 1
+ }
+ end
+
+ context "with minimally valid report" do
+ let_it_be(:report_data) { required_attributes }
+
+ it { is_expected.to be_valid }
+ end
+
+ context "when report has components" do
+ let(:report_data) { required_attributes.merge({ "components" => components }) }
+
+ context "with minimally valid components" do
+ let(:components) do
+ [
+ {
+ "type" => "library",
+ "name" => "activesupport"
+ },
+ {
+ "type" => "library",
+ "name" => "byebug"
+ }
+ ]
+ end
+
+ it { is_expected.to be_valid }
+ end
+
+ context "when components have versions" do
+ let(:components) do
+ [
+ {
+ "type" => "library",
+ "name" => "activesupport",
+ "version" => "5.1.4"
+ },
+ {
+ "type" => "library",
+ "name" => "byebug",
+ "version" => "10.0.0"
+ }
+ ]
+ end
+
+ it { is_expected.to be_valid }
+ end
+
+ context "when components are not valid" do
+ let(:components) do
+ [
+ { "type" => "foo" },
+ { "name" => "activesupport" }
+ ]
+ end
+
+ it { is_expected.not_to be_valid }
+
+ it "outputs errors for each validation failure" do
+ expect(validator.errors).to match_array([
+ "property '/components/0' is missing required keys: name",
+ "property '/components/0/type' is not one of: [\"application\", \"framework\"," \
+ " \"library\", \"container\", \"operating-system\", \"device\", \"firmware\", \"file\"]",
+ "property '/components/1' is missing required keys: type"
+ ])
+ end
+ end
+ end
+
+ context "when report has metadata" do
+ let(:metadata) do
+ {
+ "timestamp" => "2022-02-23T08:02:39Z",
+ "tools" => [{ "vendor" => "GitLab", "name" => "Gemnasium", "version" => "2.34.0" }],
+ "authors" => [{ "name" => "GitLab", "email" => "support@gitlab.com" }]
+ }
+ end
+
+ let(:report_data) { required_attributes.merge({ "metadata" => metadata }) }
+
+ it { is_expected.to be_valid }
+
+ context "when metadata has properties" do
+ before do
+ metadata.merge!({ "properties" => properties })
+ end
+
+ context "when properties are valid" do
+ let(:properties) do
+ [
+ { "name" => "gitlab:dependency_scanning:input_file", "value" => "Gemfile.lock" },
+ { "name" => "gitlab:dependency_scanning:package_manager", "value" => "bundler" }
+ ]
+ end
+
+ it { is_expected.to be_valid }
+ end
+
+ context "when properties are invalid" do
+ let(:properties) do
+ [
+ { "name" => ["gitlab:meta:schema_version"], "value" => 1 }
+ ]
+ end
+
+ it { is_expected.not_to be_valid }
+
+ it "outputs errors for each validation failure" do
+ expect(validator.errors).to match_array([
+ "property '/metadata/properties/0/name' is not of type: string",
+ "property '/metadata/properties/0/value' is not of type: string"
+ ])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
index d06077d69b6..7828aa99f6a 100644
--- a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
@@ -6,6 +6,10 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let_it_be(:project) { create(:project) }
let(:supported_dast_versions) { described_class::SUPPORTED_VERSIONS[:dast].join(', ') }
+ let(:deprecated_schema_version_message) {}
+ let(:missing_schema_version_message) do
+ "Report version not provided, dast report type supports versions: #{supported_dast_versions}"
+ end
let(:scanner) do
{
@@ -24,7 +28,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
expect(described_class::SUPPORTED_VERSIONS.keys).to eq(described_class::DEPRECATED_VERSIONS.keys)
end
- context 'when a schema JSON file exists for a particular report type version' do
+ context 'when all files under schema path are explicitly listed' do
# We only care about the part that comes before report-format.json
# https://rubular.com/r/N8Juz7r8hYDYgD
filename_regex = /(?<report_type>[-\w]*)\-report-format.json/
@@ -38,7 +42,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
matches = filename_regex.match(file)
report_type = matches[:report_type].tr("-", "_").to_sym
- it "#{report_type} #{version} is in the constant" do
+ it "#{report_type} #{version}" do
expect(described_class::SUPPORTED_VERSIONS[report_type]).to include(version)
end
end
@@ -64,11 +68,54 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
describe '#valid?' do
subject { validator.valid? }
+ context 'when given a supported MAJOR.MINOR schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) do
+ latest_vendored_version = described_class::SUPPORTED_VERSIONS[report_type].last.split(".")
+ (latest_vendored_version[0...2] << "34").join(".")
+ end
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ it { is_expected.to be_falsey }
+
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'schema_validation_fails',
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
+
+ subject
+ end
+ end
+ end
+
context 'when given a supported schema version' do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'when the report is valid' do
+ context 'and the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -79,7 +126,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_truthy }
end
- context 'when the report is invalid' do
+ context 'and the report is invalid' do
let(:report_data) do
{
'version' => report_version
@@ -118,7 +165,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
end
- context 'when the report passes schema validation' do
+ context 'and the report passes schema validation' do
let(:report_data) do
{
'version' => '10.0.0',
@@ -143,34 +190,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
- context 'when the report does not pass schema validation' do
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
- end
-
- let(:report_data) do
- {
- 'version' => 'V2.7.0'
- }
- end
-
- it { is_expected.to be_falsey }
+ context 'and the report does not pass schema validation' do
+ let(:report_data) do
+ {
+ 'version' => 'V2.7.0'
+ }
end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- let(:report_data) do
- {
- 'version' => 'V2.7.0'
- }
- end
-
- it { is_expected.to be_truthy }
- end
+ it { is_expected.to be_falsey }
end
end
@@ -178,20 +205,40 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
end
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
+ it { is_expected.to be_falsey }
+
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'using_unsupported_schema_version',
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
- it { is_expected.to be_falsey }
+ subject
+ end
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ context 'and scanner information is empty' do
+ let(:scanner) { {} }
it 'logs related information' do
expect(Gitlab::AppLogger).to receive(:info).with(
@@ -199,79 +246,26 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
security_report_type: report_type,
security_report_version: report_version,
project_id: project.id,
+ security_report_failure: 'schema_validation_fails',
+ security_report_scanner_id: nil,
+ security_report_scanner_version: nil
+ )
+
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
security_report_failure: 'using_unsupported_schema_version',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
+ security_report_scanner_id: nil,
+ security_report_scanner_version: nil
)
subject
end
end
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- context 'when scanner information is empty' do
- let(:scanner) { {} }
-
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'schema_validation_fails',
- security_report_scanner_id: nil,
- security_report_scanner_version: nil
- )
-
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'using_unsupported_schema_version',
- security_report_scanner_id: nil,
- security_report_scanner_version: nil
- )
-
- subject
- end
- end
-
- it { is_expected.to be_falsey }
- end
- end
-
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- it { is_expected.to be_truthy }
- end
+ it { is_expected.to be_falsey }
end
end
@@ -284,19 +278,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- before do
- stub_feature_flags(enforce_security_report_validation: true)
- end
-
it { is_expected.to be_falsey }
-
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- it { is_expected.to be_truthy }
- end
end
end
@@ -307,7 +289,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'when the report is valid' do
+ context 'and the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -318,34 +300,20 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_empty }
end
- context 'when the report is invalid' do
+ context 'and the report is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: project)
- end
-
- let(:expected_errors) do
- [
- 'root is missing required keys: vulnerabilities'
- ]
- end
-
- it { is_expected.to match_array(expected_errors) }
+ let(:expected_errors) do
+ [
+ 'root is missing required keys: vulnerabilities'
+ ]
end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- it { is_expected.to be_empty }
- end
+ it { is_expected.to match_array(expected_errors) }
end
end
@@ -363,7 +331,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
end
- context 'when the report passes schema validation' do
+ context 'and the report passes schema validation' do
let(:report_data) do
{
'version' => '10.0.0',
@@ -374,119 +342,77 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_empty }
end
- context 'when the report does not pass schema validation' do
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
- end
-
- let(:report_data) do
- {
- 'version' => 'V2.7.0'
- }
- end
-
- let(:expected_errors) do
- [
- "property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$",
- "root is missing required keys: vulnerabilities"
- ]
- end
-
- it { is_expected.to match_array(expected_errors) }
+ context 'and the report does not pass schema validation' do
+ let(:report_data) do
+ {
+ 'version' => 'V2.7.0'
+ }
end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- let(:report_data) do
- {
- 'version' => 'V2.7.0'
- }
- end
-
- it { is_expected.to be_empty }
+ let(:expected_errors) do
+ [
+ "property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$",
+ "root is missing required keys: vulnerabilities"
+ ]
end
+
+ it { is_expected.to match_array(expected_errors) }
end
end
context 'when given an unsupported schema version' do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
+ let(:expected_unsupported_message) do
+ "Version #{report_version} for report type #{report_type} is unsupported, supported versions for this report type are: "\
+ "#{supported_dast_versions}. GitLab will attempt to validate this report against the earliest supported "\
+ "versions of this report type, to show all the errors but will not ingest the report"
+ end
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
end
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- let(:expected_errors) do
- [
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}"
- ]
- end
-
- it { is_expected.to match_array(expected_errors) }
+ let(:expected_errors) do
+ [
+ expected_unsupported_message
+ ]
end
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- let(:expected_errors) do
- [
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}",
- "root is missing required keys: vulnerabilities"
- ]
- end
-
- it { is_expected.to match_array(expected_errors) }
- end
+ it { is_expected.to match_array(expected_errors) }
end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
end
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
+ let(:expected_errors) do
+ [
+ expected_unsupported_message,
+ "root is missing required keys: vulnerabilities"
+ ]
end
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it { is_expected.to match_array(expected_errors) }
end
end
context 'when not given a schema version' do
let(:report_type) { :dast }
let(:report_version) { nil }
+ let(:expected_missing_version_message) do
+ "Report version not provided, #{report_type} report type supports versions: #{supported_dast_versions}. GitLab "\
+ "will attempt to validate this report against the earliest supported versions of this report type, to show all "\
+ "the errors but will not ingest the report"
+ end
+
let(:report_data) do
{
'vulnerabilities' => []
@@ -496,19 +422,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:expected_errors) do
[
"root is missing required keys: version",
- "Report version not provided, dast report type supports versions: #{supported_dast_versions}"
+ expected_missing_version_message
]
end
it { is_expected.to match_array(expected_errors) }
-
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- it { is_expected.to be_empty }
- end
end
end
@@ -519,7 +437,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'when the report is valid' do
+ context 'and the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -530,7 +448,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_empty }
end
- context 'when the report is invalid' do
+ context 'and the report is invalid' do
let(:report_data) do
{
'version' => report_version
@@ -550,9 +468,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
+ let(:expected_deprecation_message) do
+ "Version #{report_version} for report type #{report_type} has been deprecated, supported versions for this "\
+ "report type are: #{supported_dast_versions}. GitLab will attempt to parse and ingest this report if valid."
+ end
+
let(:expected_deprecation_warnings) do
[
- "Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: #{supported_dast_versions}"
+ expected_deprecation_message
]
end
@@ -560,7 +483,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
end
- context 'when the report passes schema validation' do
+ context 'and the report passes schema validation' do
let(:report_data) do
{
'version' => report_version,
@@ -571,7 +494,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to match_array(expected_deprecation_warnings) }
end
- context 'when the report does not pass schema validation' do
+ context 'and the report does not pass schema validation' do
let(:report_data) do
{
'version' => 'V2.7.0'
@@ -600,11 +523,27 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
describe '#warnings' do
subject { validator.warnings }
- context 'when given a supported schema version' do
+ context 'when given a supported MAJOR.MINOR schema version' do
let(:report_type) { :dast }
- let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
+ let(:report_version) do
+ latest_vendored_version = described_class::SUPPORTED_VERSIONS[report_type].last.split(".")
+ (latest_vendored_version[0...2] << "34").join(".")
+ end
+
+ let(:latest_patch_version) do
+ ::Security::ReportSchemaVersionMatcher.new(
+ report_declared_version: report_version,
+ supported_versions: described_class::SUPPORTED_VERSIONS[report_type]
+ ).call
+ end
+
+ let(:message) do
+ "This report uses a supported MAJOR.MINOR schema version but the PATCH version doesn't match"\
+ " any vendored schema version. Validation will be attempted against version"\
+ " #{latest_patch_version}"
+ end
- context 'when the report is valid' do
+ context 'and the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -612,37 +551,57 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- it { is_expected.to be_empty }
+ it { is_expected.to match_array([message]) }
end
- context 'when the report is invalid' do
+ context 'and the report is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: project)
- end
+ it { is_expected.to match_array([message]) }
+
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'schema_validation_fails',
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
- it { is_expected.to be_empty }
+ subject
end
+ end
+ end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
+ context 'when given a supported schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- let(:expected_warnings) do
- [
- 'root is missing required keys: vulnerabilities'
- ]
- end
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to be_empty }
+ end
- it { is_expected.to match_array(expected_warnings) }
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
end
+
+ it { is_expected.to be_empty }
end
end
@@ -660,7 +619,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
end
- context 'when the report passes schema validation' do
+ context 'and the report passes schema validation' do
let(:report_data) do
{
'vulnerabilities' => []
@@ -670,35 +629,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_empty }
end
- context 'when the report does not pass schema validation' do
+ context 'and the report does not pass schema validation' do
let(:report_data) do
{
'version' => 'V2.7.0'
}
end
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
- end
-
- it { is_expected.to be_empty }
- end
-
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- let(:expected_warnings) do
- [
- "property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$",
- "root is missing required keys: vulnerabilities"
- ]
- end
-
- it { is_expected.to match_array(expected_warnings) }
- end
+ it { is_expected.to be_empty }
end
end
@@ -706,71 +644,25 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
- end
-
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
end
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it { is_expected.to be_empty }
end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- let(:expected_warnings) do
- [
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}"
- ]
- end
-
- it { is_expected.to match_array(expected_warnings) }
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
end
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- let(:expected_warnings) do
- [
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}",
- "root is missing required keys: vulnerabilities"
- ]
- end
-
- it { is_expected.to match_array(expected_warnings) }
- end
+ it { is_expected.to be_empty }
end
end
@@ -784,21 +676,6 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
it { is_expected.to be_empty }
-
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- let(:expected_warnings) do
- [
- "root is missing required keys: version",
- "Report version not provided, dast report type supports versions: #{supported_dast_versions}"
- ]
- end
-
- it { is_expected.to match_array(expected_warnings) }
- end
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
index 0d78ce3440a..de43e759193 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
@@ -282,7 +282,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
subject { command.ambiguous_ref? }
context 'when ref is not ambiguous' do
- it { is_expected. to eq(false) }
+ it { is_expected.to eq(false) }
end
context 'when ref is ambiguous' do
@@ -291,7 +291,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
project.repository.add_branch(project.creator, 'ref', 'master')
end
- it { is_expected. to eq(true) }
+ it { is_expected.to eq(true) }
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
index cbf92f8fa83..be5d3a96126 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CreateDeployments do
end
context 'when the corresponding environment does not exist' do
- let!(:environment) { }
+ let!(:environment) {}
it 'does not create a deployment record' do
expect { subject }.not_to change { Deployment.count }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb
index e30a78546af..eb5a37f19f4 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules do
before do
allow(step).to receive(:workflow_rules_result)
.and_return(
- double(pass?: true, variables: { 'VAR1' => 'val2' })
+ double(pass?: true, variables: { 'VAR1' => 'val2', 'VAR2' => 3 })
)
step.perform!
@@ -65,7 +65,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules do
end
it 'saves workflow_rules_result' do
- expect(command.workflow_rules_result.variables).to eq({ 'VAR1' => 'val2' })
+ expect(command.workflow_rules_result.variables).to eq({ 'VAR1' => 'val2', 'VAR2' => 3 })
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb
index fabfbd779f3..5ee96b0baa8 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::SeedBlock do
let(:project) { create(:project, :repository) }
let(:user) { create(:user, developer_projects: [project]) }
- let(:seeds_block) { }
+ let(:seeds_block) {}
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(
diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
index 687bb82a8ef..f7774e199fb 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user, developer_projects: [project]) }
- let(:seeds_block) { }
+ let(:seeds_block) {}
let(:command) { initialize_command }
let(:pipeline) { build(:ci_pipeline, project: project) }
@@ -205,6 +205,30 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
end
end
+ describe '#rule_variables' do
+ let(:config) do
+ {
+ variables: { VAR1: 11 },
+ workflow: {
+ rules: [{ if: '$CI_PIPELINE_SOURCE',
+ variables: { SYMBOL: :symbol, STRING: "string", INTEGER: 1 } },
+ { when: 'always' }]
+ },
+ rspec: { script: 'rake' }
+ }
+ end
+
+ let(:rspec_variables) { command.pipeline_seed.stages[0].statuses[0].variables.to_hash }
+
+ it 'correctly parses rule variables' do
+ run_chain
+
+ expect(rspec_variables['SYMBOL']).to eq("symbol")
+ expect(rspec_variables['STRING']).to eq("string")
+ expect(rspec_variables['INTEGER']).to eq("1")
+ end
+ end
+
context 'N+1 queries' do
it 'avoids N+1 queries when calculating variables of jobs', :use_sql_query_cache do
warm_up_pipeline, warm_up_command = prepare_pipeline1
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
index eeac0c85a77..fb1a360a4b7 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
@@ -148,6 +148,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
payload = Gitlab::Json.parse(params[:body])
+ expect(payload['total_builds_count']).to eq(0)
+
builds = payload['builds']
expect(builds.count).to eq(2)
expect(builds[0]['services']).to be_nil
@@ -160,6 +162,23 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
perform!
end
+
+ context "with existing jobs from other project's alive pipelines" do
+ before do
+ create(:ci_pipeline, :with_job, user: user)
+ create(:ci_pipeline, :with_job)
+ end
+
+ it 'returns the expected total_builds_count' do
+ expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
+ payload = Gitlab::Json.parse(params[:body])
+
+ expect(payload['total_builds_count']).to eq(1)
+ end
+
+ perform!
+ end
+ end
end
context 'when EXTERNAL_VALIDATION_SERVICE_TOKEN is set' do
@@ -243,7 +262,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
end
context 'when save_incompleted is false' do
- let(:save_incompleted) { false}
+ let(:save_incompleted) { false }
it 'adds errors to the pipeline without dropping it' do
perform!
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb
index 83742699d3d..47f172922a5 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb
@@ -160,14 +160,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do
let(:left_value) { 'abcde' }
it { is_expected.to eq(true) }
-
- context 'when the FF ci_fix_rules_if_comparison_with_regexp_variable is disabled' do
- before do
- stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: false)
- end
-
- it { is_expected.to eq(false) }
- end
end
context 'when not matching' do
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
index aad33106647..9e7ea3e4ea4 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
@@ -160,14 +160,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotMatches do
let(:left_value) { 'abcde' }
it { is_expected.to eq(false) }
-
- context 'when the FF ci_fix_rules_if_comparison_with_regexp_variable is disabled' do
- before do
- stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: false)
- end
-
- it { is_expected.to eq(true) }
- end
end
context 'when not matching' do
diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
index bbd11a00149..acaec07f95b 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
@@ -179,24 +179,16 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do
.to_hash
end
- where(:expression, :ff, :result) do
- '$teststring =~ "abcde"' | true | true
- '$teststring =~ "abcde"' | false | true
- '$teststring =~ $teststring' | true | true
- '$teststring =~ $teststring' | false | true
- '$teststring =~ $pattern1' | true | true
- '$teststring =~ $pattern1' | false | false
- '$teststring =~ $pattern2' | true | false
- '$teststring =~ $pattern2' | false | false
+ where(:expression, :result) do
+ '$teststring =~ "abcde"' | true
+ '$teststring =~ $teststring' | true
+ '$teststring =~ $pattern1' | true
+ '$teststring =~ $pattern2' | false
end
with_them do
let(:text) { expression }
- before do
- stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: ff)
- end
-
it { is_expected.to eq(result) }
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb b/spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb
index 8f727749ee2..a742c619584 100644
--- a/spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Quota::Deployments do
let(:pipeline) { build_stubbed(:ci_pipeline, project: project) }
- let(:pipeline_seed) { double(:pipeline_seed, deployments_count: 2)}
+ let(:pipeline_seed) { double(:pipeline_seed, deployments_count: 2) }
let(:command) do
double(:command,
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 040f3ab5830..75f6a773c2d 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -97,15 +97,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- job_variables: [{ key: 'VAR1', value: 'var 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true }],
+ job_variables: [{ key: 'VAR1', value: 'var 1' },
+ { key: 'VAR2', value: 'var 2' }],
rules: [{ if: '$VAR == null', variables: { VAR1: 'new var 1', VAR3: 'var 3' } }] }
end
it do
- is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }])
+ is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1' },
+ { key: 'VAR3', value: 'var 3' },
+ { key: 'VAR2', value: 'var 2' }])
end
end
@@ -114,13 +114,13 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
{
name: 'rspec',
ref: 'master',
- job_variables: [{ key: 'VARIABLE', value: 'value', public: true }],
+ job_variables: [{ key: 'VARIABLE', value: 'value' }],
tag_list: ['static-tag', '$VARIABLE', '$NO_VARIABLE']
}
end
it { is_expected.to include(tag_list: ['static-tag', 'value', '$NO_VARIABLE']) }
- it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value', public: true }]) }
+ it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value' }]) }
end
context 'with cache:key' do
@@ -257,19 +257,19 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- yaml_variables: [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }],
- job_variables: [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }],
+ yaml_variables: [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }],
+ job_variables: [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }],
root_variables_inheritance: root_variables_inheritance }
end
context 'when the pipeline has variables' do
let(:root_variables) do
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var pipeline 2', public: true },
- { key: 'VAR3', value: 'var pipeline 3', public: true },
- { key: 'VAR4', value: 'new var pipeline 4', public: true }]
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var pipeline 2' },
+ { key: 'VAR3', value: 'var pipeline 3' },
+ { key: 'VAR4', value: 'new var pipeline 4' }]
end
context 'when root_variables_inheritance is true' do
@@ -277,10 +277,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns calculated yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true },
- { key: 'VAR4', value: 'new var pipeline 4', public: true }]
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' },
+ { key: 'VAR4', value: 'new var pipeline 4' }]
)
end
end
@@ -290,8 +290,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns job variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }]
+ [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }]
)
end
end
@@ -301,9 +301,9 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns calculated yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }]
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }]
)
end
end
@@ -314,8 +314,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns seed yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }])
+ [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }])
end
end
end
@@ -324,8 +324,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true }],
- job_variables: [{ key: 'VAR1', value: 'var 1', public: true }],
+ yaml_variables: [{ key: 'VAR1', value: 'var 1' }],
+ job_variables: [{ key: 'VAR1', value: 'var 1' }],
root_variables_inheritance: root_variables_inheritance,
rules: rules }
end
@@ -338,14 +338,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
end
it 'recalculates the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true },
- { key: 'VAR2', value: 'new var 2', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
+ { key: 'VAR2', value: 'new var 2' })
end
end
context 'when the rules use root variables' do
let(:root_variables) do
- [{ key: 'VAR2', value: 'var pipeline 2', public: true }]
+ [{ key: 'VAR2', value: 'var pipeline 2' }]
end
let(:rules) do
@@ -353,15 +353,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
end
it 'recalculates the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true },
- { key: 'VAR2', value: 'overridden var 2', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
+ { key: 'VAR2', value: 'overridden var 2' })
end
context 'when the root_variables_inheritance is false' do
let(:root_variables_inheritance) { false }
it 'does not recalculate the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1' })
end
end
end
@@ -769,7 +769,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
with_them do
it { is_expected.not_to be_included }
- it 'correctly populates when:' do
+ it 'still correctly populates when:' do
expect(seed_build.attributes).to include(when: 'never')
end
end
@@ -958,6 +958,26 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
expect(seed_build.attributes).to include(when: 'never')
end
end
+
+ context 'with invalid rules raising error' do
+ let(:rule_set) do
+ [
+ { changes: { paths: ['README.md'], compare_to: 'invalid-ref' }, when: 'never' }
+ ]
+ end
+
+ it { is_expected.not_to be_included }
+
+ it 'correctly populates when:' do
+ expect(seed_build.attributes).to include(when: 'never')
+ end
+
+ it 'returns an error' do
+ expect(seed_build.errors).to contain_exactly(
+ 'Failed to parse rule for rspec: rules:changes:compare_to is not a valid ref'
+ )
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
new file mode 100644
index 00000000000..672117c311f
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Reports::Sbom::Component do
+ let(:attributes) do
+ {
+ 'type' => 'library',
+ 'name' => 'component-name',
+ 'version' => 'v0.0.1'
+ }
+ end
+
+ subject { described_class.new(attributes) }
+
+ it 'has correct attributes' do
+ expect(subject).to have_attributes(
+ component_type: 'library',
+ name: 'component-name',
+ version: 'v0.0.1'
+ )
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/sbom/report_spec.rb b/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
new file mode 100644
index 00000000000..d7a285ab13c
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Reports::Sbom::Report do
+ subject(:report) { described_class.new }
+
+ describe '#add_error' do
+ it 'appends errors to a list' do
+ report.add_error('error1')
+ report.add_error('error2')
+
+ expect(report.errors).to match_array(%w[error1 error2])
+ end
+ end
+
+ describe '#set_source' do
+ let_it_be(:source) do
+ {
+ 'type' => :dependency_scanning,
+ 'data' => {
+ 'input_file' => { 'path' => 'package-lock.json' },
+ 'source_file' => { 'path' => 'package.json' },
+ 'package_manager' => { 'name' => 'npm' },
+ 'language' => { 'name' => 'JavaScript' }
+ },
+ 'fingerprint' => 'c01df1dc736c1148717e053edbde56cb3a55d3e31f87cea955945b6f67c17d42'
+ }
+ end
+
+ it 'stores the source' do
+ report.set_source(source)
+
+ expect(report.source).to be_a(Gitlab::Ci::Reports::Sbom::Source)
+ end
+ end
+
+ describe '#add_component' do
+ let_it_be(:components) do
+ [
+ { 'type' => 'library', 'name' => 'component1', 'version' => 'v0.0.1' },
+ { 'type' => 'library', 'name' => 'component2', 'version' => 'v0.0.2' },
+ { 'type' => 'library', 'name' => 'component2' }
+ ]
+ end
+
+ it 'appends components to a list' do
+ components.each { |component| report.add_component(component) }
+
+ expect(report.components.size).to eq(3)
+ expect(report.components).to all(be_a(Gitlab::Ci::Reports::Sbom::Component))
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb b/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
new file mode 100644
index 00000000000..97d8d7abb33
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Reports::Sbom::Reports do
+ subject(:reports_list) { described_class.new }
+
+ describe '#add_report' do
+ let(:rep1) { Gitlab::Ci::Reports::Sbom::Report.new }
+ let(:rep2) { Gitlab::Ci::Reports::Sbom::Report.new }
+
+ it 'appends the report to the report list' do
+ reports_list.add_report(rep1)
+ reports_list.add_report(rep2)
+
+ expect(reports_list.reports.length).to eq(2)
+ expect(reports_list.reports.first).to eq(rep1)
+ expect(reports_list.reports.last).to eq(rep2)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
new file mode 100644
index 00000000000..2d6434534a0
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Reports::Sbom::Source do
+ let(:attributes) do
+ {
+ 'type' => :dependency_scanning,
+ 'data' => {
+ 'category' => 'development',
+ 'input_file' => { 'path' => 'package-lock.json' },
+ 'source_file' => { 'path' => 'package.json' },
+ 'package_manager' => { 'name' => 'npm' },
+ 'language' => { 'name' => 'JavaScript' }
+ },
+ 'fingerprint' => '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
+ }
+ end
+
+ subject { described_class.new(attributes) }
+
+ it 'has correct attributes' do
+ expect(subject).to have_attributes(
+ source_type: attributes['type'],
+ data: attributes['data'],
+ fingerprint: attributes['fingerprint']
+ )
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/security/reports_spec.rb b/spec/lib/gitlab/ci/reports/security/reports_spec.rb
index 79eee642552..e240edc4a12 100644
--- a/spec/lib/gitlab/ci/reports/security/reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/reports_spec.rb
@@ -57,7 +57,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Reports do
let(:high_severity_dast) { build(:ci_reports_security_finding, severity: 'high', report_type: 'dast') }
let(:vulnerabilities_allowed) { 0 }
let(:severity_levels) { %w(critical high) }
- let(:vulnerability_states) { %w(newly_detected)}
+ let(:vulnerability_states) { %w(newly_detected) }
subject { security_reports.violates_default_policy_against?(target_reports, vulnerabilities_allowed, severity_levels, vulnerability_states) }
diff --git a/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb
index 44e66fd9028..6f75e2c55e8 100644
--- a/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb
@@ -60,7 +60,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
end
describe '#added' do
- let(:new_location) {build(:ci_reports_security_locations_sast, :dynamic) }
+ let(:new_location) { build(:ci_reports_security_locations_sast, :dynamic) }
let(:vul_params) { vuln_params(project.id, [identifier], confidence: :high) }
let(:vuln) { build(:ci_reports_security_finding, severity: Enums::Vulnerability.severity_levels[:critical], location: new_location, **vul_params) }
let(:low_vuln) { build(:ci_reports_security_finding, severity: Enums::Vulnerability.severity_levels[:low], location: new_location, **vul_params) }
diff --git a/spec/lib/gitlab/ci/reports/test_suite_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_spec.rb
index 1d6b39a7831..4a1f77bed65 100644
--- a/spec/lib/gitlab/ci/reports/test_suite_spec.rb
+++ b/spec/lib/gitlab/ci/reports/test_suite_spec.rb
@@ -91,7 +91,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do
subject { test_suite.with_attachment! }
context 'when test cases do not contain an attachment' do
- let(:test_case) { build(:report_test_case, :failed)}
+ let(:test_case) { build(:report_test_case, :failed) }
before do
test_suite.add_test_case(test_case)
@@ -103,7 +103,7 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do
end
context 'when test cases contain an attachment' do
- let(:test_case_with_attachment) { build(:report_test_case, :failed_with_attachment)}
+ let(:test_case_with_attachment) { build(:report_test_case, :failed_with_attachment) }
before do
test_suite.add_test_case(test_case_with_attachment)
diff --git a/spec/lib/gitlab/ci/runner_releases_spec.rb b/spec/lib/gitlab/ci/runner_releases_spec.rb
index 576eb02ad83..ad1e9b12b8a 100644
--- a/spec/lib/gitlab/ci/runner_releases_spec.rb
+++ b/spec/lib/gitlab/ci/runner_releases_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::RunnerReleases do
subject { described_class.instance }
- let(:runner_releases_url) { 'the release API URL' }
+ let(:runner_releases_url) { 'http://testurl.com/runner_public_releases' }
def releases
subject.releases
@@ -18,7 +18,7 @@ RSpec.describe Gitlab::Ci::RunnerReleases do
before do
subject.reset_backoff!
- stub_application_setting(public_runner_releases_url: runner_releases_url)
+ allow(subject).to receive(:runner_releases_url).and_return(runner_releases_url)
end
describe 'caching behavior', :use_clean_rails_memory_store_caching do
@@ -77,7 +77,8 @@ RSpec.describe Gitlab::Ci::RunnerReleases do
allow(Gitlab::HTTP).to receive(:get).with(runner_releases_url, anything) do
http_call_timestamp_offsets << Time.now.utc - start_time
- raise Net::OpenTimeout if opts&.dig(:raise_timeout)
+ err_class = opts&.dig(:raise_error)
+ raise err_class if err_class
mock_http_response(response)
end
@@ -113,12 +114,13 @@ RSpec.describe Gitlab::Ci::RunnerReleases do
end
context 'when request results in timeout' do
- let(:response) { }
+ let(:response) {}
let(:expected_releases) { nil }
let(:expected_releases_by_minor) { nil }
it_behaves_like 'requests that follow cache status', 5.seconds
- it_behaves_like 'a service implementing exponential backoff', raise_timeout: true
+ it_behaves_like 'a service implementing exponential backoff', raise_error: Net::OpenTimeout
+ it_behaves_like 'a service implementing exponential backoff', raise_error: Errno::ETIMEDOUT
end
context 'when response is nil' do
diff --git a/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb b/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb
index f2507a24b10..55c3834bfa7 100644
--- a/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb
+++ b/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb
@@ -5,36 +5,35 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
using RSpec::Parameterized::TableSyntax
- describe '#check_runner_upgrade_status' do
- subject(:result) { described_class.instance.check_runner_upgrade_status(runner_version) }
+ subject(:instance) { described_class.new(gitlab_version, runner_releases) }
+
+ describe '#check_runner_upgrade_suggestion' do
+ subject(:result) { instance.check_runner_upgrade_suggestion(runner_version) }
let(:gitlab_version) { '14.1.1' }
let(:parsed_runner_version) { ::Gitlab::VersionInfo.parse(runner_version, parse_suffix: true) }
-
- before do
- allow(described_class.instance).to receive(:gitlab_version)
- .and_return(::Gitlab::VersionInfo.parse(gitlab_version))
- end
+ let(:runner_releases) { instance_double(Gitlab::Ci::RunnerReleases) }
context 'with failing Gitlab::Ci::RunnerReleases request' do
let(:runner_version) { '14.1.123' }
- let(:runner_releases_double) { instance_double(Gitlab::Ci::RunnerReleases) }
before do
- allow(Gitlab::Ci::RunnerReleases).to receive(:instance).and_return(runner_releases_double)
- allow(runner_releases_double).to receive(:releases).and_return(nil)
+ allow(runner_releases).to receive(:releases).and_return(nil)
end
it 'returns :error' do
- is_expected.to eq({ error: parsed_runner_version })
+ is_expected.to eq([parsed_runner_version, :error])
end
end
context 'with available_runner_releases configured' do
- before do
- url = ::Gitlab::CurrentSettings.current_application_settings.public_runner_releases_url
+ let(:runner_releases) { Gitlab::Ci::RunnerReleases.instance }
+ let(:runner_releases_url) do
+ ::Gitlab::CurrentSettings.current_application_settings.public_runner_releases_url
+ end
- WebMock.stub_request(:get, url).to_return(
+ before do
+ WebMock.stub_request(:get, runner_releases_url).to_return(
body: available_runner_releases.map { |v| { name: v } }.to_json,
status: 200,
headers: { 'Content-Type' => 'application/json' }
@@ -53,7 +52,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:runner_version) { 'v14.0.1' }
it 'returns :not_available' do
- is_expected.to eq({ not_available: parsed_runner_version })
+ is_expected.to eq([parsed_runner_version, :not_available])
end
end
end
@@ -68,7 +67,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:runner_version) { nil }
it 'returns :invalid_version' do
- is_expected.to match({ invalid_version: anything })
+ is_expected.to match([anything, :invalid_version])
end
end
@@ -76,7 +75,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:runner_version) { 'junk' }
it 'returns :invalid_version' do
- is_expected.to match({ invalid_version: anything })
+ is_expected.to match([anything, :invalid_version])
end
end
@@ -87,7 +86,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:runner_version) { 'v14.2.0' }
it 'returns :not_available' do
- is_expected.to eq({ not_available: parsed_runner_version })
+ is_expected.to eq([parsed_runner_version, :not_available])
end
end
end
@@ -96,7 +95,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:gitlab_version) { '14.0.1' }
context 'with valid params' do
- where(:runner_version, :expected_result, :expected_suggested_version) do
+ where(:runner_version, :expected_status, :expected_suggested_version) do
'v15.0.0' | :not_available | '15.0.0' # not available since the GitLab instance is still on 14.x, a major version might be incompatible, and a patch upgrade is not available
'v14.1.0-rc3' | :recommended | '14.1.1' # recommended since even though the GitLab instance is still on 14.0.x, there is a patch release (14.1.1) available which might contain security fixes
'v14.1.0~beta.1574.gf6ea9389' | :recommended | '14.1.1' # suffixes are correctly handled
@@ -116,7 +115,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
end
with_them do
- it { is_expected.to eq({ expected_result => Gitlab::VersionInfo.parse(expected_suggested_version) }) }
+ it { is_expected.to eq([Gitlab::VersionInfo.parse(expected_suggested_version), expected_status]) }
end
end
end
@@ -125,7 +124,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:gitlab_version) { '13.9.0' }
context 'with valid params' do
- where(:runner_version, :expected_result, :expected_suggested_version) do
+ where(:runner_version, :expected_status, :expected_suggested_version) do
'v14.0.0' | :recommended | '14.0.2' # recommended upgrade since 14.0.2 is available, even though the GitLab instance is still on 13.x and a major version might be incompatible
'v13.10.1' | :not_available | '13.10.1' # not available since 13.10.1 is already ahead of GitLab instance version and is the latest patch update for 13.10.x
'v13.10.0' | :recommended | '13.10.1' # recommended upgrade since 13.10.1 is available
@@ -136,7 +135,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
end
with_them do
- it { is_expected.to eq({ expected_result => Gitlab::VersionInfo.parse(expected_suggested_version) }) }
+ it { is_expected.to eq([Gitlab::VersionInfo.parse(expected_suggested_version), expected_status]) }
end
end
end
@@ -152,7 +151,7 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
let(:runner_version) { '14.11.0~beta.29.gd0c550e3' }
it 'recommends 15.1.0 since 14.11 is an unknown release and 15.1.0 is available' do
- is_expected.to eq({ recommended: Gitlab::VersionInfo.new(15, 1, 0) })
+ is_expected.to eq([Gitlab::VersionInfo.new(15, 1, 0), :recommended])
end
end
end
diff --git a/spec/lib/gitlab/ci/status/bridge/common_spec.rb b/spec/lib/gitlab/ci/status/bridge/common_spec.rb
index 30e6ad234a0..37524afc83d 100644
--- a/spec/lib/gitlab/ci/status/bridge/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/bridge/common_spec.rb
@@ -29,15 +29,7 @@ RSpec.describe Gitlab::Ci::Status::Bridge::Common do
end
it { expect(subject).to have_details }
- it { expect(subject.details_path).to include "jobs/#{bridge.id}" }
-
- context 'with ci_retry_downstream_pipeline ff disabled' do
- before do
- stub_feature_flags(ci_retry_downstream_pipeline: false)
- end
-
- it { expect(subject.details_path).to include "pipelines/#{downstream_pipeline.id}" }
- end
+ it { expect(subject.details_path).to include "pipelines/#{downstream_pipeline.id}" }
end
context 'when user does not have access to read downstream pipeline' do
diff --git a/spec/lib/gitlab/ci/status/build/canceled_spec.rb b/spec/lib/gitlab/ci/status/build/canceled_spec.rb
index e30a2211c8f..519b970ca5e 100644
--- a/spec/lib/gitlab/ci/status/build/canceled_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/canceled_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Gitlab::Ci::Status::Build::Canceled do
end
describe '.matches?' do
- subject {described_class.matches?(build, user) }
+ subject { described_class.matches?(build, user) }
context 'when build is canceled' do
let(:build) { create(:ci_build, :canceled) }
diff --git a/spec/lib/gitlab/ci/status/build/created_spec.rb b/spec/lib/gitlab/ci/status/build/created_spec.rb
index 49468674140..9738b3c1f36 100644
--- a/spec/lib/gitlab/ci/status/build/created_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/created_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Gitlab::Ci::Status::Build::Created do
end
describe '.matches?' do
- subject {described_class.matches?(build, user) }
+ subject { described_class.matches?(build, user) }
context 'when build is created' do
let(:build) { create(:ci_build, :created) }
diff --git a/spec/lib/gitlab/ci/status/build/manual_spec.rb b/spec/lib/gitlab/ci/status/build/manual_spec.rb
index 150705c1e36..a1152cb77e3 100644
--- a/spec/lib/gitlab/ci/status/build/manual_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/manual_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Status::Build::Manual do
end
describe '.matches?' do
- subject {described_class.matches?(build, user) }
+ subject { described_class.matches?(build, user) }
context 'when build is manual' do
let(:build) { create(:ci_build, :manual) }
diff --git a/spec/lib/gitlab/ci/status/build/pending_spec.rb b/spec/lib/gitlab/ci/status/build/pending_spec.rb
index 7b695d33877..b7dda9ce9c9 100644
--- a/spec/lib/gitlab/ci/status/build/pending_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/pending_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Gitlab::Ci::Status::Build::Pending do
end
describe '.matches?' do
- subject {described_class.matches?(build, user) }
+ subject { described_class.matches?(build, user) }
context 'when build is pending' do
let(:build) { create(:ci_build, :pending) }
diff --git a/spec/lib/gitlab/ci/status/build/skipped_spec.rb b/spec/lib/gitlab/ci/status/build/skipped_spec.rb
index 0b998a52a57..4437ac0089f 100644
--- a/spec/lib/gitlab/ci/status/build/skipped_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/skipped_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Gitlab::Ci::Status::Build::Skipped do
end
describe '.matches?' do
- subject {described_class.matches?(build, user) }
+ subject { described_class.matches?(build, user) }
context 'when build is skipped' do
let(:build) { create(:ci_build, :skipped) }
diff --git a/spec/lib/gitlab/ci/status/processable/waiting_for_resource_spec.rb b/spec/lib/gitlab/ci/status/processable/waiting_for_resource_spec.rb
index 91a9724d043..26087fd771c 100644
--- a/spec/lib/gitlab/ci/status/processable/waiting_for_resource_spec.rb
+++ b/spec/lib/gitlab/ci/status/processable/waiting_for_resource_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe Gitlab::Ci::Status::Processable::WaitingForResource do
end
describe '.matches?' do
- subject {described_class.matches?(processable, user) }
+ subject { described_class.matches?(processable, user) }
context 'when processable is waiting for resource' do
let(:processable) { create(:ci_build, :waiting_for_resource) }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb
index 0f97bc06a4e..5ff179b6fee 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb
@@ -36,9 +36,10 @@ RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:pipeline) { service.execute(merge_request).payload }
- it 'has no jobs' do
+ it 'creates a pipeline with the expected jobs' do
expect(pipeline).to be_merge_request_event
- expect(build_names).to be_empty
+ expect(pipeline.errors.full_messages).to be_empty
+ expect(build_names).to match_array(%w(kics-iac-sast))
end
end
diff --git a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
index 78d3982a79f..1a909f52ec3 100644
--- a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml' do
context 'when the project is set for deployment to AWS' do
let(:platform_value) { 'ECS' }
- let(:review_prod_build_names) { build_names.select {|n| n.include?('review') || n.include?('production')} }
+ let(:review_prod_build_names) { build_names.select { |n| n.include?('review') || n.include?('production') } }
before do
create(:ci_variable, project: project, key: 'AUTO_DEVOPS_PLATFORM_TARGET', value: platform_value)
diff --git a/spec/lib/gitlab/ci/trace/remote_checksum_spec.rb b/spec/lib/gitlab/ci/trace/remote_checksum_spec.rb
index 1cd88034166..be29543676f 100644
--- a/spec/lib/gitlab/ci/trace/remote_checksum_spec.rb
+++ b/spec/lib/gitlab/ci/trace/remote_checksum_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe Gitlab::Ci::Trace::RemoteChecksum do
end
context 'when the response does not include :content_md5' do
- let(:metadata) {{}}
+ let(:metadata) { {} }
it 'raises an exception' do
expect { subject }.to raise_error KeyError, /content_md5/
@@ -55,7 +55,7 @@ RSpec.describe Gitlab::Ci::Trace::RemoteChecksum do
end
context 'when the response include :content_md5' do
- let(:metadata) {{ content_md5: base64checksum }}
+ let(:metadata) { { content_md5: base64checksum } }
it { is_expected.to eq(checksum) }
end
diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb
index 8ec0846bdca..6ab2089cce8 100644
--- a/spec/lib/gitlab/ci/variables/builder_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Variables::Builder do
+ include Ci::TemplateHelpers
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, namespace: group) }
let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project) }
@@ -92,6 +93,8 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
value: project.pages_url },
{ key: 'CI_API_V4_URL',
value: API::Helpers::Version.new('v4').root_url },
+ { key: 'CI_TEMPLATE_REGISTRY_HOST',
+ value: template_registry_host },
{ key: 'CI_PIPELINE_IID',
value: pipeline.iid.to_s },
{ key: 'CI_PIPELINE_SOURCE',
diff --git a/spec/lib/gitlab/ci/variables/collection_spec.rb b/spec/lib/gitlab/ci/variables/collection_spec.rb
index 26c560565e0..8ac03301322 100644
--- a/spec/lib/gitlab/ci/variables/collection_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection_spec.rb
@@ -302,6 +302,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
.append(key: 'CI_BUILD_ID', value: '1')
.append(key: 'RAW_VAR', value: '$TEST1', raw: true)
.append(key: 'TEST1', value: 'test-3')
+ .append(key: 'FILEVAR1', value: 'file value 1', file: true)
end
context 'table tests' do
@@ -311,28 +312,23 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
{
"empty value": {
value: '',
- result: '',
- keep_undefined: false
+ result: ''
},
"simple expansions": {
value: 'key$TEST1-$CI_BUILD_ID',
- result: 'keytest-3-1',
- keep_undefined: false
+ result: 'keytest-3-1'
},
"complex expansion": {
value: 'key${TEST1}-${CI_JOB_NAME}',
- result: 'keytest-3-test-1',
- keep_undefined: false
+ result: 'keytest-3-test-1'
},
"complex expansions with raw variable": {
value: 'key${RAW_VAR}-${CI_JOB_NAME}',
- result: 'key$TEST1-test-1',
- keep_undefined: false
+ result: 'key$TEST1-test-1'
},
"missing variable not keeping original": {
value: 'key${MISSING_VAR}-${CI_JOB_NAME}',
- result: 'key-test-1',
- keep_undefined: false
+ result: 'key-test-1'
},
"missing variable keeping original": {
value: 'key${MISSING_VAR}-${CI_JOB_NAME}',
@@ -341,14 +337,24 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
},
"escaped characters are kept intact": {
value: 'key-$TEST1-%%HOME%%-$${HOME}',
- result: 'key-test-3-%%HOME%%-$${HOME}',
- keep_undefined: false
+ result: 'key-test-3-%%HOME%%-$${HOME}'
+ },
+ "file variable with expand_file_vars: true": {
+ value: 'key-$FILEVAR1-$TEST1',
+ result: 'key-file value 1-test-3'
+ },
+ "file variable with expand_file_vars: false": {
+ value: 'key-$FILEVAR1-$TEST1',
+ result: 'key-$FILEVAR1-test-3',
+ expand_file_vars: false
}
}
end
with_them do
- subject { collection.expand_value(value, keep_undefined: keep_undefined) }
+ let(:options) { { keep_undefined: keep_undefined, expand_file_vars: expand_file_vars }.compact }
+
+ subject(:result) { collection.expand_value(value, **options) }
it 'matches expected expansion' do
is_expected.to eq(result)
diff --git a/spec/lib/gitlab/ci/variables/helpers_spec.rb b/spec/lib/gitlab/ci/variables/helpers_spec.rb
index f13b334c10e..2a1cdaeb3a7 100644
--- a/spec/lib/gitlab/ci/variables/helpers_spec.rb
+++ b/spec/lib/gitlab/ci/variables/helpers_spec.rb
@@ -15,21 +15,27 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
end
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
subject { described_class.merge_variables(current_variables, new_variables) }
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
context 'when new variables is a hash' do
let(:new_variables) do
{ 'key2' => 'value22', 'key3' => 'value3' }
end
- it { is_expected.to eq(result) }
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
+ end
+
+ it { is_expected.to match_array(result) }
end
context 'when new variables is a hash with symbol keys' do
@@ -37,67 +43,72 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
{ key2: 'value22', key3: 'value3' }
end
- it { is_expected.to eq(result) }
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
+ end
+
+ it { is_expected.to match_array(result) }
end
context 'when new variables is nil' do
let(:new_variables) {}
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
end
- describe '.transform_to_yaml_variables' do
- let(:variables) do
- { 'key1' => 'value1', 'key2' => 'value2' }
- end
+ describe '.transform_to_array' do
+ subject { described_class.transform_to_array(variables) }
- let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
- end
+ context 'when values are strings' do
+ let(:variables) do
+ { 'key1' => 'value1', 'key2' => 'value2' }
+ end
- subject { described_class.transform_to_yaml_variables(variables) }
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
+ end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
+ end
context 'when variables is nil' do
let(:variables) {}
- it { is_expected.to eq([]) }
- end
- end
-
- describe '.transform_from_yaml_variables' do
- let(:variables) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
+ it { is_expected.to match_array([]) }
end
- let(:result) do
- { 'key1' => 'value1', 'key2' => 'value2' }
- end
+ context 'when values are hashes' do
+ let(:variables) do
+ { 'key1' => { value: 'value1', description: 'var 1' }, 'key2' => { value: 'value2' } }
+ end
- subject { described_class.transform_from_yaml_variables(variables) }
+ let(:result) do
+ [{ key: 'key1', value: 'value1', description: 'var 1' },
+ { key: 'key2', value: 'value2' }]
+ end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
- context 'when variables is nil' do
- let(:variables) {}
+ context 'when a value data has `key` as a key' do
+ let(:variables) do
+ { 'key1' => { value: 'value1', key: 'new_key1' }, 'key2' => { value: 'value2' } }
+ end
- it { is_expected.to eq({}) }
- end
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
+ end
- context 'when variables is a hash' do
- let(:variables) do
- { key1: 'value1', 'key2' => 'value2' }
+ it { is_expected.to match_array(result) }
end
-
- it { is_expected.to eq(result) }
end
end
@@ -115,35 +126,35 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
let(:inheritance) { true }
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
subject { described_class.inherit_yaml_variables(from: from, to: to, inheritance: inheritance) }
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
context 'when inheritance is false' do
let(:inheritance) { false }
let(:result) do
- [{ key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
context 'when inheritance is array' do
let(:inheritance) { ['key2'] }
let(:result) do
- [{ key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
index 8416501e949..f7a0905d9da 100644
--- a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
@@ -72,8 +72,8 @@ module Gitlab
it 'returns calculated variables with root and job variables' do
is_expected.to match_array([
- { key: 'VAR1', value: 'value 11', public: true },
- { key: 'VAR2', value: 'value 2', public: true }
+ { key: 'VAR1', value: 'value 11' },
+ { key: 'VAR2', value: 'value 2' }
])
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 22bc6b0db59..3477fe837b4 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -448,7 +448,7 @@ module Gitlab
it 'parses the root:variables as #root_variables' do
expect(subject.root_variables)
- .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
+ .to contain_exactly({ key: 'SUPPORTED', value: 'parsed' })
end
end
@@ -490,7 +490,7 @@ module Gitlab
it 'parses the root:variables as #root_variables' do
expect(subject.root_variables)
- .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
+ .to contain_exactly({ key: 'SUPPORTED', value: 'parsed' })
end
end
@@ -1098,8 +1098,8 @@ module Gitlab
it 'returns job variables' do
expect(job_variables).to contain_exactly(
- { key: 'VAR1', value: 'value1', public: true },
- { key: 'VAR2', value: 'value2', public: true }
+ { key: 'VAR1', value: 'value1' },
+ { key: 'VAR2', value: 'value2' }
)
expect(root_variables_inheritance).to eq(true)
end
@@ -1203,21 +1203,21 @@ module Gitlab
expect(config_processor.builds[0]).to include(
name: 'test1',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'test1 var 1', public: true },
- { key: 'VAR2', value: 'test2 var 2', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'test1 var 1' },
+ { key: 'VAR2', value: 'test2 var 2' }]
)
expect(config_processor.builds[1]).to include(
name: 'test2',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'base var 1', public: true },
- { key: 'VAR2', value: 'test2 var 2', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'base var 1' },
+ { key: 'VAR2', value: 'test2 var 2' }]
)
expect(config_processor.builds[2]).to include(
name: 'test3',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'base var 1', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'base var 1' }]
)
expect(config_processor.builds[3]).to include(
@@ -1425,7 +1425,7 @@ module Gitlab
it 'returns the parallel config' do
build_options = builds.map { |build| build[:options] }
parallel_config = {
- matrix: parallel[:matrix].map { |var| var.transform_values { |v| Array(v).flatten }},
+ matrix: parallel[:matrix].map { |var| var.transform_values { |v| Array(v).flatten } },
total: build_options.size
}
@@ -1766,6 +1766,7 @@ module Gitlab
script: ["make changelog | tee release_changelog.txt"],
release: {
tag_name: "$CI_COMMIT_TAG",
+ tag_message: "Annotated tag message",
name: "Release $CI_TAG_NAME",
description: "./release_changelog.txt",
ref: 'b3235930aa443112e639f941c69c578912189bdd',
@@ -1956,7 +1957,7 @@ module Gitlab
subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)).execute }
context 'no dependencies' do
- let(:dependencies) { }
+ let(:dependencies) {}
it { is_expected.to be_valid }
end
@@ -2012,8 +2013,8 @@ module Gitlab
end
describe "Job Needs" do
- let(:needs) { }
- let(:dependencies) { }
+ let(:needs) {}
+ let(:dependencies) {}
let(:config) do
{
@@ -2893,7 +2894,7 @@ module Gitlab
end
end
- describe 'Rules' do
+ describe 'Job rules' do
context 'changes' do
let(:config) do
<<~YAML
@@ -2938,6 +2939,49 @@ module Gitlab
end
end
+ describe 'Workflow rules' do
+ context 'changes' do
+ let(:config) do
+ <<~YAML
+ workflow:
+ rules:
+ - changes: [README.md]
+
+ rspec:
+ script: exit 0
+ YAML
+ end
+
+ it 'returns pipeline with correct rules' do
+ expect(processor.builds.size).to eq(1)
+ expect(processor.workflow_rules).to eq(
+ [{ changes: { paths: ["README.md"] } }]
+ )
+ end
+
+ context 'with paths' do
+ let(:config) do
+ <<~YAML
+ workflow:
+ rules:
+ - changes:
+ paths: [README.md]
+
+ rspec:
+ script: exit 0
+ YAML
+ end
+
+ it 'returns pipeline with correct rules' do
+ expect(processor.builds.size).to eq(1)
+ expect(processor.workflow_rules).to eq(
+ [{ changes: { paths: ["README.md"] } }]
+ )
+ end
+ end
+ end
+ end
+
describe '#execute' do
subject { Gitlab::Ci::YamlProcessor.new(content).execute }