summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/ci/lint_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/ci/lint_spec.rb')
-rw-r--r--spec/lib/gitlab/ci/lint_spec.rb251
1 files changed, 251 insertions, 0 deletions
diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb
new file mode 100644
index 00000000000..077c0fd3162
--- /dev/null
+++ b/spec/lib/gitlab/ci/lint_spec.rb
@@ -0,0 +1,251 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Lint do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ let(:lint) { described_class.new(project: project, current_user: user) }
+
+ describe '#validate' do
+ subject { lint.validate(content, dry_run: dry_run) }
+
+ shared_examples 'content is valid' do
+ let(:content) do
+ <<~YAML
+ build:
+ stage: build
+ before_script:
+ - before_build
+ script: echo
+ environment: staging
+ when: manual
+ rspec:
+ stage: test
+ script: rspec
+ after_script:
+ - after_rspec
+ tags: [docker]
+ YAML
+ end
+
+ it 'returns a valid result', :aggregate_failures do
+ expect(subject).to be_valid
+
+ expect(subject.errors).to be_empty
+ expect(subject.warnings).to be_empty
+ expect(subject.jobs).to be_present
+
+ build_job = subject.jobs.first
+ expect(build_job[:name]).to eq('build')
+ expect(build_job[:stage]).to eq('build')
+ expect(build_job[:before_script]).to eq(['before_build'])
+ expect(build_job[:script]).to eq(['echo'])
+ expect(build_job.fetch(:after_script)).to eq([])
+ expect(build_job[:tag_list]).to eq([])
+ expect(build_job[:environment]).to eq('staging')
+ expect(build_job[:when]).to eq('manual')
+ expect(build_job[:allow_failure]).to eq(true)
+
+ rspec_job = subject.jobs.last
+ expect(rspec_job[:name]).to eq('rspec')
+ expect(rspec_job[:stage]).to eq('test')
+ expect(rspec_job.fetch(:before_script)).to eq([])
+ expect(rspec_job[:script]).to eq(['rspec'])
+ expect(rspec_job[:after_script]).to eq(['after_rspec'])
+ expect(rspec_job[:tag_list]).to eq(['docker'])
+ expect(rspec_job.fetch(:environment)).to be_nil
+ expect(rspec_job[:when]).to eq('on_success')
+ expect(rspec_job[:allow_failure]).to eq(false)
+ end
+ end
+
+ shared_examples 'content with errors and warnings' do
+ context 'when content has errors' do
+ let(:content) do
+ <<~YAML
+ build:
+ invalid: syntax
+ YAML
+ end
+
+ it 'returns a result with errors' do
+ expect(subject).not_to be_valid
+ expect(subject.errors).to include(/jobs build config should implement a script: or a trigger: keyword/)
+ end
+ end
+
+ context 'when content has warnings' do
+ let(:content) do
+ <<~YAML
+ rspec:
+ script: rspec
+ rules:
+ - when: always
+ YAML
+ end
+
+ it 'returns a result with warnings' do
+ expect(subject).to be_valid
+ expect(subject.warnings).to include(/rspec may allow multiple pipelines to run/)
+ end
+ end
+
+ context 'when content has more warnings than max limit' do
+ # content will result in 2 warnings
+ let(:content) do
+ <<~YAML
+ rspec:
+ script: rspec
+ rules:
+ - when: always
+ rspec2:
+ script: rspec
+ rules:
+ - when: always
+ YAML
+ end
+
+ before do
+ stub_const('Gitlab::Ci::Warnings::MAX_LIMIT', 1)
+ end
+
+ it 'returns a result with warnings' do
+ expect(subject).to be_valid
+ expect(subject.warnings.size).to eq(1)
+ end
+ end
+
+ context 'when content has errors and warnings' do
+ let(:content) do
+ <<~YAML
+ rspec:
+ script: rspec
+ rules:
+ - when: always
+ karma:
+ script: karma
+ unknown: key
+ YAML
+ end
+
+ it 'returns a result with errors and warnings' do
+ expect(subject).not_to be_valid
+ expect(subject.errors).to include(/karma config contains unknown keys/)
+ expect(subject.warnings).to include(/rspec may allow multiple pipelines to run/)
+ end
+ end
+ end
+
+ shared_context 'advanced validations' do
+ let(:content) do
+ <<~YAML
+ build:
+ stage: build
+ script: echo
+ rules:
+ - if: '$CI_MERGE_REQUEST_ID'
+ test:
+ stage: test
+ script: echo
+ needs: [build]
+ YAML
+ end
+ end
+
+ context 'when user has permissions to write the ref' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'when using default static mode' do
+ let(:dry_run) { false }
+
+ it_behaves_like 'content with errors and warnings'
+
+ it_behaves_like 'content is valid' do
+ it 'includes extra attributes' do
+ subject.jobs.each do |job|
+ expect(job[:only]).to eq(refs: %w[branches tags])
+ expect(job.fetch(:except)).to be_nil
+ end
+ end
+ end
+
+ include_context 'advanced validations' do
+ it 'does not catch advanced logical errors' do
+ expect(subject).to be_valid
+ expect(subject.errors).to be_empty
+ end
+ end
+
+ it 'uses YamlProcessor' do
+ expect(Gitlab::Ci::YamlProcessor)
+ .to receive(:new)
+ .and_call_original
+
+ subject
+ end
+ end
+
+ context 'when using dry run mode' do
+ let(:dry_run) { true }
+
+ it_behaves_like 'content with errors and warnings'
+
+ it_behaves_like 'content is valid' do
+ it 'does not include extra attributes' do
+ subject.jobs.each do |job|
+ expect(job.key?(:only)).to be_falsey
+ expect(job.key?(:except)).to be_falsey
+ end
+ end
+ end
+
+ include_context 'advanced validations' do
+ it 'runs advanced logical validations' do
+ expect(subject).not_to be_valid
+ expect(subject.errors).to eq(["test: needs 'build'"])
+ end
+ end
+
+ it 'uses Ci::CreatePipelineService' do
+ expect(::Ci::CreatePipelineService)
+ .to receive(:new)
+ .and_call_original
+
+ subject
+ end
+ end
+ end
+
+ context 'when user does not have permissions to write the ref' do
+ before do
+ project.add_reporter(user)
+ end
+
+ context 'when using default static mode' do
+ let(:dry_run) { false }
+
+ it_behaves_like 'content is valid'
+ end
+
+ context 'when using dry run mode' do
+ let(:dry_run) { true }
+
+ let(:content) do
+ <<~YAML
+ job:
+ script: echo
+ YAML
+ end
+
+ it 'does not allow validation' do
+ expect(subject).not_to be_valid
+ expect(subject.errors).to include('Insufficient permissions to create a new pipeline')
+ end
+ end
+ end
+ end
+end