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:
Diffstat (limited to 'spec/models/ci/build_spec.rb')
-rw-r--r--spec/models/ci/build_spec.rb353
1 files changed, 295 insertions, 58 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