diff options
Diffstat (limited to 'spec/models/ci')
40 files changed, 639 insertions, 130 deletions
diff --git a/spec/models/ci/artifact_blob_spec.rb b/spec/models/ci/artifact_blob_spec.rb index 99983686670..44f895cc1c5 100644 --- a/spec/models/ci/artifact_blob_spec.rb +++ b/spec/models/ci/artifact_blob_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::ArtifactBlob do +RSpec.describe Ci::ArtifactBlob do let_it_be(:project) { create(:project, :public) } let_it_be(:build) { create(:ci_build, :artifacts, project: project) } let(:entry) { build.artifacts_metadata_entry('other_artifacts_0.1.2/another-subdirectory/banana_sample.gif') } diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb index 385261e0ee9..3a459e5897a 100644 --- a/spec/models/ci/bridge_spec.rb +++ b/spec/models/ci/bridge_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Bridge do +RSpec.describe Ci::Bridge do let_it_be(:project) { create(:project) } let_it_be(:target_project) { create(:project, name: 'project', namespace: create(:namespace, name: 'my')) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } @@ -47,8 +47,8 @@ describe Ci::Bridge do CI_JOB_NAME CI_JOB_STAGE CI_COMMIT_SHA CI_COMMIT_SHORT_SHA CI_COMMIT_BEFORE_SHA CI_COMMIT_REF_NAME CI_COMMIT_REF_SLUG CI_PROJECT_ID CI_PROJECT_NAME CI_PROJECT_PATH - CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PIPELINE_IID - CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE + CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PROJECT_ROOT_NAMESPACE + CI_PIPELINE_IID CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION CI_COMMIT_REF_PROTECTED ] diff --git a/spec/models/ci/build_dependencies_spec.rb b/spec/models/ci/build_dependencies_spec.rb index 8f2199ac360..4fa1b3eb5a5 100644 --- a/spec/models/ci/build_dependencies_spec.rb +++ b/spec/models/ci/build_dependencies_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildDependencies do +RSpec.describe Ci::BuildDependencies do let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project, :repository) } diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb index 588e5872cc8..e4d71632957 100644 --- a/spec/models/ci/build_metadata_spec.rb +++ b/spec/models/ci/build_metadata_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildMetadata do +RSpec.describe Ci::BuildMetadata do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, :repository, group: group, build_timeout: 2000) } @@ -92,4 +92,33 @@ describe Ci::BuildMetadata do end end end + + describe 'validations' do + context 'when attributes are valid' do + it 'returns no errors' do + metadata.secrets = { + DATABASE_PASSWORD: { + vault: { + engine: { name: 'kv-v2', path: 'kv-v2' }, + path: 'production/db', + field: 'password' + } + } + } + + expect(metadata).to be_valid + end + end + + context 'when data is invalid' do + it 'returns errors' do + metadata.secrets = { DATABASE_PASSWORD: { vault: {} } } + + aggregate_failures do + expect(metadata).to be_invalid + expect(metadata.errors.full_messages).to eq(["Secrets must be a valid json schema"]) + end + end + end + end end diff --git a/spec/models/ci/build_need_spec.rb b/spec/models/ci/build_need_spec.rb index d1186fa981d..43cce073918 100644 --- a/spec/models/ci/build_need_spec.rb +++ b/spec/models/ci/build_need_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildNeed, model: true do +RSpec.describe Ci::BuildNeed, model: true do let(:build_need) { build(:ci_build_need) } it { is_expected.to belong_to(:build) } @@ -17,4 +17,22 @@ describe Ci::BuildNeed, model: true do it { expect(described_class.artifacts).to contain_exactly(with_artifacts) } end + + describe 'BulkInsertSafe' do + let(:ci_build) { build(:ci_build) } + + it "bulk inserts from Ci::Build model" do + ci_build.needs_attributes = [ + { name: "build", artifacts: true }, + { name: "build2", artifacts: true }, + { name: "build3", artifacts: true } + ] + + expect(described_class).to receive(:bulk_insert!).and_call_original + + BulkInsertableAssociations.with_bulk_insert do + ci_build.save! + end + end + end end diff --git a/spec/models/ci/build_report_result_spec.rb b/spec/models/ci/build_report_result_spec.rb index 078b0d100a1..e78f602feef 100644 --- a/spec/models/ci/build_report_result_spec.rb +++ b/spec/models/ci/build_report_result_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildReportResult do +RSpec.describe Ci::BuildReportResult do let(:build_report_result) { build(:ci_build_report_result, :with_junit_success) } describe 'associations' do diff --git a/spec/models/ci/build_runner_session_spec.rb b/spec/models/ci/build_runner_session_spec.rb index 3e520407884..601c6ad26f9 100644 --- a/spec/models/ci/build_runner_session_spec.rb +++ b/spec/models/ci/build_runner_session_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildRunnerSession, model: true do +RSpec.describe Ci::BuildRunnerSession, model: true do let!(:build) { create(:ci_build, :with_runner_session) } let(:url) { 'https://new.example.com' } diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 6fdd8463329..857b238981b 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Build do +RSpec.describe Ci::Build do let_it_be(:user) { create(:user) } let_it_be(:group, reload: true) { create(:group) } let_it_be(:project, reload: true) { create(:project, :repository, group: group) } @@ -1811,6 +1811,50 @@ describe Ci::Build do end end + describe '.keep_artifacts!' do + let!(:build) { create(:ci_build, artifacts_expire_at: Time.current + 7.days) } + let!(:builds_for_update) do + Ci::Build.where(id: create_list(:ci_build, 3, artifacts_expire_at: Time.current + 7.days).map(&:id)) + end + + it 'resets expire_at' do + builds_for_update.keep_artifacts! + + builds_for_update.each do |build| + expect(build.reload.artifacts_expire_at).to be_nil + end + end + + it 'does not reset expire_at for other builds' do + builds_for_update.keep_artifacts! + + expect(build.reload.artifacts_expire_at).to be_present + end + + context 'when having artifacts files' do + let!(:artifact) { create(:ci_job_artifact, job: build, expire_in: '7 days') } + let!(:artifacts_for_update) do + builds_for_update.map do |build| + create(:ci_job_artifact, job: build, expire_in: '7 days') + end + end + + it 'resets dependent objects' do + builds_for_update.keep_artifacts! + + artifacts_for_update.each do |artifact| + expect(artifact.reload.expire_at).to be_nil + end + end + + it 'does not reset dependent object for other builds' do + builds_for_update.keep_artifacts! + + expect(artifact.reload.expire_at).to be_present + end + end + end + describe '#keep_artifacts!' do let(:build) { create(:ci_build, artifacts_expire_at: Time.current + 7.days) } @@ -2336,6 +2380,7 @@ describe Ci::Build do { key: 'CI_PROJECT_PATH', value: project.full_path, public: true, masked: false }, { key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true, masked: false }, { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true, masked: false }, + { key: 'CI_PROJECT_ROOT_NAMESPACE', value: project.namespace.root_ancestor.path, public: true, masked: false }, { key: 'CI_PROJECT_URL', value: project.web_url, public: true, masked: false }, { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true, masked: false }, { key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: project.repository_languages.map(&:name).join(',').downcase, public: true, masked: false }, @@ -2929,19 +2974,6 @@ 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 } } @@ -3269,17 +3301,6 @@ describe Ci::Build do 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 @@ -4050,6 +4071,10 @@ describe Ci::Build do it 'parses blobs and add the results to the terraform report' do expect { build.collect_terraform_reports!(terraform_reports) }.not_to raise_error + terraform_reports.plans.each do |key, hash_value| + expect(hash_value.keys).to match_array(%w[create delete job_id job_name job_path update]) + end + expect(terraform_reports.plans).to match( a_hash_including( build.id.to_s => a_hash_including( @@ -4068,9 +4093,19 @@ describe Ci::Build 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 + it 'adds invalid plan report' do + expect { build.collect_terraform_reports!(terraform_reports) }.not_to raise_error + + terraform_reports.plans.each do |key, hash_value| + expect(hash_value.keys).to match_array(%w[job_id job_name job_path tf_report_error]) + end + + expect(terraform_reports.plans).to match( + a_hash_including( + build.id.to_s => a_hash_including( + 'tf_report_error' => :invalid_json_format + ) + ) ) end end @@ -4258,15 +4293,15 @@ describe Ci::Build do end end - context 'when `release_steps` feature is required by build' do + context 'when `multi_build_steps` feature is required by build' do before do expect(build).to receive(:runner_required_feature_names) do - [:release_steps] + [:multi_build_steps] end end context 'when runner provides given feature' do - let(:runner_features) { { release_steps: true } } + let(:runner_features) { { multi_build_steps: true } } it { is_expected.to be_truthy } end diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 85873847fca..dab523f67ab 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do +RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do include ExclusiveLeaseHelpers let_it_be(:build) { create(:ci_build, :running) } diff --git a/spec/models/ci/build_trace_chunks/database_spec.rb b/spec/models/ci/build_trace_chunks/database_spec.rb index eb94d7dae38..245625b8046 100644 --- a/spec/models/ci/build_trace_chunks/database_spec.rb +++ b/spec/models/ci/build_trace_chunks/database_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceChunks::Database do +RSpec.describe Ci::BuildTraceChunks::Database do let(:data_store) { described_class.new } describe '#available?' do diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb index b8d78bcd069..7ef3018d87b 100644 --- a/spec/models/ci/build_trace_chunks/fog_spec.rb +++ b/spec/models/ci/build_trace_chunks/fog_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceChunks::Fog do +RSpec.describe Ci::BuildTraceChunks::Fog do let(:data_store) { described_class.new } before do diff --git a/spec/models/ci/build_trace_chunks/redis_spec.rb b/spec/models/ci/build_trace_chunks/redis_spec.rb index 6cff33d24fa..c37b8697a4d 100644 --- a/spec/models/ci/build_trace_chunks/redis_spec.rb +++ b/spec/models/ci/build_trace_chunks/redis_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do +RSpec.describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do let(:data_store) { described_class.new } describe '#available?' do diff --git a/spec/models/ci/build_trace_section_name_spec.rb b/spec/models/ci/build_trace_section_name_spec.rb index 11e2d27ff79..b220e67d48e 100644 --- a/spec/models/ci/build_trace_section_name_spec.rb +++ b/spec/models/ci/build_trace_section_name_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceSectionName, model: true do +RSpec.describe Ci::BuildTraceSectionName, model: true do subject { build(:ci_build_trace_section_name) } it { is_expected.to belong_to(:project) } diff --git a/spec/models/ci/build_trace_section_spec.rb b/spec/models/ci/build_trace_section_spec.rb index 5bd3a953ec0..640bd202b3a 100644 --- a/spec/models/ci/build_trace_section_spec.rb +++ b/spec/models/ci/build_trace_section_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTraceSection, model: true do +RSpec.describe Ci::BuildTraceSection, model: true do it { is_expected.to belong_to(:build)} it { is_expected.to belong_to(:project)} it { is_expected.to belong_to(:section_name)} diff --git a/spec/models/ci/build_trace_spec.rb b/spec/models/ci/build_trace_spec.rb index 2471a6fa827..3beca0565c6 100644 --- a/spec/models/ci/build_trace_spec.rb +++ b/spec/models/ci/build_trace_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::BuildTrace do +RSpec.describe Ci::BuildTrace do let(:build) { build_stubbed(:ci_build) } let(:state) { nil } let(:data) { StringIO.new('the-stream') } @@ -11,7 +11,7 @@ describe Ci::BuildTrace do Gitlab::Ci::Trace::Stream.new { data } end - subject { described_class.new(build: build, stream: stream, state: state, content_format: content_format) } + subject { described_class.new(build: build, stream: stream, state: state) } shared_examples 'delegates methods' do it { is_expected.to delegate_method(:state).to(:trace) } @@ -25,29 +25,11 @@ describe Ci::BuildTrace do it { is_expected.to delegate_method(:complete?).to(:build).with_prefix } end - context 'with :json content format' do - let(:content_format) { :json } + it_behaves_like 'delegates methods' - it_behaves_like 'delegates methods' - - it { is_expected.to be_json } - - it 'returns formatted trace' do - expect(subject.trace.lines).to eq([ - { offset: 0, content: [{ text: 'the-stream' }] } - ]) - end - end - - context 'with :html content format' do - let(:content_format) { :html } - - it_behaves_like 'delegates methods' - - it { is_expected.to be_html } - - it 'returns formatted trace' do - expect(subject.trace.html).to eq('<span>the-stream</span>') - end + it 'returns formatted trace' do + expect(subject.lines).to eq([ + { offset: 0, content: [{ text: 'the-stream' }] } + ]) end end diff --git a/spec/models/ci/daily_build_group_report_result_spec.rb b/spec/models/ci/daily_build_group_report_result_spec.rb index f2ce1b5775f..059a5b76b9a 100644 --- a/spec/models/ci/daily_build_group_report_result_spec.rb +++ b/spec/models/ci/daily_build_group_report_result_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::DailyBuildGroupReportResult do +RSpec.describe Ci::DailyBuildGroupReportResult do let(:daily_build_group_report_result) { build(:ci_daily_build_group_report_result)} describe 'associations' do diff --git a/spec/models/ci/freeze_period_status_spec.rb b/spec/models/ci/freeze_period_status_spec.rb index b700ec8c45f..831895cb528 100644 --- a/spec/models/ci/freeze_period_status_spec.rb +++ b/spec/models/ci/freeze_period_status_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -describe Ci::FreezePeriodStatus do +RSpec.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' } diff --git a/spec/models/ci/group_spec.rb b/spec/models/ci/group_spec.rb index 868382e3756..dc9aee906ea 100644 --- a/spec/models/ci/group_spec.rb +++ b/spec/models/ci/group_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Group do +RSpec.describe Ci::Group do let_it_be(:project) { create(:project) } let!(:jobs) { build_list(:ci_build, 1, :success, project: project) } diff --git a/spec/models/ci/group_variable_spec.rb b/spec/models/ci/group_variable_spec.rb index 610db9bf0e5..c8eac4d8765 100644 --- a/spec/models/ci/group_variable_spec.rb +++ b/spec/models/ci/group_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::GroupVariable do +RSpec.describe Ci::GroupVariable do subject { build(:ci_group_variable) } it_behaves_like "CI variable" diff --git a/spec/models/ci/instance_variable_spec.rb b/spec/models/ci/instance_variable_spec.rb index 4d69b7ac2f8..344ba5bfafd 100644 --- a/spec/models/ci/instance_variable_spec.rb +++ b/spec/models/ci/instance_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::InstanceVariable do +RSpec.describe Ci::InstanceVariable do subject { build(:ci_instance_variable) } it_behaves_like "CI variable" @@ -15,21 +15,6 @@ describe Ci::InstanceVariable do subject { build(:ci_instance_variable) } end - context 'with instance level variable feature flag disabled' do - let(:plan_limits) { create(:plan_limits, :default_plan) } - - before do - stub_feature_flags(ci_instance_level_variables_limit: false) - plan_limits.update(described_class.limit_name => 1) - create(:ci_instance_variable) - end - - it 'can create new models exceeding the plan limits', :aggregate_failures do - expect { subject.save }.to change { described_class.count } - expect(subject.errors[:base]).to be_empty - end - end - describe '.unprotected' do subject { described_class.unprotected } diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index 17e00533ac3..b5f9128b7c5 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::JobArtifact do +RSpec.describe Ci::JobArtifact do let(:artifact) { create(:ci_job_artifact, :archive) } describe "Associations" do @@ -110,6 +110,21 @@ describe Ci::JobArtifact do end end + describe '.associated_file_types_for' do + using RSpec::Parameterized::TableSyntax + + subject { Ci::JobArtifact.associated_file_types_for(file_type) } + + where(:file_type, :result) do + 'codequality' | %w(codequality) + 'quality' | nil + end + + with_them do + it { is_expected.to eq result } + end + end + describe '.erasable' do subject { described_class.erasable } @@ -174,18 +189,6 @@ describe Ci::JobArtifact do end end - 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 - describe '.for_job_name' do it 'returns job artifacts for a given job name' do first_job = create(:ci_build, name: 'first') @@ -501,4 +504,100 @@ describe Ci::JobArtifact do end end end + + describe '.file_types' do + context 'all file types have corresponding limit' do + let_it_be(:plan_limits) { create(:plan_limits) } + + where(:file_type) do + described_class.file_types.keys + end + + with_them do + let(:limit_name) { "#{described_class::PLAN_LIMIT_PREFIX}#{file_type}" } + + it { expect(plan_limits.attributes).to include(limit_name), file_type_limit_failure_message(file_type, limit_name) } + end + end + end + + describe '.max_artifact_size' do + let(:build) { create(:ci_build) } + + subject(:max_size) { described_class.max_artifact_size(type: artifact_type, project: build.project) } + + context 'when file type is supported' do + let(:project_closest_setting) { 1024 } + let(:artifact_type) { 'junit' } + + before do + stub_feature_flags(ci_max_artifact_size_per_type: flag_enabled) + allow(build.project).to receive(:closest_setting).with(:max_artifacts_size).and_return(project_closest_setting) + end + + shared_examples_for 'basing off the project closest setting' do + it { is_expected.to eq(project_closest_setting.megabytes.to_i) } + end + + shared_examples_for 'basing off the plan limit' do + it { is_expected.to eq(max_size_for_type.megabytes.to_i) } + end + + context 'and feature flag for custom max size per type is enabled' do + let(:flag_enabled) { true } + let(:limit_name) { "#{described_class::PLAN_LIMIT_PREFIX}#{artifact_type}" } + + let!(:plan_limits) { create(:plan_limits, :default_plan) } + + context 'and plan limit is disabled for the given artifact type' do + before do + plan_limits.update!(limit_name => 0) + end + + it_behaves_like 'basing off the project closest setting' + + context 'and project closest setting results to zero' do + let(:project_closest_setting) { 0 } + + it { is_expected.to eq(0) } + end + end + + context 'and plan limit is enabled for the given artifact type' do + before do + plan_limits.update!(limit_name => max_size_for_type) + end + + context 'and plan limit is smaller than project setting' do + let(:max_size_for_type) { project_closest_setting - 1 } + + it_behaves_like 'basing off the plan limit' + end + + context 'and plan limit is smaller than project setting' do + let(:max_size_for_type) { project_closest_setting + 1 } + + it_behaves_like 'basing off the project closest setting' + end + end + end + + context 'and feature flag for custom max size per type is disabled' do + let(:flag_enabled) { false } + + it_behaves_like 'basing off the project closest setting' + end + end + end + + def file_type_limit_failure_message(type, limit_name) + <<~MSG + The artifact type `#{type}` is missing its counterpart plan limit which is expected to be named `#{limit_name}`. + + Please refer to https://docs.gitlab.com/ee/development/application_limits.html on how to add new plan limit columns. + + Take note that while existing max size plan limits default to 0, succeeding new limits are recommended to have + non-zero default values. + MSG + end end diff --git a/spec/models/ci/job_variable_spec.rb b/spec/models/ci/job_variable_spec.rb index b94a914c784..4aebd3283f0 100644 --- a/spec/models/ci/job_variable_spec.rb +++ b/spec/models/ci/job_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::JobVariable do +RSpec.describe Ci::JobVariable do subject { build(:ci_job_variable) } it_behaves_like "CI variable" diff --git a/spec/models/ci/legacy_stage_spec.rb b/spec/models/ci/legacy_stage_spec.rb index f503fc10c08..c53f6abb037 100644 --- a/spec/models/ci/legacy_stage_spec.rb +++ b/spec/models/ci/legacy_stage_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::LegacyStage do +RSpec.describe Ci::LegacyStage do let(:stage) { build(:ci_stage) } let(:pipeline) { stage.pipeline } let(:stage_name) { stage.name } diff --git a/spec/models/ci/persistent_ref_spec.rb b/spec/models/ci/persistent_ref_spec.rb index 89dd9b05331..18552317025 100644 --- a/spec/models/ci/persistent_ref_spec.rb +++ b/spec/models/ci/persistent_ref_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::PersistentRef do +RSpec.describe Ci::PersistentRef do it 'cleans up persistent refs after pipeline finished' do pipeline = create(:ci_pipeline, :running) diff --git a/spec/models/ci/pipeline_config_spec.rb b/spec/models/ci/pipeline_config_spec.rb index 25f514ee5ab..3d033d33df3 100644 --- a/spec/models/ci/pipeline_config_spec.rb +++ b/spec/models/ci/pipeline_config_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::PipelineConfig, type: :model do +RSpec.describe Ci::PipelineConfig, type: :model do it { is_expected.to belong_to(:pipeline) } it { is_expected.to validate_presence_of(:pipeline) } diff --git a/spec/models/ci/pipeline_message_spec.rb b/spec/models/ci/pipeline_message_spec.rb new file mode 100644 index 00000000000..6c97a025625 --- /dev/null +++ b/spec/models/ci/pipeline_message_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::PipelineMessage do + describe 'validations' do + subject { described_class.new(pipeline: pipeline, content: content) } + + let(:pipeline) { create(:ci_pipeline) } + + context 'when message content is longer than the limit' do + let(:content) { 'x' * (described_class::MAX_CONTENT_LENGTH + 1) } + + it 'is truncated with ellipsis' do + subject.save! + + expect(subject.content).to end_with('x...') + expect(subject.content.length).to eq(described_class::MAX_CONTENT_LENGTH) + end + end + + context 'when message is not present' do + let(:content) { '' } + + it 'returns an error' do + expect(subject.save).to be_falsey + expect(subject.errors[:content]).to be_present + end + end + + context 'when message content is valid' do + let(:content) { 'valid message content' } + + it 'is saved with default error severity' do + subject.save! + + expect(subject.content).to eq(content) + expect(subject.severity).to eq('error') + expect(subject).to be_error + end + + it 'is persist the defined severity' do + subject.severity = :warning + + subject.save! + + expect(subject.content).to eq(content) + expect(subject.severity).to eq('warning') + expect(subject).to be_warning + end + end + end +end diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb index 4ba70552f01..949d5f7bd04 100644 --- a/spec/models/ci/pipeline_schedule_spec.rb +++ b/spec/models/ci/pipeline_schedule_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::PipelineSchedule do +RSpec.describe Ci::PipelineSchedule do subject { build(:ci_pipeline_schedule) } it { is_expected.to belong_to(:project) } diff --git a/spec/models/ci/pipeline_schedule_variable_spec.rb b/spec/models/ci/pipeline_schedule_variable_spec.rb index c96a24d5042..fd6b1d3dce0 100644 --- a/spec/models/ci/pipeline_schedule_variable_spec.rb +++ b/spec/models/ci/pipeline_schedule_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::PipelineScheduleVariable do +RSpec.describe Ci::PipelineScheduleVariable do subject { build(:ci_pipeline_schedule_variable) } it_behaves_like "CI variable" diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 782a4206c36..ed2466d6413 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Pipeline, :mailer do +RSpec.describe Ci::Pipeline, :mailer do include ProjectForksHelper include StubRequests @@ -219,6 +219,50 @@ describe Ci::Pipeline, :mailer do end end + describe '.outside_pipeline_family' do + subject(:outside_pipeline_family) { described_class.outside_pipeline_family(upstream_pipeline) } + + let(:upstream_pipeline) { create(:ci_pipeline, project: project) } + let(:child_pipeline) { create(:ci_pipeline, project: project) } + + let!(:other_pipeline) { create(:ci_pipeline, project: project) } + + before do + create(:ci_sources_pipeline, + source_job: create(:ci_build, pipeline: upstream_pipeline), + source_project: project, + pipeline: child_pipeline, + project: project) + end + + it 'only returns pipelines outside pipeline family' do + expect(outside_pipeline_family).to contain_exactly(other_pipeline) + end + end + + describe '.before_pipeline' do + subject(:before_pipeline) { described_class.before_pipeline(child_pipeline) } + + let!(:older_other_pipeline) { create(:ci_pipeline, project: project) } + + let!(:upstream_pipeline) { create(:ci_pipeline, project: project) } + let!(:child_pipeline) { create(:ci_pipeline, project: project) } + + let!(:other_pipeline) { create(:ci_pipeline, project: project) } + + before do + create(:ci_sources_pipeline, + source_job: create(:ci_build, pipeline: upstream_pipeline), + source_project: project, + pipeline: child_pipeline, + project: project) + end + + it 'only returns older pipelines outside pipeline family' do + expect(before_pipeline).to contain_exactly(older_other_pipeline) + end + end + describe '#merge_request?' do let(:pipeline) { create(:ci_pipeline, merge_request: merge_request) } let(:merge_request) { create(:merge_request) } @@ -1488,6 +1532,35 @@ describe Ci::Pipeline, :mailer do sha: project.commit.sha) end + describe '#lazy_ref_commit' do + let(:another) do + create(:ci_pipeline, + project: project, + ref: 'feature', + sha: project.commit('feature').sha) + end + + let(:unicode) do + create(:ci_pipeline, + project: project, + ref: 'ΓΌ/unicode/multi-byte') + end + + it 'returns the latest commit for a ref lazily' do + expect(project.repository) + .to receive(:list_commits_by_ref_name).once + .and_call_original + + pipeline.lazy_ref_commit + another.lazy_ref_commit + unicode.lazy_ref_commit + + expect(pipeline.lazy_ref_commit.id).to eq pipeline.sha + expect(another.lazy_ref_commit.id).to eq another.sha + expect(unicode.lazy_ref_commit).to be_nil + end + end + describe '#latest?' do context 'with latest sha' do it 'returns true' do @@ -1496,17 +1569,26 @@ describe Ci::Pipeline, :mailer do end context 'with a branch name as the ref' do - it 'looks up commit with the full ref name' do - expect(pipeline.project).to receive(:commit).with('refs/heads/master').and_call_original + it 'looks up a commit for a branch' do + expect(pipeline.ref).to eq 'master' + expect(pipeline).to be_latest + end + end + + context 'with a tag name as a ref' do + it 'looks up a commit for a tag' do + expect(project.repository.branch_names).not_to include 'v1.0.0' + pipeline.update(sha: project.commit('v1.0.0').sha, ref: 'v1.0.0', tag: true) + + expect(pipeline).to be_tag expect(pipeline).to be_latest end end context 'with not latest sha' do before do - pipeline.update( - sha: project.commit("#{project.default_branch}~1").sha) + pipeline.update(sha: project.commit("#{project.default_branch}~1").sha) end it 'returns false' do @@ -1932,6 +2014,23 @@ describe Ci::Pipeline, :mailer do end end + describe '.last_finished_for_ref_id' do + let(:project) { create(:project, :repository) } + let(:branch) { project.default_branch } + let(:ref) { project.ci_refs.take } + let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] } + let!(:pipeline1) { create(:ci_pipeline, :success, project: project, ref: branch) } + let!(:pipeline2) { create(:ci_pipeline, :success, project: project, ref: branch) } + let!(:pipeline3) { create(:ci_pipeline, :failed, project: project, ref: branch) } + let!(:pipeline4) { create(:ci_pipeline, :success, project: project, ref: branch) } + let!(:pipeline5) { create(:ci_pipeline, :success, project: project, ref: branch, config_source: config_source) } + + it 'returns the expected pipeline' do + result = described_class.last_finished_for_ref_id(ref.id) + expect(result).to eq(pipeline4) + end + end + describe '.internal_sources' do subject { described_class.internal_sources } @@ -2087,7 +2186,7 @@ describe Ci::Pipeline, :mailer do it 'raises an exception' do expect { pipeline.update_legacy_status } - .to raise_error(HasStatus::UnknownStatusError) + .to raise_error(Ci::HasStatus::UnknownStatusError) end end end @@ -2580,6 +2679,55 @@ describe Ci::Pipeline, :mailer do end end + describe '#same_family_pipeline_ids' do + subject(:same_family_pipeline_ids) { pipeline.same_family_pipeline_ids } + + context 'when pipeline is not child nor parent' do + it 'returns just the pipeline id' do + expect(same_family_pipeline_ids).to contain_exactly(pipeline.id) + end + end + + context 'when pipeline is child' do + let(:parent) { create(:ci_pipeline, project: pipeline.project) } + let(:sibling) { create(:ci_pipeline, project: pipeline.project) } + + before do + create(:ci_sources_pipeline, + source_job: create(:ci_build, pipeline: parent), + source_project: parent.project, + pipeline: pipeline, + project: pipeline.project) + + create(:ci_sources_pipeline, + source_job: create(:ci_build, pipeline: parent), + source_project: parent.project, + pipeline: sibling, + project: sibling.project) + end + + it 'returns parent sibling and self ids' do + expect(same_family_pipeline_ids).to contain_exactly(parent.id, pipeline.id, sibling.id) + end + end + + context 'when pipeline is parent' do + let(:child) { create(:ci_pipeline, project: pipeline.project) } + + before do + create(:ci_sources_pipeline, + source_job: create(:ci_build, pipeline: pipeline), + source_project: pipeline.project, + pipeline: child, + project: child.project) + end + + it 'returns self and child ids' do + expect(same_family_pipeline_ids).to contain_exactly(pipeline.id, child.id) + end + end + end + describe '#stuck?' do before do create(:ci_build, :pending, pipeline: pipeline) @@ -2602,6 +2750,28 @@ describe Ci::Pipeline, :mailer do end end + describe '#add_error_message' do + let(:pipeline) { build_stubbed(:ci_pipeline) } + + it 'adds a new pipeline error message' do + pipeline.add_error_message('The error message') + + expect(pipeline.messages.map(&:content)).to contain_exactly('The error message') + end + + context 'when feature flag ci_store_pipeline_messages is disabled' do + before do + stub_feature_flags(ci_store_pipeline_messages: false) + end + + it ' does not add pipeline error message' do + pipeline.add_error_message('The error message') + + expect(pipeline.messages).to be_empty + end + end + end + describe '#has_yaml_errors?' do context 'when yaml_errors is set' do before do @@ -2825,6 +2995,16 @@ describe Ci::Pipeline, :mailer do end end + describe '#batch_lookup_report_artifact_for_file_type' do + context 'with code quality report artifact' do + let(:pipeline) { create(:ci_pipeline, :with_codequality_report, project: project) } + + it "returns the code quality artifact" do + expect(pipeline.batch_lookup_report_artifact_for_file_type(:codequality)).to eq(pipeline.job_artifacts.sample) + end + end + end + describe '#latest_report_builds' do it 'returns build with test artifacts' do test_build = create(:ci_build, :test_reports, pipeline: pipeline, project: project) @@ -2891,6 +3071,39 @@ describe Ci::Pipeline, :mailer do end end + describe '#test_report_summary' do + subject { pipeline.test_report_summary } + + context 'when pipeline has multiple builds with report results' do + let(:pipeline) { create(:ci_pipeline, :success, project: project) } + + before do + create(:ci_build, :success, :report_results, name: 'rspec', pipeline: pipeline, project: project) + create(:ci_build, :success, :report_results, name: 'java', pipeline: pipeline, project: project) + end + + it 'returns test report summary with collected data', :aggregate_failures do + expect(subject.total_time).to be(0.84) + expect(subject.total_count).to be(4) + expect(subject.success_count).to be(0) + expect(subject.failed_count).to be(0) + expect(subject.error_count).to be(4) + expect(subject.skipped_count).to be(0) + end + end + + context 'when pipeline does not have any builds with report results' do + it 'returns empty test report sumary', :aggregate_failures do + expect(subject.total_time).to be(0) + expect(subject.total_count).to be(0) + expect(subject.success_count).to be(0) + expect(subject.failed_count).to be(0) + expect(subject.error_count).to be(0) + expect(subject.skipped_count).to be(0) + end + end + end + describe '#test_reports' do subject { pipeline.test_reports } @@ -3069,6 +3282,32 @@ describe Ci::Pipeline, :mailer do end end end + + context 'when transitioning to success' do + context 'when feature is enabled' do + before do + stub_feature_flags(keep_latest_artifacts_for_ref: true) + end + + it 'calls the PipelineSuccessUnlockArtifactsWorker' do + expect(Ci::PipelineSuccessUnlockArtifactsWorker).to receive(:perform_async).with(pipeline.id) + + pipeline.succeed! + end + end + + context 'when feature is disabled' do + before do + stub_feature_flags(keep_latest_artifacts_for_ref: false) + end + + it 'does not call the PipelineSuccessUnlockArtifactsWorker' do + expect(Ci::PipelineSuccessUnlockArtifactsWorker).not_to receive(:perform_async) + + pipeline.succeed! + end + end + end end describe '#default_branch?' do @@ -3133,8 +3372,8 @@ describe Ci::Pipeline, :mailer do end end - describe '#error_messages' do - subject { pipeline.error_messages } + describe '#full_error_messages' do + subject { pipeline.full_error_messages } before do pipeline.valid? diff --git a/spec/models/ci/pipeline_variable_spec.rb b/spec/models/ci/pipeline_variable_spec.rb index e8c7ce088e2..04fcaab4c2d 100644 --- a/spec/models/ci/pipeline_variable_spec.rb +++ b/spec/models/ci/pipeline_variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::PipelineVariable do +RSpec.describe Ci::PipelineVariable do subject { build(:ci_pipeline_variable) } it_behaves_like "CI variable" diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb index e67f740279b..35764e2bbbe 100644 --- a/spec/models/ci/processable_spec.rb +++ b/spec/models/ci/processable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Processable do +RSpec.describe Ci::Processable do let_it_be(:project) { create(:project) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } diff --git a/spec/models/ci/ref_spec.rb b/spec/models/ci/ref_spec.rb index 3d75cb63141..fd4742a8ad2 100644 --- a/spec/models/ci/ref_spec.rb +++ b/spec/models/ci/ref_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Ref do +RSpec.describe Ci::Ref do it { is_expected.to belong_to(:project) } describe '.ensure_for' do @@ -62,6 +62,35 @@ describe Ci::Ref do end end + describe '#last_finished_pipeline_id' do + let(:pipeline_status) { :running } + let(:config_source) { Ci::PipelineEnums.config_sources[:repository_source] } + let(:pipeline) { create(:ci_pipeline, pipeline_status, config_source: config_source) } + let(:ci_ref) { pipeline.ci_ref } + + context 'when there are no finished pipelines' do + it 'returns nil' do + expect(ci_ref.last_finished_pipeline_id).to be_nil + end + end + + context 'when there are finished pipelines' do + let(:pipeline_status) { :success } + + it 'returns the pipeline id' do + expect(ci_ref.last_finished_pipeline_id).to eq(pipeline.id) + end + + context 'when the pipeline is not a ci_source' do + let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] } + + it 'returns nil' do + expect(ci_ref.last_finished_pipeline_id).to be_nil + end + end + end + end + describe '#update_status_by!' do subject { ci_ref.update_status_by!(pipeline) } diff --git a/spec/models/ci/resource_group_spec.rb b/spec/models/ci/resource_group_spec.rb index ce8b03282bc..9f72d1a82e5 100644 --- a/spec/models/ci/resource_group_spec.rb +++ b/spec/models/ci/resource_group_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::ResourceGroup do +RSpec.describe Ci::ResourceGroup do describe 'validation' do it 'valids when key includes allowed character' do resource_group = build(:ci_resource_group, key: 'test') diff --git a/spec/models/ci/resource_spec.rb b/spec/models/ci/resource_spec.rb index 27e512e2c45..90f26ef2b31 100644 --- a/spec/models/ci/resource_spec.rb +++ b/spec/models/ci/resource_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Resource do +RSpec.describe Ci::Resource do describe '.free' do subject { described_class.free } diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 296240b1602..8247ebf1144 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Runner do +RSpec.describe Ci::Runner do it_behaves_like 'having unique enum values' describe 'validation' do @@ -713,6 +713,46 @@ describe Ci::Runner do end end + describe '#belongs_to_more_than_one_project?' do + context 'project runner' do + let(:project1) { create(:project) } + let(:project2) { create(:project) } + + context 'two projects assigned to runner' do + let(:runner) { create(:ci_runner, :project, projects: [project1, project2]) } + + it 'returns true' do + expect(runner.belongs_to_more_than_one_project?).to be_truthy + end + end + + context 'one project assigned to runner' do + let(:runner) { create(:ci_runner, :project, projects: [project1]) } + + it 'returns false' do + expect(runner.belongs_to_more_than_one_project?).to be_falsey + end + end + end + + context 'group runner' do + let(:group) { create(:group) } + let(:runner) { create(:ci_runner, :group, groups: [group]) } + + it 'returns false' do + expect(runner.belongs_to_more_than_one_project?).to be_falsey + end + end + + context 'shared runner' do + let(:runner) { create(:ci_runner, :instance) } + + it 'returns false' do + expect(runner.belongs_to_more_than_one_project?).to be_falsey + end + end + end + describe '#has_tags?' do context 'when runner has tags' do subject { create(:ci_runner, tag_list: ['tag']) } diff --git a/spec/models/ci/sources/pipeline_spec.rb b/spec/models/ci/sources/pipeline_spec.rb index 5023747b487..ccf3140650b 100644 --- a/spec/models/ci/sources/pipeline_spec.rb +++ b/spec/models/ci/sources/pipeline_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Sources::Pipeline do +RSpec.describe Ci::Sources::Pipeline do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:pipeline) } diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index a1549532559..3d873a1b9c1 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Stage, :models do +RSpec.describe Ci::Stage, :models do let_it_be(:pipeline) { create(:ci_empty_pipeline) } let(:stage) { create(:ci_stage_entity, pipeline: pipeline, project: pipeline.project) } @@ -172,7 +172,7 @@ describe Ci::Stage, :models do it 'raises an exception' do expect { stage.update_legacy_status } - .to raise_error(HasStatus::UnknownStatusError) + .to raise_error(Ci::HasStatus::UnknownStatusError) end end end diff --git a/spec/models/ci/trigger_request_spec.rb b/spec/models/ci/trigger_request_spec.rb index d04349bec92..0d462741089 100644 --- a/spec/models/ci/trigger_request_spec.rb +++ b/spec/models/ci/trigger_request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::TriggerRequest do +RSpec.describe Ci::TriggerRequest do describe 'validation' do it 'be invalid if saving a variable' do trigger = build(:ci_trigger_request, variables: { TRIGGER_KEY_1: 'TRIGGER_VALUE_1' } ) diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 5b0815f8156..4ba6c6e50f7 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Trigger do +RSpec.describe Ci::Trigger do let(:project) { create :project } describe 'associations' do diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 810a0ddfd2e..26a7a2596af 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Ci::Variable do +RSpec.describe Ci::Variable do subject { build(:ci_variable) } it_behaves_like "CI variable" |