summaryrefslogtreecommitdiff
path: root/spec/models/ci
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/ci')
-rw-r--r--spec/models/ci/build_spec.rb353
-rw-r--r--spec/models/ci/daily_build_group_report_result_spec.rb (renamed from spec/models/ci/daily_report_result_spec.rb)25
-rw-r--r--spec/models/ci/freeze_period_spec.rb50
-rw-r--r--spec/models/ci/freeze_period_status_spec.rb62
-rw-r--r--spec/models/ci/instance_variable_spec.rb93
-rw-r--r--spec/models/ci/job_artifact_spec.rb120
-rw-r--r--spec/models/ci/persistent_ref_spec.rb12
-rw-r--r--spec/models/ci/pipeline_schedule_spec.rb8
-rw-r--r--spec/models/ci/pipeline_spec.rb145
-rw-r--r--spec/models/ci/processable_spec.rb159
-rw-r--r--spec/models/ci/runner_spec.rb8
-rw-r--r--spec/models/ci/stage_spec.rb26
12 files changed, 790 insertions, 271 deletions
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index a4f3fa518c6..6605866d9c0 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -106,10 +106,14 @@ describe Ci::Build do
end
end
- describe '.with_artifacts_archive' do
- subject { described_class.with_artifacts_archive }
+ describe '.with_downloadable_artifacts' do
+ subject { described_class.with_downloadable_artifacts }
- context 'when job does not have an archive' do
+ before do
+ stub_feature_flags(drop_license_management_artifact: false)
+ end
+
+ context 'when job does not have a downloadable artifact' do
let!(:job) { create(:ci_build) }
it 'does not return the job' do
@@ -117,15 +121,23 @@ describe Ci::Build do
end
end
- context 'when job has a job artifact archive' do
- let!(:job) { create(:ci_build, :artifacts) }
+ ::Ci::JobArtifact::DOWNLOADABLE_TYPES.each do |type|
+ context "when job has a #{type} artifact" do
+ it 'returns the job' do
+ job = create(:ci_build)
+ create(
+ :ci_job_artifact,
+ file_format: ::Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS[type.to_sym],
+ file_type: type,
+ job: job
+ )
- it 'returns the job' do
- is_expected.to include(job)
+ is_expected.to include(job)
+ end
end
end
- context 'when job has a job artifact trace' do
+ context 'when job has a non-downloadable artifact' do
let!(:job) { create(:ci_build, :trace_artifact) }
it 'does not return the job' do
@@ -1419,6 +1431,8 @@ describe Ci::Build do
subject { build.erase_erasable_artifacts! }
before do
+ stub_feature_flags(drop_license_management_artifact: false)
+
Ci::JobArtifact.file_types.keys.each do |file_type|
create(:ci_job_artifact, job: build, file_type: file_type, file_format: Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS[file_type.to_sym])
end
@@ -2367,12 +2381,14 @@ describe Ci::Build do
let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true, masked: false } }
let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true, masked: false } }
let(:job_jwt_var) { { key: 'CI_JOB_JWT', value: 'ci.job.jwt', public: false, masked: true } }
+ let(:job_dependency_var) { { key: 'job_dependency', value: 'value', public: true, masked: false } }
before do
allow(build).to receive(:predefined_variables) { [build_pre_var] }
allow(build).to receive(:yaml_variables) { [build_yaml_var] }
allow(build).to receive(:persisted_variables) { [] }
allow(build).to receive(:job_jwt_variables) { [job_jwt_var] }
+ allow(build).to receive(:dependency_variables) { [job_dependency_var] }
allow_any_instance_of(Project)
.to receive(:predefined_variables) { [project_pre_var] }
@@ -2390,6 +2406,7 @@ describe Ci::Build do
project_pre_var,
pipeline_pre_var,
build_yaml_var,
+ job_dependency_var,
{ key: 'secret', value: 'value', public: false, masked: false }])
end
end
@@ -2884,6 +2901,19 @@ describe Ci::Build do
it { is_expected.to include(deployment_variable) }
end
+ context 'when build has a freeze period' do
+ let(:freeze_variable) { { key: 'CI_DEPLOY_FREEZE', value: 'true', masked: false, public: true } }
+
+ before do
+ expect_next_instance_of(Ci::FreezePeriodStatus) do |freeze_period|
+ expect(freeze_period).to receive(:execute)
+ .and_return(true)
+ end
+ end
+
+ it { is_expected.to include(freeze_variable) }
+ end
+
context 'when project has default CI config path' do
let(:ci_config_path) { { key: 'CI_CONFIG_PATH', value: '.gitlab-ci.yml', public: true, masked: false } }
@@ -2987,6 +3017,15 @@ describe Ci::Build do
end
end
end
+
+ context 'when build has dependency which has dotenv variable' do
+ let!(:prepare) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
+ let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 1, options: { dependencies: [prepare.name] }) }
+
+ let!(:job_variable) { create(:ci_job_variable, :dotenv_source, job: prepare) }
+
+ it { is_expected.to include(key: job_variable.key, value: job_variable.value, public: false, masked: false) }
+ end
end
describe '#scoped_variables' do
@@ -3049,71 +3088,36 @@ describe Ci::Build do
end
end
end
- end
- describe '#secret_group_variables' do
- subject { build.secret_group_variables }
-
- let!(:variable) { create(:ci_group_variable, protected: true, group: group) }
+ context 'with dependency variables' do
+ let!(:prepare) { create(:ci_build, name: 'prepare', pipeline: pipeline, stage_idx: 0) }
+ let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 1, options: { dependencies: ['prepare'] }) }
- context 'when ref is branch' do
- let(:build) { create(:ci_build, ref: 'master', tag: false, project: project) }
+ let!(:job_variable) { create(:ci_job_variable, :dotenv_source, job: prepare) }
- context 'when ref is protected' do
+ context 'FF ci_dependency_variables is enabled' do
before do
- create(:protected_branch, :developers_can_merge, name: 'master', project: project)
+ stub_feature_flags(ci_dependency_variables: true)
end
- it { is_expected.to include(variable) }
- end
-
- context 'when ref is not protected' do
- it { is_expected.not_to include(variable) }
- end
- end
-
- context 'when ref is tag' do
- let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, project: project) }
-
- context 'when ref is protected' do
- before do
- create(:protected_tag, project: project, name: 'v*')
+ it 'inherits dependent variables' do
+ expect(build.scoped_variables.to_hash).to include(job_variable.key => job_variable.value)
end
-
- it { is_expected.to include(variable) }
- end
-
- context 'when ref is not protected' do
- it { is_expected.not_to include(variable) }
end
- end
- context 'when ref is merge request' do
- let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
- let(:pipeline) { merge_request.pipelines_for_merge_request.first }
- let(:build) { create(:ci_build, ref: merge_request.source_branch, tag: false, pipeline: pipeline, project: project) }
-
- context 'when ref is protected' do
+ context 'FF ci_dependency_variables is disabled' do
before do
- create(:protected_branch, :developers_can_merge, name: merge_request.source_branch, project: project)
+ stub_feature_flags(ci_dependency_variables: false)
end
- it 'does not return protected variables as it is not supported for merge request pipelines' do
- is_expected.not_to include(variable)
+ it 'does not inherit dependent variables' do
+ expect(build.scoped_variables.to_hash).not_to include(job_variable.key => job_variable.value)
end
end
-
- context 'when ref is not protected' do
- it { is_expected.not_to include(variable) }
- end
end
end
- describe '#secret_project_variables' do
- subject { build.secret_project_variables }
-
- let!(:variable) { create(:ci_variable, protected: true, project: project) }
-
+ shared_examples "secret CI variables" do
context 'when ref is branch' do
let(:build) { create(:ci_build, ref: 'master', tag: false, project: project) }
@@ -3167,6 +3171,30 @@ describe Ci::Build do
end
end
+ describe '#secret_instance_variables' do
+ subject { build.secret_instance_variables }
+
+ let_it_be(:variable) { create(:ci_instance_variable, protected: true) }
+
+ include_examples "secret CI variables"
+ end
+
+ describe '#secret_group_variables' do
+ subject { build.secret_group_variables }
+
+ let_it_be(:variable) { create(:ci_group_variable, protected: true, group: group) }
+
+ include_examples "secret CI variables"
+ end
+
+ describe '#secret_project_variables' do
+ subject { build.secret_project_variables }
+
+ let_it_be(:variable) { create(:ci_variable, protected: true, project: project) }
+
+ include_examples "secret CI variables"
+ end
+
describe '#deployment_variables' do
let(:build) { create(:ci_build, environment: environment) }
let(:environment) { 'production' }
@@ -3217,6 +3245,29 @@ describe Ci::Build do
expect(build.scoped_variables_hash).not_to include('MY_VAR': 'myvar')
end
end
+
+ context 'when overriding CI instance variables' do
+ before do
+ create(:ci_instance_variable, key: 'MY_VAR', value: 'my value 1')
+ group.variables.create!(key: 'MY_VAR', value: 'my value 2')
+ end
+
+ it 'returns a regular hash created using valid ordering' do
+ expect(build.scoped_variables_hash).to include('MY_VAR': 'my value 2')
+ expect(build.scoped_variables_hash).not_to include('MY_VAR': 'my value 1')
+ end
+ end
+
+ context 'when CI instance variables are disabled' do
+ before do
+ create(:ci_instance_variable, key: 'MY_VAR', value: 'my value 1')
+ stub_feature_flags(ci_instance_level_variables: false)
+ end
+
+ it 'does not include instance level variables' do
+ expect(build.scoped_variables_hash).not_to include('MY_VAR': 'my value 1')
+ end
+ end
end
describe '#any_unmet_prerequisites?' do
@@ -3293,6 +3344,41 @@ describe Ci::Build do
end
end
+ describe '#dependency_variables' do
+ subject { build.dependency_variables }
+
+ context 'when using dependencies' do
+ let!(:prepare1) { create(:ci_build, name: 'prepare1', pipeline: pipeline, stage_idx: 0) }
+ let!(:prepare2) { create(:ci_build, name: 'prepare2', pipeline: pipeline, stage_idx: 0) }
+ let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 1, options: { dependencies: ['prepare1'] }) }
+
+ let!(:job_variable_1) { create(:ci_job_variable, :dotenv_source, job: prepare1) }
+ let!(:job_variable_2) { create(:ci_job_variable, job: prepare1) }
+ let!(:job_variable_3) { create(:ci_job_variable, :dotenv_source, job: prepare2) }
+
+ it 'inherits only dependent variables' do
+ expect(subject.to_hash).to eq(job_variable_1.key => job_variable_1.value)
+ end
+ end
+
+ context 'when using needs' do
+ let!(:prepare1) { create(:ci_build, name: 'prepare1', pipeline: pipeline, stage_idx: 0) }
+ let!(:prepare2) { create(:ci_build, name: 'prepare2', pipeline: pipeline, stage_idx: 0) }
+ let!(:prepare3) { create(:ci_build, name: 'prepare3', pipeline: pipeline, stage_idx: 0) }
+ let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 1, scheduling_type: 'dag') }
+ let!(:build_needs_prepare1) { create(:ci_build_need, build: build, name: 'prepare1', artifacts: true) }
+ let!(:build_needs_prepare2) { create(:ci_build_need, build: build, name: 'prepare2', artifacts: false) }
+
+ let!(:job_variable_1) { create(:ci_job_variable, :dotenv_source, job: prepare1) }
+ let!(:job_variable_2) { create(:ci_job_variable, :dotenv_source, job: prepare2) }
+ let!(:job_variable_3) { create(:ci_job_variable, :dotenv_source, job: prepare3) }
+
+ it 'inherits only needs with artifacts variables' do
+ expect(subject.to_hash).to eq(job_variable_1.key => job_variable_1.value)
+ end
+ end
+ end
+
describe 'state transition: any => [:preparing]' do
let(:build) { create(:ci_build, :created) }
@@ -3822,8 +3908,68 @@ describe Ci::Build do
create(:ci_job_artifact, :junit_with_corrupted_data, job: build, project: build.project)
end
- it 'raises an error' do
- expect { subject }.to raise_error(Gitlab::Ci::Parsers::Test::Junit::JunitParserError)
+ it 'returns no test data and includes a suite_error message' do
+ expect { subject }.not_to raise_error
+
+ expect(test_reports.get_suite(build.name).total_count).to eq(0)
+ expect(test_reports.get_suite(build.name).success_count).to eq(0)
+ expect(test_reports.get_suite(build.name).failed_count).to eq(0)
+ expect(test_reports.get_suite(build.name).suite_error).to eq('JUnit XML parsing failed: 1:1: FATAL: Document is empty')
+ end
+ end
+ end
+ end
+
+ describe '#collect_accessibility_reports!' do
+ subject { build.collect_accessibility_reports!(accessibility_report) }
+
+ let(:accessibility_report) { Gitlab::Ci::Reports::AccessibilityReports.new }
+
+ it { expect(accessibility_report.urls).to eq({}) }
+
+ context 'when build has an accessibility report' do
+ context 'when there is an accessibility report with errors' do
+ before do
+ create(:ci_job_artifact, :accessibility, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the accessibility report' do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.urls.keys).to match_array(['https://about.gitlab.com/'])
+ expect(accessibility_report.errors_count).to eq(10)
+ expect(accessibility_report.scans_count).to eq(1)
+ expect(accessibility_report.passes_count).to eq(0)
+ end
+ end
+
+ context 'when there is an accessibility report without errors' do
+ before do
+ create(:ci_job_artifact, :accessibility_without_errors, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the accessibility report' do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.urls.keys).to match_array(['https://pa11y.org/'])
+ expect(accessibility_report.errors_count).to eq(0)
+ expect(accessibility_report.scans_count).to eq(1)
+ expect(accessibility_report.passes_count).to eq(1)
+ end
+ end
+
+ context 'when there is an accessibility report with an invalid url' do
+ before do
+ create(:ci_job_artifact, :accessibility_with_invalid_url, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the accessibility report' do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.urls).to be_empty
+ expect(accessibility_report.errors_count).to eq(0)
+ expect(accessibility_report.scans_count).to eq(0)
+ expect(accessibility_report.passes_count).to eq(0)
end
end
end
@@ -3876,6 +4022,48 @@ describe Ci::Build do
end
end
+ describe '#collect_terraform_reports!' do
+ let(:terraform_reports) { Gitlab::Ci::Reports::TerraformReports.new }
+
+ it 'returns an empty hash' do
+ expect(build.collect_terraform_reports!(terraform_reports).plans).to eq({})
+ end
+
+ context 'when build has a terraform report' do
+ context 'when there is a valid tfplan.json' do
+ before do
+ create(:ci_job_artifact, :terraform, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the terraform report' do
+ expect { build.collect_terraform_reports!(terraform_reports) }.not_to raise_error
+
+ expect(terraform_reports.plans).to match(
+ a_hash_including(
+ 'tfplan.json' => a_hash_including(
+ 'create' => 0,
+ 'update' => 1,
+ 'delete' => 0
+ )
+ )
+ )
+ end
+ end
+
+ context 'when there is an invalid tfplan.json' do
+ before do
+ create(:ci_job_artifact, :terraform_with_corrupted_data, job: build, project: build.project)
+ end
+
+ it 'raises an error' do
+ expect { build.collect_terraform_reports!(terraform_reports) }.to raise_error(
+ Gitlab::Ci::Parsers::Terraform::Tfplan::TfplanParserError
+ )
+ end
+ end
+ end
+ end
+
describe '#report_artifacts' do
subject { build.report_artifacts }
@@ -3986,6 +4174,28 @@ describe Ci::Build do
it { is_expected.to include(:upload_multiple_artifacts) }
end
+
+ context 'when artifacts exclude is defined and the is feature enabled' do
+ let(:options) do
+ { artifacts: { exclude: %w[something] } }
+ end
+
+ context 'when a feature flag is enabled' do
+ before do
+ stub_feature_flags(ci_artifacts_exclude: true)
+ end
+
+ it { is_expected.to include(:artifacts_exclude) }
+ end
+
+ context 'when a feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_artifacts_exclude: false)
+ end
+
+ it { is_expected.not_to include(:artifacts_exclude) }
+ end
+ end
end
describe '#supported_runner?' do
@@ -4312,4 +4522,31 @@ describe Ci::Build do
it { is_expected.to be_nil }
end
end
+
+ describe '#degradation_threshold' do
+ subject { build.degradation_threshold }
+
+ context 'when threshold variable is defined' do
+ before do
+ build.yaml_variables = [
+ { key: 'SOME_VAR_1', value: 'SOME_VAL_1' },
+ { key: 'DEGRADATION_THRESHOLD', value: '5' },
+ { key: 'SOME_VAR_2', value: 'SOME_VAL_2' }
+ ]
+ end
+
+ it { is_expected.to eq(5) }
+ end
+
+ context 'when threshold variable is not defined' do
+ before do
+ build.yaml_variables = [
+ { key: 'SOME_VAR_1', value: 'SOME_VAL_1' },
+ { key: 'SOME_VAR_2', value: 'SOME_VAL_2' }
+ ]
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
end
diff --git a/spec/models/ci/daily_report_result_spec.rb b/spec/models/ci/daily_build_group_report_result_spec.rb
index 61aa58c6692..d4c305c649a 100644
--- a/spec/models/ci/daily_report_result_spec.rb
+++ b/spec/models/ci/daily_build_group_report_result_spec.rb
@@ -2,14 +2,14 @@
require 'spec_helper'
-describe Ci::DailyReportResult do
+describe Ci::DailyBuildGroupReportResult do
describe '.upsert_reports' do
let!(:rspec_coverage) do
create(
- :ci_daily_report_result,
- title: 'rspec',
+ :ci_daily_build_group_report_result,
+ group_name: 'rspec',
date: '2020-03-09',
- value: 71.2
+ data: { coverage: 71.2 }
)
end
let!(:new_pipeline) { create(:ci_pipeline) }
@@ -19,20 +19,18 @@ describe Ci::DailyReportResult do
{
project_id: rspec_coverage.project_id,
ref_path: rspec_coverage.ref_path,
- param_type: described_class.param_types[rspec_coverage.param_type],
last_pipeline_id: new_pipeline.id,
date: rspec_coverage.date,
- title: 'rspec',
- value: 81.0
+ group_name: 'rspec',
+ data: { 'coverage' => 81.0 }
},
{
project_id: rspec_coverage.project_id,
ref_path: rspec_coverage.ref_path,
- param_type: described_class.param_types[rspec_coverage.param_type],
last_pipeline_id: new_pipeline.id,
date: rspec_coverage.date,
- title: 'karma',
- value: 87.0
+ group_name: 'karma',
+ data: { 'coverage' => 87.0 }
}
])
@@ -40,16 +38,15 @@ describe Ci::DailyReportResult do
expect(rspec_coverage).to have_attributes(
last_pipeline_id: new_pipeline.id,
- value: 81.0
+ data: { 'coverage' => 81.0 }
)
- expect(described_class.find_by_title('karma')).to have_attributes(
+ expect(described_class.find_by_group_name('karma')).to have_attributes(
project_id: rspec_coverage.project_id,
ref_path: rspec_coverage.ref_path,
- param_type: rspec_coverage.param_type,
last_pipeline_id: new_pipeline.id,
date: rspec_coverage.date,
- value: 87.0
+ data: { 'coverage' => 87.0 }
)
end
diff --git a/spec/models/ci/freeze_period_spec.rb b/spec/models/ci/freeze_period_spec.rb
new file mode 100644
index 00000000000..f7f840c6696
--- /dev/null
+++ b/spec/models/ci/freeze_period_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::FreezePeriod, type: :model do
+ subject { build(:ci_freeze_period) }
+
+ let(:invalid_cron) { '0 0 0 * *' }
+
+ it { is_expected.to belong_to(:project) }
+
+ it { is_expected.to respond_to(:freeze_start) }
+ it { is_expected.to respond_to(:freeze_end) }
+ it { is_expected.to respond_to(:cron_timezone) }
+
+ describe 'cron validations' do
+ it 'allows valid cron patterns' do
+ freeze_period = build(:ci_freeze_period)
+
+ expect(freeze_period).to be_valid
+ end
+
+ it 'does not allow invalid cron patterns on freeze_start' do
+ freeze_period = build(:ci_freeze_period, freeze_start: invalid_cron)
+
+ expect(freeze_period).not_to be_valid
+ end
+
+ it 'does not allow invalid cron patterns on freeze_end' do
+ freeze_period = build(:ci_freeze_period, freeze_end: invalid_cron)
+
+ expect(freeze_period).not_to be_valid
+ end
+
+ it 'does not allow an invalid timezone' do
+ freeze_period = build(:ci_freeze_period, cron_timezone: 'invalid')
+
+ expect(freeze_period).not_to be_valid
+ end
+
+ context 'when cron contains trailing whitespaces' do
+ it 'strips the attribute' do
+ freeze_period = build(:ci_freeze_period, freeze_start: ' 0 0 * * * ')
+
+ expect(freeze_period).to be_valid
+ expect(freeze_period.freeze_start).to eq('0 0 * * *')
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/freeze_period_status_spec.rb b/spec/models/ci/freeze_period_status_spec.rb
new file mode 100644
index 00000000000..b700ec8c45f
--- /dev/null
+++ b/spec/models/ci/freeze_period_status_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Ci::FreezePeriodStatus do
+ let(:project) { create :project }
+ # '0 23 * * 5' == "At 23:00 on Friday."", '0 7 * * 1' == "At 07:00 on Monday.""
+ let(:friday_2300) { '0 23 * * 5' }
+ let(:monday_0700) { '0 7 * * 1' }
+
+ subject { described_class.new(project: project).execute }
+
+ shared_examples 'within freeze period' do |time|
+ it 'is frozen' do
+ Timecop.freeze(time) do
+ expect(subject).to be_truthy
+ end
+ end
+ end
+
+ shared_examples 'outside freeze period' do |time|
+ it 'is not frozen' do
+ Timecop.freeze(time) do
+ expect(subject).to be_falsy
+ end
+ end
+ end
+
+ describe 'single freeze period' do
+ let!(:freeze_period) { create(:ci_freeze_period, project: project, freeze_start: friday_2300, freeze_end: monday_0700) }
+
+ it_behaves_like 'outside freeze period', Time.utc(2020, 4, 10, 22, 59)
+
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 1)
+
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 6, 59)
+
+ it_behaves_like 'outside freeze period', Time.utc(2020, 4, 13, 7, 1)
+ end
+
+ describe 'multiple freeze periods' do
+ # '30 23 * * 5' == "At 23:30 on Friday."", '0 8 * * 1' == "At 08:00 on Monday.""
+ let(:friday_2330) { '30 23 * * 5' }
+ let(:monday_0800) { '0 8 * * 1' }
+
+ let!(:freeze_period_1) { create(:ci_freeze_period, project: project, freeze_start: friday_2300, freeze_end: monday_0700) }
+ let!(:freeze_period_2) { create(:ci_freeze_period, project: project, freeze_start: friday_2330, freeze_end: monday_0800) }
+
+ it_behaves_like 'outside freeze period', Time.utc(2020, 4, 10, 22, 59)
+
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 29)
+
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 11, 10, 0)
+
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 1)
+
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 6, 59)
+
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 7, 59)
+
+ it_behaves_like 'outside freeze period', Time.utc(2020, 4, 13, 8, 1)
+ end
+end
diff --git a/spec/models/ci/instance_variable_spec.rb b/spec/models/ci/instance_variable_spec.rb
new file mode 100644
index 00000000000..ff8676e1424
--- /dev/null
+++ b/spec/models/ci/instance_variable_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::InstanceVariable do
+ subject { build(:ci_instance_variable) }
+
+ it_behaves_like "CI variable"
+
+ it { is_expected.to include_module(Ci::Maskable) }
+ it { is_expected.to validate_uniqueness_of(:key).with_message(/\(\w+\) has already been taken/) }
+
+ describe '.unprotected' do
+ subject { described_class.unprotected }
+
+ context 'when variable is protected' do
+ before do
+ create(:ci_instance_variable, :protected)
+ end
+
+ it 'returns nothing' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when variable is not protected' do
+ let(:variable) { create(:ci_instance_variable, protected: false) }
+
+ it 'returns the variable' do
+ is_expected.to contain_exactly(variable)
+ end
+ end
+ end
+
+ describe '.all_cached', :use_clean_rails_memory_store_caching do
+ let_it_be(:unprotected_variable) { create(:ci_instance_variable, protected: false) }
+ let_it_be(:protected_variable) { create(:ci_instance_variable, protected: true) }
+
+ it { expect(described_class.all_cached).to contain_exactly(protected_variable, unprotected_variable) }
+
+ it 'memoizes the result' do
+ expect(described_class).to receive(:store_cache).with(:ci_instance_variable_data).once.and_call_original
+
+ 2.times do
+ expect(described_class.all_cached).to contain_exactly(protected_variable, unprotected_variable)
+ end
+ end
+
+ it 'removes scopes' do
+ expect(described_class.unprotected.all_cached).to contain_exactly(protected_variable, unprotected_variable)
+ end
+
+ it 'resets the cache when records are deleted' do
+ expect(described_class.all_cached).to contain_exactly(protected_variable, unprotected_variable)
+
+ protected_variable.destroy
+
+ expect(described_class.all_cached).to contain_exactly(unprotected_variable)
+ end
+
+ it 'resets the cache when records are inserted' do
+ expect(described_class.all_cached).to contain_exactly(protected_variable, unprotected_variable)
+
+ variable = create(:ci_instance_variable, protected: true)
+
+ expect(described_class.all_cached).to contain_exactly(protected_variable, unprotected_variable, variable)
+ end
+
+ it 'resets the cache when the shared key is missing' do
+ expect(Rails.cache).to receive(:read).with(:ci_instance_variable_changed_at).twice.and_return(nil)
+ expect(described_class).to receive(:store_cache).with(:ci_instance_variable_data).thrice.and_call_original
+
+ 3.times do
+ expect(described_class.all_cached).to contain_exactly(protected_variable, unprotected_variable)
+ end
+ end
+ end
+
+ describe '.unprotected_cached', :use_clean_rails_memory_store_caching do
+ let_it_be(:unprotected_variable) { create(:ci_instance_variable, protected: false) }
+ let_it_be(:protected_variable) { create(:ci_instance_variable, protected: true) }
+
+ it { expect(described_class.unprotected_cached).to contain_exactly(unprotected_variable) }
+
+ it 'memoizes the result' do
+ expect(described_class).to receive(:store_cache).with(:ci_instance_variable_data).once.and_call_original
+
+ 2.times do
+ expect(described_class.unprotected_cached).to contain_exactly(unprotected_variable)
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 6f6ff3704b4..4cdc74d7a41 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -19,24 +19,8 @@ describe Ci::JobArtifact do
it_behaves_like 'having unique enum values'
- context 'with update_project_statistics_after_commit enabled' do
- before do
- stub_feature_flags(update_project_statistics_after_commit: true)
- end
-
- it_behaves_like 'UpdateProjectStatistics' do
- subject { build(:ci_job_artifact, :archive, size: 107464) }
- end
- end
-
- context 'with update_project_statistics_after_commit disabled' do
- before do
- stub_feature_flags(update_project_statistics_after_commit: false)
- end
-
- it_behaves_like 'UpdateProjectStatistics' do
- subject { build(:ci_job_artifact, :archive, size: 107464) }
- end
+ it_behaves_like 'UpdateProjectStatistics' do
+ subject { build(:ci_job_artifact, :archive, size: 107464) }
end
describe '.with_reports' do
@@ -70,6 +54,22 @@ describe Ci::JobArtifact do
end
end
+ describe '.accessibility_reports' do
+ subject { described_class.accessibility_reports }
+
+ context 'when there is an accessibility report' do
+ let(:artifact) { create(:ci_job_artifact, :accessibility) }
+
+ it { is_expected.to eq([artifact]) }
+ end
+
+ context 'when there are no accessibility report' do
+ let(:artifact) { create(:ci_job_artifact, :archive) }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
describe '.coverage_reports' do
subject { described_class.coverage_reports }
@@ -86,6 +86,22 @@ describe Ci::JobArtifact do
end
end
+ describe '.terraform_reports' do
+ context 'when there is a terraform report' do
+ it 'return the job artifact' do
+ artifact = create(:ci_job_artifact, :terraform)
+
+ expect(described_class.terraform_reports).to eq([artifact])
+ end
+ end
+
+ context 'when there are no terraform reports' do
+ it 'return the an empty array' do
+ expect(described_class.terraform_reports).to eq([])
+ end
+ end
+ end
+
describe '.erasable' do
subject { described_class.erasable }
@@ -128,15 +144,26 @@ describe Ci::JobArtifact do
end
describe '.for_sha' do
+ let(:first_pipeline) { create(:ci_pipeline) }
+ let(:second_pipeline) { create(:ci_pipeline, project: first_pipeline.project, sha: Digest::SHA1.hexdigest(SecureRandom.hex)) }
+ let!(:first_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: first_pipeline)) }
+ let!(:second_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: second_pipeline)) }
+
it 'returns job artifacts for a given pipeline sha' do
- project = create(:project)
- first_pipeline = create(:ci_pipeline, project: project)
- second_pipeline = create(:ci_pipeline, project: project, sha: Digest::SHA1.hexdigest(SecureRandom.hex))
- first_artifact = create(:ci_job_artifact, job: create(:ci_build, pipeline: first_pipeline))
- second_artifact = create(:ci_job_artifact, job: create(:ci_build, pipeline: second_pipeline))
+ expect(described_class.for_sha(first_pipeline.sha, first_pipeline.project.id)).to eq([first_artifact])
+ expect(described_class.for_sha(second_pipeline.sha, first_pipeline.project.id)).to eq([second_artifact])
+ end
+ end
- expect(described_class.for_sha(first_pipeline.sha, project.id)).to eq([first_artifact])
- expect(described_class.for_sha(second_pipeline.sha, project.id)).to eq([second_artifact])
+ describe '.for_ref' do
+ let(:first_pipeline) { create(:ci_pipeline, ref: 'first_ref') }
+ let(:second_pipeline) { create(:ci_pipeline, ref: 'second_ref', project: first_pipeline.project) }
+ let!(:first_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: first_pipeline)) }
+ let!(:second_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: second_pipeline)) }
+
+ it 'returns job artifacts for a given pipeline ref' do
+ expect(described_class.for_ref(first_pipeline.ref, first_pipeline.project.id)).to eq([first_artifact])
+ expect(described_class.for_ref(second_pipeline.ref, first_pipeline.project.id)).to eq([second_artifact])
end
end
@@ -153,9 +180,9 @@ describe Ci::JobArtifact do
end
describe 'callbacks' do
- subject { create(:ci_job_artifact, :archive) }
-
describe '#schedule_background_upload' do
+ subject { create(:ci_job_artifact, :archive) }
+
context 'when object storage is disabled' do
before do
stub_artifacts_object_storage(enabled: false)
@@ -212,9 +239,35 @@ describe Ci::JobArtifact do
end
end
+ describe 'validates if file format is supported' do
+ subject { artifact }
+
+ let(:artifact) { build(:ci_job_artifact, file_type: :license_management, file_format: :raw) }
+
+ context 'when license_management is supported' do
+ before do
+ stub_feature_flags(drop_license_management_artifact: false)
+ end
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when license_management is not supported' do
+ before do
+ stub_feature_flags(drop_license_management_artifact: true)
+ end
+
+ it { is_expected.not_to be_valid }
+ end
+ end
+
describe 'validates file format' do
subject { artifact }
+ before do
+ stub_feature_flags(drop_license_management_artifact: false)
+ end
+
described_class::TYPE_AND_FORMAT_PAIRS.except(:trace).each do |file_type, file_format|
context "when #{file_type} type with #{file_format} format" do
let(:artifact) { build(:ci_job_artifact, file_type: file_type, file_format: file_format) }
@@ -351,19 +404,6 @@ describe Ci::JobArtifact do
describe 'file is being stored' do
subject { create(:ci_job_artifact, :archive) }
- context 'when object has nil store' do
- before do
- subject.update_column(:file_store, nil)
- subject.reload
- end
-
- it 'is stored locally' do
- expect(subject.file_store).to be(nil)
- expect(subject.file).to be_file_storage
- expect(subject.file.object_store).to eq(ObjectStorage::Store::LOCAL)
- end
- end
-
context 'when existing object has local store' do
it 'is stored locally' do
expect(subject.file_store).to be(ObjectStorage::Store::LOCAL)
diff --git a/spec/models/ci/persistent_ref_spec.rb b/spec/models/ci/persistent_ref_spec.rb
index 4cece0664cf..89dd9b05331 100644
--- a/spec/models/ci/persistent_ref_spec.rb
+++ b/spec/models/ci/persistent_ref_spec.rb
@@ -45,18 +45,6 @@ describe Ci::PersistentRef do
expect(pipeline.persistent_ref).to be_exist
end
- context 'when depend_on_persistent_pipeline_ref feature flag is disabled' do
- before do
- stub_feature_flags(depend_on_persistent_pipeline_ref: false)
- end
-
- it 'does not create a persistent ref' do
- expect(project.repository).not_to receive(:create_ref)
-
- subject
- end
- end
-
context 'when sha does not exist in the repository' do
let(:sha) { 'not-exist' }
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index 4ed4b7e38d8..9a10c7629b2 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -17,14 +17,18 @@ describe Ci::PipelineSchedule do
it { is_expected.to respond_to(:description) }
it { is_expected.to respond_to(:next_run_at) }
+ it_behaves_like 'includes Limitable concern' do
+ subject { build(:ci_pipeline_schedule) }
+ end
+
describe 'validations' do
- it 'does not allow invalid cron patters' do
+ it 'does not allow invalid cron patterns' do
pipeline_schedule = build(:ci_pipeline_schedule, cron: '0 0 0 * *')
expect(pipeline_schedule).not_to be_valid
end
- it 'does not allow invalid cron patters' do
+ it 'does not allow invalid cron patterns' do
pipeline_schedule = build(:ci_pipeline_schedule, cron_timezone: 'invalid')
expect(pipeline_schedule).not_to be_valid
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 90412136c1d..4f53b6b4418 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -53,6 +53,29 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#set_status' do
+ where(:from_status, :to_status) do
+ from_status_names = described_class.state_machines[:status].states.map(&:name)
+ to_status_names = from_status_names - [:created] # we never want to transition into created
+
+ from_status_names.product(to_status_names)
+ end
+
+ with_them do
+ it do
+ pipeline.status = from_status.to_s
+
+ if from_status != to_status
+ expect(pipeline.set_status(to_status.to_s))
+ .to eq(true)
+ else
+ expect(pipeline.set_status(to_status.to_s))
+ .to eq(false), "loopback transitions are not allowed"
+ end
+ end
+ end
+ end
+
describe '.processables' do
before do
create(:ci_build, name: 'build', pipeline: pipeline)
@@ -364,6 +387,26 @@ describe Ci::Pipeline, :mailer do
end
end
+ context 'when pipeline has an accessibility report' do
+ subject { described_class.with_reports(Ci::JobArtifact.accessibility_reports) }
+
+ let(:pipeline_with_report) { create(:ci_pipeline, :with_accessibility_reports) }
+
+ it 'selects the pipeline' do
+ is_expected.to eq([pipeline_with_report])
+ end
+ end
+
+ context 'when pipeline has a terraform report' do
+ it 'selects the pipeline' do
+ pipeline_with_report = create(:ci_pipeline, :with_terraform_reports)
+
+ expect(described_class.with_reports(Ci::JobArtifact.terraform_reports)).to eq(
+ [pipeline_with_report]
+ )
+ end
+ end
+
context 'when pipeline does not have metrics reports' do
subject { described_class.with_reports(Ci::JobArtifact.test_reports) }
@@ -699,6 +742,28 @@ describe Ci::Pipeline, :mailer do
)
end
end
+
+ describe 'variable CI_KUBERNETES_ACTIVE' do
+ context 'when pipeline.has_kubernetes_active? is true' do
+ before do
+ allow(pipeline).to receive(:has_kubernetes_active?).and_return(true)
+ end
+
+ it "is included with value 'true'" do
+ expect(subject.to_hash).to include('CI_KUBERNETES_ACTIVE' => 'true')
+ end
+ end
+
+ context 'when pipeline.has_kubernetes_active? is false' do
+ before do
+ allow(pipeline).to receive(:has_kubernetes_active?).and_return(false)
+ end
+
+ it 'is not included' do
+ expect(subject.to_hash).not_to have_key('CI_KUBERNETES_ACTIVE')
+ end
+ end
+ end
end
describe '#protected_ref?' do
@@ -944,7 +1009,10 @@ describe Ci::Pipeline, :mailer do
context 'when using legacy stages' do
before do
- stub_feature_flags(ci_pipeline_persisted_stages: false)
+ stub_feature_flags(
+ ci_pipeline_persisted_stages: false,
+ ci_atomic_processing: false
+ )
end
it 'returns legacy stages in valid order' do
@@ -952,9 +1020,40 @@ describe Ci::Pipeline, :mailer do
end
end
+ context 'when using atomic processing' do
+ before do
+ stub_feature_flags(
+ ci_atomic_processing: true
+ )
+ end
+
+ context 'when pipelines is not complete' do
+ it 'returns stages in valid order' do
+ expect(subject).to all(be_a Ci::Stage)
+ expect(subject.map(&:name))
+ .to eq %w[sanity build test deploy cleanup]
+ end
+ end
+
+ context 'when pipeline is complete' do
+ before do
+ pipeline.succeed!
+ end
+
+ it 'returns stages in valid order' do
+ expect(subject).to all(be_a Ci::Stage)
+ expect(subject.map(&:name))
+ .to eq %w[sanity build test deploy cleanup]
+ end
+ end
+ end
+
context 'when using persisted stages' do
before do
- stub_feature_flags(ci_pipeline_persisted_stages: true)
+ stub_feature_flags(
+ ci_pipeline_persisted_stages: true,
+ ci_atomic_processing: false
+ )
end
context 'when pipelines is not complete' do
@@ -1119,8 +1218,8 @@ describe Ci::Pipeline, :mailer do
context "from #{status}" do
let(:from_status) { status }
- it 'schedules pipeline success worker' do
- expect(Ci::DailyReportResultsWorker).to receive(:perform_in).with(10.minutes, pipeline.id)
+ it 'schedules daily build group report results worker' do
+ expect(Ci::DailyBuildGroupReportResultsWorker).to receive(:perform_in).with(10.minutes, pipeline.id)
pipeline.succeed
end
@@ -2307,7 +2406,7 @@ describe Ci::Pipeline, :mailer do
def have_requested_pipeline_hook(status)
have_requested(:post, stubbed_hostname(hook.url)).with do |req|
- json_body = JSON.parse(req.body)
+ json_body = Gitlab::Json.parse(req.body)
json_body['object_attributes']['status'] == status &&
json_body['builds'].length == 2
end
@@ -2755,6 +2854,42 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#accessibility_reports' do
+ subject { pipeline.accessibility_reports }
+
+ context 'when pipeline has multiple builds with accessibility reports' do
+ let(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline, project: project) }
+ let(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline, project: project) }
+
+ before do
+ create(:ci_job_artifact, :accessibility, job: build_rspec, project: project)
+ create(:ci_job_artifact, :accessibility_without_errors, job: build_golang, project: project)
+ end
+
+ it 'returns accessibility report with collected data' do
+ expect(subject.urls.keys).to match_array([
+ "https://pa11y.org/",
+ "https://about.gitlab.com/"
+ ])
+ end
+
+ context 'when builds are retried' do
+ let(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline, project: project) }
+ let(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline, project: project) }
+
+ it 'returns empty urls for accessibility reports' do
+ expect(subject.urls).to be_empty
+ end
+ end
+ end
+
+ context 'when pipeline does not have any builds with accessibility reports' do
+ it 'returns empty urls for accessibility reports' do
+ expect(subject.urls).to be_empty
+ end
+ end
+ end
+
describe '#coverage_reports' do
subject { pipeline.coverage_reports }
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index 4490371bde5..e67f740279b 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -6,16 +6,12 @@ describe Ci::Processable do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
- let_it_be(:detached_merge_request_pipeline) do
- create(:ci_pipeline, :detached_merge_request_pipeline, :with_job, project: project)
- end
-
- let_it_be(:legacy_detached_merge_request_pipeline) do
- create(:ci_pipeline, :legacy_detached_merge_request_pipeline, :with_job, project: project)
- end
+ describe 'delegations' do
+ subject { Ci::Processable.new }
- let_it_be(:merged_result_pipeline) do
- create(:ci_pipeline, :merged_result_pipeline, :with_job, project: project)
+ it { is_expected.to delegate_method(:merge_request?).to(:pipeline) }
+ it { is_expected.to delegate_method(:merge_request_ref?).to(:pipeline) }
+ it { is_expected.to delegate_method(:legacy_detached_merge_request_pipeline?).to(:pipeline) }
end
describe '#aggregated_needs_names' do
@@ -52,69 +48,28 @@ describe Ci::Processable do
end
describe 'validate presence of scheduling_type' do
- context 'on create' do
- let(:processable) do
- build(
- :ci_build, :created, project: project, pipeline: pipeline,
- importing: importing, scheduling_type: nil
- )
- end
-
- context 'when importing' do
- let(:importing) { true }
-
- context 'when validate_scheduling_type_of_processables is true' do
- before do
- stub_feature_flags(validate_scheduling_type_of_processables: true)
- end
+ using RSpec::Parameterized::TableSyntax
- it 'does not validate' do
- expect(processable).to be_valid
- end
- end
-
- context 'when validate_scheduling_type_of_processables is false' do
- before do
- stub_feature_flags(validate_scheduling_type_of_processables: false)
- end
-
- it 'does not validate' do
- expect(processable).to be_valid
- end
- end
- end
+ subject { build(:ci_build, project: project, pipeline: pipeline, importing: importing) }
- context 'when not importing' do
- let(:importing) { false }
-
- context 'when validate_scheduling_type_of_processables is true' do
- before do
- stub_feature_flags(validate_scheduling_type_of_processables: true)
- end
-
- it 'validates' do
- expect(processable).not_to be_valid
- end
- end
-
- context 'when validate_scheduling_type_of_processables is false' do
- before do
- stub_feature_flags(validate_scheduling_type_of_processables: false)
- end
+ where(:importing, :should_validate) do
+ false | true
+ true | false
+ end
- it 'does not validate' do
- expect(processable).to be_valid
+ with_them do
+ context 'on create' do
+ it 'validates presence' do
+ if should_validate
+ is_expected.to validate_presence_of(:scheduling_type).on(:create)
+ else
+ is_expected.not_to validate_presence_of(:scheduling_type).on(:create)
end
end
end
- end
-
- context 'on update' do
- let(:processable) { create(:ci_build, :created, project: project, pipeline: pipeline) }
- it 'does not validate' do
- processable.scheduling_type = nil
- expect(processable).to be_valid
+ context 'on update' do
+ it { is_expected.not_to validate_presence_of(:scheduling_type).on(:update) }
end
end
end
@@ -147,6 +102,8 @@ describe Ci::Processable do
describe '#needs_attributes' do
let(:build) { create(:ci_build, :created, project: project, pipeline: pipeline) }
+ subject { build.needs_attributes }
+
context 'with needs' do
before do
create(:ci_build_need, build: build, name: 'test1')
@@ -154,7 +111,7 @@ describe Ci::Processable do
end
it 'returns all needs attributes' do
- expect(build.needs_attributes).to contain_exactly(
+ is_expected.to contain_exactly(
{ 'artifacts' => true, 'name' => 'test1' },
{ 'artifacts' => true, 'name' => 'test2' }
)
@@ -162,75 +119,7 @@ describe Ci::Processable do
end
context 'without needs' do
- it 'returns all needs attributes' do
- expect(build.needs_attributes).to be_empty
- end
- end
- end
-
- describe '#merge_request?' do
- subject { pipeline.processables.first.merge_request? }
-
- context 'in a detached merge request pipeline' do
- let(:pipeline) { detached_merge_request_pipeline }
-
- it { is_expected.to eq(pipeline.merge_request?) }
- end
-
- context 'in a legacy detached merge_request_pipeline' do
- let(:pipeline) { legacy_detached_merge_request_pipeline }
-
- it { is_expected.to eq(pipeline.merge_request?) }
- end
-
- context 'in a pipeline for merged results' do
- let(:pipeline) { merged_result_pipeline }
-
- it { is_expected.to eq(pipeline.merge_request?) }
- end
- end
-
- describe '#merge_request_ref?' do
- subject { pipeline.processables.first.merge_request_ref? }
-
- context 'in a detached merge request pipeline' do
- let(:pipeline) { detached_merge_request_pipeline }
-
- it { is_expected.to eq(pipeline.merge_request_ref?) }
- end
-
- context 'in a legacy detached merge_request_pipeline' do
- let(:pipeline) { legacy_detached_merge_request_pipeline }
-
- it { is_expected.to eq(pipeline.merge_request_ref?) }
- end
-
- context 'in a pipeline for merged results' do
- let(:pipeline) { merged_result_pipeline }
-
- it { is_expected.to eq(pipeline.merge_request_ref?) }
- end
- end
-
- describe '#legacy_detached_merge_request_pipeline?' do
- subject { pipeline.processables.first.legacy_detached_merge_request_pipeline? }
-
- context 'in a detached merge request pipeline' do
- let(:pipeline) { detached_merge_request_pipeline }
-
- it { is_expected.to eq(pipeline.legacy_detached_merge_request_pipeline?) }
- end
-
- context 'in a legacy detached merge_request_pipeline' do
- let(:pipeline) { legacy_detached_merge_request_pipeline }
-
- it { is_expected.to eq(pipeline.legacy_detached_merge_request_pipeline?) }
- end
-
- context 'in a pipeline for merged results' do
- let(:pipeline) { merged_result_pipeline }
-
- it { is_expected.to eq(pipeline.legacy_detached_merge_request_pipeline?) }
+ it { is_expected.to be_empty }
end
end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 2dedff7f15b..8b6a4fa6ade 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -270,7 +270,7 @@ describe Ci::Runner do
it { is_expected.to eq([@runner2])}
end
- describe '#online?' do
+ describe '#online?', :clean_gitlab_redis_cache do
let(:runner) { create(:ci_runner, :instance) }
subject { runner.online? }
@@ -332,7 +332,7 @@ describe Ci::Runner do
end
def stub_redis_runner_contacted_at(value)
- Gitlab::Redis::SharedState.with do |redis|
+ Gitlab::Redis::Cache.with do |redis|
cache_key = runner.send(:cache_attribute_key)
expect(redis).to receive(:get).with(cache_key)
.and_return({ contacted_at: value }.to_json).at_least(:once)
@@ -640,7 +640,7 @@ describe Ci::Runner do
end
def expect_redis_update
- Gitlab::Redis::SharedState.with do |redis|
+ Gitlab::Redis::Cache.with do |redis|
redis_key = runner.send(:cache_attribute_key)
expect(redis).to receive(:set).with(redis_key, anything, any_args)
end
@@ -664,7 +664,7 @@ describe Ci::Runner do
end
it 'cleans up the queue' do
- Gitlab::Redis::SharedState.with do |redis|
+ Gitlab::Redis::Cache.with do |redis|
expect(redis.get(queue_key)).to be_nil
end
end
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index 3aeaa27abce..a1549532559 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
describe Ci::Stage, :models do
- let(:stage) { create(:ci_stage_entity) }
+ let_it_be(:pipeline) { create(:ci_empty_pipeline) }
+ let(:stage) { create(:ci_stage_entity, pipeline: pipeline, project: pipeline.project) }
it_behaves_like 'having unique enum values'
@@ -55,6 +56,29 @@ describe Ci::Stage, :models do
end
end
+ describe '#set_status' do
+ where(:from_status, :to_status) do
+ from_status_names = described_class.state_machines[:status].states.map(&:name)
+ to_status_names = from_status_names - [:created] # we never want to transition into created
+
+ from_status_names.product(to_status_names)
+ end
+
+ with_them do
+ it do
+ stage.status = from_status.to_s
+
+ if from_status != to_status
+ expect(stage.set_status(to_status.to_s))
+ .to eq(true)
+ else
+ expect(stage.set_status(to_status.to_s))
+ .to eq(false), "loopback transitions are not allowed"
+ end
+ end
+ end
+ end
+
describe '#update_status' do
context 'when stage objects needs to be updated' do
before do