diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 18:42:06 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 18:42:06 +0000 |
commit | 6e4e1050d9dba2b7b2523fdd1768823ab85feef4 (patch) | |
tree | 78be5963ec075d80116a932011d695dd33910b4e /spec/models/ci | |
parent | 1ce776de4ae122aba3f349c02c17cebeaa8ecf07 (diff) | |
download | gitlab-ce-6e4e1050d9dba2b7b2523fdd1768823ab85feef4.tar.gz |
Add latest changes from gitlab-org/gitlab@13-3-stable-ee
Diffstat (limited to 'spec/models/ci')
-rw-r--r-- | spec/models/ci/build_spec.rb | 248 | ||||
-rw-r--r-- | spec/models/ci/build_trace_chunk_spec.rb | 14 | ||||
-rw-r--r-- | spec/models/ci/build_trace_chunks/database_spec.rb | 18 | ||||
-rw-r--r-- | spec/models/ci/build_trace_chunks/fog_spec.rb | 28 | ||||
-rw-r--r-- | spec/models/ci/build_trace_chunks/redis_spec.rb | 98 | ||||
-rw-r--r-- | spec/models/ci/daily_build_group_report_result_spec.rb | 1 | ||||
-rw-r--r-- | spec/models/ci/group_spec.rb | 20 | ||||
-rw-r--r-- | spec/models/ci/instance_variable_spec.rb | 29 | ||||
-rw-r--r-- | spec/models/ci/job_artifact_spec.rb | 76 | ||||
-rw-r--r-- | spec/models/ci/pipeline_artifact_spec.rb | 82 | ||||
-rw-r--r-- | spec/models/ci/pipeline_spec.rb | 311 | ||||
-rw-r--r-- | spec/models/ci/ref_spec.rb | 71 |
12 files changed, 514 insertions, 482 deletions
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 857b238981b..069ac23c5a4 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -612,6 +612,62 @@ RSpec.describe Ci::Build do end end + describe '#available_artifacts?' do + let(:build) { create(:ci_build) } + + subject { build.available_artifacts? } + + context 'when artifacts are not expired' do + before do + build.artifacts_expire_at = Date.tomorrow + end + + context 'when artifacts exist' do + before do + create(:ci_job_artifact, :archive, job: build) + end + + it { is_expected.to be_truthy } + end + + context 'when artifacts do not exist' do + it { is_expected.to be_falsey } + end + end + + context 'when artifacts are expired' do + before do + build.artifacts_expire_at = Date.yesterday + end + + context 'when artifacts are not locked' do + before do + build.pipeline.locked = :unlocked + end + + it { is_expected.to be_falsey } + end + + context 'when artifacts are locked' do + before do + build.pipeline.locked = :artifacts_locked + end + + context 'when artifacts exist' do + before do + create(:ci_job_artifact, :archive, job: build) + end + + it { is_expected.to be_truthy } + end + + context 'when artifacts do not exist' do + it { is_expected.to be_falsey } + end + end + end + end + describe '#browsable_artifacts?' do subject { build.browsable_artifacts? } @@ -1195,18 +1251,6 @@ RSpec.describe Ci::Build do is_expected.to eq('review/host') end - - context 'when ci_persisted_expanded_environment_name feature flag is disabled' do - before do - stub_feature_flags(ci_persisted_expanded_environment_name: false) - end - - it 'returns an expanded environment name with a list of variables' do - expect(build).to receive(:simple_variables).once.and_call_original - - is_expected.to eq('review/host') - end - end end end @@ -1703,112 +1747,6 @@ RSpec.describe Ci::Build do end end end - - describe '#options_retry_max' do - context 'with retries max config option' do - subject { create(:ci_build, options: { retry: { max: 1 } }) } - - context 'when build_metadata_config is set' do - before do - stub_feature_flags(ci_build_metadata_config: true) - end - - it 'returns the number of configured max retries' do - expect(subject.options_retry_max).to eq 1 - end - end - - context 'when build_metadata_config is not set' do - before do - stub_feature_flags(ci_build_metadata_config: false) - end - - it 'returns the number of configured max retries' do - expect(subject.options_retry_max).to eq 1 - end - end - end - - context 'without retries max config option' do - subject { create(:ci_build) } - - it 'returns nil' do - expect(subject.options_retry_max).to be_nil - end - end - - context 'when build is degenerated' do - subject { create(:ci_build, :degenerated) } - - it 'returns nil' do - expect(subject.options_retry_max).to be_nil - end - end - - context 'with integer only config option' do - subject { create(:ci_build, options: { retry: 1 }) } - - it 'returns the number of configured max retries' do - expect(subject.options_retry_max).to eq 1 - end - end - end - - describe '#options_retry_when' do - context 'with retries when config option' do - subject { create(:ci_build, options: { retry: { when: ['some_reason'] } }) } - - it 'returns the configured when' do - expect(subject.options_retry_when).to eq ['some_reason'] - end - end - - context 'without retries when config option' do - subject { create(:ci_build) } - - it 'returns always array' do - expect(subject.options_retry_when).to eq ['always'] - end - end - - context 'with integer only config option' do - subject { create(:ci_build, options: { retry: 1 }) } - - it 'returns always array' do - expect(subject.options_retry_when).to eq ['always'] - end - end - end - - describe '#retry_failure?' do - using RSpec::Parameterized::TableSyntax - - let(:build) { create(:ci_build) } - - subject { build.retry_failure? } - - where(:description, :retry_count, :options, :failure_reason, :result) do - "retries are disabled" | 0 | { max: 0 } | nil | false - "max equals count" | 2 | { max: 2 } | nil | false - "max is higher than count" | 1 | { max: 2 } | nil | true - "matching failure reason" | 0 | { when: %w[api_failure], max: 2 } | :api_failure | true - "not matching with always" | 0 | { when: %w[always], max: 2 } | :api_failure | true - "not matching reason" | 0 | { when: %w[script_error], max: 2 } | :api_failure | false - "scheduler failure override" | 1 | { when: %w[scheduler_failure], max: 1 } | :scheduler_failure | false - "default for scheduler failure" | 1 | {} | :scheduler_failure | true - end - - with_them do - before do - allow(build).to receive(:retries_count) { retry_count } - - build.options[:retry] = options - build.failure_reason = failure_reason - end - - it { is_expected.to eq(result) } - end - end end describe '.keep_artifacts!' do @@ -2115,23 +2053,13 @@ RSpec.describe Ci::Build do it { is_expected.to be_nil } end - context 'when build has a start environment' do - let(:build) { create(:ci_build, :with_deployment, :deploy_to_production, pipeline: pipeline) } - - it 'does not expand environment name' do - expect(build).not_to receive(:expanded_environment_name) - - subject - end - end - context 'when build has a stop environment' do - let(:build) { create(:ci_build, :stop_review_app, pipeline: pipeline) } + let(:build) { create(:ci_build, :stop_review_app, pipeline: pipeline, environment: "foo-#{project.default_branch}") } it 'expands environment name' do - expect(build).to receive(:expanded_environment_name) + expect(build).to receive(:expanded_environment_name).and_call_original - subject + is_expected.to eq(environment) end end end @@ -2925,6 +2853,7 @@ RSpec.describe Ci::Build do let(:ci_registry) do { key: 'CI_REGISTRY', value: 'registry.example.com', public: true, masked: false } end + let(:ci_registry_image) do { key: 'CI_REGISTRY_IMAGE', value: project.container_registry_url, public: true, masked: false } end @@ -3007,25 +2936,46 @@ RSpec.describe Ci::Build do end context 'when build is parallelized' do - let(:total) { 5 } - let(:index) { 3 } + shared_examples 'parallelized jobs config' do + let(:index) { 3 } + let(:total) { 5 } - before do - build.options[:parallel] = total - build.options[:instance] = index - build.name = "#{build.name} #{index}/#{total}" + before do + build.options[:parallel] = config + build.options[:instance] = index + end + + it 'includes CI_NODE_INDEX' do + is_expected.to include( + { key: 'CI_NODE_INDEX', value: index.to_s, public: true, masked: false } + ) + end + + it 'includes correct CI_NODE_TOTAL' do + is_expected.to include( + { key: 'CI_NODE_TOTAL', value: total.to_s, public: true, masked: false } + ) + end end - it 'includes CI_NODE_INDEX' do - is_expected.to include( - { key: 'CI_NODE_INDEX', value: index.to_s, public: true, masked: false } - ) + context 'when parallel is a number' do + let(:config) { 5 } + + it_behaves_like 'parallelized jobs config' end - it 'includes correct CI_NODE_TOTAL' do - is_expected.to include( - { key: 'CI_NODE_TOTAL', value: total.to_s, public: true, masked: false } - ) + context 'when parallel is hash with the total key' do + let(:config) { { total: 5 } } + + it_behaves_like 'parallelized jobs config' + end + + context 'when parallel is nil' do + let(:config) {} + + it_behaves_like 'parallelized jobs config' do + let(:total) { 1 } + end end end @@ -3161,6 +3111,14 @@ RSpec.describe Ci::Build do end end + describe '#simple_variables_without_dependencies' do + it 'does not load dependencies' do + expect(build).not_to receive(:dependency_variables) + + build.simple_variables_without_dependencies + end + end + shared_examples "secret CI variables" do context 'when ref is branch' do let(:build) { create(:ci_build, ref: 'master', tag: false, project: project) } diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index dab523f67ab..a6362d46449 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -262,6 +262,12 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do expect(build_trace_chunk.data).to be_empty end + it 'does not read data when appending' do + expect(build_trace_chunk).not_to receive(:data) + + build_trace_chunk.append(new_data, offset) + end + it_behaves_like 'Appending correctly' it_behaves_like 'Scheduling sidekiq worker to flush data to persist store' end @@ -486,7 +492,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do expect(build_trace_chunk.redis?).to be_truthy expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to eq(data) expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil - expect { Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk) }.to raise_error(Excon::Error::NotFound) + expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to be_nil subject @@ -508,7 +514,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do expect(build_trace_chunk.redis?).to be_truthy expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to eq(data) expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil - expect { Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk) }.to raise_error(Excon::Error::NotFound) + expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to be_nil end end end @@ -535,7 +541,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do expect(build_trace_chunk.database?).to be_truthy expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to eq(data) - expect { Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk) }.to raise_error(Excon::Error::NotFound) + expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to be_nil subject @@ -557,7 +563,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do expect(build_trace_chunk.database?).to be_truthy expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to eq(data) - expect { Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk) }.to raise_error(Excon::Error::NotFound) + expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to be_nil end end end diff --git a/spec/models/ci/build_trace_chunks/database_spec.rb b/spec/models/ci/build_trace_chunks/database_spec.rb index 245625b8046..313328ac037 100644 --- a/spec/models/ci/build_trace_chunks/database_spec.rb +++ b/spec/models/ci/build_trace_chunks/database_spec.rb @@ -89,6 +89,24 @@ RSpec.describe Ci::BuildTraceChunks::Database do end end + describe '#size' do + context 'when data exists' do + let(:model) { create(:ci_build_trace_chunk, :database_with_data, initial_data: 'üabcdef') } + + it 'returns data bytesize correctly' do + expect(data_store.size(model)).to eq 8 + end + end + + context 'when data does not exist' do + let(:model) { create(:ci_build_trace_chunk, :database_without_data) } + + it 'returns zero' do + expect(data_store.size(model)).to be_zero + end + end + end + describe '#keys' do subject { data_store.keys(relation) } diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb index 7ef3018d87b..a44ae58dfd2 100644 --- a/spec/models/ci/build_trace_chunks/fog_spec.rb +++ b/spec/models/ci/build_trace_chunks/fog_spec.rb @@ -40,7 +40,7 @@ RSpec.describe Ci::BuildTraceChunks::Fog do let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } it 'returns nil' do - expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound) + expect(data_store.data(model)).to be_nil end end end @@ -66,7 +66,7 @@ RSpec.describe Ci::BuildTraceChunks::Fog do let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } it 'sets new data' do - expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound) + expect(data_store.data(model)).to be_nil subject @@ -86,7 +86,7 @@ RSpec.describe Ci::BuildTraceChunks::Fog do subject - expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound) + expect(data_store.data(model)).to be_nil end end @@ -94,11 +94,29 @@ RSpec.describe Ci::BuildTraceChunks::Fog do let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } it 'does nothing' do - expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound) + expect(data_store.data(model)).to be_nil subject - expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound) + expect(data_store.data(model)).to be_nil + end + end + end + + describe '#size' do + context 'when data exists' do + let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'üabcd') } + + it 'returns data bytesize correctly' do + expect(data_store.size(model)).to eq 6 + end + end + + context 'when data does not exist' do + let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } + + it 'returns zero' do + expect(data_store.size(model)).to be_zero end end end diff --git a/spec/models/ci/build_trace_chunks/redis_spec.rb b/spec/models/ci/build_trace_chunks/redis_spec.rb index c37b8697a4d..cb0b6baadeb 100644 --- a/spec/models/ci/build_trace_chunks/redis_spec.rb +++ b/spec/models/ci/build_trace_chunks/redis_spec.rb @@ -61,6 +61,86 @@ RSpec.describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do end end + describe '#append_data' do + context 'when valid offset is used with existing data' do + let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: 'abcd') } + + it 'appends data' do + expect(data_store.data(model)).to eq('abcd') + + length = data_store.append_data(model, '12345', 4) + + expect(length).to eq 9 + expect(data_store.data(model)).to eq('abcd12345') + end + end + + context 'when data does not exist yet' do + let(:model) { create(:ci_build_trace_chunk, :redis_without_data) } + + it 'sets new data' do + expect(data_store.data(model)).to be_nil + + length = data_store.append_data(model, 'abc', 0) + + expect(length).to eq 3 + expect(data_store.data(model)).to eq('abc') + end + end + + context 'when data needs to be truncated' do + let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: '12345678') } + + it 'appends data and truncates stored value' do + expect(data_store.data(model)).to eq('12345678') + + length = data_store.append_data(model, 'ab', 4) + + expect(length).to eq 6 + expect(data_store.data(model)).to eq('1234ab') + end + end + + context 'when invalid offset is provided' do + let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: 'abc') } + + it 'raises an exception' do + length = data_store.append_data(model, '12345', 4) + + expect(length).to be_negative + end + end + + context 'when trace contains multi-byte UTF8 characters' do + let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: 'aüc') } + + it 'appends data' do + length = data_store.append_data(model, '1234', 4) + + data_store.data(model).then do |new_data| + expect(new_data.bytesize).to eq 8 + expect(new_data).to eq 'aüc1234' + end + + expect(length).to eq 8 + end + end + + context 'when trace contains non-UTF8 characters' do + let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: "a\255c") } + + it 'appends data' do + length = data_store.append_data(model, '1234', 3) + + data_store.data(model).then do |new_data| + expect(new_data.bytesize).to eq 7 + end + + expect(length).to eq 7 + end + end + end + describe '#delete_data' do subject { data_store.delete_data(model) } @@ -89,6 +169,24 @@ RSpec.describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do end end + describe '#size' do + context 'when data exists' do + let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: 'üabcd') } + + it 'returns data bytesize correctly' do + expect(data_store.size(model)).to eq 6 + end + end + + context 'when data does not exist' do + let(:model) { create(:ci_build_trace_chunk, :redis_without_data) } + + it 'returns zero' do + expect(data_store.size(model)).to be_zero + end + end + end + describe '#keys' do subject { data_store.keys(relation) } 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 059a5b76b9a..326366666cb 100644 --- a/spec/models/ci/daily_build_group_report_result_spec.rb +++ b/spec/models/ci/daily_build_group_report_result_spec.rb @@ -36,6 +36,7 @@ RSpec.describe Ci::DailyBuildGroupReportResult do data: { coverage: 71.2 } ) end + let!(:new_pipeline) { create(:ci_pipeline) } it 'creates or updates matching report results' do diff --git a/spec/models/ci/group_spec.rb b/spec/models/ci/group_spec.rb index dc9aee906ea..c20b7e61044 100644 --- a/spec/models/ci/group_spec.rb +++ b/spec/models/ci/group_spec.rb @@ -29,24 +29,8 @@ RSpec.describe Ci::Group do [create(:ci_build, :failed)] end - context 'when ci_composite_status is enabled' do - before do - stub_feature_flags(ci_composite_status: true) - end - - it 'returns a failed status' do - expect(subject.status).to eq('failed') - end - end - - context 'when ci_composite_status is disabled' do - before do - stub_feature_flags(ci_composite_status: false) - end - - it 'returns a failed status' do - expect(subject.status).to eq('failed') - end + it 'returns a failed status' do + expect(subject.status).to eq('failed') end end diff --git a/spec/models/ci/instance_variable_spec.rb b/spec/models/ci/instance_variable_spec.rb index 344ba5bfafd..15d0c911bc4 100644 --- a/spec/models/ci/instance_variable_spec.rb +++ b/spec/models/ci/instance_variable_spec.rb @@ -9,12 +9,39 @@ RSpec.describe Ci::InstanceVariable do it { is_expected.to include_module(Ci::Maskable) } it { is_expected.to validate_uniqueness_of(:key).with_message(/\(\w+\) has already been taken/) } - it { is_expected.to validate_length_of(:encrypted_value).is_at_most(1024).with_message(/Variables over 700 characters risk exceeding the limit/) } + it { is_expected.to validate_length_of(:value).is_at_most(10_000).with_message(/The value of the provided variable exceeds the 10000 character limit/) } it_behaves_like 'includes Limitable concern' do subject { build(:ci_instance_variable) } end + describe '#value' do + context 'without application limit' do + # Ensures breakage if encryption algorithm changes + let(:variable) { build(:ci_instance_variable, key: 'too_long', value: value) } + + before do + allow(variable).to receive(:valid?).and_return(true) + end + + context 'when value is over the limit' do + let(:value) { SecureRandom.alphanumeric(10_002) } + + it 'raises a database level error' do + expect { variable.save }.to raise_error(ActiveRecord::StatementInvalid) + end + end + + context 'when value is under the limit' do + let(:value) { SecureRandom.alphanumeric(10_000) } + + it 'does not raise database level error' do + expect { variable.save }.not_to raise_error + end + end + 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 b5f9128b7c5..91a669aa3f4 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -483,11 +483,7 @@ RSpec.describe Ci::JobArtifact do subject { create(:ci_job_artifact, :archive) } context 'when existing object has local store' do - it 'is stored locally' do - expect(subject.file_store).to be(ObjectStorage::Store::LOCAL) - expect(subject.file).to be_file_storage - expect(subject.file.object_store).to eq(ObjectStorage::Store::LOCAL) - end + it_behaves_like 'mounted file in local store' end context 'when direct upload is enabled' do @@ -496,11 +492,7 @@ RSpec.describe Ci::JobArtifact do end context 'when file is stored' do - it 'is stored remotely' do - expect(subject.file_store).to eq(ObjectStorage::Store::REMOTE) - expect(subject.file).not_to be_file_storage - expect(subject.file.object_store).to eq(ObjectStorage::Store::REMOTE) - end + it_behaves_like 'mounted file in object store' end end end @@ -529,11 +521,9 @@ RSpec.describe Ci::JobArtifact do context 'when file type is supported' do let(:project_closest_setting) { 1024 } let(:artifact_type) { 'junit' } + let(:limit_name) { "#{described_class::PLAN_LIMIT_PREFIX}#{artifact_type}" } - 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 + let!(:plan_limits) { create(:plan_limits, :default_plan) } shared_examples_for 'basing off the project closest setting' do it { is_expected.to eq(project_closest_setting.megabytes.to_i) } @@ -543,49 +533,40 @@ RSpec.describe Ci::JobArtifact 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) } + before do + allow(build.project).to receive(:closest_setting).with(:max_artifacts_size).and_return(project_closest_setting) + end - context 'and plan limit is disabled for the given artifact type' do - before do - plan_limits.update!(limit_name => 0) - end + 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' + it_behaves_like 'basing off the project closest setting' - context 'and project closest setting results to zero' do - let(:project_closest_setting) { 0 } + context 'and project closest setting results to zero' do + let(:project_closest_setting) { 0 } - it { is_expected.to eq(0) } - end + 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 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 } + 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 + it_behaves_like 'basing off the plan limit' end - end - context 'and feature flag for custom max size per type is disabled' do - let(:flag_enabled) { false } + context 'and plan limit is larger than project setting' do + let(:max_size_for_type) { project_closest_setting + 1 } - it_behaves_like 'basing off the project closest setting' + it_behaves_like 'basing off the project closest setting' + end end end end @@ -597,7 +578,8 @@ RSpec.describe Ci::JobArtifact do 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. + non-zero default values. Also, remember to update the plan limits documentation (doc/administration/instance_limits.md) + when changes or new entries are made. MSG end end diff --git a/spec/models/ci/pipeline_artifact_spec.rb b/spec/models/ci/pipeline_artifact_spec.rb new file mode 100644 index 00000000000..9d63d74a6cc --- /dev/null +++ b/spec/models/ci/pipeline_artifact_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::PipelineArtifact, type: :model do + let_it_be(:coverage_report) { create(:ci_pipeline_artifact) } + + describe 'associations' do + it { is_expected.to belong_to(:pipeline) } + it { is_expected.to belong_to(:project) } + end + + it_behaves_like 'having unique enum values' + + describe 'validations' do + it { is_expected.to validate_presence_of(:pipeline) } + it { is_expected.to validate_presence_of(:project) } + it { is_expected.to validate_presence_of(:file_type) } + it { is_expected.to validate_presence_of(:file_format) } + it { is_expected.to validate_presence_of(:size) } + it { is_expected.to validate_presence_of(:file) } + + context 'when attributes are valid' do + it 'returns no errors' do + expect(coverage_report).to be_valid + end + end + + context 'when file_store is invalid' do + it 'returns errors' do + coverage_report.file_store = 0 + + expect(coverage_report).to be_invalid + expect(coverage_report.errors.full_messages).to eq(["File store is not included in the list"]) + end + end + + context 'when size is over 10 megabytes' do + it 'returns errors' do + coverage_report.size = 11.megabytes + + expect(coverage_report).to be_invalid + end + end + end + + describe '#set_size' do + subject { create(:ci_pipeline_artifact) } + + context 'when file is being created' do + it 'sets the size' do + expect(subject.size).to eq(85) + end + end + + context 'when file is being updated' do + it 'updates the size' do + subject.update!(file: fixture_file_upload('spec/fixtures/dk.png')) + + expect(subject.size).to eq(1062) + end + end + end + + describe 'file is being stored' do + subject { create(:ci_pipeline_artifact) } + + context 'when existing object has local store' do + it_behaves_like 'mounted file in local store' + end + + context 'when direct upload is enabled' do + before do + stub_artifacts_object_storage(Ci::PipelineArtifactUploader, direct_upload: true) + end + + context 'when file is stored' do + it_behaves_like 'mounted file in object store' + end + end + end +end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index ed2466d6413..b4e80fa7588 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -46,6 +46,7 @@ RSpec.describe Ci::Pipeline, :mailer do it { is_expected.to respond_to :git_author_email } it { is_expected.to respond_to :short_sha } it { is_expected.to delegate_method(:full_path).to(:project).with_prefix } + it { is_expected.to have_many(:pipeline_artifacts) } describe 'associations' do it 'has a bidirectional relationship with projects' do @@ -813,6 +814,8 @@ RSpec.describe Ci::Pipeline, :mailer do expect(subject.to_hash) .to include( 'CI_EXTERNAL_PULL_REQUEST_IID' => pull_request.pull_request_iid.to_s, + 'CI_EXTERNAL_PULL_REQUEST_SOURCE_REPOSITORY' => pull_request.source_repository, + 'CI_EXTERNAL_PULL_REQUEST_TARGET_REPOSITORY' => pull_request.target_repository, 'CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_SHA' => pull_request.source_sha, 'CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_SHA' => pull_request.target_sha, 'CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_NAME' => pull_request.source_branch, @@ -936,69 +939,59 @@ RSpec.describe Ci::Pipeline, :mailer do subject { pipeline.legacy_stages } - where(:ci_composite_status) do - [false, true] + context 'stages list' do + it 'returns ordered list of stages' do + expect(subject.map(&:name)).to eq(%w[build test deploy]) + end end - with_them do - before do - stub_feature_flags(ci_composite_status: ci_composite_status) + context 'stages with statuses' do + let(:statuses) do + subject.map { |stage| [stage.name, stage.status] } end - context 'stages list' do - it 'returns ordered list of stages' do - expect(subject.map(&:name)).to eq(%w[build test deploy]) - end + it 'returns list of stages with correct statuses' do + expect(statuses).to eq([%w(build failed), + %w(test success), + %w(deploy running)]) end - context 'stages with statuses' do - let(:statuses) do - subject.map { |stage| [stage.name, stage.status] } + context 'when commit status is retried' do + before do + create(:commit_status, pipeline: pipeline, + stage: 'build', + name: 'mac', + stage_idx: 0, + status: 'success') + + Ci::ProcessPipelineService + .new(pipeline) + .execute end - it 'returns list of stages with correct statuses' do - expect(statuses).to eq([%w(build failed), + it 'ignores the previous state' do + expect(statuses).to eq([%w(build success), %w(test success), %w(deploy running)]) end - - context 'when commit status is retried' do - before do - create(:commit_status, pipeline: pipeline, - stage: 'build', - name: 'mac', - stage_idx: 0, - status: 'success') - - Ci::ProcessPipelineService - .new(pipeline) - .execute - end - - it 'ignores the previous state' do - expect(statuses).to eq([%w(build success), - %w(test success), - %w(deploy running)]) - end - end end + end - context 'when there is a stage with warnings' do - before do - create(:commit_status, pipeline: pipeline, - stage: 'deploy', - name: 'prod:2', - stage_idx: 2, - status: 'failed', - allow_failure: true) - end + context 'when there is a stage with warnings' do + before do + create(:commit_status, pipeline: pipeline, + stage: 'deploy', + name: 'prod:2', + stage_idx: 2, + status: 'failed', + allow_failure: true) + end - it 'populates stage with correct number of warnings' do - deploy_stage = pipeline.legacy_stages.third + it 'populates stage with correct number of warnings' do + deploy_stage = pipeline.legacy_stages.third - expect(deploy_stage).not_to receive(:statuses) - expect(deploy_stage).to have_warnings - end + expect(deploy_stage).not_to receive(:statuses) + expect(deploy_stage).to have_warnings end end end @@ -1044,19 +1037,6 @@ RSpec.describe Ci::Pipeline, :mailer do before do create(:ci_stage_entity, project: project, pipeline: pipeline, - name: 'build') - end - - it 'returns persisted stages' do - expect(pipeline.stages).not_to be_empty - expect(pipeline.stages).to all(be_persisted) - end - end - - describe '#ordered_stages' do - before do - create(:ci_stage_entity, project: project, - pipeline: pipeline, position: 4, name: 'deploy') @@ -1083,60 +1063,25 @@ RSpec.describe Ci::Pipeline, :mailer do name: 'cleanup') end - subject { pipeline.ordered_stages } + subject { pipeline.stages } - 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 + 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 using persisted stages' do + context 'when pipeline is complete' do before do - stub_feature_flags( - ci_atomic_processing: false - ) + pipeline.succeed! end - context 'when pipelines is not complete' do - it 'still returns legacy stages' do - expect(subject).to all(be_a Ci::LegacyStage) - expect(subject.map(&:name)).to eq %w[build test] - 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 + 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 @@ -1932,6 +1877,7 @@ RSpec.describe Ci::Pipeline, :mailer do project: project ) end + let!(:commit_123_ref_develop) do create( :ci_empty_pipeline, @@ -1941,6 +1887,7 @@ RSpec.describe Ci::Pipeline, :mailer do project: project ) end + let!(:commit_456_ref_test) do create( :ci_empty_pipeline, @@ -2139,58 +2086,6 @@ RSpec.describe Ci::Pipeline, :mailer do end end - describe '#update_status' do - context 'when pipeline is empty' do - it 'updates does not change pipeline status' do - expect(pipeline.statuses.latest.slow_composite_status(project: project)).to be_nil - - expect { pipeline.update_legacy_status } - .to change { pipeline.reload.status } - .from('created') - .to('skipped') - end - end - - context 'when updating status to pending' do - before do - create(:ci_build, pipeline: pipeline, status: :running) - end - - it 'updates pipeline status to running' do - expect { pipeline.update_legacy_status } - .to change { pipeline.reload.status } - .from('created') - .to('running') - end - end - - context 'when updating status to scheduled' do - before do - create(:ci_build, pipeline: pipeline, status: :scheduled) - end - - it 'updates pipeline status to scheduled' do - expect { pipeline.update_legacy_status } - .to change { pipeline.reload.status } - .from('created') - .to('scheduled') - end - end - - context 'when statuses status was not recognized' do - before do - allow(pipeline) - .to receive(:latest_builds_status) - .and_return(:unknown) - end - - it 'raises an exception' do - expect { pipeline.update_legacy_status } - .to raise_error(Ci::HasStatus::UnknownStatusError) - end - end - end - describe '#detailed_status' do subject { pipeline.detailed_status(user) } @@ -2918,25 +2813,9 @@ RSpec.describe Ci::Pipeline, :mailer do describe '#ensure_ci_ref!' do subject { pipeline.ensure_ci_ref! } - shared_examples_for 'protected by feature flag' do - context 'when feature flag is disabled' do - before do - stub_feature_flags(ci_pipeline_fixed_notifications: false) - end - - it 'does not do anything' do - expect(Ci::Ref).not_to receive(:ensure_for) - - subject - end - end - end - context 'when ci_ref does not exist yet' do let!(:pipeline) { create(:ci_pipeline, ci_ref_presence: false) } - it_behaves_like 'protected by feature flag' - it 'creates a new ci_ref and assigns it' do expect { subject }.to change { Ci::Ref.count }.by(1) @@ -2947,8 +2826,6 @@ RSpec.describe Ci::Pipeline, :mailer do context 'when ci_ref already exists' do let!(:pipeline) { create(:ci_pipeline) } - it_behaves_like 'protected by feature flag' - it 'fetches a new ci_ref and assigns it' do expect { subject }.not_to change { Ci::Ref.count } @@ -3082,24 +2959,14 @@ RSpec.describe Ci::Pipeline, :mailer do 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) + it 'returns test report summary with collected data' do + expect(subject.total).to include(time: 0.84, count: 4, success: 0, failed: 0, skipped: 0, error: 4) 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) + it 'returns empty test report summary' do + expect(subject.total).to include(time: 0, count: 0, success: 0, failed: 0, skipped: 0, error: 0) end end end @@ -3141,40 +3008,6 @@ RSpec.describe Ci::Pipeline, :mailer do end end - describe '#test_reports_count', :use_clean_rails_memory_store_caching do - subject { pipeline.test_reports } - - context 'when pipeline has multiple builds with test reports' do - let!(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline, project: project) } - let!(:build_java) { create(:ci_build, :success, name: 'java', pipeline: pipeline, project: project) } - - before do - create(:ci_job_artifact, :junit, job: build_rspec, project: project) - create(:ci_job_artifact, :junit_with_ant, job: build_java, project: project) - end - - it 'returns test report count equal to test reports total_count' do - expect(subject.total_count).to eq(7) - expect(subject.total_count).to eq(pipeline.test_reports_count) - end - - it 'reads from cache when records are cached' do - expect(Rails.cache.fetch(['project', project.id, 'pipeline', pipeline.id, 'test_reports_count'], force: false)).to be_nil - - pipeline.test_reports_count - - expect(ActiveRecord::QueryRecorder.new { pipeline.test_reports_count }.count).to eq(0) - end - end - - context 'when pipeline does not have any builds with test reports' do - it 'returns empty test report count' do - expect(subject.total_count).to eq(0) - expect(subject.total_count).to eq(pipeline.test_reports_count) - end - end - end - describe '#accessibility_reports' do subject { pipeline.accessibility_reports } @@ -3282,32 +3115,6 @@ RSpec.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 diff --git a/spec/models/ci/ref_spec.rb b/spec/models/ci/ref_spec.rb index fd4742a8ad2..8bce3c10d8c 100644 --- a/spec/models/ci/ref_spec.rb +++ b/spec/models/ci/ref_spec.rb @@ -3,8 +3,69 @@ require 'spec_helper' RSpec.describe Ci::Ref do + using RSpec::Parameterized::TableSyntax + it { is_expected.to belong_to(:project) } + describe 'state machine transitions' do + context 'unlock artifacts transition' do + let(:ci_ref) { create(:ci_ref) } + let(:unlock_artifacts_worker_spy) { class_spy(::Ci::PipelineSuccessUnlockArtifactsWorker) } + + before do + stub_const('Ci::PipelineSuccessUnlockArtifactsWorker', unlock_artifacts_worker_spy) + end + + context 'when keep latest artifact feature is enabled' do + before do + stub_feature_flags(keep_latest_artifacts_for_ref: true) + end + + where(:initial_state, :action, :count) do + :unknown | :succeed! | 1 + :unknown | :do_fail! | 0 + :success | :succeed! | 1 + :success | :do_fail! | 0 + :failed | :succeed! | 1 + :failed | :do_fail! | 0 + :fixed | :succeed! | 1 + :fixed | :do_fail! | 0 + :broken | :succeed! | 1 + :broken | :do_fail! | 0 + :still_failing | :succeed | 1 + :still_failing | :do_fail | 0 + end + + with_them do + context "when transitioning states" do + before do + status_value = Ci::Ref.state_machines[:status].states[initial_state].value + ci_ref.update!(status: status_value) + end + + it 'calls unlock artifacts service' do + ci_ref.send(action) + + expect(unlock_artifacts_worker_spy).to have_received(:perform_async).exactly(count).times + end + end + end + end + + context 'when keep latest artifact feature is not enabled' do + before do + stub_feature_flags(keep_latest_artifacts_for_ref: false) + end + + it 'does not call unlock artifacts service' do + ci_ref.succeed! + + expect(unlock_artifacts_worker_spy).not_to have_received(:perform_async) + end + end + end + end + describe '.ensure_for' do let_it_be(:project) { create(:project, :repository) } @@ -161,16 +222,6 @@ RSpec.describe Ci::Ref do it_behaves_like 'no-op' end - context 'when feature flag is disabled' do - let(:pipeline) { create(:ci_pipeline, :success, ci_ref: ci_ref) } - - before do - stub_feature_flags(ci_pipeline_fixed_notifications: false) - end - - it_behaves_like 'no-op' - end - context 'when pipeline is not the latest pipeline' do let!(:pipeline) { create(:ci_pipeline, :success, ci_ref: ci_ref) } let!(:latest_pipeline) { create(:ci_pipeline, :success, ci_ref: ci_ref) } |