diff options
Diffstat (limited to 'spec/lib/gitlab/ci')
29 files changed, 822 insertions, 243 deletions
diff --git a/spec/lib/gitlab/ci/build/image_spec.rb b/spec/lib/gitlab/ci/build/image_spec.rb index f8c0d69be2e..3854437483d 100644 --- a/spec/lib/gitlab/ci/build/image_spec.rb +++ b/spec/lib/gitlab/ci/build/image_spec.rb @@ -29,7 +29,7 @@ RSpec.describe Gitlab::Ci::Build::Image do context 'when image is defined as hash' do let(:entrypoint) { '/bin/sh' } let(:pull_policy) { %w[always if-not-present] } - let(:executor_opts) { { docker: { platform: 'arm64' } } } + let(:executor_opts) { { docker: { platform: 'arm64', user: 'dave' } } } let(:job) do create(:ci_build, options: { image: { name: image_name, @@ -101,7 +101,7 @@ RSpec.describe Gitlab::Ci::Build::Image do let(:service_entrypoint) { '/bin/sh' } let(:service_alias) { 'db' } let(:service_command) { 'sleep 30' } - let(:executor_opts) { { docker: { platform: 'amd64' } } } + let(:executor_opts) { { docker: { platform: 'amd64', user: 'dave' } } } let(:pull_policy) { %w[always if-not-present] } let(:job) do create(:ci_build, options: { services: [{ name: service_image_name, entrypoint: service_entrypoint, diff --git a/spec/lib/gitlab/ci/build/policy/changes_spec.rb b/spec/lib/gitlab/ci/build/policy/changes_spec.rb index 00e44650d44..4ee8903dcd3 100644 --- a/spec/lib/gitlab/ci/build/policy/changes_spec.rb +++ b/spec/lib/gitlab/ci/build/policy/changes_spec.rb @@ -134,7 +134,7 @@ RSpec.describe Gitlab::Ci::Build::Policy::Changes do ref: 'feature', source: source, sha: '0b4bc9a4', - before_sha: Gitlab::Git::BLANK_SHA, + before_sha: Gitlab::Git::SHA1_BLANK_SHA, merge_request: merge_request ) end diff --git a/spec/lib/gitlab/ci/build/rules_spec.rb b/spec/lib/gitlab/ci/build/rules_spec.rb index 99577539798..61bd9f41182 100644 --- a/spec/lib/gitlab/ci/build/rules_spec.rb +++ b/spec/lib/gitlab/ci/build/rules_spec.rb @@ -254,6 +254,18 @@ RSpec.describe Gitlab::Ci::Build::Rules, feature_category: :pipeline_composition end end + context 'with auto_cancel' do + context 'with matching rule' do + let(:rule_list) { [{ if: '$VAR == null', auto_cancel: { on_new_commit: 'interruptible' } }] } + + it do + is_expected.to eq( + described_class::Result.new(when: 'on_success', auto_cancel: { on_new_commit: 'interruptible' }) + ) + end + end + end + context 'with a regexp variable matching rule' do let(:rule_list) { [{ if: '"abcde" =~ $pattern' }] } diff --git a/spec/lib/gitlab/ci/config/entry/auto_cancel_spec.rb b/spec/lib/gitlab/ci/config/entry/auto_cancel_spec.rb index bdd66cc00a1..764908ee040 100644 --- a/spec/lib/gitlab/ci/config/entry/auto_cancel_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/auto_cancel_spec.rb @@ -25,7 +25,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::AutoCancel, feature_category: :pipelin it 'returns errors' do expect(config.errors) - .to include('auto cancel on new commit must be one of: conservative, interruptible, disabled') + .to include('auto cancel on new commit must be one of: conservative, interruptible, none') end end end diff --git a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb index 35f2a99ee87..04154b72453 100644 --- a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb @@ -23,6 +23,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge, feature_category: :continuous_ end end + describe '.visible?' do + it 'always returns true' do + expect(described_class.visible?).to be_truthy + end + end + describe '.matching?' do subject { described_class.matching?(name, config) } diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb index 99a6e25b313..0a82010c20c 100644 --- a/spec/lib/gitlab/ci/config/entry/image_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb @@ -112,7 +112,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do end end - context "when docker specifies an option" do + context "when docker specifies platform" do let(:config) { { name: 'image:1.0', docker: { platform: 'amd64' } } } it 'is valid' do @@ -129,15 +129,73 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do ) end end + + context "when invalid data type is specified for platform option" do + let(:config) { { name: 'image:1.0', docker: { platform: 1 } } } + + it 'raises an error' do + expect(entry).not_to be_valid + expect(entry.errors.first) + .to match %r{image executor opts '/docker/platform' must be a valid 'string'} + end + end + end + + context "when docker specifies user" do + let(:config) { { name: 'image:1.0', docker: { user: 'dave' } } } + + it 'is valid' do + expect(entry).to be_valid + end + + describe '#value' do + it "returns value" do + expect(entry.value).to eq( + name: 'image:1.0', + executor_opts: { + docker: { user: 'dave' } + } + ) + end + end + + context "when user is a UID" do + let(:config) { { name: 'image:1.0', docker: { user: '1001' } } } + + it 'is valid' do + expect(entry).to be_valid + end + + describe '#value' do + it "returns value" do + expect(entry.value).to eq( + name: 'image:1.0', + executor_opts: { + docker: { user: '1001' } + } + ) + end + end + end + + context "when invalid data type is specified for user option" do + let(:config) { { name: 'image:1.0', docker: { user: 1 } } } + + it 'raises an error' do + expect(entry).not_to be_valid + expect(entry.errors.first) + .to match %r{image executor opts '/docker/user' must be a valid 'string'} + end + end end context "when docker specifies an invalid option" do - let(:config) { { name: 'image:1.0', docker: { platform: 1 } } } + let(:config) { { name: 'image:1.0', docker: { unknown_key: 'foo' } } } it 'is not valid' do expect(entry).not_to be_valid expect(entry.errors.first) - .to match %r{image executor opts '/docker/platform' must be a valid 'string'} + .to match %r{image executor opts '/docker/unknown_key' must be a valid 'schema'} end end end diff --git a/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb index cd8e35ede61..a9f891a7b50 100644 --- a/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'fast_spec_helper' +require 'spec_helper' require_dependency 'active_model' RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules::Rule, feature_category: :pipeline_composition do diff --git a/spec/lib/gitlab/ci/config/entry/needs_spec.rb b/spec/lib/gitlab/ci/config/entry/needs_spec.rb index d1a8a74ac06..61bb3e912ba 100644 --- a/spec/lib/gitlab/ci/config/entry/needs_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/needs_spec.rb @@ -52,6 +52,27 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Needs, feature_category: :pipeline_c end end + context 'when config has disallowed keys' do + let(:config) { ['some_value'] } + + before do + needs.metadata[:allowed_needs] = %i[cross_dependency] + needs.compose! + end + + describe '#valid?' do + it 'returns invalid' do + expect(needs.valid?).to be_falsey + end + end + + describe '#errors' do + it 'returns invalid types error' do + expect(needs.errors).to include('needs config uses invalid types: job') + end + end + end + context 'when wrong needs type is used' do let(:config) { [{ job: 'job_name', artifacts: true, some: :key }] } 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 3531d6e9f1a..d5bf532c216 100644 --- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' require 'gitlab_chronic_duration' -RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do +RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule, feature_category: :pipeline_composition do let(:factory) do Gitlab::Config::Entry::Factory.new(described_class) .metadata(metadata) @@ -11,7 +11,10 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do end let(:metadata) do - { allowed_when: %w[on_success on_failure always never manual delayed] } + { + allowed_when: %w[on_success on_failure always never manual delayed], + allowed_keys: %i[if changes exists when start_in allow_failure variables needs auto_cancel] + } end let(:entry) { factory.create! } @@ -296,18 +299,18 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do end end - context 'with a string passed in metadata but not allowed in the class' do - let(:metadata) { { allowed_when: %w[explode] } } + context 'with an invalid when' do + let(:metadata) { { allowed_when: %w[always never], allowed_keys: %i[if when] } } let(:config) do - { if: '$THIS == "that"', when: 'explode' } + { if: '$THIS == "that"', when: 'on_success' } end it { is_expected.to be_a(described_class) } it { is_expected.not_to be_valid } it 'returns an error about invalid when:' do - expect(subject.errors).to include(/when unknown value: explode/) + expect(subject.errors).to include(/when unknown value: on_success/) end context 'when composed' do @@ -318,41 +321,30 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do it { is_expected.not_to be_valid } it 'returns an error about invalid when:' do - expect(subject.errors).to include(/when unknown value: explode/) + expect(subject.errors).to include(/when unknown value: on_success/) end end end - context 'with a string allowed in the class but not passed in metadata' do - let(:metadata) { { allowed_when: %w[always never] } } - + context 'with an invalid variables' do let(:config) do - { if: '$THIS == "that"', when: 'on_success' } + { if: '$THIS == "that"', variables: 'hello' } end - it { is_expected.to be_a(described_class) } - it { is_expected.not_to be_valid } - - it 'returns an error about invalid when:' do - expect(subject.errors).to include(/when unknown value: on_success/) + before do + subject.compose! end - context 'when composed' do - before do - subject.compose! - end - - it { is_expected.not_to be_valid } + it { is_expected.not_to be_valid } - it 'returns an error about invalid when:' do - expect(subject.errors).to include(/when unknown value: on_success/) - end + it 'returns an error about invalid variables:' do + expect(subject.errors).to include(/variables config should be a hash/) end end - context 'with an invalid variables' do + context 'with an invalid auto_cancel' do let(:config) do - { if: '$THIS == "that"', variables: 'hello' } + { if: '$THIS == "that"', auto_cancel: { on_new_commit: 'xyz' } } end before do @@ -361,8 +353,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do it { is_expected.not_to be_valid } - it 'returns an error about invalid variables:' do - expect(subject.errors).to include(/variables config should be a hash/) + it 'returns an error' do + expect(subject.errors).to include( + 'auto_cancel on new commit must be one of: conservative, interruptible, none') end end end @@ -445,6 +438,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do it { is_expected.to eq(config) } end + + context 'when it has auto_cancel' do + let(:config) { { if: '$THIS || $THAT', auto_cancel: { on_new_commit: 'interruptible' } } } + + it { is_expected.to eq(config) } + end end describe '.default' do diff --git a/spec/lib/gitlab/ci/config/entry/rules_spec.rb b/spec/lib/gitlab/ci/config/entry/rules_spec.rb index b0871f2345e..0113b6c1f7f 100644 --- a/spec/lib/gitlab/ci/config/entry/rules_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/rules_spec.rb @@ -1,16 +1,18 @@ # frozen_string_literal: true -require 'fast_spec_helper' +require 'spec_helper' require_dependency 'active_model' -RSpec.describe Gitlab::Ci::Config::Entry::Rules do +RSpec.describe Gitlab::Ci::Config::Entry::Rules, feature_category: :pipeline_composition do let(:factory) do Gitlab::Config::Entry::Factory.new(described_class) .metadata(metadata) .value(config) end - let(:metadata) { { allowed_when: %w[always never] } } + let(:metadata) do + { allowed_when: %w[always never], allowed_keys: %i[if when] } + end subject(:entry) { factory.create! } diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb index 82747e7b521..8ce0f890b46 100644 --- a/spec/lib/gitlab/ci/config/entry/service_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb @@ -154,22 +154,45 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do end context 'when configuration has docker options' do - let(:config) { { name: 'postgresql:9.5', docker: { platform: 'amd64' } } } + context "with platform option" do + let(:config) { { name: 'postgresql:9.5', docker: { platform: 'amd64' } } } - describe '#valid?' do - it 'is valid' do - expect(entry).to be_valid + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end + end + + describe '#value' do + it "returns value" do + expect(entry.value).to eq( + name: 'postgresql:9.5', + executor_opts: { + docker: { platform: 'amd64' } + } + ) + end end end - describe '#value' do - it "returns value" do - expect(entry.value).to eq( - name: 'postgresql:9.5', - executor_opts: { - docker: { platform: 'amd64' } - } - ) + context "with user option" do + let(:config) { { name: 'postgresql:9.5', docker: { user: 'dave' } } } + + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end + end + + describe '#value' do + it "returns value" do + expect(entry.value).to eq( + name: 'postgresql:9.5', + executor_opts: { + docker: { user: 'dave' } + } + ) + end end end end diff --git a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb index d3ce3ffe641..dbd25010884 100644 --- a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb @@ -6,6 +6,10 @@ RSpec.describe Gitlab::Ci::Config::Entry::Workflow, feature_category: :pipeline_ subject(:config) { described_class.new(workflow_hash) } describe 'validations' do + before do + config.compose! + end + context 'when work config value is a string' do let(:workflow_hash) { 'build' } @@ -27,6 +31,28 @@ RSpec.describe Gitlab::Ci::Config::Entry::Workflow, feature_category: :pipeline_ end context 'when work config value is a hash' do + context 'with an invalid key' do + let(:workflow_hash) { { trash: [{ if: '$VAR' }] } } + + describe '#valid?' do + it 'is invalid' do + expect(config).not_to be_valid + end + + it 'attaches an error specifying the unknown key' do + expect(config.errors).to include('workflow config contains unknown keys: trash') + end + end + + describe '#value' do + it 'returns the invalid configuration' do + expect(config.value).to eq(workflow_hash) + end + end + end + end + + context 'when config has rules' do let(:workflow_hash) { { rules: [{ if: '$VAR' }] } } describe '#valid?' do @@ -45,8 +71,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Workflow, feature_category: :pipeline_ end end - context 'with an invalid key' do - let(:workflow_hash) { { trash: [{ if: '$VAR' }] } } + context 'when rules has an invalid key' do + let(:workflow_hash) { { rules: [{ if: '$VAR', trash: 'something' }] } } describe '#valid?' do it 'is invalid' do @@ -54,7 +80,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Workflow, feature_category: :pipeline_ end it 'attaches an error specifying the unknown key' do - expect(config.errors).to include('workflow config contains unknown keys: trash') + expect(config.errors).to include('rules:rule config contains unknown keys: trash') end end @@ -64,6 +90,41 @@ RSpec.describe Gitlab::Ci::Config::Entry::Workflow, feature_category: :pipeline_ end end end + + context 'when rules has auto_cancel' do + let(:workflow_hash) { { rules: [{ if: '$VAR', auto_cancel: { on_new_commit: 'interruptible' } }] } } + + describe '#valid?' do + it 'is valid' do + expect(config).to be_valid + end + + it 'attaches no errors' do + expect(config.errors).to be_empty + end + end + + describe '#value' do + it 'returns the config' do + expect(config.value).to eq(workflow_hash) + end + end + + context 'when auto_cancel has an invalid value' do + let(:workflow_hash) { { rules: [{ if: '$VAR', auto_cancel: { on_new_commit: 'xyz' } }] } } + + describe '#valid?' do + it 'is invalid' do + expect(config).not_to be_valid + end + + it 'returns error' do + expect(config.errors).to include( + 'rules:rule:auto_cancel on new commit must be one of: conservative, interruptible, none') + end + end + end + end end end diff --git a/spec/lib/gitlab/ci/config/external/context_spec.rb b/spec/lib/gitlab/ci/config/external/context_spec.rb index 9ac72ebbac8..3409fc53d19 100644 --- a/spec/lib/gitlab/ci/config/external/context_spec.rb +++ b/spec/lib/gitlab/ci/config/external/context_spec.rb @@ -159,10 +159,14 @@ RSpec.describe Gitlab::Ci::Config::External::Context, feature_category: :pipelin shared_examples 'a mutated context' do let(:mutated) { subject.mutate(new_attributes) } + let(:lazy_response) { double('lazy_response') } before do + allow(lazy_response).to receive(:execute).and_return(lazy_response) + subject.expandset << :a_file subject.set_deadline(15.seconds) + subject.execute_remote_parallel_request(lazy_response) end it { expect(mutated).not_to eq(subject) } @@ -170,8 +174,9 @@ RSpec.describe Gitlab::Ci::Config::External::Context, feature_category: :pipelin it { expect(mutated).to have_attributes(new_attributes) } it { expect(mutated.pipeline).to eq(subject.pipeline) } it { expect(mutated.expandset).to eq(subject.expandset) } - it { expect(mutated.execution_deadline).to eq(mutated.execution_deadline) } - it { expect(mutated.logger).to eq(mutated.logger) } + it { expect(mutated.execution_deadline).to eq(subject.execution_deadline) } + it { expect(mutated.logger).to eq(subject.logger) } + it { expect(mutated.parallel_requests).to eq(subject.parallel_requests) } end context 'with attributes' do @@ -212,4 +217,80 @@ RSpec.describe Gitlab::Ci::Config::External::Context, feature_category: :pipelin end end end + + describe '#execute_remote_parallel_request' do + let(:lazy_response1) { double('lazy_response', wait: true, complete?: complete1) } + let(:lazy_response2) { double('lazy_response') } + + let(:complete1) { false } + + before do + allow(lazy_response1).to receive(:execute).and_return(lazy_response1) + allow(lazy_response2).to receive(:execute).and_return(lazy_response2) + end + + context 'when the queue is empty' do + before do + stub_const("Gitlab::Ci::Config::External::Context::MAX_PARALLEL_REMOTE_REQUESTS", 2) + end + + it 'adds the new lazy response to the queue' do + expect { subject.execute_remote_parallel_request(lazy_response1) } + .to change { subject.parallel_requests } + .from([]) + .to([lazy_response1]) + end + end + + context 'when there is a lazy response in the queue' do + before do + subject.execute_remote_parallel_request(lazy_response1) + end + + context 'when there is a free slot in the queue' do + before do + stub_const("Gitlab::Ci::Config::External::Context::MAX_PARALLEL_REMOTE_REQUESTS", 2) + end + + it 'adds the new lazy response to the queue' do + expect { subject.execute_remote_parallel_request(lazy_response2) } + .to change { subject.parallel_requests } + .from([lazy_response1]) + .to([lazy_response1, lazy_response2]) + end + end + + context 'when the queue is full' do + before do + stub_const("Gitlab::Ci::Config::External::Context::MAX_PARALLEL_REMOTE_REQUESTS", 1) + end + + context 'when the first lazy response in the queue is complete' do + let(:complete1) { true } + + it 'removes the completed lazy response and adds the new one to the queue' do + expect(lazy_response1).not_to receive(:wait) + + expect { subject.execute_remote_parallel_request(lazy_response2) } + .to change { subject.parallel_requests } + .from([lazy_response1]) + .to([lazy_response2]) + end + end + + context 'when the first lazy response in the queue is not complete' do + let(:complete1) { false } + + it 'waits for the first lazy response to complete and then adds the new one to the queue' do + expect(lazy_response1).to receive(:wait) + + expect { subject.execute_remote_parallel_request(lazy_response2) } + .to change { subject.parallel_requests } + .from([lazy_response1]) + .to([lazy_response1, lazy_response2]) + end + end + end + end + end end diff --git a/spec/lib/gitlab/ci/config/external/file/component_spec.rb b/spec/lib/gitlab/ci/config/external/file/component_spec.rb index 88e272ac3fd..7907837db6a 100644 --- a/spec/lib/gitlab/ci/config/external/file/component_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/component_spec.rb @@ -146,6 +146,16 @@ RSpec.describe Gitlab::Ci::Config::External::File::Component, feature_category: external_resource.content end + + context 'when user is missing in a context' do + let_it_be(:user) { nil } + + it 'does not track the event' do + expect(::Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event) + + external_resource.content + end + end end context 'when component is invalid' do 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 7293e640112..adca9e750d0 100644 --- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb @@ -157,6 +157,40 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote, feature_category: :pi it_behaves_like "#content" end + describe '#preload_content' do + context 'when the parallel request queue is full' do + let(:location1) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.secret_file1.yml' } + let(:location2) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.secret_file2.yml' } + + before do + # Makes the parallel queue full easily + stub_const("Gitlab::Ci::Config::External::Context::MAX_PARALLEL_REMOTE_REQUESTS", 1) + + # Adding a failing promise to the queue + promise = Concurrent::Promise.new do + sleep 1.1 + raise Timeout::Error + end + + context.execute_remote_parallel_request( + Gitlab::HTTP_V2::LazyResponse.new(promise, location1, {}, nil) + ) + + stub_full_request(location2).to_return(body: remote_file_content) + end + + it 'waits for the queue' do + file2 = described_class.new({ remote: location2 }, context) + + start_at = Time.current + file2.preload_content + end_at = Time.current + + expect(end_at - start_at).to be > 1 + end + end + end + describe "#error_message" do subject(:error_message) do Gitlab::Ci::Config::External::Mapper::Verifier.new(context).process([remote_file]) diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb index 2c57106b07c..9718d16756c 100644 --- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb +++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb @@ -3,118 +3,173 @@ require 'fast_spec_helper' RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties, feature_category: :dependency_management do - subject(:parse_source_from_properties) { described_class.parse_source(properties) } + shared_examples 'handling invalid properties' do + context 'when properties are nil' do + let(:properties) { nil } - context 'when properties are nil' do - let(:properties) { nil } + it { is_expected.to be_nil } + end + + context 'when report does not have valid properties' do + let(:properties) { ['name' => 'foo', 'value' => 'bar'] } - it { is_expected.to be_nil } + it { is_expected.to be_nil } + end end - context 'when report does not have gitlab properties' do - let(:properties) { ['name' => 'foo', 'value' => 'bar'] } + describe '#parse_source' do + subject(:parse_source_from_properties) { described_class.parse_source(properties) } - it { is_expected.to be_nil } - end + it_behaves_like 'handling invalid properties' - 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 + 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 + 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' } - ] + 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 - it { is_expected.to be_nil } - end + context 'when no dependency_scanning or container_scanning properties are present' do + let(:properties) do + [ + { 'name' => 'gitlab:meta:schema_version', 'value' => '1' }, + { 'name' => 'gitlab::aquasecurity:trivy:FilePath', 'value' => '1' } + ] + end - context 'when no dependency_scanning or container_scanning properties are present' do - let(:properties) do - [ - { 'name' => 'gitlab:meta:schema_version', 'value' => '1' } - ] + it 'does not call source parsers' do + expect(Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning).not_to receive(:source) + expect(Gitlab::Ci::Parsers::Sbom::Source::ContainerScanning).not_to receive(:source) + + parse_source_from_properties + end end - it 'does not call source parsers' do - expect(Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning).not_to receive(:source) - expect(Gitlab::Ci::Parsers::Sbom::Source::ContainerScanning).not_to receive(:source) + 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 - parse_source_from_properties - end - 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) - 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' } - ] + parse_source_from_properties + end 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 + context 'when container_scanning properties are present' do + let(:properties) do + [ + { 'name' => 'gitlab:meta:schema_version', 'value' => '1' }, + { 'name' => 'gitlab:container_scanning:image:name', 'value' => 'photon' }, + { 'name' => 'gitlab:container_scanning:image:tag', 'value' => '5.0-20231007' }, + { 'name' => 'gitlab:container_scanning:operating_system:name', 'value' => 'Photon OS' }, + { 'name' => 'gitlab:container_scanning:operating_system:version', 'value' => '5.0' } + ] + end + + let(:expected_input) do + { + 'image' => { + 'name' => 'photon', + 'tag' => '5.0-20231007' + }, + 'operating_system' => { + 'name' => 'Photon OS', + 'version' => '5.0' + } + } + 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) + it 'passes only supported properties to the container scanning parser' do + expect(Gitlab::Ci::Parsers::Sbom::Source::ContainerScanning).to receive(:source).with(expected_input) - parse_source_from_properties + parse_source_from_properties + end end end - context 'when container_scanning properties are present' do - let(:properties) do - [ - { 'name' => 'gitlab:meta:schema_version', 'value' => '1' }, - { 'name' => 'gitlab:container_scanning:image:name', 'value' => 'photon' }, - { 'name' => 'gitlab:container_scanning:image:tag', 'value' => '5.0-20231007' }, - { 'name' => 'gitlab:container_scanning:operating_system:name', 'value' => 'Photon OS' }, - { 'name' => 'gitlab:container_scanning:operating_system:version', 'value' => '5.0' } - ] + describe '#parse_trivy_source' do + subject(:parse_trivy_source_from_properties) { described_class.parse_trivy_source(properties) } + + it_behaves_like 'handling invalid properties' + + context 'when no trivy properties are present' do + let(:properties) do + [ + { 'name' => 'gitlab:meta:schema_version', 'value' => '1' }, + { 'name' => 'gitlab::aquasecurity:trivy:FilePath', 'value' => '1' } + ] + end + + it 'does not call source parsers' do + expect(Gitlab::Ci::Parsers::Sbom::Source::Trivy).not_to receive(:source) + + parse_trivy_source_from_properties + end end - let(:expected_input) do - { - 'image' => { - 'name' => 'photon', - 'tag' => '5.0-20231007' - }, - 'operating_system' => { - 'name' => 'Photon OS', - 'version' => '5.0' + context 'when trivy properties are present' do + let(:properties) do + [ + { 'name' => 'aquasecurity:trivy:PkgID', 'value' => 'sha256:47ce8fad8..' }, + { 'name' => 'aquasecurity:trivy:LayerDigest', + 'value' => 'registry.test.com/atiwari71/container-scanning-test/main@sha256:e14a4bcf..' }, + { 'name' => 'aquasecurity:trivy:LayerDiffID', 'value' => 'sha256:94dd7d531fa..' }, + { 'name' => 'aquasecurity:trivy:SrcEpoch', 'value' => 'sha256:5d20c808c..' } + ] + end + + let(:expected_input) do + { + 'PkgID' => 'sha256:47ce8fad8..', + 'LayerDigest' => 'registry.test.com/atiwari71/container-scanning-test/main@sha256:e14a4bcf..', + 'LayerDiffID' => 'sha256:94dd7d531fa..', + 'SrcEpoch' => 'sha256:5d20c808c..' } - } - end + end - it 'passes only supported properties to the container scanning parser' do - expect(Gitlab::Ci::Parsers::Sbom::Source::ContainerScanning).to receive(:source).with(expected_input) + it 'passes only supported properties to the container scanning parser' do + expect(Gitlab::Ci::Parsers::Sbom::Source::Trivy).to receive(:source).with(expected_input) - parse_source_from_properties + parse_trivy_source_from_properties + end 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 index 9c8402faf77..6a6fe59bce1 100644 --- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb +++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb @@ -125,6 +125,56 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx, feature_category: :dependen parse! end + context 'when component is trivy type' do + let(:parsed_properties) do + { + 'PkgID' => 'adduser@3.134', + 'PkgType' => 'debian' + } + end + + let(:components) do + [ + { + # Trivy component + "bom-ref" => "0eda252d-d8a4-4250-b816-b6314f029063", + "type" => "library", + "name" => "analyzer", + "purl" => "pkg:gem/activesupport@5.1.4", + "properties" => [ + { + "name" => "aquasecurity:trivy:PkgID", + "value" => "apt@2.6.1" + }, + { + "name" => "aquasecurity:trivy:PkgType", + "value" => "debian" + } + ] + } + ] + end + + before do + allow(properties_parser).to receive(:parse_trivy_source).and_return(parsed_properties) + stub_const('Gitlab::Ci::Parsers::Sbom::CyclonedxProperties', properties_parser) + end + + it 'adds each component, ignoring unused attributes' do + expect(report).to receive(:add_component) + .with( + an_object_having_attributes( + component_type: "library", + properties: parsed_properties, + purl: an_object_having_attributes( + type: "gem" + ) + ) + ) + parse! + end + end + context 'when a component has an invalid purl' do before do components.push( diff --git a/spec/lib/gitlab/ci/parsers/sbom/source/trivy_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/source/trivy_spec.rb new file mode 100644 index 00000000000..460ca4f28a1 --- /dev/null +++ b/spec/lib/gitlab/ci/parsers/sbom/source/trivy_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::Trivy, feature_category: :dependency_management do + subject { described_class.source(property_data) } + + context 'when all property data is present' do + let(:property_data) do + { + 'PkgID' => 'sha256:47ce8fad8..', + 'LayerDigest' => 'registry.test.com/atiwari71/container-scanning-test/main@sha256:e14a4bcf..', + 'LayerDiffID' => 'sha256:94dd7d531fa..', + 'SrcEpoch' => 'sha256:5d20c808c..' + } + end + + it 'returns expected source data' do + is_expected.to have_attributes( + source_type: :trivy, + data: property_data + ) + end + end +end diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb index 431a6d94c48..6aa526c1829 100644 --- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb +++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb @@ -185,7 +185,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera context 'when name is provided' do it 'sets name from the report as a name' do - finding = report.findings.find { |x| x.compare_key == 'CVE-1030' } + finding = report.findings.second expected_name = Gitlab::Json.parse(finding.raw_metadata)['name'] expect(finding.name).to eq(expected_name) @@ -197,7 +197,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera let(:location) { nil } it 'returns only identifier name' do - finding = report.findings.find { |x| x.compare_key == 'CVE-2017-11429' } + finding = report.findings.third + expect(finding.name).to eq("CVE-2017-11429") end end @@ -205,21 +206,24 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera context 'when location exists' do context 'when CVE identifier exists' do it 'combines identifier with location to create name' do - finding = report.findings.find { |x| x.compare_key == 'CVE-2017-11429' } + finding = report.findings.third + expect(finding.name).to eq("CVE-2017-11429 in yarn.lock") end end context 'when CWE identifier exists' do it 'combines identifier with location to create name' do - finding = report.findings.find { |x| x.compare_key == 'CWE-2017-11429' } + finding = report.findings.fourth + expect(finding.name).to eq("CWE-2017-11429 in yarn.lock") end end context 'when neither CVE nor CWE identifier exist' do it 'combines identifier with location to create name' do - finding = report.findings.find { |x| x.compare_key == 'OTHER-2017-11429' } + finding = report.findings.fifth + expect(finding.name).to eq("other-2017-11429 in yarn.lock") end end @@ -476,6 +480,20 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera end end end + + describe 'handling the unicode null characters' do + let(:artifact) { build(:ci_job_artifact, :common_security_report_with_unicode_null_character) } + + it 'escapes the unicode null characters while parsing the report' do + finding = report.findings.first + + expect(finding.solution).to eq('Upgrade to latest version.\u0000') + end + + it 'adds warning to report' do + expect(report.warnings).to include({ type: 'Parsing', message: 'Report artifact contained unicode null characters which are escaped during the ingestion.' }) + end + 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 68158503628..37535b80cd4 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb @@ -200,7 +200,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do let(:command) { described_class.new(project: project) } it 'uses BLANK_SHA' do - is_expected.to eq(Gitlab::Git::BLANK_SHA) + is_expected.to eq(Gitlab::Git::SHA1_BLANK_SHA) end end end 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 44ccb1eeae1..bf146791659 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 @@ -12,13 +12,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules do end let(:step) { described_class.new(pipeline, command) } - let(:ff_always_set_pipeline_failure_reason) { true } describe '#perform!' do context 'when pipeline has been skipped by workflow configuration' do before do - stub_feature_flags(always_set_pipeline_failure_reason: ff_always_set_pipeline_failure_reason) - allow(step).to receive(:workflow_rules_result) .and_return( double(pass?: false, variables: {}) @@ -47,15 +44,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules do expect(pipeline).to be_failed expect(pipeline).to be_filtered_by_workflow_rules end - - context 'when always_set_pipeline_failure_reason is disabled' do - let(:ff_always_set_pipeline_failure_reason) { false } - - it 'does not set the failure reason', :aggregate_failures do - expect(pipeline).not_to be_failed - expect(pipeline.failure_reason).to be_blank - end - end end context 'when pipeline has not been skipped by workflow configuration' do diff --git a/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb index 84c2fb6525e..5956137a725 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb @@ -52,22 +52,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Helpers, feature_category: :continuo expect(pipeline.status).to eq 'failed' expect(pipeline.failure_reason).to eq drop_reason.to_s end - - context 'when feature flag always_set_pipeline_failure_reason is false' do - before do - stub_feature_flags(always_set_pipeline_failure_reason: false) - end - - specify do - subject.error(message, config_error: config_error, drop_reason: drop_reason) - - if command.save_incompleted - expect(pipeline.failure_reason).to eq drop_reason.to_s - else - expect(pipeline.failure_reason).not_to be_present - end - end - end end context 'when the error includes malicious HTML' do @@ -93,6 +77,37 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Helpers, feature_category: :continuo end end + context 'when drop_reason is nil' do + let(:command) { double(project: nil) } + + shared_examples "error function with no drop reason" do + it 'drops with out failure reason' do + expect(command).to receive(:increment_pipeline_failure_reason_counter) + + call_error + + expect(pipeline.failure_reason).to be_nil + expect(pipeline.yaml_errors).to be_nil + expect(pipeline.errors[:base]).to include(message) + expect(pipeline).to be_failed + expect(pipeline).not_to be_persisted + end + end + + context 'when no drop_reason argument is passed' do + let(:call_error) { subject.error(message) } + + it_behaves_like "error function with no drop reason" + end + + context 'when drop_reason argument is passed as nil' do + let(:drop_reason) { nil } + let(:call_error) { subject.error(message, drop_reason: drop_reason) } + + it_behaves_like "error function with no drop reason" + end + end + context 'when config error is false' do context 'does not set the yaml error or override the drop reason' do let(:drop_reason) { :size_limit_exceeded } @@ -107,7 +122,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Helpers, feature_category: :continuo expect(pipeline).to be_persisted end - context ' when the drop reason is not persistable' do + context 'when the drop reason is not persistable' do let(:drop_reason) { :filtered_by_rules } let(:command) { double(project: nil) } diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb index 732748d8c8b..787a458f0ff 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb @@ -240,6 +240,78 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::PopulateMetadata, feature_category: expect(pipeline.pipeline_metadata).not_to be_persisted end end + + context 'with workflow:rules:auto_cancel' do + context 'with auto_cancel:on_new_commit not set and rules:workflow:auto_cancel:on_new_commit set' do + let(:config) do + { + variables: { MY_VAR: my_var_value }, + workflow: { + auto_cancel: { on_job_failure: 'all' }, + rules: [{ if: '$MY_VAR == "something"', auto_cancel: { on_new_commit: 'interruptible' } }] + }, + rspec: { script: 'rspec' } + } + end + + context 'when the rule is matched' do + let(:my_var_value) { 'something' } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all') + end + end + + context 'when the rule is not matched' do + let(:my_var_value) { 'something else' } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('conservative') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('all') + end + end + end + + context 'with auto_cancel:on_new_commit set and rules:workflow:auto_cancel:on_new_commit set' do + let(:config) do + { + variables: { MY_VAR: my_var_value }, + workflow: { + auto_cancel: { on_new_commit: 'interruptible' }, + rules: [{ if: '$MY_VAR == "something"', auto_cancel: { on_new_commit: 'none' } }] + }, + rspec: { script: 'rspec' } + } + end + + context 'when the rule is matched' do + let(:my_var_value) { 'something' } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('none') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + end + end + + context 'when the rule is not matched' do + let(:my_var_value) { 'something else' } + + it 'builds pipeline_metadata' do + run_chain + + expect(pipeline.pipeline_metadata.auto_cancel_on_new_commit).to eq('interruptible') + expect(pipeline.pipeline_metadata.auto_cancel_on_job_failure).to eq('none') + end + end + end + end end context 'with both pipeline name and auto_cancel' do diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index 476b1be35a9..22ff367c746 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -34,15 +34,12 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate, feature_category: :continu { rspec: { script: 'rspec' } } end - let(:ff_always_set_pipeline_failure_reason) { true } - def run_chain dependencies.map(&:perform!) step.perform! end before do - stub_feature_flags(always_set_pipeline_failure_reason: ff_always_set_pipeline_failure_reason) stub_ci_pipeline_yaml_file(YAML.dump(config)) end @@ -113,18 +110,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate, feature_category: :continu expect(pipeline).to be_failed expect(pipeline).to be_filtered_by_rules end - - context 'when ff always_set_pipeline_failure_reason is disabled' do - let(:ff_always_set_pipeline_failure_reason) { false } - - it 'sets the failure reason without persisting the pipeline', :aggregate_failures do - run_chain - - expect(pipeline).not_to be_persisted - expect(pipeline).not_to be_failed - expect(pipeline).not_to be_filtered_by_rules - end - end end describe 'pipeline protect' do diff --git a/spec/lib/gitlab/ci/reports/security/report_spec.rb b/spec/lib/gitlab/ci/reports/security/report_spec.rb index d7f967f1c55..dabee0f32de 100644 --- a/spec/lib/gitlab/ci/reports/security/report_spec.rb +++ b/spec/lib/gitlab/ci/reports/security/report_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Reports::Security::Report do +RSpec.describe Gitlab::Ci::Reports::Security::Report, feature_category: :vulnerability_management do let_it_be(:pipeline) { create(:ci_pipeline) } let(:created_at) { 2.weeks.ago } @@ -89,7 +89,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do let(:other_report) do create( :ci_reports_security_report, - findings: [create(:ci_reports_security_finding, compare_key: 'other_finding')], + findings: [create(:ci_reports_security_finding)], scanners: [create(:ci_reports_security_scanner, external_id: 'other_scanner', name: 'Other Scanner')], identifiers: [create(:ci_reports_security_identifier, external_id: 'other_id', name: 'other_scanner')] ) diff --git a/spec/lib/gitlab/ci/variables/builder/pipeline_spec.rb b/spec/lib/gitlab/ci/variables/builder/pipeline_spec.rb index f8d67a6f0b4..18ad723b75c 100644 --- a/spec/lib/gitlab/ci/variables/builder/pipeline_spec.rb +++ b/spec/lib/gitlab/ci/variables/builder/pipeline_spec.rb @@ -152,51 +152,6 @@ RSpec.describe Gitlab::Ci::Variables::Builder::Pipeline, feature_category: :secr end end - context 'when truncate_ci_merge_request_description feature flag is disabled' do - before do - stub_feature_flags(truncate_ci_merge_request_description: false) - end - - context 'when merge request description hits the limit' do - let(:merge_request_description) { 'a' * (MergeRequest::CI_MERGE_REQUEST_DESCRIPTION_MAX_LENGTH + 1) } - - it 'does not truncate the exposed description' do - expect(subject.to_hash) - .to include( - 'CI_MERGE_REQUEST_DESCRIPTION' => merge_request.description - ) - expect(subject.to_hash) - .not_to have_key('CI_MERGE_REQUEST_DESCRIPTION_IS_TRUNCATED') - end - end - - context 'when merge request description fits the length limit' do - let(:merge_request_description) { 'a' * (MergeRequest::CI_MERGE_REQUEST_DESCRIPTION_MAX_LENGTH - 1) } - - it 'does not truncate the exposed description' do - expect(subject.to_hash) - .to include( - 'CI_MERGE_REQUEST_DESCRIPTION' => merge_request.description - ) - expect(subject.to_hash) - .not_to have_key('CI_MERGE_REQUEST_DESCRIPTION_IS_TRUNCATED') - end - end - - context 'when merge request description does not exist' do - let(:merge_request_description) { nil } - - it 'does not truncate the exposed description' do - expect(subject.to_hash) - .to include( - 'CI_MERGE_REQUEST_DESCRIPTION' => merge_request.description - ) - expect(subject.to_hash) - .not_to have_key('CI_MERGE_REQUEST_DESCRIPTION_IS_TRUNCATED') - end - end - end - it 'exposes diff variables' do expect(subject.to_hash) .to include( diff --git a/spec/lib/gitlab/ci/yaml_processor/test_cases/include_spec.rb b/spec/lib/gitlab/ci/yaml_processor/test_cases/include_spec.rb new file mode 100644 index 00000000000..d8f8a58edf3 --- /dev/null +++ b/spec/lib/gitlab/ci/yaml_processor/test_cases/include_spec.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require 'spec_helper' + +module Gitlab + module Ci + RSpec.describe YamlProcessor, feature_category: :pipeline_composition do + include StubRequests + + subject(:processor) do + described_class.new(config, project: project, user: project.first_owner, logger: logger) + end + + let_it_be(:project) { create(:project, :repository) } + + let(:logger) { Gitlab::Ci::Pipeline::Logger.new(project: project) } + let(:result) { processor.execute } + let(:builds) { result.builds } + + context 'with include:remote' do + let(:config) do + <<~YAML + include: + - remote: http://my.domain.com/config1.yml + - remote: http://my.domain.com/config2.yml + YAML + end + + before do + stub_full_request('http://my.domain.com/config1.yml') + .to_return(body: 'build1: { script: echo Hello World }') + + stub_full_request('http://my.domain.com/config2.yml') + .to_return(body: 'build2: { script: echo Hello World }') + end + + it 'returns builds from included files' do + expect(builds.pluck(:name)).to eq %w[build1 build2] + end + + it 'stores instrumentation logs' do + result + + expect(logger.observations_hash['config_mapper_process_duration_s']['count']).to eq(1) + end + + # Remove with the FF ci_parallel_remote_includes + it 'does not store log with config_file_fetch_remote_content' do + result + + expect(logger.observations_hash).not_to have_key('config_file_fetch_remote_content_duration_s') + end + + context 'when the FF ci_parallel_remote_includes is disabled' do + before do + stub_feature_flags(ci_parallel_remote_includes: false) + end + + it 'stores log with config_file_fetch_remote_content' do + result + + expect(logger.observations_hash['config_file_fetch_remote_content_duration_s']['count']).to eq(2) + end + + context 'when the FF is specifically enabled for the project' do + before do + stub_feature_flags(ci_parallel_remote_includes: [project]) + end + + it 'does not store log with config_file_fetch_remote_content' do + result + + expect(logger.observations_hash).not_to have_key('config_file_fetch_remote_content_duration_s') + end + end + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/yaml_processor/test_cases/interruptible_spec.rb b/spec/lib/gitlab/ci/yaml_processor/test_cases/interruptible_spec.rb index 03ff7077969..297872f4cf3 100644 --- a/spec/lib/gitlab/ci/yaml_processor/test_cases/interruptible_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor/test_cases/interruptible_spec.rb @@ -5,9 +5,10 @@ require 'spec_helper' module Gitlab module Ci RSpec.describe YamlProcessor, feature_category: :pipeline_composition do - subject(:processor) { described_class.new(config, user: nil).execute } + subject(:processor) { described_class.new(config, user: nil) } - let(:builds) { processor.builds } + let(:result) { processor.execute } + let(:builds) { result.builds } context 'with interruptible' do let(:default_config) { nil } diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index 844a6849c8f..4f759109b26 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -516,6 +516,32 @@ module Gitlab }) end end + + context 'with rules and auto_cancel' do + let(:config) do + <<-YML + workflow: + rules: + - if: $VAR == "value" + auto_cancel: + on_new_commit: none + on_job_failure: none + + hello: + script: echo world + YML + end + + it 'parses workflow_rules' do + expect(subject.workflow_rules).to contain_exactly({ + if: '$VAR == "value"', + auto_cancel: { + on_new_commit: 'none', + on_job_failure: 'none' + } + }) + end + end end describe '#warnings' do @@ -1295,10 +1321,12 @@ module Gitlab name: ruby:2.7 docker: platform: linux/amd64 + user: dave services: - name: postgres:11.9 docker: platform: linux/amd64 + user: john YAML end @@ -1313,9 +1341,9 @@ module Gitlab options: { script: ["exit 0"], image: { name: "ruby:2.7", - executor_opts: { docker: { platform: 'linux/amd64' } } }, + executor_opts: { docker: { platform: 'linux/amd64', user: 'dave' } } }, services: [{ name: "postgres:11.9", - executor_opts: { docker: { platform: 'linux/amd64' } } }] + executor_opts: { docker: { platform: 'linux/amd64', user: 'john' } } }] }, allow_failure: false, when: "on_success", |