diff options
Diffstat (limited to 'spec/lib/gitlab/ci/config')
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/auto_cancel_spec.rb | 2 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/bridge_spec.rb | 6 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/image_spec.rb | 64 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb | 2 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/needs_spec.rb | 21 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb | 57 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/rules_spec.rb | 8 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/service_spec.rb | 47 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/workflow_spec.rb | 67 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/external/context_spec.rb | 85 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/external/file/component_spec.rb | 10 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/external/file/remote_spec.rb | 34 |
12 files changed, 349 insertions, 54 deletions
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]) |