Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-20 17:34:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-20 17:34:42 +0300
commit9f46488805e86b1bc341ea1620b866016c2ce5ed (patch)
treef9748c7e287041e37d6da49e0a29c9511dc34768 /spec/lib/gitlab/ci
parentdfc92d081ea0332d69c8aca2f0e745cb48ae5e6d (diff)
Add latest changes from gitlab-org/gitlab@13-0-stable-ee
Diffstat (limited to 'spec/lib/gitlab/ci')
-rw-r--r--spec/lib/gitlab/ci/config/entry/artifacts_spec.rb48
-rw-r--r--spec/lib/gitlab/ci/config/entry/reports_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/trigger_spec.rb13
-rw-r--r--spec/lib/gitlab/ci/cron_parser_spec.rb314
-rw-r--r--spec/lib/gitlab/ci/parsers/accessibility/pa11y_spec.rb118
-rw-r--r--spec/lib/gitlab/ci/parsers/terraform/tfplan_spec.rb51
-rw-r--r--spec/lib/gitlab/ci/parsers/test/junit_spec.rb60
-rw-r--r--spec/lib/gitlab/ci/parsers_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb270
-rw-r--r--spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb232
-rw-r--r--spec/lib/gitlab/ci/reports/terraform_reports_spec.rb34
-rw-r--r--spec/lib/gitlab/ci/reports/test_case_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/test_reports_spec.rb25
-rw-r--r--spec/lib/gitlab/ci/reports/test_suite_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/browser_performance_testing_gitlab_ci_yaml_spec.rb85
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb58
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb86
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb222
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb86
-rw-r--r--spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb100
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb22
22 files changed, 1702 insertions, 179 deletions
diff --git a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
index 513a9b8f2b4..8cfd07df777 100644
--- a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
@@ -123,25 +123,53 @@ describe Gitlab::Ci::Config::Entry::Artifacts do
end
end
end
+ end
- context 'when feature flag :ci_expose_arbitrary_artifacts_in_mr is disabled' do
+ describe 'excluded artifacts' do
+ context 'when configuration is valid and the feature is enabled' do
before do
- stub_feature_flags(ci_expose_arbitrary_artifacts_in_mr: false)
+ stub_feature_flags(ci_artifacts_exclude: true)
end
- context 'when syntax is correct' do
- let(:config) { { expose_as: 'Test results', paths: ['test.txt'] } }
+ context 'when configuration is valid' do
+ let(:config) { { untracked: true, exclude: ['some/directory/'] } }
- it 'is valid' do
- expect(entry.errors).to be_empty
+ it 'correctly parses the configuration' do
+ expect(entry).to be_valid
+ expect(entry.value).to eq config
end
end
- context 'when syntax for :expose_as is incorrect' do
- let(:config) { { paths: %w[results.txt], expose_as: '' } }
+ context 'when configuration is not valid' do
+ let(:config) { { untracked: true, exclude: 1234 } }
+
+ it 'returns an error' do
+ expect(entry).not_to be_valid
+ expect(entry.errors)
+ .to include 'artifacts exclude should be an array of strings'
+ end
+ end
+ end
+
+ context 'when artifacts/exclude feature is disabled' do
+ before do
+ stub_feature_flags(ci_artifacts_exclude: false)
+ end
+
+ context 'when configuration has been provided' do
+ let(:config) { { untracked: true, exclude: ['some/directory/'] } }
+
+ it 'returns an error' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'artifacts exclude feature is disabled'
+ end
+ end
+
+ context 'when configuration is not present' do
+ let(:config) { { untracked: true } }
- it 'is valid' do
- expect(entry.errors).to be_empty
+ it 'is a valid configuration' do
+ expect(entry).to be_valid
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/reports_spec.rb b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
index 9bba3eb2b77..8c6c91d919e 100644
--- a/spec/lib/gitlab/ci/config/entry/reports_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
@@ -47,6 +47,8 @@ describe Gitlab::Ci::Config::Entry::Reports do
:dotenv | 'build.dotenv'
:cobertura | 'cobertura-coverage.xml'
:terraform | 'tfplan.json'
+ :accessibility | 'gl-accessibility.json'
+ :cluster_applications | 'gl-cluster-applications.json'
end
with_them do
diff --git a/spec/lib/gitlab/ci/config/entry/trigger_spec.rb b/spec/lib/gitlab/ci/config/entry/trigger_spec.rb
index 752c3f59a95..dfd9807583c 100644
--- a/spec/lib/gitlab/ci/config/entry/trigger_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/trigger_spec.rb
@@ -114,19 +114,6 @@ describe Gitlab::Ci::Config::Entry::Trigger do
.to match /config contains unknown keys: branch/
end
end
-
- context 'when feature flag is off' do
- before do
- stub_feature_flags(ci_parent_child_pipeline: false)
- end
-
- let(:config) { { include: 'path/to/config.yml' } }
-
- it 'is returns an error if include is used' do
- expect(subject.errors.first)
- .to match /config must specify project/
- end
- end
end
context 'when config contains unknown keys' do
diff --git a/spec/lib/gitlab/ci/cron_parser_spec.rb b/spec/lib/gitlab/ci/cron_parser_spec.rb
index 385df72fa41..8f9f3d7fa37 100644
--- a/spec/lib/gitlab/ci/cron_parser_spec.rb
+++ b/spec/lib/gitlab/ci/cron_parser_spec.rb
@@ -7,198 +7,240 @@ describe Gitlab::Ci::CronParser do
it { is_expected.to be > Time.now }
end
- describe '#next_time_from' do
- subject { described_class.new(cron, cron_timezone).next_time_from(Time.now) }
+ shared_examples_for "returns time in the past" do
+ it { is_expected.to be < Time.now }
+ end
- context 'when cron and cron_timezone are valid' do
- context 'when specific time' do
- let(:cron) { '3 4 5 6 *' }
- let(:cron_timezone) { 'UTC' }
+ shared_examples_for 'when cron and cron_timezone are valid' do |returns_time_for_epoch|
+ context 'when specific time' do
+ let(:cron) { '3 4 5 6 *' }
+ let(:cron_timezone) { 'UTC' }
- it_behaves_like "returns time in the future"
+ it_behaves_like returns_time_for_epoch
- it 'returns exact time' do
- expect(subject.min).to eq(3)
- expect(subject.hour).to eq(4)
- expect(subject.day).to eq(5)
- expect(subject.month).to eq(6)
- end
+ it 'returns exact time' do
+ expect(subject.min).to eq(3)
+ expect(subject.hour).to eq(4)
+ expect(subject.day).to eq(5)
+ expect(subject.month).to eq(6)
end
+ end
- context 'when specific day of week' do
- let(:cron) { '* * * * 0' }
- let(:cron_timezone) { 'UTC' }
+ context 'when specific day of week' do
+ let(:cron) { '* * * * 0' }
+ let(:cron_timezone) { 'UTC' }
- it_behaves_like "returns time in the future"
+ it_behaves_like returns_time_for_epoch
- it 'returns exact day of week' do
- expect(subject.wday).to eq(0)
- end
+ it 'returns exact day of week' do
+ expect(subject.wday).to eq(0)
end
+ end
- context 'when slash used' do
- let(:cron) { '*/10 */6 */10 */10 *' }
- let(:cron_timezone) { 'UTC' }
+ context 'when slash used' do
+ let(:cron) { '*/10 */6 */10 */10 *' }
+ let(:cron_timezone) { 'UTC' }
- it_behaves_like "returns time in the future"
+ it_behaves_like returns_time_for_epoch
- it 'returns specific time' do
- expect(subject.min).to be_in([0, 10, 20, 30, 40, 50])
- expect(subject.hour).to be_in([0, 6, 12, 18])
- expect(subject.day).to be_in([1, 11, 21, 31])
- expect(subject.month).to be_in([1, 11])
- end
+ it 'returns specific time' do
+ expect(subject.min).to be_in([0, 10, 20, 30, 40, 50])
+ expect(subject.hour).to be_in([0, 6, 12, 18])
+ expect(subject.day).to be_in([1, 11, 21, 31])
+ expect(subject.month).to be_in([1, 11])
end
+ end
- context 'when range used' do
- let(:cron) { '0,20,40 * 1-5 * *' }
- let(:cron_timezone) { 'UTC' }
+ context 'when range used' do
+ let(:cron) { '0,20,40 * 1-5 * *' }
+ let(:cron_timezone) { 'UTC' }
- it_behaves_like "returns time in the future"
+ it_behaves_like returns_time_for_epoch
- it 'returns specific time' do
- expect(subject.min).to be_in([0, 20, 40])
- expect(subject.day).to be_in((1..5).to_a)
- end
+ it 'returns specific time' do
+ expect(subject.min).to be_in([0, 20, 40])
+ expect(subject.day).to be_in((1..5).to_a)
end
+ end
- context 'when cron_timezone is TZInfo format' do
- before do
- allow(Time).to receive(:zone)
- .and_return(ActiveSupport::TimeZone['UTC'])
- end
+ context 'when cron_timezone is TZInfo format' do
+ before do
+ allow(Time).to receive(:zone)
+ .and_return(ActiveSupport::TimeZone['UTC'])
+ end
- let(:hour_in_utc) do
- ActiveSupport::TimeZone[cron_timezone]
- .now.change(hour: 0).in_time_zone('UTC').hour
- end
+ let(:hour_in_utc) do
+ ActiveSupport::TimeZone[cron_timezone]
+ .now.change(hour: 0).in_time_zone('UTC').hour
+ end
- context 'when cron_timezone is US/Pacific' do
- let(:cron) { '* 0 * * *' }
- let(:cron_timezone) { 'US/Pacific' }
+ context 'when cron_timezone is US/Pacific' do
+ let(:cron) { '* 0 * * *' }
+ let(:cron_timezone) { 'US/Pacific' }
- it_behaves_like "returns time in the future"
+ it_behaves_like returns_time_for_epoch
- context 'when PST (Pacific Standard Time)' do
- it 'converts time in server time zone' do
- Timecop.freeze(Time.utc(2017, 1, 1)) do
- expect(subject.hour).to eq(hour_in_utc)
- end
+ context 'when PST (Pacific Standard Time)' do
+ it 'converts time in server time zone' do
+ Timecop.freeze(Time.utc(2017, 1, 1)) do
+ expect(subject.hour).to eq(hour_in_utc)
end
end
+ end
- context 'when PDT (Pacific Daylight Time)' do
- it 'converts time in server time zone' do
- Timecop.freeze(Time.utc(2017, 6, 1)) do
- expect(subject.hour).to eq(hour_in_utc)
- end
+ context 'when PDT (Pacific Daylight Time)' do
+ it 'converts time in server time zone' do
+ Timecop.freeze(Time.utc(2017, 6, 1)) do
+ expect(subject.hour).to eq(hour_in_utc)
end
end
end
end
+ end
- context 'when cron_timezone is ActiveSupport::TimeZone format' do
- before do
- allow(Time).to receive(:zone)
- .and_return(ActiveSupport::TimeZone['UTC'])
- end
+ context 'when cron_timezone is ActiveSupport::TimeZone format' do
+ before do
+ allow(Time).to receive(:zone)
+ .and_return(ActiveSupport::TimeZone['UTC'])
+ end
- let(:hour_in_utc) do
- ActiveSupport::TimeZone[cron_timezone]
- .now.change(hour: 0).in_time_zone('UTC').hour
- end
+ let(:hour_in_utc) do
+ ActiveSupport::TimeZone[cron_timezone]
+ .now.change(hour: 0).in_time_zone('UTC').hour
+ end
- context 'when cron_timezone is Berlin' do
- let(:cron) { '* 0 * * *' }
- let(:cron_timezone) { 'Berlin' }
+ context 'when cron_timezone is Berlin' do
+ let(:cron) { '* 0 * * *' }
+ let(:cron_timezone) { 'Berlin' }
- it_behaves_like "returns time in the future"
+ it_behaves_like returns_time_for_epoch
- context 'when CET (Central European Time)' do
- it 'converts time in server time zone' do
- Timecop.freeze(Time.utc(2017, 1, 1)) do
- expect(subject.hour).to eq(hour_in_utc)
- end
+ context 'when CET (Central European Time)' do
+ it 'converts time in server time zone' do
+ Timecop.freeze(Time.utc(2017, 1, 1)) do
+ expect(subject.hour).to eq(hour_in_utc)
end
end
+ end
- context 'when CEST (Central European Summer Time)' do
- it 'converts time in server time zone' do
- Timecop.freeze(Time.utc(2017, 6, 1)) do
- expect(subject.hour).to eq(hour_in_utc)
- end
+ context 'when CEST (Central European Summer Time)' do
+ it 'converts time in server time zone' do
+ Timecop.freeze(Time.utc(2017, 6, 1)) do
+ expect(subject.hour).to eq(hour_in_utc)
end
end
end
+ end
+ end
+ end
- context 'when cron_timezone is Eastern Time (US & Canada)' do
- let(:cron) { '* 0 * * *' }
- let(:cron_timezone) { 'Eastern Time (US & Canada)' }
+ shared_examples_for 'when cron_timezone is Eastern Time (US & Canada)' do |returns_time_for_epoch, year|
+ let(:cron) { '* 0 * * *' }
+ let(:cron_timezone) { 'Eastern Time (US & Canada)' }
- it_behaves_like "returns time in the future"
+ before do
+ allow(Time).to receive(:zone)
+ .and_return(ActiveSupport::TimeZone['UTC'])
+ end
- context 'when EST (Eastern Standard Time)' do
- it 'converts time in server time zone' do
- Timecop.freeze(Time.utc(2017, 1, 1)) do
- expect(subject.hour).to eq(hour_in_utc)
- end
- end
- end
+ let(:hour_in_utc) do
+ ActiveSupport::TimeZone[cron_timezone]
+ .now.change(hour: 0).in_time_zone('UTC').hour
+ end
- context 'when EDT (Eastern Daylight Time)' do
- it 'converts time in server time zone' do
- Timecop.freeze(Time.utc(2017, 6, 1)) do
- expect(subject.hour).to eq(hour_in_utc)
- end
- end
- end
+ it_behaves_like returns_time_for_epoch
- context 'when time crosses a Daylight Savings boundary' do
- let(:cron) { '* 0 1 12 *'}
-
- # Note this previously only failed if the time zone is set
- # to a zone that observes Daylight Savings
- # (e.g. America/Chicago) at the start of the test. Stubbing
- # TZ doesn't appear to be enough.
- it 'generates day without TZInfo::AmbiguousTime error' do
- Timecop.freeze(Time.utc(2020, 1, 1)) do
- expect(subject.year).to eq(2020)
- expect(subject.month).to eq(12)
- expect(subject.day).to eq(1)
- end
- end
- end
+ context 'when EST (Eastern Standard Time)' do
+ it 'converts time in server time zone' do
+ Timecop.freeze(Time.utc(2017, 1, 1)) do
+ expect(subject.hour).to eq(hour_in_utc)
end
end
end
- context 'when cron and cron_timezone are invalid' do
- let(:cron) { 'invalid_cron' }
- let(:cron_timezone) { 'invalid_cron_timezone' }
+ context 'when EDT (Eastern Daylight Time)' do
+ it 'converts time in server time zone' do
+ Timecop.freeze(Time.utc(2017, 6, 1)) do
+ expect(subject.hour).to eq(hour_in_utc)
+ end
+ end
+ end
- it { is_expected.to be_nil }
+ context 'when time crosses a Daylight Savings boundary' do
+ let(:cron) { '* 0 1 12 *'}
+
+ # Note this previously only failed if the time zone is set
+ # to a zone that observes Daylight Savings
+ # (e.g. America/Chicago) at the start of the test. Stubbing
+ # TZ doesn't appear to be enough.
+ it 'generates day without TZInfo::AmbiguousTime error' do
+ Timecop.freeze(Time.utc(2020, 1, 1)) do
+ expect(subject.year).to eq(year)
+ expect(subject.month).to eq(12)
+ expect(subject.day).to eq(1)
+ end
+ end
end
+ end
- context 'when cron syntax is quoted' do
- let(:cron) { "'0 * * * *'" }
- let(:cron_timezone) { 'UTC' }
+ shared_examples_for 'when cron and cron_timezone are invalid' do
+ let(:cron) { 'invalid_cron' }
+ let(:cron_timezone) { 'invalid_cron_timezone' }
- it { expect(subject).to be_nil }
- end
+ it { is_expected.to be_nil }
+ end
- context 'when cron syntax is rufus-scheduler syntax' do
- let(:cron) { 'every 3h' }
- let(:cron_timezone) { 'UTC' }
+ shared_examples_for 'when cron syntax is quoted' do
+ let(:cron) { "'0 * * * *'" }
+ let(:cron_timezone) { 'UTC' }
- it { expect(subject).to be_nil }
- end
+ it { expect(subject).to be_nil }
+ end
- context 'when cron is scheduled to a non existent day' do
- let(:cron) { '0 12 31 2 *' }
- let(:cron_timezone) { 'UTC' }
+ shared_examples_for 'when cron syntax is rufus-scheduler syntax' do
+ let(:cron) { 'every 3h' }
+ let(:cron_timezone) { 'UTC' }
- it { expect(subject).to be_nil }
- end
+ it { expect(subject).to be_nil }
+ end
+
+ shared_examples_for 'when cron is scheduled to a non existent day' do
+ let(:cron) { '0 12 31 2 *' }
+ let(:cron_timezone) { 'UTC' }
+
+ it { expect(subject).to be_nil }
+ end
+
+ describe '#next_time_from' do
+ subject { described_class.new(cron, cron_timezone).next_time_from(Time.now) }
+
+ it_behaves_like 'when cron and cron_timezone are valid', 'returns time in the future'
+
+ it_behaves_like 'when cron_timezone is Eastern Time (US & Canada)', 'returns time in the future', 2020
+
+ it_behaves_like 'when cron and cron_timezone are invalid'
+
+ it_behaves_like 'when cron syntax is quoted'
+
+ it_behaves_like 'when cron syntax is rufus-scheduler syntax'
+
+ it_behaves_like 'when cron is scheduled to a non existent day'
+ end
+
+ describe '#previous_time_from' do
+ subject { described_class.new(cron, cron_timezone).previous_time_from(Time.now) }
+
+ it_behaves_like 'when cron and cron_timezone are valid', 'returns time in the past'
+
+ it_behaves_like 'when cron_timezone is Eastern Time (US & Canada)', 'returns time in the past', 2019
+
+ it_behaves_like 'when cron and cron_timezone are invalid'
+
+ it_behaves_like 'when cron syntax is quoted'
+
+ it_behaves_like 'when cron syntax is rufus-scheduler syntax'
+
+ it_behaves_like 'when cron is scheduled to a non existent day'
end
describe '#cron_valid?' do
diff --git a/spec/lib/gitlab/ci/parsers/accessibility/pa11y_spec.rb b/spec/lib/gitlab/ci/parsers/accessibility/pa11y_spec.rb
new file mode 100644
index 00000000000..4d87e3b201a
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/accessibility/pa11y_spec.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+describe Gitlab::Ci::Parsers::Accessibility::Pa11y do
+ describe '#parse!' do
+ subject { described_class.new.parse!(pa11y, accessibility_report) }
+
+ let(:accessibility_report) { Gitlab::Ci::Reports::AccessibilityReports.new }
+
+ context "when data is pa11y style JSON" do
+ context "when there are no URLs provided" do
+ let(:pa11y) do
+ {
+ "total": 1,
+ "passes": 0,
+ "errors": 0,
+ "results": {
+ "": [
+ {
+ "message": "Protocol error (Page.navigate): Cannot navigate to invalid URL"
+ }
+ ]
+ }
+ }.to_json
+ end
+
+ it "returns an accessibility report" do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.errors_count).to eq(0)
+ expect(accessibility_report.passes_count).to eq(0)
+ expect(accessibility_report.scans_count).to eq(0)
+ expect(accessibility_report.urls).to be_empty
+ expect(accessibility_report.error_message).to eq("Empty URL detected in gl-accessibility.json")
+ end
+ end
+
+ context "when there are no errors" do
+ let(:pa11y) do
+ {
+ "total": 1,
+ "passes": 1,
+ "errors": 0,
+ "results": {
+ "http://pa11y.org/": []
+ }
+ }.to_json
+ end
+
+ it "returns an accessibility report" do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.urls['http://pa11y.org/']).to be_empty
+ expect(accessibility_report.errors_count).to eq(0)
+ expect(accessibility_report.passes_count).to eq(1)
+ expect(accessibility_report.scans_count).to eq(1)
+ end
+ end
+
+ context "when there are errors" do
+ let(:pa11y) do
+ {
+ "total": 1,
+ "passes": 0,
+ "errors": 1,
+ "results": {
+ "https://about.gitlab.com/": [
+ {
+ "code": "WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent",
+ "type": "error",
+ "typeCode": 1,
+ "message": "Anchor element found with a valid href attribute, but no link content has been supplied.",
+ "context": "<a href=\"/\" class=\"navbar-brand animated\"><svg height=\"36\" viewBox=\"0 0 1...</a>",
+ "selector": "#main-nav > div:nth-child(1) > a",
+ "runner": "htmlcs",
+ "runnerExtras": {}
+ }
+ ]
+ }
+ }.to_json
+ end
+
+ it "returns an accessibility report" do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.errors_count).to eq(1)
+ expect(accessibility_report.passes_count).to eq(0)
+ expect(accessibility_report.scans_count).to eq(1)
+ expect(accessibility_report.urls['https://about.gitlab.com/']).to be_present
+ expect(accessibility_report.urls['https://about.gitlab.com/'].first[:code]).to be_present
+ end
+ end
+ end
+
+ context "when data is not a valid JSON string" do
+ let(:pa11y) do
+ {
+ "total": 1,
+ "passes": 1,
+ "errors": 0,
+ "results": {
+ "http://pa11y.org/": []
+ }
+ }
+ end
+
+ it "sets error_message" do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.error_message).to include('Pa11y parsing failed')
+ expect(accessibility_report.errors_count).to eq(0)
+ expect(accessibility_report.passes_count).to eq(0)
+ expect(accessibility_report.scans_count).to eq(0)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/terraform/tfplan_spec.rb b/spec/lib/gitlab/ci/parsers/terraform/tfplan_spec.rb
new file mode 100644
index 00000000000..19cd75e586c
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/terraform/tfplan_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Parsers::Terraform::Tfplan do
+ describe '#parse!' do
+ let_it_be(:artifact) { create(:ci_job_artifact, :terraform) }
+
+ let(:reports) { Gitlab::Ci::Reports::TerraformReports.new }
+
+ context 'when data is tfplan.json' do
+ context 'when there is no data' do
+ it 'raises an error' do
+ plan = '{}'
+
+ expect { subject.parse!(plan, reports, artifact: artifact) }.to raise_error(
+ described_class::TfplanParserError
+ )
+ end
+ end
+
+ context 'when there is data' do
+ it 'parses JSON and returns a report' do
+ plan = '{ "create": 0, "update": 1, "delete": 0 }'
+
+ expect { subject.parse!(plan, reports, artifact: artifact) }.not_to raise_error
+
+ expect(reports.plans).to match(
+ a_hash_including(
+ 'tfplan.json' => a_hash_including(
+ 'create' => 0,
+ 'update' => 1,
+ 'delete' => 0
+ )
+ )
+ )
+ end
+ end
+ end
+
+ context 'when data is not tfplan.json' do
+ it 'raises an error' do
+ plan = { 'create' => 0, 'update' => 1, 'delete' => 0 }.to_s
+
+ expect { subject.parse!(plan, reports, artifact: artifact) }.to raise_error(
+ described_class::TfplanParserError
+ )
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
index b4be5a41cd7..7b7ace02bba 100644
--- a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
@@ -215,8 +215,64 @@ describe Gitlab::Ci::Parsers::Test::Junit do
context 'when data is not JUnit style XML' do
let(:junit) { { testsuite: 'abc' }.to_json }
- it 'raises an error' do
- expect { subject }.to raise_error(described_class::JunitParserError)
+ it 'attaches an error to the TestSuite object' do
+ expect { subject }.not_to raise_error
+ expect(test_cases).to be_empty
+ end
+ end
+
+ context 'when data is malformed JUnit XML' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ <testcase classname='Calculator' name='sumTest2' time='0.02'></testcase
+ </testsuite>
+ EOF
+ end
+
+ it 'attaches an error to the TestSuite object' do
+ expect { subject }.not_to raise_error
+ expect(test_suite.suite_error).to eq("JUnit XML parsing failed: 4:1: FATAL: expected '>'")
+ end
+
+ it 'returns 0 tests cases' do
+ subject
+
+ expect(test_cases).to be_empty
+ expect(test_suite.total_count).to eq(0)
+ expect(test_suite.success_count).to eq(0)
+ expect(test_suite.error_count).to eq(0)
+ end
+
+ it 'returns a failure status' do
+ subject
+
+ expect(test_suite.total_status).to eq(Gitlab::Ci::Reports::TestCase::STATUS_ERROR)
+ end
+ end
+
+ context 'when data is not XML' do
+ let(:junit) { double(:random_trash) }
+
+ it 'attaches an error to the TestSuite object' do
+ expect { subject }.not_to raise_error
+ expect(test_suite.suite_error).to eq('JUnit data parsing failed: no implicit conversion of RSpec::Mocks::Double into String')
+ end
+
+ it 'returns 0 tests cases' do
+ subject
+
+ expect(test_cases).to be_empty
+ expect(test_suite.total_count).to eq(0)
+ expect(test_suite.success_count).to eq(0)
+ expect(test_suite.error_count).to eq(0)
+ end
+
+ it 'returns a failure status' do
+ subject
+
+ expect(test_suite.total_status).to eq(Gitlab::Ci::Reports::TestCase::STATUS_ERROR)
end
end
diff --git a/spec/lib/gitlab/ci/parsers_spec.rb b/spec/lib/gitlab/ci/parsers_spec.rb
index 9d6896b3cb4..0a266e7a206 100644
--- a/spec/lib/gitlab/ci/parsers_spec.rb
+++ b/spec/lib/gitlab/ci/parsers_spec.rb
@@ -22,6 +22,22 @@ describe Gitlab::Ci::Parsers do
end
end
+ context 'when file_type is accessibility' do
+ let(:file_type) { 'accessibility' }
+
+ it 'fabricates the class' do
+ is_expected.to be_a(described_class::Accessibility::Pa11y)
+ end
+ end
+
+ context 'when file_type is terraform' do
+ let(:file_type) { 'terraform' }
+
+ it 'fabricates the class' do
+ is_expected.to be_a(described_class::Terraform::Tfplan)
+ end
+ end
+
context 'when file_type does not exist' do
let(:file_type) { 'undefined' }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
index 9033b71b19f..f82e49f9323 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
@@ -5,11 +5,13 @@ require 'spec_helper'
describe Gitlab::Ci::Pipeline::Chain::Sequence do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
+
let(:pipeline) { build_stubbed(:ci_pipeline) }
let(:command) { Gitlab::Ci::Pipeline::Chain::Command.new }
let(:first_step) { spy('first step') }
let(:second_step) { spy('second step') }
let(:sequence) { [first_step, second_step] }
+ let(:histogram) { spy('prometheus metric') }
subject do
described_class.new(pipeline, command, sequence)
@@ -52,5 +54,13 @@ describe Gitlab::Ci::Pipeline::Chain::Sequence do
it 'returns a pipeline object' do
expect(subject.build!).to eq pipeline
end
+
+ it 'adds sequence duration to duration histogram' do
+ allow(command).to receive(:duration_histogram).and_return(histogram)
+
+ subject.build!
+
+ expect(histogram).to have_received(:observe)
+ end
end
end
diff --git a/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb
new file mode 100644
index 00000000000..31a330f46b1
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb
@@ -0,0 +1,270 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::AccessibilityReportsComparer do
+ let(:comparer) { described_class.new(base_reports, head_reports) }
+ let(:base_reports) { Gitlab::Ci::Reports::AccessibilityReports.new }
+ let(:head_reports) { Gitlab::Ci::Reports::AccessibilityReports.new }
+ let(:url) { "https://gitlab.com" }
+ let(:single_error) do
+ [
+ {
+ "code" => "WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent",
+ "type" => "error",
+ "typeCode" => 1,
+ "message" => "Anchor element found with a valid href attribute, but no link content has been supplied.",
+ "context" => %{<a href="/" class="navbar-brand animated"><svg height="36" viewBox="0 0 1...</a>},
+ "selector" => "#main-nav > div:nth-child(1) > a",
+ "runner" => "htmlcs",
+ "runnerExtras" => {}
+ }
+ ]
+ end
+ let(:different_error) do
+ [
+ {
+ "code" => "WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail",
+ "type" => "error",
+ "typeCode" => 1,
+ "message" => "This element has insufficient contrast at this conformance level.",
+ "context" => %{<a href="/stages-devops-lifecycle/" class="main-nav-link">Product</a>},
+ "selector" => "#main-nav > div:nth-child(2) > ul > li:nth-child(1) > a",
+ "runner" => "htmlcs",
+ "runnerExtras" => {}
+ }
+ ]
+ end
+
+ describe '#status' do
+ subject { comparer.status }
+
+ context 'when head report has an error' do
+ before do
+ head_reports.add_url(url, single_error)
+ end
+
+ it 'returns status failed' do
+ expect(subject).to eq(described_class::STATUS_FAILED)
+ end
+ end
+
+ context 'when head reports does not have errors' do
+ before do
+ head_reports.add_url(url, [])
+ end
+
+ it 'returns status success' do
+ expect(subject).to eq(described_class::STATUS_SUCCESS)
+ end
+ end
+ end
+
+ describe '#errors_count' do
+ subject { comparer.errors_count }
+
+ context 'when head report has an error' do
+ before do
+ head_reports.add_url(url, single_error)
+ end
+
+ it 'returns the number of new errors' do
+ expect(subject).to eq(1)
+ end
+ end
+
+ context 'when head reports does not have an error' do
+ before do
+ head_reports.add_url(url, [])
+ end
+
+ it 'returns the number new errors' do
+ expect(subject).to eq(0)
+ end
+ end
+ end
+
+ describe '#resolved_count' do
+ subject { comparer.resolved_count }
+
+ context 'when base reports has an error and head has a different error' do
+ before do
+ base_reports.add_url(url, single_error)
+ head_reports.add_url(url, different_error)
+ end
+
+ it 'returns the resolved count' do
+ expect(subject).to eq(1)
+ end
+ end
+
+ context 'when base reports has errors head has no errors' do
+ before do
+ base_reports.add_url(url, single_error)
+ head_reports.add_url(url, [])
+ end
+
+ it 'returns the resolved count' do
+ expect(subject).to eq(1)
+ end
+ end
+
+ context 'when base reports has errors and head has the same error' do
+ before do
+ base_reports.add_url(url, single_error)
+ head_reports.add_url(url, single_error)
+ end
+
+ it 'returns zero' do
+ expect(subject).to eq(0)
+ end
+ end
+
+ context 'when base reports does not have errors and head has errors' do
+ before do
+ head_reports.add_url(url, single_error)
+ end
+
+ it 'returns the number of resolved errors' do
+ expect(subject).to eq(0)
+ end
+ end
+ end
+
+ describe '#total_count' do
+ subject { comparer.total_count }
+
+ context 'when base reports has an error' do
+ before do
+ base_reports.add_url(url, single_error)
+ end
+
+ it 'returns the error count' do
+ expect(subject).to eq(1)
+ end
+ end
+
+ context 'when head report has an error' do
+ before do
+ head_reports.add_url(url, single_error)
+ end
+
+ it 'returns the error count' do
+ expect(subject).to eq(1)
+ end
+ end
+
+ context 'when base report has errors and head report has errors' do
+ before do
+ base_reports.add_url(url, single_error)
+ head_reports.add_url(url, different_error)
+ end
+
+ it 'returns the error count' do
+ expect(subject).to eq(2)
+ end
+ end
+ end
+
+ describe '#existing_errors' do
+ subject { comparer.existing_errors }
+
+ context 'when base report has errors and head has a different error' do
+ before do
+ base_reports.add_url(url, single_error)
+ head_reports.add_url(url, different_error)
+ end
+
+ it 'returns the existing errors' do
+ expect(subject.size).to eq(1)
+ expect(subject.first["code"]).to eq("WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent")
+ end
+ end
+
+ context 'when base report does not have errors and head has errors' do
+ before do
+ base_reports.add_url(url, [])
+ head_reports.add_url(url, single_error)
+ end
+
+ it 'returns an empty array' do
+ expect(subject).to be_empty
+ end
+ end
+ end
+
+ describe '#new_errors' do
+ subject { comparer.new_errors }
+
+ context 'when base reports has errors and head has more errors' do
+ before do
+ base_reports.add_url(url, single_error)
+ head_reports.add_url(url, single_error + different_error)
+ end
+
+ it 'returns new errors between base and head reports' do
+ expect(subject.size).to eq(1)
+ expect(subject.first["code"]).to eq("WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail")
+ end
+ end
+
+ context 'when base reports has an error and head has no errors' do
+ before do
+ base_reports.add_url(url, single_error)
+ head_reports.add_url(url, [])
+ end
+
+ it 'returns an empty array' do
+ expect(subject).to be_empty
+ end
+ end
+
+ context 'when base reports does not have errors and head has errors' do
+ before do
+ head_reports.add_url(url, single_error)
+ end
+
+ it 'returns the new error' do
+ expect(subject.size).to eq(1)
+ expect(subject.first["code"]).to eq("WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent")
+ end
+ end
+ end
+
+ describe '#resolved_errors' do
+ subject { comparer.resolved_errors }
+
+ context 'when base report has errors and head has more errors' do
+ before do
+ base_reports.add_url(url, single_error)
+ head_reports.add_url(url, single_error + different_error)
+ end
+
+ it 'returns an empty array' do
+ expect(subject).to be_empty
+ end
+ end
+
+ context 'when base reports has errors and head has a different error' do
+ before do
+ base_reports.add_url(url, single_error)
+ head_reports.add_url(url, different_error)
+ end
+
+ it 'returns the resolved errors' do
+ expect(subject.size).to eq(1)
+ expect(subject.first["code"]).to eq("WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent")
+ end
+ end
+
+ context 'when base reports does not have errors and head has errors' do
+ before do
+ head_reports.add_url(url, single_error)
+ end
+
+ it 'returns an empty array' do
+ expect(subject).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb b/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb
new file mode 100644
index 00000000000..0dc13b464b1
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb
@@ -0,0 +1,232 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::AccessibilityReports do
+ let(:accessibility_report) { described_class.new }
+ let(:url) { 'https://gitlab.com' }
+ let(:data) do
+ [
+ {
+ "code": "WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent",
+ "type": "error",
+ "typeCode": 1,
+ "message": "Anchor element found with a valid href attribute, but no link content has been supplied.",
+ "context": %{<a href="/customers/worldline"><svg viewBox="0 0 509 89" xmln...</a>},
+ "selector": "html > body > div:nth-child(9) > div:nth-child(2) > a:nth-child(17)",
+ "runner": "htmlcs",
+ "runnerExtras": {}
+ },
+ {
+ "code": "WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent",
+ "type": "error",
+ "typeCode": 1,
+ "message": "Anchor element found with a valid href attribute, but no link content has been supplied.",
+ "context": %{<a href="/customers/equinix"><svg xmlns="http://www.w3.org/...</a>},
+ "selector": "html > body > div:nth-child(9) > div:nth-child(2) > a:nth-child(18)",
+ "runner": "htmlcs",
+ "runnerExtras": {}
+ }
+ ]
+ end
+
+ describe '#scans_count' do
+ subject { accessibility_report.scans_count }
+
+ context 'when data has errors' do
+ let(:different_url) { 'https://about.gitlab.com' }
+
+ before do
+ accessibility_report.add_url(url, data)
+ accessibility_report.add_url(different_url, data)
+ end
+
+ it 'returns the scans_count' do
+ expect(subject).to eq(2)
+ end
+ end
+
+ context 'when data has no errors' do
+ before do
+ accessibility_report.add_url(url, [])
+ end
+
+ it 'returns the scans_count' do
+ expect(subject).to eq(1)
+ end
+ end
+
+ context 'when data has no url' do
+ before do
+ accessibility_report.add_url("", [])
+ end
+
+ it 'returns the scans_count' do
+ expect(subject).to eq(0)
+ end
+ end
+ end
+
+ describe '#passes_count' do
+ subject { accessibility_report.passes_count }
+
+ context 'when data has errors' do
+ before do
+ accessibility_report.add_url(url, data)
+ end
+
+ it 'returns the passes_count' do
+ expect(subject).to eq(0)
+ end
+ end
+
+ context 'when data has no errors' do
+ before do
+ accessibility_report.add_url(url, [])
+ end
+
+ it 'returns the passes_count' do
+ expect(subject).to eq(1)
+ end
+ end
+
+ context 'when data has no url' do
+ before do
+ accessibility_report.add_url("", [])
+ end
+
+ it 'returns the scans_count' do
+ expect(subject).to eq(0)
+ end
+ end
+ end
+
+ describe '#errors_count' do
+ subject { accessibility_report.errors_count }
+
+ context 'when data has errors' do
+ let(:different_url) { 'https://about.gitlab.com' }
+
+ before do
+ accessibility_report.add_url(url, data)
+ accessibility_report.add_url(different_url, data)
+ end
+
+ it 'returns the errors_count' do
+ expect(subject).to eq(4)
+ end
+ end
+
+ context 'when data has no errors' do
+ before do
+ accessibility_report.add_url(url, [])
+ end
+
+ it 'returns the errors_count' do
+ expect(subject).to eq(0)
+ end
+ end
+
+ context 'when data has no url' do
+ before do
+ accessibility_report.add_url("", [])
+ end
+
+ it 'returns the errors_count' do
+ expect(subject).to eq(0)
+ end
+ end
+ end
+
+ describe '#add_url' do
+ subject { accessibility_report.add_url(url, data) }
+
+ context 'when data has errors' do
+ it 'adds urls and data to accessibility report' do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.urls.keys).to eq([url])
+ expect(accessibility_report.urls.values.flatten.size).to eq(2)
+ end
+ end
+
+ context 'when data does not have errors' do
+ let(:data) { [] }
+
+ it 'adds data to accessibility report' do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.urls.keys).to eq([url])
+ expect(accessibility_report.urls.values.flatten.size).to eq(0)
+ end
+ end
+
+ context 'when url does not exist' do
+ let(:url) { '' }
+ let(:data) { [{ message: "Protocol error (Page.navigate): Cannot navigate to invalid URL" }] }
+
+ it 'sets error_message and decreases total' do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.scans_count).to eq(0)
+ expect(accessibility_report.error_message).to eq('Empty URL detected in gl-accessibility.json')
+ end
+ end
+ end
+
+ describe '#set_error_message' do
+ let(:set_accessibility_error) { accessibility_report.set_error_message('error') }
+
+ context 'when error is nil' do
+ it 'returns the error' do
+ expect(set_accessibility_error).to eq('error')
+ end
+
+ it 'sets the error' do
+ set_accessibility_error
+
+ expect(accessibility_report.error_message).to eq('error')
+ end
+ end
+
+ context 'when a error has already been set' do
+ before do
+ accessibility_report.set_error_message('old error')
+ end
+
+ it 'overwrites the existing message' do
+ expect { set_accessibility_error }.to change(accessibility_report, :error_message).from('old error').to('error')
+ end
+ end
+ end
+
+ describe '#all_errors' do
+ subject { accessibility_report.all_errors }
+
+ context 'when data has errors' do
+ before do
+ accessibility_report.add_url(url, data)
+ end
+
+ it 'returns all errors' do
+ expect(subject.size).to eq(2)
+ end
+ end
+
+ context 'when data has no errors' do
+ before do
+ accessibility_report.add_url(url, [])
+ end
+
+ it 'returns an empty array' do
+ expect(subject).to eq([])
+ end
+ end
+
+ context 'when accessibility report has no data' do
+ it 'returns an empty array' do
+ expect(subject).to eq([])
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb b/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb
new file mode 100644
index 00000000000..061029299ac
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TerraformReports do
+ it 'initializes plans with and empty hash' do
+ expect(subject.plans).to eq({})
+ end
+
+ describe '#add_plan' do
+ context 'when providing two unique plans' do
+ it 'returns two plans' do
+ subject.add_plan('a/tfplan.json', { 'create' => 0, 'update' => 1, 'delete' => 0 })
+ subject.add_plan('b/tfplan.json', { 'create' => 0, 'update' => 1, 'delete' => 0 })
+
+ expect(subject.plans).to eq({
+ 'a/tfplan.json' => { 'create' => 0, 'update' => 1, 'delete' => 0 },
+ 'b/tfplan.json' => { 'create' => 0, 'update' => 1, 'delete' => 0 }
+ })
+ end
+ end
+
+ context 'when providing the same plan twice' do
+ it 'returns the last added plan' do
+ subject.add_plan('tfplan.json', { 'create' => 0, 'update' => 0, 'delete' => 0 })
+ subject.add_plan('tfplan.json', { 'create' => 0, 'update' => 1, 'delete' => 0 })
+
+ expect(subject.plans).to eq({
+ 'tfplan.json' => { 'create' => 0, 'update' => 1, 'delete' => 0 }
+ })
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/test_case_spec.rb b/spec/lib/gitlab/ci/reports/test_case_spec.rb
index c0652288cca..b5883867983 100644
--- a/spec/lib/gitlab/ci/reports/test_case_spec.rb
+++ b/spec/lib/gitlab/ci/reports/test_case_spec.rb
@@ -62,7 +62,7 @@ describe Gitlab::Ci::Reports::TestCase do
end
context 'when attachment is present' do
- let(:attachment_test_case) { build(:test_case, :with_attachment) }
+ let(:attachment_test_case) { build(:test_case, :failed_with_attachment) }
it "initializes the attachment if present" do
expect(attachment_test_case.attachment).to eq("some/path.png")
diff --git a/spec/lib/gitlab/ci/reports/test_reports_spec.rb b/spec/lib/gitlab/ci/reports/test_reports_spec.rb
index 638acde69eb..e51728496e1 100644
--- a/spec/lib/gitlab/ci/reports/test_reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/test_reports_spec.rb
@@ -127,7 +127,7 @@ describe Gitlab::Ci::Reports::TestReports do
context 'when test suites contain an attachment' do
let(:test_case_succes) { build(:test_case) }
- let(:test_case_with_attachment) { build(:test_case, :with_attachment) }
+ let(:test_case_with_attachment) { build(:test_case, :failed_with_attachment) }
before do
test_reports.get_suite('rspec').add_test_case(test_case_succes)
@@ -141,6 +141,29 @@ describe Gitlab::Ci::Reports::TestReports do
end
end
+ describe '#suite_errors' do
+ subject { test_reports.suite_errors }
+
+ context 'when a suite has normal spec errors or failures' do
+ before do
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_error)
+ end
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'when there is an error test case' do
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').set_suite_error('Existential parsing error')
+ end
+
+ it { is_expected.to eq({ 'junit' => 'Existential parsing error' }) }
+ end
+ end
+
Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
describe "##{status_type}_count" do
subject { test_reports.public_send("#{status_type}_count") }
diff --git a/spec/lib/gitlab/ci/reports/test_suite_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_spec.rb
index 9d9774afc82..e0b2593353a 100644
--- a/spec/lib/gitlab/ci/reports/test_suite_spec.rb
+++ b/spec/lib/gitlab/ci/reports/test_suite_spec.rb
@@ -101,7 +101,7 @@ describe Gitlab::Ci::Reports::TestSuite do
end
context 'when test cases contain an attachment' do
- let(:test_case_with_attachment) { build(:test_case, :with_attachment)}
+ let(:test_case_with_attachment) { build(:test_case, :failed_with_attachment)}
before do
test_suite.add_test_case(test_case_with_attachment)
@@ -114,6 +114,31 @@ describe Gitlab::Ci::Reports::TestSuite do
end
end
+ describe '#set_suite_error' do
+ let(:set_suite_error) { test_suite.set_suite_error('message') }
+
+ context 'when @suite_error is nil' do
+ it 'returns message' do
+ expect(set_suite_error).to eq('message')
+ end
+
+ it 'sets the new message' do
+ set_suite_error
+ expect(test_suite.suite_error).to eq('message')
+ end
+ end
+
+ context 'when a suite_error has already been set' do
+ before do
+ test_suite.set_suite_error('old message')
+ end
+
+ it 'overwrites the existing message' do
+ expect { set_suite_error }.to change(test_suite, :suite_error).from('old message').to('message')
+ end
+ end
+ end
+
Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
describe "##{status_type}" do
subject { test_suite.public_send("#{status_type}") }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/browser_performance_testing_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/browser_performance_testing_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..54c3500b0a0
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/Jobs/browser_performance_testing_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Jobs/Browser-Performance-Testing.gitlab-ci.yml' do
+ subject(:template) do
+ <<~YAML
+ stages:
+ - test
+ - performance
+
+ include:
+ - template: 'Jobs/Browser-Performance-Testing.gitlab-ci.yml'
+
+ placeholder:
+ script:
+ - keep pipeline validator happy by having a job when stages are intentionally empty
+ YAML
+ end
+
+ describe 'the created pipeline' do
+ let(:user) { create(:admin) }
+ let(:project) do
+ create(:project, :repository, variables: [
+ build(:ci_variable, key: 'CI_KUBERNETES_ACTIVE', value: 'true')
+ ])
+ end
+
+ let(:default_branch) { 'master' }
+ let(:pipeline_ref) { default_branch }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
+ let(:pipeline) { service.execute!(:push) }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template)
+
+ allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ it 'has no errors' do
+ expect(pipeline.errors).to be_empty
+ end
+
+ shared_examples_for 'performance job on tag or branch' do
+ it 'by default' do
+ expect(build_names).to include('performance')
+ end
+
+ it 'when PERFORMANCE_DISABLED' do
+ create(:ci_variable, project: project, key: 'PERFORMANCE_DISABLED', value: '1')
+
+ expect(build_names).not_to include('performance')
+ end
+ end
+
+ context 'on master' do
+ it_behaves_like 'performance job on tag or branch'
+ end
+
+ context 'on another branch' do
+ let(:pipeline_ref) { 'feature' }
+
+ it_behaves_like 'performance job on tag or branch'
+ end
+
+ context 'on tag' do
+ let(:pipeline_ref) { 'v1.0.0' }
+
+ it_behaves_like 'performance job on tag or branch'
+ end
+
+ context 'on merge request' do
+ let(:service) { MergeRequests::CreatePipelineService.new(project, user) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+ let(:pipeline) { service.execute(merge_request) }
+
+ it 'has no jobs' do
+ expect(pipeline).to be_merge_request_event
+ expect(build_names).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..b2a9e3f5cf4
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Jobs/Build.gitlab-ci.yml' do
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/Build') }
+
+ describe 'the created pipeline' do
+ let_it_be(:user) { create(:admin) }
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:default_branch) { 'master' }
+ let(:pipeline_ref) { default_branch }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
+ let(:pipeline) { service.execute!(:push) }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template.content)
+ allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ context 'on master' do
+ it 'creates the build job' do
+ expect(build_names).to contain_exactly('build')
+ end
+ end
+
+ context 'on another branch' do
+ let(:pipeline_ref) { 'feature' }
+
+ it 'creates the build job' do
+ expect(build_names).to contain_exactly('build')
+ end
+ end
+
+ context 'on tag' do
+ let(:pipeline_ref) { 'v1.0.0' }
+
+ it 'creates the build job' do
+ expect(pipeline).to be_tag
+ expect(build_names).to contain_exactly('build')
+ end
+ end
+
+ context 'on merge request' do
+ let(:service) { MergeRequests::CreatePipelineService.new(project, user) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+ let(:pipeline) { service.execute(merge_request) }
+
+ it 'has no jobs' do
+ expect(pipeline).to be_merge_request_event
+ expect(build_names).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..9c5b2fd5099
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Jobs/Code-Quality.gitlab-ci.yml' do
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/Code-Quality') }
+
+ describe 'the created pipeline' do
+ let_it_be(:user) { create(:admin) }
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:default_branch) { 'master' }
+ let(:pipeline_ref) { default_branch }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
+ let(:pipeline) { service.execute!(:push) }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template.content)
+ allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ context 'on master' do
+ it 'creates the code_quality job' do
+ expect(build_names).to contain_exactly('code_quality')
+ end
+ end
+
+ context 'on another branch' do
+ let(:pipeline_ref) { 'feature' }
+
+ it 'creates the code_quality job' do
+ expect(build_names).to contain_exactly('code_quality')
+ end
+ end
+
+ context 'on tag' do
+ let(:pipeline_ref) { 'v1.0.0' }
+
+ it 'creates the code_quality job' do
+ expect(pipeline).to be_tag
+ expect(build_names).to contain_exactly('code_quality')
+ end
+ end
+
+ context 'on merge request' do
+ let(:service) { MergeRequests::CreatePipelineService.new(project, user) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+ let(:pipeline) { service.execute(merge_request) }
+
+ it 'has no jobs' do
+ expect(pipeline).to be_merge_request_event
+ expect(build_names).to be_empty
+ end
+ end
+
+ context 'CODE_QUALITY_DISABLED is set' do
+ before do
+ create(:ci_variable, key: 'CODE_QUALITY_DISABLED', value: 'true', project: project)
+ end
+
+ context 'on master' do
+ it 'has no jobs' do
+ expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
+ end
+ end
+
+ context 'on another branch' do
+ let(:pipeline_ref) { 'feature' }
+
+ it 'has no jobs' do
+ expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
+ end
+ end
+
+ context 'on tag' do
+ let(:pipeline_ref) { 'v1.0.0' }
+
+ it 'has no jobs' do
+ expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..a6ae23c85d3
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,222 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Jobs/Deploy.gitlab-ci.yml' do
+ subject(:template) do
+ <<~YAML
+ stages:
+ - test
+ - review
+ - staging
+ - canary
+ - production
+ - incremental rollout 10%
+ - incremental rollout 25%
+ - incremental rollout 50%
+ - incremental rollout 100%
+ - cleanup
+
+ include:
+ - template: Jobs/Deploy.gitlab-ci.yml
+
+ placeholder:
+ script:
+ - echo "Ensure at least one job to keep pipeline validator happy"
+ YAML
+ end
+
+ describe 'the created pipeline' do
+ let(:user) { create(:admin) }
+ let(:project) { create(:project, :repository) }
+
+ let(:default_branch) { 'master' }
+ let(:pipeline_ref) { default_branch }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
+ let(:pipeline) { service.execute!(:push) }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template)
+
+ allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ context 'with no cluster' do
+ it 'does not create any kubernetes deployment jobs' do
+ expect(build_names).to eq %w(placeholder)
+ end
+ end
+
+ context 'with only a disabled cluster' do
+ let!(:cluster) { create(:cluster, :project, :provided_by_gcp, enabled: false, projects: [project]) }
+
+ it 'does not create any kubernetes deployment jobs' do
+ expect(build_names).to eq %w(placeholder)
+ end
+ end
+
+ context 'with an active cluster' do
+ let!(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) }
+
+ context 'on master' do
+ it 'by default' do
+ expect(build_names).to include('production')
+ expect(build_names).not_to include('review')
+ end
+
+ it 'when CANARY_ENABLED' do
+ create(:ci_variable, project: project, key: 'CANARY_ENABLED', value: 'true')
+
+ expect(build_names).to include('production_manual')
+ expect(build_names).to include('canary')
+ expect(build_names).not_to include('production')
+ end
+
+ it 'when STAGING_ENABLED' do
+ create(:ci_variable, project: project, key: 'STAGING_ENABLED', value: 'true')
+
+ expect(build_names).to include('production_manual')
+ expect(build_names).to include('staging')
+ expect(build_names).not_to include('production')
+ end
+
+ it 'when INCREMENTAL_ROLLOUT_MODE == timed' do
+ create(:ci_variable, project: project, key: 'INCREMENTAL_ROLLOUT_ENABLED', value: 'true')
+ create(:ci_variable, project: project, key: 'INCREMENTAL_ROLLOUT_MODE', value: 'timed')
+
+ expect(build_names).not_to include('production_manual')
+ expect(build_names).not_to include('production')
+ expect(build_names).not_to include(
+ 'rollout 10%',
+ 'rollout 25%',
+ 'rollout 50%',
+ 'rollout 100%'
+ )
+ expect(build_names).to include(
+ 'timed rollout 10%',
+ 'timed rollout 25%',
+ 'timed rollout 50%',
+ 'timed rollout 100%'
+ )
+ end
+
+ it 'when INCREMENTAL_ROLLOUT_ENABLED' do
+ create(:ci_variable, project: project, key: 'INCREMENTAL_ROLLOUT_ENABLED', value: 'true')
+
+ expect(build_names).not_to include('production_manual')
+ expect(build_names).not_to include('production')
+ expect(build_names).not_to include(
+ 'timed rollout 10%',
+ 'timed rollout 25%',
+ 'timed rollout 50%',
+ 'timed rollout 100%'
+ )
+ expect(build_names).to include(
+ 'rollout 10%',
+ 'rollout 25%',
+ 'rollout 50%',
+ 'rollout 100%'
+ )
+ end
+
+ it 'when INCREMENTAL_ROLLOUT_MODE == manual' do
+ create(:ci_variable, project: project, key: 'INCREMENTAL_ROLLOUT_MODE', value: 'manual')
+
+ expect(build_names).not_to include('production_manual')
+ expect(build_names).not_to include('production')
+ expect(build_names).not_to include(
+ 'timed rollout 10%',
+ 'timed rollout 25%',
+ 'timed rollout 50%',
+ 'timed rollout 100%'
+ )
+ expect(build_names).to include(
+ 'rollout 10%',
+ 'rollout 25%',
+ 'rollout 50%',
+ 'rollout 100%'
+ )
+ end
+ end
+
+ shared_examples_for 'review app deployment' do
+ it 'creates the review and stop_review jobs but no production jobs' do
+ expect(build_names).to include('review')
+ expect(build_names).to include('stop_review')
+ expect(build_names).not_to include('production')
+ expect(build_names).not_to include('production_manual')
+ expect(build_names).not_to include('staging')
+ expect(build_names).not_to include('canary')
+ expect(build_names).not_to include('timed rollout 10%')
+ expect(build_names).not_to include('timed rollout 25%')
+ expect(build_names).not_to include('timed rollout 50%')
+ expect(build_names).not_to include('timed rollout 100%')
+ expect(build_names).not_to include('rollout 10%')
+ expect(build_names).not_to include('rollout 25%')
+ expect(build_names).not_to include('rollout 50%')
+ expect(build_names).not_to include('rollout 100%')
+ end
+
+ it 'does not include review when REVIEW_DISABLED' do
+ create(:ci_variable, project: project, key: 'REVIEW_DISABLED', value: 'true')
+
+ expect(build_names).not_to include('review')
+ expect(build_names).not_to include('stop_review')
+ end
+ end
+
+ context 'on branch' do
+ let(:pipeline_ref) { 'feature' }
+
+ before do
+ allow_any_instance_of(Gitlab::Ci::Pipeline::Chain::Validate::Repository).to receive(:perform!).and_return(true)
+ end
+
+ it_behaves_like 'review app deployment'
+
+ context 'when INCREMENTAL_ROLLOUT_ENABLED' do
+ before do
+ create(:ci_variable, project: project, key: 'INCREMENTAL_ROLLOUT_ENABLED', value: 'true')
+ end
+
+ it_behaves_like 'review app deployment'
+ end
+
+ context 'when INCREMENTAL_ROLLOUT_MODE == "timed"' do
+ before do
+ create(:ci_variable, project: project, key: 'INCREMENTAL_ROLLOUT_MODE', value: 'timed')
+ end
+
+ it_behaves_like 'review app deployment'
+ end
+
+ context 'when INCREMENTAL_ROLLOUT_MODE == "manual"' do
+ before do
+ create(:ci_variable, project: project, key: 'INCREMENTAL_ROLLOUT_MODE', value: 'manual')
+ end
+
+ it_behaves_like 'review app deployment'
+ end
+ end
+
+ context 'on tag' do
+ let(:pipeline_ref) { 'v1.0.0' }
+
+ it_behaves_like 'review app deployment'
+ end
+
+ context 'on merge request' do
+ let(:service) { MergeRequests::CreatePipelineService.new(project, user) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+ let(:pipeline) { service.execute(merge_request) }
+
+ it 'has no jobs' do
+ expect(pipeline).to be_merge_request_event
+ expect(build_names).to be_empty
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..2186bf038eb
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Jobs/Test.gitlab-ci.yml' do
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/Test') }
+
+ describe 'the created pipeline' do
+ let_it_be(:user) { create(:admin) }
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:default_branch) { 'master' }
+ let(:pipeline_ref) { default_branch }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
+ let(:pipeline) { service.execute!(:push) }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template.content)
+ allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ context 'on master' do
+ it 'creates the test job' do
+ expect(build_names).to contain_exactly('test')
+ end
+ end
+
+ context 'on another branch' do
+ let(:pipeline_ref) { 'feature' }
+
+ it 'creates the test job' do
+ expect(build_names).to contain_exactly('test')
+ end
+ end
+
+ context 'on tag' do
+ let(:pipeline_ref) { 'v1.0.0' }
+
+ it 'creates the test job' do
+ expect(pipeline).to be_tag
+ expect(build_names).to contain_exactly('test')
+ end
+ end
+
+ context 'on merge request' do
+ let(:service) { MergeRequests::CreatePipelineService.new(project, user) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+ let(:pipeline) { service.execute(merge_request) }
+
+ it 'has no jobs' do
+ expect(pipeline).to be_merge_request_event
+ expect(build_names).to be_empty
+ end
+ end
+
+ context 'TEST_DISABLED is set' do
+ before do
+ create(:ci_variable, key: 'TEST_DISABLED', value: 'true', project: project)
+ end
+
+ context 'on master' do
+ it 'has no jobs' do
+ expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
+ end
+ end
+
+ context 'on another branch' do
+ let(:pipeline_ref) { 'feature' }
+
+ it 'has no jobs' do
+ expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
+ end
+ end
+
+ context 'on tag' do
+ let(:pipeline_ref) { 'v1.0.0' }
+
+ it 'has no jobs' do
+ expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
index 0c5d172f17c..af6ec25b9d6 100644
--- a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
@@ -20,16 +20,8 @@ describe 'Auto-DevOps.gitlab-ci.yml' do
allow(project).to receive(:default_branch).and_return(default_branch)
end
- it 'creates a build and a test job' do
- expect(build_names).to include('build', 'test')
- end
-
- context 'when the project has no active cluster' do
- it 'only creates a build and a test stage' do
- expect(pipeline.stages_names).to eq(%w(build test))
- end
-
- it 'does not create any deployment-related builds' do
+ shared_examples 'no Kubernetes deployment job' do
+ it 'does not create any Kubernetes deployment-related builds' do
expect(build_names).not_to include('production')
expect(build_names).not_to include('production_manual')
expect(build_names).not_to include('staging')
@@ -39,13 +31,95 @@ describe 'Auto-DevOps.gitlab-ci.yml' do
end
end
- context 'when the project has an active cluster' do
- let(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) }
+ it 'creates a build and a test job' do
+ expect(build_names).to include('build', 'test')
+ end
+
+ context 'when the project is set for deployment to AWS' do
+ let(:platform_value) { 'ECS' }
before do
- allow(cluster).to receive(:active?).and_return(true)
+ create(:ci_variable, project: project, key: 'AUTO_DEVOPS_PLATFORM_TARGET', value: platform_value)
+ end
+
+ shared_examples 'no ECS job when AUTO_DEVOPS_PLATFORM_TARGET is not present' do |job_name|
+ context 'when AUTO_DEVOPS_PLATFORM_TARGET is nil' do
+ let(:platform_value) { nil }
+
+ it 'does not trigger the job' do
+ expect(build_names).not_to include(job_name)
+ end
+ end
+
+ context 'when AUTO_DEVOPS_PLATFORM_TARGET is empty' do
+ let(:platform_value) { '' }
+
+ it 'does not trigger the job' do
+ expect(build_names).not_to include(job_name)
+ end
+ end
+ end
+
+ it_behaves_like 'no Kubernetes deployment job'
+
+ it_behaves_like 'no ECS job when AUTO_DEVOPS_PLATFORM_TARGET is not present' do
+ let(:job_name) { 'production_ecs' }
+ end
+
+ it 'creates an ECS deployment job for production only' do
+ expect(build_names).not_to include('review_ecs')
+ expect(build_names).to include('production_ecs')
end
+ context 'and we are not on the default branch' do
+ let(:platform_value) { 'ECS' }
+ let(:pipeline_branch) { 'patch-1' }
+
+ before do
+ project.repository.create_branch(pipeline_branch)
+ end
+
+ it_behaves_like 'no ECS job when AUTO_DEVOPS_PLATFORM_TARGET is not present' do
+ let(:job_name) { 'review_ecs' }
+ end
+
+ it 'creates an ECS deployment job for review only' do
+ expect(build_names).to include('review_ecs')
+ expect(build_names).not_to include('production_ecs')
+ expect(build_names).not_to include('review')
+ expect(build_names).not_to include('production')
+ end
+ end
+
+ context 'and when the project has an active cluster' do
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) }
+
+ before do
+ allow(cluster).to receive(:active?).and_return(true)
+ end
+
+ context 'on default branch' do
+ it 'triggers the deployment to Kubernetes, not to ECS' do
+ expect(build_names).not_to include('review')
+ expect(build_names).to include('production')
+ expect(build_names).not_to include('production_ecs')
+ expect(build_names).not_to include('review_ecs')
+ end
+ end
+ end
+ end
+
+ context 'when the project has no active cluster' do
+ it 'only creates a build and a test stage' do
+ expect(pipeline.stages_names).to eq(%w(build test))
+ end
+
+ it_behaves_like 'no Kubernetes deployment job'
+ end
+
+ context 'when the project has an active cluster' do
+ let!(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) }
+
describe 'deployment-related builds' do
context 'on default branch' do
it 'does not include rollout jobs besides production' do
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 70c3c5ab339..c93bb901981 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -1364,6 +1364,24 @@ module Gitlab
expect { described_class.new(config) }.to raise_error(described_class::ValidationError)
end
+
+ it 'populates a build options with complete artifacts configuration' do
+ stub_feature_flags(ci_artifacts_exclude: true)
+
+ config = <<~YAML
+ test:
+ script: echo "Hello World"
+ artifacts:
+ paths:
+ - my/test
+ exclude:
+ - my/test/something
+ YAML
+
+ attributes = Gitlab::Ci::YamlProcessor.new(config).build_attributes('test')
+
+ expect(attributes.dig(*%i[options artifacts exclude])).to eq(%w[my/test/something])
+ end
end
describe "release" do
@@ -2264,14 +2282,14 @@ module Gitlab
config = YAML.dump({ rspec: { script: "test", type: "acceptance" } })
expect do
Gitlab::Ci::YamlProcessor.new(config)
- end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "rspec job: stage parameter should be .pre, build, test, deploy, .post")
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "rspec job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post")
end
it "returns errors if job stage is not a defined stage" do
config = YAML.dump({ types: %w(build test), rspec: { script: "test", type: "acceptance" } })
expect do
Gitlab::Ci::YamlProcessor.new(config)
- end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "rspec job: stage parameter should be .pre, build, test, .post")
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "rspec job: chosen stage does not exist; available stages are .pre, build, test, .post")
end
it "returns errors if stages is not an array" do