summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/ci/pipeline/chain
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/ci/pipeline/chain')
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/build_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb221
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb103
3 files changed, 325 insertions, 1 deletions
diff --git a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
index a631cd2777b..b81094f8b4a 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
let(:step) { described_class.new(pipeline, command) }
before do
- stub_repository_ci_yaml_file(sha: anything)
+ stub_ci_pipeline_yaml_file(gitlab_ci_yaml)
end
it 'never breaks the chain' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
new file mode 100644
index 00000000000..7ebe5842fd0
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
@@ -0,0 +1,221 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::Config::Content do
+ let(:project) { create(:project, ci_config_path: ci_config_path) }
+ let(:pipeline) { build(:ci_pipeline, project: project) }
+ let(:command) { Gitlab::Ci::Pipeline::Chain::Command.new(project: project) }
+
+ subject { described_class.new(pipeline, command) }
+
+ describe '#perform!' do
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_root_config_content: false)
+ end
+
+ context 'when config is defined in a custom path in the repository' do
+ let(:ci_config_path) { 'path/to/config.yml' }
+
+ before do
+ expect(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(pipeline.sha, ci_config_path)
+ .and_return('the-content')
+ end
+
+ it 'returns the content of the YAML file' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'repository_source'
+ expect(command.config_content).to eq('the-content')
+ end
+ end
+
+ context 'when config is defined remotely' do
+ let(:ci_config_path) { 'http://example.com/path/to/ci/config.yml' }
+
+ it 'does not support URLs and default to AutoDevops' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'auto_devops_source'
+ template = Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps')
+ expect(command.config_content).to eq(template.content)
+ end
+ end
+
+ context 'when config is defined in a separate repository' do
+ let(:ci_config_path) { 'path/to/.gitlab-ci.yml@another-group/another-repo' }
+
+ it 'does not support YAML from external repository and default to AutoDevops' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'auto_devops_source'
+ template = Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps')
+ expect(command.config_content).to eq(template.content)
+ end
+ end
+
+ context 'when config is defined in the default .gitlab-ci.yml' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(pipeline.sha, '.gitlab-ci.yml')
+ .and_return('the-content')
+ end
+
+ it 'returns the content of the canonical config file' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'repository_source'
+ expect(command.config_content).to eq('the-content')
+ end
+ end
+
+ context 'when config is the Auto-Devops template' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project).to receive(:auto_devops_enabled?).and_return(true)
+ end
+
+ it 'returns the content of AutoDevops template' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'auto_devops_source'
+ template = Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps')
+ expect(command.config_content).to eq(template.content)
+ end
+ end
+
+ context 'when config is not defined anywhere' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project).to receive(:auto_devops_enabled?).and_return(false)
+ end
+
+ it 'builds root config including the auto-devops template' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq('unknown_source')
+ expect(command.config_content).to be_nil
+ expect(pipeline.errors.full_messages).to include('Missing CI config file')
+ end
+ end
+ end
+
+ context 'when config is defined in a custom path in the repository' do
+ let(:ci_config_path) { 'path/to/config.yml' }
+
+ before do
+ expect(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(pipeline.sha, ci_config_path)
+ .and_return('the-content')
+ end
+
+ it 'builds root config including the local custom file' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'repository_source'
+ expect(command.config_content).to eq(<<~EOY)
+ ---
+ include:
+ - local: #{ci_config_path}
+ EOY
+ end
+ end
+
+ context 'when config is defined remotely' do
+ let(:ci_config_path) { 'http://example.com/path/to/ci/config.yml' }
+
+ it 'builds root config including the remote config' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'remote_source'
+ expect(command.config_content).to eq(<<~EOY)
+ ---
+ include:
+ - remote: #{ci_config_path}
+ EOY
+ end
+ end
+
+ context 'when config is defined in a separate repository' do
+ let(:ci_config_path) { 'path/to/.gitlab-ci.yml@another-group/another-repo' }
+
+ it 'builds root config including the path to another repository' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'external_project_source'
+ expect(command.config_content).to eq(<<~EOY)
+ ---
+ include:
+ - project: another-group/another-repo
+ file: path/to/.gitlab-ci.yml
+ EOY
+ end
+ end
+
+ context 'when config is defined in the default .gitlab-ci.yml' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(pipeline.sha, '.gitlab-ci.yml')
+ .and_return('the-content')
+ end
+
+ it 'builds root config including the canonical CI config file' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'repository_source'
+ expect(command.config_content).to eq(<<~EOY)
+ ---
+ include:
+ - local: ".gitlab-ci.yml"
+ EOY
+ end
+ end
+
+ context 'when config is the Auto-Devops template' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project).to receive(:auto_devops_enabled?).and_return(true)
+ end
+
+ it 'builds root config including the auto-devops template' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'auto_devops_source'
+ expect(command.config_content).to eq(<<~EOY)
+ ---
+ include:
+ - template: Auto-DevOps.gitlab-ci.yml
+ EOY
+ end
+ end
+
+ context 'when config is not defined anywhere' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project).to receive(:auto_devops_enabled?).and_return(false)
+ end
+
+ it 'builds root config including the auto-devops template' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq('unknown_source')
+ expect(command.config_content).to be_nil
+ expect(pipeline.errors.full_messages).to include('Missing CI config file')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
new file mode 100644
index 00000000000..f2a0b93ef28
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::Validate::External do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:pipeline) { build(:ci_empty_pipeline, user: user, project: project) }
+ let!(:step) { described_class.new(pipeline, command) }
+
+ let(:ci_yaml) do
+ <<-CI_YAML
+ stages:
+ - first_stage
+ - second_stage
+
+ first_stage_job_name:
+ stage: first_stage
+ image: hello_world
+ script:
+ - echo 'hello'
+
+ second_stage_job_name:
+ stage: second_stage
+ services:
+ - postgres
+ before_script:
+ - echo 'first hello'
+ script:
+ - echo 'second hello'
+ CI_YAML
+ end
+
+ let(:yaml_processor) do
+ ::Gitlab::Ci::YamlProcessor.new(
+ ci_yaml, {
+ project: project,
+ sha: pipeline.sha,
+ user: user
+ }
+ )
+ end
+
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, config_processor: yaml_processor
+ )
+ end
+
+ describe '#perform!' do
+ subject(:perform!) { step.perform! }
+
+ context 'when validation returns true' do
+ before do
+ allow(step).to receive(:validate_external).and_return(true)
+ end
+
+ it 'does not drop the pipeline' do
+ perform!
+
+ expect(pipeline.status).not_to eq('failed')
+ expect(pipeline.errors).to be_empty
+ end
+
+ it 'does not break the chain' do
+ perform!
+
+ expect(step.break?).to be false
+ end
+ end
+
+ context 'when validation return false' do
+ before do
+ allow(step).to receive(:validate_external).and_return(false)
+ end
+
+ it 'drops the pipeline' do
+ perform!
+
+ expect(pipeline.status).to eq('failed')
+ expect(pipeline.errors.to_a).to include('External validation failed')
+ end
+
+ it 'breaks the chain' do
+ perform!
+
+ expect(step.break?).to be true
+ end
+ end
+ end
+
+ describe '#validation_service_payload' do
+ subject(:validation_service_payload) { step.send(:validation_service_payload, pipeline, command.config_processor.stages_attributes) }
+
+ it 'respects the defined schema' do
+ expect(validation_service_payload).to match_schema('/external_validation')
+ end
+
+ it 'does not fire sql queries' do
+ expect { validation_service_payload }.not_to exceed_query_limit(1)
+ end
+ end
+end