summaryrefslogtreecommitdiff
path: root/spec/models/ci
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/ci')
-rw-r--r--spec/models/ci/build_spec.rb248
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb14
-rw-r--r--spec/models/ci/build_trace_chunks/database_spec.rb18
-rw-r--r--spec/models/ci/build_trace_chunks/fog_spec.rb28
-rw-r--r--spec/models/ci/build_trace_chunks/redis_spec.rb98
-rw-r--r--spec/models/ci/daily_build_group_report_result_spec.rb1
-rw-r--r--spec/models/ci/group_spec.rb20
-rw-r--r--spec/models/ci/instance_variable_spec.rb29
-rw-r--r--spec/models/ci/job_artifact_spec.rb76
-rw-r--r--spec/models/ci/pipeline_artifact_spec.rb82
-rw-r--r--spec/models/ci/pipeline_spec.rb311
-rw-r--r--spec/models/ci/ref_spec.rb71
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) }