diff options
Diffstat (limited to 'spec/lib/gitlab/ci/config')
14 files changed, 354 insertions, 19 deletions
diff --git a/spec/lib/gitlab/ci/config/edge_stages_injector_spec.rb b/spec/lib/gitlab/ci/config/edge_stages_injector_spec.rb new file mode 100644 index 00000000000..042f9b591b6 --- /dev/null +++ b/spec/lib/gitlab/ci/config/edge_stages_injector_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +describe Gitlab::Ci::Config::EdgeStagesInjector do + describe '#call' do + subject { described_class.new(config).to_hash } + + context 'without stages' do + let(:config) do + { + test: { script: 'test' } + } + end + + it { is_expected.to match config } + end + + context 'with values' do + let(:config) do + { + stages: %w[stage1 stage2], + test: { script: 'test' } + } + end + + let(:expected_stages) do + %w[.pre stage1 stage2 .post] + end + + it { is_expected.to match(config.merge(stages: expected_stages)) } + end + + context 'with bad values' do + let(:config) do + { + stages: 'stage1', + test: { script: 'test' } + } + end + + it { is_expected.to match(config) } + end + + context 'with collision values' do + let(:config) do + { + stages: %w[.post stage1 .pre .post stage2], + test: { script: 'test' } + } + end + + let(:expected_stages) do + %w[.pre stage1 stage2 .post] + end + + it { is_expected.to match(config.merge(stages: expected_stages)) } + end + + context 'with types' do + let(:config) do + { + types: %w[stage1 stage2], + test: { script: 'test' } + } + end + + let(:expected_config) do + { + types: %w[.pre stage1 stage2 .post], + test: { script: 'test' } + } + end + + it { is_expected.to match expected_config } + end + + context 'with types' do + let(:config) do + { + types: %w[.post stage1 .pre .post stage2], + test: { script: 'test' } + } + end + + let(:expected_config) do + { + types: %w[.pre stage1 stage2 .post], + test: { script: 'test' } + } + end + + it { is_expected.to match expected_config } + end + end + + describe '.wrap_stages' do + subject { described_class.wrap_stages(stages) } + + context 'with empty value' do + let(:stages) {} + + it { is_expected.to eq %w[.pre .post] } + end + + context 'with values' do + let(:stages) { %w[s1 .pre] } + + it { is_expected.to eq %w[.pre s1 .post] } + end + end +end diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb index 4cb63168ec7..9aab3664e1c 100644 --- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb @@ -69,6 +69,7 @@ describe Gitlab::Ci::Config::Entry::Cache do context 'when entry value is not correct' do describe '#errors' do subject { entry.errors } + context 'when is not a hash' do let(:config) { 'ls' } diff --git a/spec/lib/gitlab/ci/config/entry/coverage_spec.rb b/spec/lib/gitlab/ci/config/entry/coverage_spec.rb index 48d0864cfca..877e3ec6216 100644 --- a/spec/lib/gitlab/ci/config/entry/coverage_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/coverage_spec.rb @@ -11,11 +11,13 @@ describe Gitlab::Ci::Config::Entry::Coverage do describe '#errors' do subject { entry.errors } + it { is_expected.to include(/coverage config must be a regular expression/) } end describe '#valid?' do subject { entry } + it { is_expected.not_to be_valid } end end @@ -25,16 +27,19 @@ describe Gitlab::Ci::Config::Entry::Coverage do describe '#value' do subject { entry.value } + it { is_expected.to eq(config[1...-1]) } end describe '#errors' do subject { entry.errors } + it { is_expected.to be_empty } end describe '#valid?' do subject { entry } + it { is_expected.to be_valid } end end @@ -44,11 +49,13 @@ describe Gitlab::Ci::Config::Entry::Coverage do describe '#errors' do subject { entry.errors } + it { is_expected.to include(/coverage config must be a regular expression/) } end describe '#valid?' do subject { entry } + it { is_expected.not_to be_valid } end end diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb index 968dbb9c7f2..7e1a80414d4 100644 --- a/spec/lib/gitlab/ci/config/entry/root_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb @@ -215,7 +215,7 @@ describe Gitlab::Ci::Config::Entry::Root do describe '#stages_value' do it 'returns an array of root stages' do - expect(root.stages_value).to eq %w[build test deploy] + expect(root.stages_value).to eq %w[.pre build test deploy .post] 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 18037a5612c..9d4f7153cd0 100644 --- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb @@ -103,6 +103,52 @@ describe Gitlab::Ci::Config::Entry::Rules::Rule do end end + context 'when using a long list as an invalid changes: clause' do + let(:config) { { changes: ['app/'] * 51 } } + + it { is_expected.not_to be_valid } + + it 'returns errors' do + expect(subject.errors).to include(/changes is too long \(maximum is 50 characters\)/) + end + end + + context 'when using a exists: clause' do + let(:config) { { exists: %w[app/ lib/ spec/ other/* paths/**/*.rb] } } + + it { is_expected.to be_valid } + end + + context 'when using a string as an invalid exists: clause' do + let(:config) { { exists: 'a regular string' } } + + it { is_expected.not_to be_valid } + + it 'reports an error about invalid policy' do + expect(subject.errors).to include(/should be an array of strings/) + end + end + + context 'when using a list as an invalid exists: clause' do + let(:config) { { exists: [1, 2] } } + + it { is_expected.not_to be_valid } + + it 'returns errors' do + expect(subject.errors).to include(/exists should be an array of strings/) + end + end + + context 'when using a long list as an invalid exists: clause' do + let(:config) { { exists: ['app/'] * 51 } } + + it { is_expected.not_to be_valid } + + it 'returns errors' do + expect(subject.errors).to include(/exists is too long \(maximum is 50 characters\)/) + end + end + context 'specifying a delayed job' do let(:config) { { if: '$THIS || $THAT', when: 'delayed', start_in: '15 minutes' } } @@ -198,6 +244,12 @@ describe Gitlab::Ci::Config::Entry::Rules::Rule do expect(entry.value).to eq(config) end end + + context 'when using a exists: clause' do + let(:config) { { exists: %w[app/ lib/ spec/ other/* paths/**/*.rb] } } + + it { is_expected.to eq(config) } + end end describe '.default' do diff --git a/spec/lib/gitlab/ci/config/entry/stages_spec.rb b/spec/lib/gitlab/ci/config/entry/stages_spec.rb index 97970522104..3e6ff8eca28 100644 --- a/spec/lib/gitlab/ci/config/entry/stages_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/stages_spec.rb @@ -42,7 +42,7 @@ describe Gitlab::Ci::Config::Entry::Stages do describe '.default' do it 'returns default stages' do - expect(described_class.default).to eq %w[build test deploy] + expect(described_class.default).to eq %w[.pre build test deploy .post] 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 new file mode 100644 index 00000000000..610646ca85a --- /dev/null +++ b/spec/lib/gitlab/ci/config/external/context_spec.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +describe Gitlab::Ci::Config::External::Context do + let(:project) { double('Project') } + let(:user) { double('User') } + let(:sha) { '12345' } + let(:attributes) { { project: project, user: user, sha: sha } } + + subject(:subject) { described_class.new(**attributes) } + + describe 'attributes' do + context 'with values' do + it { is_expected.to have_attributes(**attributes) } + it { expect(subject.expandset).to eq(Set.new) } + it { expect(subject.execution_deadline).to eq(0) } + end + + context 'without values' do + let(:attributes) { { project: nil, user: nil, sha: nil } } + + it { is_expected.to have_attributes(**attributes) } + it { expect(subject.expandset).to eq(Set.new) } + it { expect(subject.execution_deadline).to eq(0) } + end + end + + describe '#set_deadline' do + let(:stubbed_time) { 1 } + + before do + allow(subject).to receive(:current_monotonic_time).and_return(stubbed_time) + end + + context 'with a float value' do + let(:timeout_seconds) { 10.5.seconds } + + it 'updates execution_deadline' do + expect { subject.set_deadline(timeout_seconds) } + .to change { subject.execution_deadline } + .to(timeout_seconds + stubbed_time) + end + end + + context 'with nil as a value' do + let(:timeout_seconds) {} + + it 'updates execution_deadline' do + expect { subject.set_deadline(timeout_seconds) } + .to change { subject.execution_deadline } + .to(stubbed_time) + end + end + end + + describe '#check_execution_time!' do + before do + allow(subject).to receive(:current_monotonic_time).and_return(stubbed_time) + allow(subject).to receive(:execution_deadline).and_return(stubbed_deadline) + end + + context 'when execution is expired' do + let(:stubbed_time) { 2 } + let(:stubbed_deadline) { 1 } + + it 'raises an error' do + expect { subject.check_execution_time! } + .to raise_error(described_class::TimeoutError) + end + end + + context 'when execution is not expired' do + let(:stubbed_time) { 1 } + let(:stubbed_deadline) { 2 } + + it 'does not raises any errors' do + expect { subject.check_execution_time! }.not_to raise_error + end + end + + context 'without setting a deadline' do + let(:stubbed_time) { 2 } + let(:stubbed_deadline) { 1 } + + before do + allow(subject).to receive(:execution_deadline).and_call_original + end + + it 'does not raises any errors' do + expect { subject.check_execution_time! }.not_to raise_error + end + end + end + + describe '#mutate' do + shared_examples 'a mutated context' do + let(:mutated) { subject.mutate(new_attributes) } + + before do + subject.expandset << :a_file + subject.set_deadline(15.seconds) + end + + it { expect(mutated).not_to eq(subject) } + it { expect(mutated).to be_a(described_class) } + it { expect(mutated).to have_attributes(new_attributes) } + it { expect(mutated.expandset).to eq(subject.expandset) } + it { expect(mutated.execution_deadline).to eq(mutated.execution_deadline) } + end + + context 'with attributes' do + let(:new_attributes) { { project: double, user: double, sha: '56789' } } + + it_behaves_like 'a mutated context' + end + + context 'without attributes' do + let(:new_attributes) { {} } + + it_behaves_like 'a mutated context' + end + end + + describe '#sentry_payload' do + it { expect(subject.sentry_payload).to match(a_hash_including(:project, :user)) } + end +end 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 af995f4869a..d472d6527e2 100644 --- a/spec/lib/gitlab/ci/config/external/file/base_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb @@ -1,13 +1,14 @@ # frozen_string_literal: true -require 'fast_spec_helper' +require 'spec_helper' describe Gitlab::Ci::Config::External::File::Base do - let(:context) { described_class::Context.new(nil, 'HEAD', nil, Set.new) } + let(:context_params) { { sha: 'HEAD' } } + let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) } let(:test_class) do Class.new(described_class) do - def initialize(params, context = {}) + def initialize(params, context) @location = params super @@ -20,6 +21,9 @@ describe Gitlab::Ci::Config::External::File::Base do before do allow_any_instance_of(test_class) .to receive(:content).and_return('key: value') + + allow_any_instance_of(Gitlab::Ci::Config::External::Context) + .to receive(:check_execution_time!) end describe '#matching?' do 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 9451db9522a..95f0c93e758 100644 --- a/spec/lib/gitlab/ci/config/external/file/local_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb @@ -7,10 +7,17 @@ describe Gitlab::Ci::Config::External::File::Local do set(:user) { create(:user) } let(:sha) { '12345' } - let(:context) { described_class::Context.new(project, sha, user, Set.new) } + let(:context_params) { { project: project, sha: sha, user: user } } + let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) } + let(:params) { { local: location } } let(:local_file) { described_class.new(params, context) } + before do + allow_any_instance_of(Gitlab::Ci::Config::External::Context) + .to receive(:check_execution_time!) + end + describe '#matching?' do context 'when a local is specified' do let(:params) { { local: 'file' } } @@ -109,7 +116,7 @@ describe Gitlab::Ci::Config::External::File::Local do describe '#expand_context' do let(:location) { 'location.yml' } - subject { local_file.send(:expand_context) } + subject { local_file.send(:expand_context_attrs) } it 'inherits project, user and sha' do is_expected.to include(user: user, project: project, sha: sha) diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb index 4acb4f324d3..dd869c227a1 100644 --- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb @@ -8,11 +8,15 @@ describe Gitlab::Ci::Config::External::File::Project do set(:user) { create(:user) } let(:context_user) { user } - let(:context) { described_class::Context.new(context_project, '12345', context_user, Set.new) } + let(:context_params) { { project: context_project, sha: '12345', user: context_user } } + let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) } let(:project_file) { described_class.new(params, context) } before do project.add_developer(user) + + allow_any_instance_of(Gitlab::Ci::Config::External::Context) + .to receive(:check_execution_time!) end describe '#matching?' do @@ -145,7 +149,7 @@ describe Gitlab::Ci::Config::External::File::Project do describe '#expand_context' do let(:params) { { file: 'file.yml', project: project.full_path, ref: 'master' } } - subject { project_file.send(:expand_context) } + subject { project_file.send(:expand_context_attrs) } it 'inherits user, and target project and sha' do is_expected.to include(user: user, project: project, sha: project.commit('master').id) 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 4a097b59216..08db00dda9d 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,8 @@ require 'spec_helper' describe Gitlab::Ci::Config::External::File::Remote do include StubRequests - let(:context) { described_class::Context.new(nil, '12345', nil, Set.new) } + let(:context_params) { { sha: '12345' } } + let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) } let(:params) { { remote: location } } let(:remote_file) { described_class.new(params, context) } let(:location) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' } @@ -19,6 +20,11 @@ describe Gitlab::Ci::Config::External::File::Remote do HEREDOC end + before do + allow_any_instance_of(Gitlab::Ci::Config::External::Context) + .to receive(:check_execution_time!) + end + describe '#matching?' do context 'when a remote is specified' do let(:params) { { remote: 'http://remote' } } @@ -187,10 +193,10 @@ describe Gitlab::Ci::Config::External::File::Remote do describe '#expand_context' do let(:params) { { remote: 'http://remote' } } - subject { remote_file.send(:expand_context) } + subject { remote_file.send(:expand_context_attrs) } it 'drops all parameters' do - is_expected.to include(user: nil, project: nil, sha: nil) + is_expected.to be_empty end end end diff --git a/spec/lib/gitlab/ci/config/external/file/template_spec.rb b/spec/lib/gitlab/ci/config/external/file/template_spec.rb index 1609b8fd66b..164b5800abf 100644 --- a/spec/lib/gitlab/ci/config/external/file/template_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/template_spec.rb @@ -6,12 +6,18 @@ describe Gitlab::Ci::Config::External::File::Template do set(:project) { create(:project) } set(:user) { create(:user) } - let(:context) { described_class::Context.new(project, '12345', user, Set.new) } + let(:context_params) { { project: project, sha: '12345', user: user } } + let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) } let(:template) { 'Auto-DevOps.gitlab-ci.yml' } let(:params) { { template: template } } let(:template_file) { described_class.new(params, context) } + before do + allow_any_instance_of(Gitlab::Ci::Config::External::Context) + .to receive(:check_execution_time!) + end + describe '#matching?' do context 'when a template is specified' do let(:params) { { template: 'some-template' } } @@ -97,10 +103,10 @@ describe Gitlab::Ci::Config::External::File::Template do describe '#expand_context' do let(:location) { 'location.yml' } - subject { template_file.send(:expand_context) } + subject { template_file.send(:expand_context_attrs) } it 'drops all parameters' do - is_expected.to include(user: nil, project: nil, sha: nil) + is_expected.to be_empty end end end diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb index 43708852594..8d09aa47f12 100644 --- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb +++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb @@ -11,7 +11,8 @@ describe Gitlab::Ci::Config::External::Mapper do let(:local_file) { '/lib/gitlab/ci/templates/non-existent-file.yml' } let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' } let(:template_file) { 'Auto-DevOps.gitlab-ci.yml' } - let(:expandset) { Set.new } + let(:context_params) { { project: project, sha: '123456', user: user } } + let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) } let(:file_content) do <<~HEREDOC @@ -21,10 +22,13 @@ describe Gitlab::Ci::Config::External::Mapper do before do stub_full_request(remote_url).to_return(body: file_content) + + allow_any_instance_of(Gitlab::Ci::Config::External::Context) + .to receive(:check_execution_time!) end describe '#process' do - subject { described_class.new(values, project: project, sha: '123456', user: user, expandset: expandset).process } + subject { described_class.new(values, context).process } context "when single 'include' keyword is defined" do context 'when the string is a local file' do diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb index 3b1a1e804f0..bb2d3f66972 100644 --- a/spec/lib/gitlab/ci/config/external/processor_spec.rb +++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb @@ -9,12 +9,16 @@ describe Gitlab::Ci::Config::External::Processor do set(:another_project) { create(:project, :repository) } set(:user) { create(:user) } - let(:expandset) { Set.new } let(:sha) { '12345' } - let(:processor) { described_class.new(values, project: project, sha: '12345', user: user, expandset: expandset) } + let(:context_params) { { project: project, sha: sha, user: user } } + let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) } + let(:processor) { described_class.new(values, context) } before do project.add_developer(user) + + allow_any_instance_of(Gitlab::Ci::Config::External::Context) + .to receive(:check_execution_time!) end describe "#perform" do |