Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-12-20 17:22:11 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-20 17:22:11 +0300
commit0c872e02b2c822e3397515ec324051ff540f0cd5 (patch)
treece2fb6ce7030e4dad0f4118d21ab6453e5938cdd /spec/models/ci
parentf7e05a6853b12f02911494c4b3fe53d9540d74fc (diff)
Add latest changes from gitlab-org/gitlab@15-7-stable-eev15.7.0-rc42
Diffstat (limited to 'spec/models/ci')
-rw-r--r--spec/models/ci/bridge_spec.rb20
-rw-r--r--spec/models/ci/build_metadata_spec.rb161
-rw-r--r--spec/models/ci/build_need_spec.rb60
-rw-r--r--spec/models/ci/build_pending_state_spec.rb29
-rw-r--r--spec/models/ci/build_report_result_spec.rb32
-rw-r--r--spec/models/ci/build_runner_session_spec.rb18
-rw-r--r--spec/models/ci/build_spec.rb132
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb33
-rw-r--r--spec/models/ci/build_trace_metadata_spec.rb22
-rw-r--r--spec/models/ci/freeze_period_spec.rb129
-rw-r--r--spec/models/ci/freeze_period_status_spec.rb71
-rw-r--r--spec/models/ci/job_artifact_spec.rb44
-rw-r--r--spec/models/ci/job_token/allowlist_spec.rb81
-rw-r--r--spec/models/ci/job_token/project_scope_link_spec.rb16
-rw-r--r--spec/models/ci/job_token/scope_spec.rb66
-rw-r--r--spec/models/ci/job_variable_spec.rb62
-rw-r--r--spec/models/ci/pending_build_spec.rb22
-rw-r--r--spec/models/ci/pipeline_schedule_spec.rb65
-rw-r--r--spec/models/ci/pipeline_spec.rb46
-rw-r--r--spec/models/ci/processable_spec.rb5
-rw-r--r--spec/models/ci/resource_group_spec.rb20
-rw-r--r--spec/models/ci/runner_namespace_spec.rb23
-rw-r--r--spec/models/ci/runner_spec.rb26
-rw-r--r--spec/models/ci/runner_version_spec.rb18
-rw-r--r--spec/models/ci/running_build_spec.rb24
-rw-r--r--spec/models/ci/secure_file_spec.rb33
-rw-r--r--spec/models/ci/sources/pipeline_spec.rb18
-rw-r--r--spec/models/ci/unit_test_failure_spec.rb42
28 files changed, 997 insertions, 321 deletions
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index df24c92149d..169b00b9c74 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Bridge do
+RSpec.describe Ci::Bridge, feature_category: :continuous_integration 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) }
@@ -34,6 +34,24 @@ RSpec.describe Ci::Bridge do
expect(bridge).to have_one(:downstream_pipeline)
end
+ describe '#sourced_pipelines' do
+ subject { bridge.sourced_pipelines }
+
+ it 'raises error' do
+ expect { subject }.to raise_error RuntimeError, 'Ci::Bridge does not have sourced_pipelines association'
+ end
+
+ context 'when ci_bridge_remove_sourced_pipelines is disabled' do
+ before do
+ stub_feature_flags(ci_bridge_remove_sourced_pipelines: false)
+ end
+
+ it 'returns the sourced_pipelines association' do
+ expect(bridge.sourced_pipelines).to eq([])
+ end
+ end
+ end
+
describe '#retryable?' do
let(:bridge) { create(:ci_bridge, :success) }
diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb
index e728ce0f474..8bf3af44be6 100644
--- a/spec/models/ci/build_metadata_spec.rb
+++ b/spec/models/ci/build_metadata_spec.rb
@@ -6,7 +6,6 @@ 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) }
-
let_it_be(:pipeline) do
create(:ci_pipeline, project: project,
sha: project.commit.id,
@@ -14,7 +13,9 @@ RSpec.describe Ci::BuildMetadata do
status: 'success')
end
- let(:job) { create(:ci_build, pipeline: pipeline) }
+ let_it_be_with_reload(:runner) { create(:ci_runner) }
+
+ let(:job) { create(:ci_build, pipeline: pipeline, runner: runner) }
let(:metadata) { job.metadata }
it_behaves_like 'having unique enum values'
@@ -32,63 +33,110 @@ RSpec.describe Ci::BuildMetadata do
end
end
- context 'when project timeout is set' do
- context 'when runner is assigned to the job' do
+ context 'when job, project and runner timeouts are set' do
+ context 'when job timeout is lower then runner timeout' do
before do
- job.update!(runner: runner)
+ runner.update!(maximum_timeout: 4000)
+ job.update!(options: { job_timeout: 3000 })
end
- context 'when runner timeout is not set' do
- let(:runner) { create(:ci_runner, maximum_timeout: nil) }
+ it_behaves_like 'sets timeout', 'job_timeout_source', 3000
+ end
- it_behaves_like 'sets timeout', 'project_timeout_source', 2000
+ context 'when runner timeout is lower then job timeout' do
+ before do
+ runner.update!(maximum_timeout: 2000)
+ job.update!(options: { job_timeout: 3000 })
end
- context 'when runner timeout is lower than project timeout' do
- let(:runner) { create(:ci_runner, maximum_timeout: 1900) }
+ it_behaves_like 'sets timeout', 'runner_timeout_source', 2000
+ end
+ end
- it_behaves_like 'sets timeout', 'runner_timeout_source', 1900
+ context 'when job, project timeout values are set and runner is assigned' do
+ context 'when runner has no timeout set' do
+ before do
+ runner.update!(maximum_timeout: nil)
+ job.update!(options: { job_timeout: 3000 })
end
- context 'when runner timeout is higher than project timeout' do
- let(:runner) { create(:ci_runner, maximum_timeout: 2100) }
+ it_behaves_like 'sets timeout', 'job_timeout_source', 3000
+ end
+ end
- it_behaves_like 'sets timeout', 'project_timeout_source', 2000
+ context 'when only job and project timeouts are defined' do
+ context 'when job timeout is lower then project timeout' do
+ before do
+ job.update!(options: { job_timeout: 1000 })
end
- end
- context 'when job timeout is set' do
- context 'when job timeout is higher than project timeout' do
- let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 3000 }) }
+ it_behaves_like 'sets timeout', 'job_timeout_source', 1000
+ end
- it_behaves_like 'sets timeout', 'job_timeout_source', 3000
+ context 'when project timeout is lower then job timeout' do
+ before do
+ job.update!(options: { job_timeout: 3000 })
end
- context 'when job timeout is lower than project timeout' do
- let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 1000 }) }
+ it_behaves_like 'sets timeout', 'job_timeout_source', 3000
+ end
+ end
+
+ context 'when only project and runner timeouts are defined' do
+ before do
+ runner.update!(maximum_timeout: 1900)
+ end
+
+ context 'when runner timeout is lower then project timeout' do
+ it_behaves_like 'sets timeout', 'runner_timeout_source', 1900
+ end
- it_behaves_like 'sets timeout', 'job_timeout_source', 1000
+ context 'when project timeout is lower then runner timeout' do
+ before do
+ runner.update!(maximum_timeout: 2100)
end
+
+ it_behaves_like 'sets timeout', 'project_timeout_source', 2000
end
+ end
- context 'when both runner and job timeouts are set' do
+ context 'when only job and runner timeouts are defined' do
+ context 'when runner timeout is lower them job timeout' do
before do
- job.update!(runner: runner)
+ job.update!(options: { job_timeout: 2000 })
+ runner.update!(maximum_timeout: 1900)
end
- context 'when job timeout is higher than runner timeout' do
- let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 3000 }) }
- let(:runner) { create(:ci_runner, maximum_timeout: 2100) }
+ it_behaves_like 'sets timeout', 'runner_timeout_source', 1900
+ end
- it_behaves_like 'sets timeout', 'runner_timeout_source', 2100
+ context 'when job timeout is lower them runner timeout' do
+ before do
+ job.update!(options: { job_timeout: 1000 })
+ runner.update!(maximum_timeout: 1900)
end
- context 'when job timeout is lower than runner timeout' do
- let(:job) { create(:ci_build, pipeline: pipeline, options: { job_timeout: 1900 }) }
- let(:runner) { create(:ci_runner, maximum_timeout: 2100) }
+ it_behaves_like 'sets timeout', 'job_timeout_source', 1000
+ end
+ end
+
+ context 'when only job timeout is defined and runner is assigned, but has no timeout set' do
+ before do
+ job.update!(options: { job_timeout: 1000 })
+ runner.update!(maximum_timeout: nil)
+ end
+
+ it_behaves_like 'sets timeout', 'job_timeout_source', 1000
+ end
- it_behaves_like 'sets timeout', 'job_timeout_source', 1900
+ context 'when only one timeout value is defined' do
+ context 'when only project timeout value is defined' do
+ before do
+ job.update!(options: { job_timeout: nil })
+ runner.update!(maximum_timeout: nil)
end
+
+ it_behaves_like 'sets timeout', 'project_timeout_source', 2000
end
end
end
@@ -107,9 +155,7 @@ RSpec.describe Ci::BuildMetadata do
}
metadata.id_tokens = {
TEST_JWT_TOKEN: {
- id_token: {
- aud: 'https://gitlab.test'
- }
+ aud: 'https://gitlab.test'
}
}
@@ -152,6 +198,29 @@ RSpec.describe Ci::BuildMetadata do
end
end
+ describe '#enable_debug_trace!' do
+ subject { metadata.enable_debug_trace! }
+
+ context 'when debug_trace_enabled is false' do
+ it 'sets debug_trace_enabled to true' do
+ subject
+
+ expect(metadata.debug_trace_enabled).to eq(true)
+ end
+ end
+
+ context 'when debug_trace_enabled is true' do
+ before do
+ metadata.update!(debug_trace_enabled: true)
+ end
+
+ it 'does not set debug_trace_enabled to true', :aggregate_failures do
+ expect(described_class).not_to receive(:save!)
+ expect(metadata.debug_trace_enabled).to eq(true)
+ end
+ end
+ end
+
describe 'partitioning' do
context 'with job' do
let(:status) { build(:commit_status, partition_id: 123) }
@@ -183,28 +252,6 @@ RSpec.describe Ci::BuildMetadata do
end
end
- describe 'routing table switch' do
- context 'with ff disabled' do
- before do
- stub_feature_flags(ci_partitioning_use_ci_builds_metadata_routing_table: false)
- end
-
- it 'uses the legacy table' do
- expect(described_class.table_name).to eq('ci_builds_metadata')
- end
- end
-
- context 'with ff enabled' do
- before do
- stub_feature_flags(ci_partitioning_use_ci_builds_metadata_routing_table: true)
- end
-
- it 'uses the routing table' do
- expect(described_class.table_name).to eq('p_ci_builds_metadata')
- end
- end
- end
-
context 'jsonb fields serialization' do
it 'changing other fields does not change config_options' do
expect { metadata.id = metadata.id }.not_to change(metadata, :changes)
diff --git a/spec/models/ci/build_need_spec.rb b/spec/models/ci/build_need_spec.rb
index c2cf9027055..aa1c57d1788 100644
--- a/spec/models/ci/build_need_spec.rb
+++ b/spec/models/ci/build_need_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::BuildNeed, model: true do
+RSpec.describe Ci::BuildNeed, model: true, feature_category: :continuous_integration do
let(:build_need) { build(:ci_build_need) }
it { is_expected.to belong_to(:build).class_name('Ci::Processable') }
@@ -35,4 +35,62 @@ RSpec.describe Ci::BuildNeed, model: true do
end
end
end
+
+ describe 'partitioning' do
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+ let(:build_need) { FactoryBot.build(:ci_build_need, build: build) }
+
+ it 'sets partition_id to the current partition value' do
+ expect { build_need.valid? }.to change { build_need.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:build_need) { FactoryBot.build(:ci_build_need, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { build_need.valid? }.not_to change { build_need.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ let(:build_need) { FactoryBot.build(:ci_build_need, build: nil) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { build_need.valid? }.not_to change { build_need.partition_id }
+ end
+ end
+
+ context 'when using bulk_insert' do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:ci_build) { build(:ci_build, pipeline: new_pipeline) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'creates build needs successfully', :aggregate_failures 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
+
+ expect(described_class.count).to eq(3)
+ expect(described_class.first.partition_id).to eq(ci_testing_partition_id)
+ expect(described_class.second.partition_id).to eq(ci_testing_partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_pending_state_spec.rb b/spec/models/ci/build_pending_state_spec.rb
index a546d2aff65..756180621ec 100644
--- a/spec/models/ci/build_pending_state_spec.rb
+++ b/spec/models/ci/build_pending_state_spec.rb
@@ -24,4 +24,33 @@ RSpec.describe Ci::BuildPendingState do
end
end
end
+
+ describe 'partitioning' do
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+ let(:build_pending_state) { FactoryBot.build(:ci_build_pending_state, build: build) }
+
+ it 'sets partition_id to the current partition value' do
+ expect { build_pending_state.valid? }.to change { build_pending_state.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:build_pending_state) { FactoryBot.build(:ci_build_pending_state, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { build_pending_state.valid? }.not_to change { build_pending_state.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ let(:build_pending_state) { FactoryBot.build(:ci_build_pending_state, build: nil) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { build_pending_state.valid? }.not_to change { build_pending_state.partition_id }
+ 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 09ea19cf077..90b23d3e824 100644
--- a/spec/models/ci/build_report_result_spec.rb
+++ b/spec/models/ci/build_report_result_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::BuildReportResult do
- let(:build_report_result) { build(:ci_build_report_result, :with_junit_success) }
+ let_it_be_with_reload(:build_report_result) { create(:ci_build_report_result, :with_junit_success) }
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:project) }
@@ -70,4 +70,34 @@ RSpec.describe Ci::BuildReportResult do
expect(build_report_result.tests_skipped).to eq(0)
end
end
+
+ describe 'partitioning' do
+ let(:build_report_result) { FactoryBot.build(:ci_build_report_result, build: build) }
+
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+
+ it 'copies the partition_id from build' do
+ expect { build_report_result.valid? }.to change { build_report_result.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:build_report_result) { FactoryBot.build(:ci_build_report_result, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { build_report_result.valid? }.not_to change { build_report_result.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ subject(:build_report_result) { FactoryBot.build(:ci_build_report_result, build: nil, partition_id: 125) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { build_report_result.valid? }.not_to change { build_report_result.partition_id }
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_runner_session_spec.rb b/spec/models/ci/build_runner_session_spec.rb
index 8dfe854511c..5e1a489ed8b 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'
-RSpec.describe Ci::BuildRunnerSession, model: true do
+RSpec.describe Ci::BuildRunnerSession, model: true, feature_category: :continuous_integration do
let!(:build) { create(:ci_build, :with_runner_session) }
let(:url) { 'https://new.example.com' }
@@ -174,4 +174,20 @@ RSpec.describe Ci::BuildRunnerSession, model: true do
end
end
end
+
+ describe 'partitioning' do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:new_build) { create(:ci_build, pipeline: new_pipeline) }
+ let(:build_runner_session) { create(:ci_build_runner_session, build: new_build) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'assigns the same partition id as the one that build has' do
+ expect(build_runner_session.partition_id).to eq(ci_testing_partition_id)
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 813b4b3faa6..c978e33bf54 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Build do
+RSpec.describe Ci::Build, feature_category: :continuous_integration do
include Ci::TemplateHelpers
include AfterNextHelpers
@@ -1754,8 +1754,8 @@ RSpec.describe Ci::Build do
end
end
- describe '#starts_environment?' do
- subject { build.starts_environment? }
+ describe '#deployment_job?' do
+ subject { build.deployment_job? }
context 'when environment is defined' do
before do
@@ -2528,20 +2528,24 @@ RSpec.describe Ci::Build do
end
describe '#ref_slug' do
- {
- 'master' => 'master',
- '1-foo' => '1-foo',
- 'fix/1-foo' => 'fix-1-foo',
- 'fix-1-foo' => 'fix-1-foo',
- 'a' * 63 => 'a' * 63,
- 'a' * 64 => 'a' * 63,
- 'FOO' => 'foo',
- '-' + 'a' * 61 + '-' => 'a' * 61,
- '-' + 'a' * 62 + '-' => 'a' * 62,
- '-' + 'a' * 63 + '-' => 'a' * 62,
- 'a' * 62 + ' ' => 'a' * 62
- }.each do |ref, slug|
- it "transforms #{ref} to #{slug}" do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:ref, :slug) do
+ 'master' | 'master'
+ '1-foo' | '1-foo'
+ 'fix/1-foo' | 'fix-1-foo'
+ 'fix-1-foo' | 'fix-1-foo'
+ 'a' * 63 | 'a' * 63
+ 'a' * 64 | 'a' * 63
+ 'FOO' | 'foo'
+ '-' + 'a' * 61 + '-' | 'a' * 61
+ '-' + 'a' * 62 + '-' | 'a' * 62
+ '-' + 'a' * 63 + '-' | 'a' * 62
+ 'a' * 62 + ' ' | 'a' * 62
+ end
+
+ with_them do
+ it "transforms ref to slug" do
build.ref = ref
expect(build.ref_slug).to eq(slug)
@@ -2737,6 +2741,7 @@ RSpec.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_NAMESPACE_ID', value: project.namespace.id.to_s, 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 },
@@ -2883,6 +2888,9 @@ RSpec.describe Ci::Build do
value: 'var',
public: true }]
build.environment = 'staging'
+
+ # CI_ENVIRONMENT_NAME is set in predefined_variables when job environment is provided
+ predefined_variables.insert(20, { key: 'CI_ENVIRONMENT_NAME', value: 'staging', public: true, masked: false })
end
it 'matches explicit variables ordering' do
@@ -3539,8 +3547,8 @@ RSpec.describe Ci::Build do
rsa_key = OpenSSL::PKey::RSA.generate(3072).to_s
stub_application_setting(ci_jwt_signing_key: rsa_key)
build.metadata.update!(id_tokens: {
- 'ID_TOKEN_1' => { id_token: { aud: 'developers' } },
- 'ID_TOKEN_2' => { id_token: { aud: 'maintainers' } }
+ 'ID_TOKEN_1' => { aud: 'developers' },
+ 'ID_TOKEN_2' => { aud: 'maintainers' }
})
end
@@ -3817,22 +3825,6 @@ RSpec.describe Ci::Build do
it 'assigns the token' do
expect { build.enqueue }.to change(build, :token).from(nil).to(an_instance_of(String))
end
-
- context 'with ci_assign_job_token_on_scheduling disabled' do
- before do
- stub_feature_flags(ci_assign_job_token_on_scheduling: false)
- end
-
- it 'assigns the token on creation' do
- expect(build.token).to be_present
- end
-
- it 'does not change the token when enqueuing' do
- expect { build.enqueue }.not_to change(build, :token)
-
- expect(build).to be_pending
- end
- end
end
describe 'state transition: pending: :running' do
@@ -5442,7 +5434,7 @@ RSpec.describe Ci::Build do
it 'delegates to Ci::BuildTraceMetadata' do
expect(Ci::BuildTraceMetadata)
.to receive(:find_or_upsert_for!)
- .with(build.id)
+ .with(build.id, build.partition_id)
build.ensure_trace_metadata!
end
@@ -5617,4 +5609,72 @@ RSpec.describe Ci::Build do
end
end
end
+
+ describe '#runtime_hooks' do
+ let(:build1) do
+ FactoryBot.build(:ci_build,
+ options: { hooks: { pre_get_sources_script: ["echo 'hello pre_get_sources_script'"] } })
+ end
+
+ subject(:runtime_hooks) { build1.runtime_hooks }
+
+ it 'returns an array of hook objects' do
+ expect(runtime_hooks.size).to eq(1)
+ expect(runtime_hooks[0].name).to eq('pre_get_sources_script')
+ expect(runtime_hooks[0].script).to eq(["echo 'hello pre_get_sources_script'"])
+ end
+ end
+
+ describe 'partitioning', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:ci_build) { FactoryBot.build(:ci_build, pipeline: new_pipeline) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'assigns partition_id to job variables successfully', :aggregate_failures do
+ ci_build.job_variables_attributes = [
+ { key: 'TEST_KEY', value: 'new value' },
+ { key: 'NEW_KEY', value: 'exciting new value' }
+ ]
+
+ ci_build.save!
+
+ expect(ci_build.job_variables.count).to eq(2)
+ expect(ci_build.job_variables.first.partition_id).to eq(ci_testing_partition_id)
+ expect(ci_build.job_variables.second.partition_id).to eq(ci_testing_partition_id)
+ end
+ end
+
+ describe 'assigning token', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:ci_build) { create(:ci_build, pipeline: new_pipeline) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'includes partition_id as a token prefix' do
+ prefix = ci_build.token.split('_').first.to_i(16)
+
+ expect(prefix).to eq(ci_testing_partition_id)
+ end
+
+ context 'when ci_build_partition_id_token_prefix is disabled' do
+ before do
+ stub_feature_flags(ci_build_partition_id_token_prefix: false)
+ end
+
+ it 'does not include partition_id as a token prefix' do
+ prefix = ci_build.token.split('_').first.to_i(16)
+
+ expect(prefix).not_to eq(ci_testing_partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 3328ed62f15..ac0a18a176d 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -15,13 +15,13 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git
described_class.new(build: build, chunk_index: chunk_index, data_store: data_store, raw_data: raw_data)
end
- it_behaves_like 'having unique enum values'
-
before do
stub_feature_flags(ci_enable_live_trace: true)
stub_artifacts_object_storage
end
+ it_behaves_like 'having unique enum values'
+
def redis_instance
{
redis: Gitlab::Redis::SharedState,
@@ -954,4 +954,33 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git
it { is_expected.to eq(value) }
end
end
+
+ describe 'partitioning' do
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+ let(:build_trace_chunk) { FactoryBot.build(:ci_build_trace_chunk, build: build) }
+
+ it 'sets partition_id to the current partition value' do
+ expect { build_trace_chunk.valid? }.to change { build_trace_chunk.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:build_trace_chunk) { FactoryBot.build(:ci_build_trace_chunk, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { build_trace_chunk.valid? }.not_to change { build_trace_chunk.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ let(:build_trace_chunk) { FactoryBot.build(:ci_build_trace_chunk, build: nil, partition_id: 125) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { build_trace_chunk.valid? }.not_to change { build_trace_chunk.partition_id }
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_trace_metadata_spec.rb b/spec/models/ci/build_trace_metadata_spec.rb
index 120e4289da2..2ab300e4054 100644
--- a/spec/models/ci/build_trace_metadata_spec.rb
+++ b/spec/models/ci/build_trace_metadata_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::BuildTraceMetadata do
+RSpec.describe Ci::BuildTraceMetadata, feature_category: :continuous_integration do
it { is_expected.to belong_to(:build) }
it { is_expected.to belong_to(:trace_artifact) }
@@ -106,7 +106,7 @@ RSpec.describe Ci::BuildTraceMetadata do
let_it_be(:build) { create(:ci_build) }
subject(:execute) do
- described_class.find_or_upsert_for!(build.id)
+ described_class.find_or_upsert_for!(build.id, build.partition_id)
end
it 'creates a new record' do
@@ -158,4 +158,22 @@ RSpec.describe Ci::BuildTraceMetadata do
it { is_expected.to eq(result) }
end
end
+
+ describe 'partitioning' do
+ include Ci::PartitioningHelpers
+
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+ let_it_be(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:new_build) { create(:ci_build, pipeline: new_pipeline) }
+ let(:metadata) { create(:ci_build_trace_metadata, build: new_build) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'assigns the same partition id as the one that build has' do
+ expect(metadata.partition_id).to eq(ci_testing_partition_id)
+ end
+ end
end
diff --git a/spec/models/ci/freeze_period_spec.rb b/spec/models/ci/freeze_period_spec.rb
index b9bf1657e28..d8add736d6a 100644
--- a/spec/models/ci/freeze_period_spec.rb
+++ b/spec/models/ci/freeze_period_spec.rb
@@ -2,16 +2,22 @@
require 'spec_helper'
-RSpec.describe Ci::FreezePeriod, type: :model do
+RSpec.describe Ci::FreezePeriod, feature_category: :release_orchestration, type: :model do
+ let_it_be(:project) { create(:project) }
+
+ # Freeze period factory is on a weekend, so we travel in time, in and around that.
+ let(:friday_2300_time) { Time.utc(2020, 4, 10, 23, 0) }
+ let(:saturday_1200_time) { Time.utc(2020, 4, 11, 12, 0) }
+ let(:monday_0700_time) { Time.utc(2020, 4, 13, 7, 0) }
+ let(:tuesday_0800_time) { Time.utc(2020, 4, 14, 8, 0) }
+
subject { build(:ci_freeze_period) }
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:project) }
- let!(:model) { create(:ci_freeze_period, project: parent) }
+ let!(:model) { create(:ci_freeze_period, project: parent) }
end
- let(:invalid_cron) { '0 0 0 * *' }
-
it { is_expected.to belong_to(:project) }
it { is_expected.to respond_to(:freeze_start) }
@@ -19,37 +25,142 @@ RSpec.describe Ci::FreezePeriod, type: :model do
it { is_expected.to respond_to(:cron_timezone) }
describe 'cron validations' do
+ let(:invalid_cron) { '0 0 0 * *' }
+
it 'allows valid cron patterns' do
- freeze_period = build(:ci_freeze_period)
+ freeze_period = build_stubbed(:ci_freeze_period)
expect(freeze_period).to be_valid
end
it 'does not allow invalid cron patterns on freeze_start' do
- freeze_period = build(:ci_freeze_period, freeze_start: invalid_cron)
+ freeze_period = build_stubbed(:ci_freeze_period, freeze_start: invalid_cron)
expect(freeze_period).not_to be_valid
end
it 'does not allow invalid cron patterns on freeze_end' do
- freeze_period = build(:ci_freeze_period, freeze_end: invalid_cron)
+ freeze_period = build_stubbed(:ci_freeze_period, freeze_end: invalid_cron)
expect(freeze_period).not_to be_valid
end
it 'does not allow an invalid timezone' do
- freeze_period = build(:ci_freeze_period, cron_timezone: 'invalid')
+ freeze_period = build_stubbed(:ci_freeze_period, cron_timezone: 'invalid')
expect(freeze_period).not_to be_valid
end
context 'when cron contains trailing whitespaces' do
it 'strips the attribute' do
- freeze_period = build(:ci_freeze_period, freeze_start: ' 0 0 * * * ')
+ freeze_period = build_stubbed(:ci_freeze_period, freeze_start: ' 0 0 * * * ')
expect(freeze_period).to be_valid
expect(freeze_period.freeze_start).to eq('0 0 * * *')
end
end
end
+
+ shared_examples 'within freeze period' do |time|
+ it 'is frozen' do
+ travel_to(time) do
+ expect(subject).to eq(Ci::FreezePeriod::STATUS_ACTIVE)
+ end
+ end
+ end
+
+ shared_examples 'outside freeze period' do |time|
+ it 'is not frozen' do
+ travel_to(time) do
+ expect(subject).to eq(Ci::FreezePeriod::STATUS_INACTIVE)
+ end
+ end
+ end
+
+ describe '#status' do
+ subject { freeze_period.status }
+
+ describe 'single freeze period' do
+ let(:freeze_period) do
+ build_stubbed(:ci_freeze_period, project: project)
+ end
+
+ it_behaves_like 'outside freeze period', Time.utc(2020, 4, 10, 22, 59)
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 1)
+ it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 6, 59)
+ it_behaves_like 'outside freeze period', Time.utc(2020, 4, 13, 7, 1)
+ end
+
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/370472
+ context 'when period overlaps with itself' do
+ let(:freeze_period) do
+ build_stubbed(:ci_freeze_period, project: project, freeze_start: '* * * 8 *', freeze_end: '* * * 10 *')
+ end
+
+ it_behaves_like 'within freeze period', Time.utc(2020, 8, 11, 0, 0)
+ it_behaves_like 'outside freeze period', Time.utc(2020, 10, 11, 0, 0)
+ end
+ end
+
+ shared_examples 'a freeze period method' do
+ let(:freeze_period) { build_stubbed(:ci_freeze_period, project: project) }
+
+ it 'returns the correct value' do
+ travel_to(now) do
+ expect(freeze_period.send(method)).to eq(expected)
+ end
+ end
+ end
+
+ describe '#active?' do
+ context 'when freeze period status is active' do
+ it_behaves_like 'a freeze period method' do
+ let(:now) { saturday_1200_time }
+ let(:method) { :active? }
+ let(:expected) { true }
+ end
+ end
+
+ context 'when freeze period status is inactive' do
+ it_behaves_like 'a freeze period method' do
+ let(:now) { tuesday_0800_time }
+ let(:method) { :active? }
+ let(:expected) { false }
+ end
+ end
+ end
+
+ describe '#time_start' do
+ it_behaves_like 'a freeze period method' do
+ let(:now) { monday_0700_time }
+ let(:method) { :time_start }
+ let(:expected) { friday_2300_time }
+ end
+ end
+
+ describe '#next_time_start' do
+ let(:next_friday_2300_time) { Time.utc(2020, 4, 17, 23, 0) }
+
+ it_behaves_like 'a freeze period method' do
+ let(:now) { monday_0700_time }
+ let(:method) { :next_time_start }
+ let(:expected) { next_friday_2300_time }
+ end
+ end
+
+ describe '#time_end_from_now' do
+ it_behaves_like 'a freeze period method' do
+ let(:now) { saturday_1200_time }
+ let(:method) { :time_end_from_now }
+ let(:expected) { monday_0700_time }
+ end
+ end
+
+ describe '#time_end_from_start' do
+ it_behaves_like 'a freeze period method' do
+ let(:now) { saturday_1200_time }
+ let(:method) { :time_end_from_start }
+ let(:expected) { monday_0700_time }
+ end
+ end
end
diff --git a/spec/models/ci/freeze_period_status_spec.rb b/spec/models/ci/freeze_period_status_spec.rb
deleted file mode 100644
index ecbb7af64f7..00000000000
--- a/spec/models/ci/freeze_period_status_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-
-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' }
- let(:monday_0700) { '0 7 * * 1' }
-
- subject { described_class.new(project: project).execute }
-
- shared_examples 'within freeze period' do |time|
- it 'is frozen' do
- travel_to(time) do
- expect(subject).to be_truthy
- end
- end
- end
-
- shared_examples 'outside freeze period' do |time|
- it 'is not frozen' do
- travel_to(time) do
- expect(subject).to be_falsy
- end
- end
- end
-
- describe 'single freeze period' do
- let!(:freeze_period) { create(:ci_freeze_period, project: project, freeze_start: friday_2300, freeze_end: monday_0700) }
-
- it_behaves_like 'outside freeze period', Time.utc(2020, 4, 10, 22, 59)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 1)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 6, 59)
-
- it_behaves_like 'outside freeze period', Time.utc(2020, 4, 13, 7, 1)
- end
-
- describe 'multiple freeze periods' do
- # '30 23 * * 5' == "At 23:30 on Friday."", '0 8 * * 1' == "At 08:00 on Monday.""
- let(:friday_2330) { '30 23 * * 5' }
- let(:monday_0800) { '0 8 * * 1' }
-
- let!(:freeze_period_1) { create(:ci_freeze_period, project: project, freeze_start: friday_2300, freeze_end: monday_0700) }
- let!(:freeze_period_2) { create(:ci_freeze_period, project: project, freeze_start: friday_2330, freeze_end: monday_0800) }
-
- it_behaves_like 'outside freeze period', Time.utc(2020, 4, 10, 22, 59)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 29)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 11, 10, 0)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 10, 23, 1)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 6, 59)
-
- it_behaves_like 'within freeze period', Time.utc(2020, 4, 13, 7, 59)
-
- it_behaves_like 'outside freeze period', Time.utc(2020, 4, 13, 8, 1)
- end
-
- # https://gitlab.com/gitlab-org/gitlab/-/issues/370472
- context 'when period overlaps with itself' do
- let!(:freeze_period) { create(:ci_freeze_period, project: project, freeze_start: '* * * 8 *', freeze_end: '* * * 10 *') }
-
- it_behaves_like 'within freeze period', Time.utc(2020, 8, 11, 0, 0)
-
- it_behaves_like 'outside freeze period', Time.utc(2020, 10, 11, 0, 0)
- end
-end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 098f8bd4514..18aaab1d1f3 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -356,50 +356,6 @@ RSpec.describe Ci::JobArtifact do
end
end
- describe 'callbacks' do
- describe '#schedule_background_upload' do
- subject { create(:ci_job_artifact, :archive) }
-
- context 'when object storage is disabled' do
- before do
- stub_artifacts_object_storage(enabled: false)
- end
-
- it 'does not schedule the migration' do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- subject
- end
- end
-
- context 'when object storage is enabled' do
- context 'when background upload is enabled' do
- before do
- stub_artifacts_object_storage(background_upload: true)
- end
-
- it 'schedules the model for migration' do
- expect(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async).with('JobArtifactUploader', described_class.name, :file, kind_of(Numeric))
-
- subject
- end
- end
-
- context 'when background upload is disabled' do
- before do
- stub_artifacts_object_storage(background_upload: false)
- end
-
- it 'schedules the model for migration' do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- subject
- end
- end
- end
- end
- end
-
context 'creating the artifact' do
let(:project) { create(:project) }
let(:artifact) { create(:ci_job_artifact, :archive, project: project) }
diff --git a/spec/models/ci/job_token/allowlist_spec.rb b/spec/models/ci/job_token/allowlist_spec.rb
new file mode 100644
index 00000000000..45083d64393
--- /dev/null
+++ b/spec/models/ci/job_token/allowlist_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::JobToken::Allowlist, feature_category: :continuous_integration do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:source_project) { create(:project) }
+
+ let(:allowlist) { described_class.new(source_project, direction: direction) }
+ let(:direction) { :outbound }
+
+ describe '#projects' do
+ subject(:projects) { allowlist.projects }
+
+ context 'when no projects are added to the scope' do
+ [:inbound, :outbound].each do |d|
+ let(:direction) { d }
+
+ it 'returns the project defining the scope' do
+ expect(projects).to contain_exactly(source_project)
+ end
+ end
+ end
+
+ context 'when projects are added to the scope' do
+ include_context 'with scoped projects'
+
+ where(:direction, :additional_project) do
+ :outbound | ref(:outbound_scoped_project)
+ :inbound | ref(:inbound_scoped_project)
+ end
+
+ with_them do
+ it 'returns all projects that can be accessed from a given scope' do
+ expect(projects).to contain_exactly(source_project, additional_project)
+ end
+ end
+ end
+ end
+
+ describe '#includes?' do
+ subject { allowlist.includes?(includes_project) }
+
+ context 'without scoped projects' do
+ let(:unscoped_project) { build(:project) }
+
+ where(:includes_project, :direction, :result) do
+ ref(:source_project) | :outbound | false
+ ref(:source_project) | :inbound | false
+ ref(:unscoped_project) | :outbound | false
+ ref(:unscoped_project) | :inbound | false
+ end
+
+ with_them do
+ it { is_expected.to be result }
+ end
+ end
+
+ context 'with scoped projects' do
+ include_context 'with scoped projects'
+
+ where(:includes_project, :direction, :result) do
+ ref(:source_project) | :outbound | false
+ ref(:source_project) | :inbound | false
+ ref(:inbound_scoped_project) | :outbound | false
+ ref(:inbound_scoped_project) | :inbound | true
+ ref(:outbound_scoped_project) | :outbound | true
+ ref(:outbound_scoped_project) | :inbound | false
+ ref(:unscoped_project1) | :outbound | false
+ ref(:unscoped_project1) | :inbound | false
+ ref(:unscoped_project2) | :outbound | false
+ ref(:unscoped_project2) | :inbound | false
+ end
+
+ with_them do
+ it { is_expected.to be result }
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/job_token/project_scope_link_spec.rb b/spec/models/ci/job_token/project_scope_link_spec.rb
index 92ed86b55b2..91491733c44 100644
--- a/spec/models/ci/job_token/project_scope_link_spec.rb
+++ b/spec/models/ci/job_token/project_scope_link_spec.rb
@@ -2,14 +2,14 @@
require 'spec_helper'
-RSpec.describe Ci::JobToken::ProjectScopeLink do
+RSpec.describe Ci::JobToken::ProjectScopeLink, feature_category: :continuous_integration do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:group) { create(:group) }
+
it { is_expected.to belong_to(:source_project) }
it { is_expected.to belong_to(:target_project) }
it { is_expected.to belong_to(:added_by) }
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project) }
-
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:user) }
let!(:model) { create(:ci_job_token_project_scope_link, added_by: parent) }
@@ -50,8 +50,8 @@ RSpec.describe Ci::JobToken::ProjectScopeLink do
end
end
- describe '.from_project' do
- subject { described_class.from_project(project) }
+ describe '.with_source' do
+ subject { described_class.with_source(project) }
let!(:source_link) { create(:ci_job_token_project_scope_link, source_project: project) }
let!(:target_link) { create(:ci_job_token_project_scope_link, target_project: project) }
@@ -61,8 +61,8 @@ RSpec.describe Ci::JobToken::ProjectScopeLink do
end
end
- describe '.to_project' do
- subject { described_class.to_project(project) }
+ describe '.with_target' do
+ subject { described_class.with_target(project) }
let!(:source_link) { create(:ci_job_token_project_scope_link, source_project: project) }
let!(:target_link) { create(:ci_job_token_project_scope_link, target_project: project) }
diff --git a/spec/models/ci/job_token/scope_spec.rb b/spec/models/ci/job_token/scope_spec.rb
index 1e3f6d044d2..37c56973506 100644
--- a/spec/models/ci/job_token/scope_spec.rb
+++ b/spec/models/ci/job_token/scope_spec.rb
@@ -2,58 +2,72 @@
require 'spec_helper'
-RSpec.describe Ci::JobToken::Scope do
- let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
+RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration do
+ let_it_be(:source_project) { create(:project, ci_outbound_job_token_scope_enabled: true) }
- let(:scope) { described_class.new(project) }
+ let(:scope) { described_class.new(source_project) }
describe '#all_projects' do
subject(:all_projects) { scope.all_projects }
context 'when no projects are added to the scope' do
it 'returns the project defining the scope' do
- expect(all_projects).to contain_exactly(project)
+ expect(all_projects).to contain_exactly(source_project)
end
end
- context 'when other projects are added to the scope' do
- let_it_be(:scoped_project) { create(:project) }
- let_it_be(:unscoped_project) { create(:project) }
-
- let!(:link_in_scope) { create(:ci_job_token_project_scope_link, source_project: project, target_project: scoped_project) }
- let!(:link_out_of_scope) { create(:ci_job_token_project_scope_link, target_project: unscoped_project) }
+ context 'when projects are added to the scope' do
+ include_context 'with scoped projects'
it 'returns all projects that can be accessed from a given scope' do
- expect(subject).to contain_exactly(project, scoped_project)
+ expect(subject).to contain_exactly(source_project, outbound_scoped_project)
end
end
end
- describe '#includes?' do
- subject { scope.includes?(target_project) }
+ describe '#allows?' do
+ subject { scope.allows?(includes_project) }
- context 'when param is the project defining the scope' do
- let(:target_project) { project }
+ context 'without scoped projects' do
+ context 'when self referential' do
+ let(:includes_project) { source_project }
- it { is_expected.to be_truthy }
+ it { is_expected.to be_truthy }
+ end
end
- context 'when param is a project in scope' do
- let(:target_link) { create(:ci_job_token_project_scope_link, source_project: project) }
- let(:target_project) { target_link.target_project }
+ context 'with scoped projects' do
+ include_context 'with scoped projects'
- it { is_expected.to be_truthy }
- end
+ context 'when project is in outbound scope' do
+ let(:includes_project) { outbound_scoped_project }
- context 'when param is a project in another scope' do
- let(:scope_link) { create(:ci_job_token_project_scope_link) }
- let(:target_project) { scope_link.target_project }
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when project is in inbound scope' do
+ let(:includes_project) { inbound_scoped_project }
+
+ it { is_expected.to be_falsey }
+ end
- it { is_expected.to be_falsey }
+ context 'when project is linked to a different project' do
+ let(:includes_project) { unscoped_project1 }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when project is unlinked to a project' do
+ let(:includes_project) { unscoped_project2 }
+
+ it { is_expected.to be_falsey }
+ end
context 'when project scope setting is disabled' do
+ let(:includes_project) { unscoped_project1 }
+
before do
- project.ci_outbound_job_token_scope_enabled = false
+ source_project.ci_outbound_job_token_scope_enabled = false
end
it 'considers any project to be part of the scope' do
diff --git a/spec/models/ci/job_variable_spec.rb b/spec/models/ci/job_variable_spec.rb
index 4aebd3283f0..0a65708160a 100644
--- a/spec/models/ci/job_variable_spec.rb
+++ b/spec/models/ci/job_variable_spec.rb
@@ -2,11 +2,63 @@
require 'spec_helper'
-RSpec.describe Ci::JobVariable do
- subject { build(:ci_job_variable) }
-
+RSpec.describe Ci::JobVariable, feature_category: :continuous_integration do
it_behaves_like "CI variable"
- it { is_expected.to belong_to(:job) }
- it { is_expected.to validate_uniqueness_of(:key).scoped_to(:job_id) }
+ describe 'associations' do
+ let!(:job_variable) { create(:ci_job_variable) }
+
+ it { is_expected.to belong_to(:job) }
+ it { is_expected.to validate_uniqueness_of(:key).scoped_to(:job_id) }
+ end
+
+ describe 'partitioning' do
+ let(:job_variable) { build(:ci_job_variable, job: ci_build) }
+
+ context 'with build' do
+ let(:ci_build) { build(:ci_build, partition_id: ci_testing_partition_id) }
+
+ it 'copies the partition_id from build' do
+ expect { job_variable.valid? }.to change { job_variable.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:job_variable) { build(:ci_job_variable, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { job_variable.valid? }.not_to change { job_variable.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ subject(:job_variable) { build(:ci_job_variable, job: nil, partition_id: 125) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { job_variable.valid? }.not_to change { job_variable.partition_id }
+ end
+ end
+
+ context 'when using bulk_insert', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:ci_build) { create(:ci_build, pipeline: new_pipeline) }
+ let(:job_variable_2) { build(:ci_job_variable, job: ci_build) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'creates job variables successfully', :aggregate_failures do
+ described_class.bulk_insert!([job_variable, job_variable_2])
+
+ expect(described_class.count).to eq(2)
+ expect(described_class.first.partition_id).to eq(ci_testing_partition_id)
+ expect(described_class.last.partition_id).to eq(ci_testing_partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/pending_build_spec.rb b/spec/models/ci/pending_build_spec.rb
index 4bb43233dbd..331522070df 100644
--- a/spec/models/ci/pending_build_spec.rb
+++ b/spec/models/ci/pending_build_spec.rb
@@ -196,6 +196,28 @@ RSpec.describe Ci::PendingBuild do
end
end
+ describe 'partitioning', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ before do
+ stub_current_partition_id
+ end
+
+ let(:new_pipeline ) { create(:ci_pipeline, project: pipeline.project) }
+ let(:new_build) { create(:ci_build, pipeline: new_pipeline) }
+
+ it 'assigns the same partition id as the one that build has', :aggregate_failures do
+ expect(new_build.partition_id).to eq ci_testing_partition_id
+ expect(new_build.partition_id).not_to eq pipeline.partition_id
+
+ described_class.upsert_from_build!(build)
+ described_class.upsert_from_build!(new_build)
+
+ expect(build.reload.queuing_entry.partition_id).to eq pipeline.partition_id
+ expect(new_build.reload.queuing_entry.partition_id).to eq ci_testing_partition_id
+ end
+ end
+
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:namespace) }
let!(:model) { create(:ci_pending_build, namespace: parent) }
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index b28b61e2b39..9b70f7c2839 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineSchedule do
+RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration do
let_it_be_with_reload(:project) { create_default(:project) }
subject { build(:ci_pipeline_schedule) }
@@ -41,6 +41,12 @@ RSpec.describe Ci::PipelineSchedule do
expect(pipeline_schedule).not_to be_valid
end
+ it 'does not allow empty variable key' do
+ pipeline_schedule = build(:ci_pipeline_schedule, variables_attributes: [{ secret_value: 'test_value' }])
+
+ expect(pipeline_schedule).not_to be_valid
+ end
+
context 'when active is false' do
it 'does not allow nullified ref' do
pipeline_schedule = build(:ci_pipeline_schedule, :inactive, ref: nil)
@@ -110,48 +116,18 @@ RSpec.describe Ci::PipelineSchedule do
end
describe '#set_next_run_at' do
- using RSpec::Parameterized::TableSyntax
-
- where(:worker_cron, :schedule_cron, :plan_limit, :now, :result) do
- '0 1 2 3 *' | '0 1 * * *' | nil | Time.zone.local(2021, 3, 2, 1, 0) | Time.zone.local(2022, 3, 2, 1, 0)
- '0 1 2 3 *' | '0 1 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 3, 2, 1, 0) | Time.zone.local(2022, 3, 2, 1, 0)
- '*/5 * * * *' | '*/1 * * * *' | nil | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 5)
- '*/5 * * * *' | '*/1 * * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
- '*/5 * * * *' | '*/1 * * * *' | (1.day.in_minutes / 10).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10)
- '*/5 * * * *' | '*/1 * * * *' | 200 | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10)
- '*/5 * * * *' | '0 * * * *' | nil | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 5)
- '*/5 * * * *' | '0 * * * *' | (1.day.in_minutes / 10).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
- '*/5 * * * *' | '0 * * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
- '*/5 * * * *' | '0 * * * *' | (1.day.in_minutes / 2.hours.in_minutes).to_i | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 5)
- '*/5 * * * *' | '0 1 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
- '*/5 * * * *' | '0 1 * * *' | (1.day.in_minutes / 10).to_i | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
- '*/5 * * * *' | '0 1 * * *' | (1.day.in_minutes / 8).to_i | Time.zone.local(2021, 5, 27, 1, 0) | Time.zone.local(2021, 5, 28, 1, 0)
- '*/5 * * * *' | '0 1 1 * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 1, 1, 0) | Time.zone.local(2021, 6, 1, 1, 0)
- '*/9 * * * *' | '0 1 1 * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 1, 1, 9) | Time.zone.local(2021, 6, 1, 1, 0)
- '*/5 * * * *' | '59 14 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | Time.zone.local(2021, 5, 1, 15, 0) | Time.zone.local(2021, 5, 2, 15, 0)
- '*/5 * * * *' | '45 21 1 2 *' | (1.day.in_minutes / 5).to_i | Time.zone.local(2021, 2, 1, 21, 45) | Time.zone.local(2022, 2, 1, 21, 45)
- end
+ let(:now) { Time.zone.local(2021, 3, 2, 1, 0) }
+ let(:pipeline_schedule) { create(:ci_pipeline_schedule, cron: "0 1 * * *") }
- with_them do
- let(:pipeline_schedule) { create(:ci_pipeline_schedule, cron: schedule_cron) }
+ it 'calls fallback method next_run_at if there is no plan limit' do
+ allow(Settings).to receive(:cron_jobs).and_return({ 'pipeline_schedule_worker' => { 'cron' => "0 1 2 3 *" } })
- before do
- allow(Settings).to receive(:cron_jobs) do
- { 'pipeline_schedule_worker' => { 'cron' => worker_cron } }
- end
+ travel_to(now) do
+ expect(pipeline_schedule).to receive(:calculate_next_run_at).and_call_original
- create(:plan_limits, :default_plan, ci_daily_pipeline_schedule_triggers: plan_limit) if plan_limit
+ pipeline_schedule.set_next_run_at
- # Setting this here to override initial save with the current time
- pipeline_schedule.next_run_at = now
- end
-
- it 'updates next_run_at' do
- travel_to(now) do
- pipeline_schedule.set_next_run_at
-
- expect(pipeline_schedule.next_run_at).to eq(result)
- end
+ expect(pipeline_schedule.next_run_at).to eq(Time.zone.local(2022, 3, 2, 1, 0))
end
end
@@ -288,6 +264,17 @@ RSpec.describe Ci::PipelineSchedule do
end
end
+ describe '#worker_cron' do
+ before do
+ allow(Settings).to receive(:cron_jobs)
+ .and_return({ pipeline_schedule_worker: { cron: "* 1 2 3 4" } }.with_indifferent_access)
+ end
+
+ it "returns cron expression set in Settings" do
+ expect(subject.worker_cron_expression).to eq("* 1 2 3 4")
+ end
+ end
+
context 'loose foreign key on ci_pipeline_schedules.project_id' do
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:project) }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 2c945898e61..b72693d9994 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -219,6 +219,29 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '.for_name' do
+ subject { described_class.for_name(name) }
+
+ let_it_be(:pipeline1) { create(:ci_pipeline, name: 'Build pipeline') }
+ let_it_be(:pipeline2) { create(:ci_pipeline, name: 'Chatops pipeline') }
+
+ context 'when name exists' do
+ let(:name) { 'build Pipeline' }
+
+ it 'performs case insensitive compare' do
+ is_expected.to contain_exactly(pipeline1)
+ end
+ end
+
+ context 'when name does not exist' do
+ let(:name) { 'absent-name' }
+
+ it 'returns empty' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
describe '.created_after' do
let_it_be(:old_pipeline) { create(:ci_pipeline, created_at: 1.week.ago) }
let_it_be(:pipeline) { create(:ci_pipeline) }
@@ -5287,6 +5310,20 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
end
+
+ context 'when the current user is not the bridge user' do
+ let(:current_user) { create(:user) }
+
+ before do
+ project.add_maintainer(current_user)
+ end
+
+ it 'changes bridge user to current user' do
+ expect { reset_bridge }
+ .to change { bridge.reload.user }
+ .from(owner).to(current_user)
+ end
+ end
end
context 'when the user does not have permissions for the processable' do
@@ -5305,6 +5342,15 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
.and not_change { bridge_dependant_dag_job.reload.status }
end
end
+
+ context 'when the current user is not the bridge user' do
+ let(:current_user) { create(:user) }
+
+ it 'does not change bridge user' do
+ expect { reset_bridge }
+ .to not_change { bridge.reload.user }
+ end
+ end
end
end
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index e62e5f84a6d..07fac4ee2f7 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -73,6 +73,7 @@ RSpec.describe Ci::Processable do
job_artifacts_network_referee job_artifacts_dotenv
job_artifacts_cobertura needs job_artifacts_accessibility
job_artifacts_requirements job_artifacts_coverage_fuzzing
+ job_artifacts_requirements_v2
job_artifacts_api_fuzzing terraform_state_versions job_artifacts_cyclonedx].freeze
end
@@ -423,8 +424,8 @@ RSpec.describe Ci::Processable do
it 'returns all needs attributes' do
is_expected.to contain_exactly(
- { 'artifacts' => true, 'name' => 'test1', 'optional' => false },
- { 'artifacts' => true, 'name' => 'test2', 'optional' => false }
+ { 'artifacts' => true, 'name' => 'test1', 'optional' => false, 'partition_id' => build.partition_id },
+ { 'artifacts' => true, 'name' => 'test2', 'optional' => false, 'partition_id' => build.partition_id }
)
end
end
diff --git a/spec/models/ci/resource_group_spec.rb b/spec/models/ci/resource_group_spec.rb
index e8eccc233db..01acf5194f0 100644
--- a/spec/models/ci/resource_group_spec.rb
+++ b/spec/models/ci/resource_group_spec.rb
@@ -33,7 +33,13 @@ RSpec.describe Ci::ResourceGroup do
end
end
- describe '#assign_resource_to' do
+ describe '#assign_resource_to', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ before do
+ stub_current_partition_id
+ end
+
subject { resource_group.assign_resource_to(build) }
let(:build) { create(:ci_build) }
@@ -41,10 +47,12 @@ RSpec.describe Ci::ResourceGroup do
it 'retains resource for the processable' do
expect(resource_group.resources.first.processable).to be_nil
+ expect(resource_group.resources.first.partition_id).to be_nil
is_expected.to eq(true)
expect(resource_group.resources.first.processable).to eq(build)
+ expect(resource_group.resources.first.partition_id).to eq(build.partition_id)
end
context 'when there are no free resources' do
@@ -66,7 +74,13 @@ RSpec.describe Ci::ResourceGroup do
end
end
- describe '#release_resource_from' do
+ describe '#release_resource_from', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ before do
+ stub_current_partition_id
+ end
+
subject { resource_group.release_resource_from(build) }
let(:build) { create(:ci_build) }
@@ -79,10 +93,12 @@ RSpec.describe Ci::ResourceGroup do
it 'releases resource from the build' do
expect(resource_group.resources.first.processable).to eq(build)
+ expect(resource_group.resources.first.partition_id).to eq(build.partition_id)
is_expected.to eq(true)
expect(resource_group.resources.first.processable).to be_nil
+ expect(resource_group.resources.first.partition_id).to be_nil
end
end
diff --git a/spec/models/ci/runner_namespace_spec.rb b/spec/models/ci/runner_namespace_spec.rb
index 2d1fe11147c..6cbb151d703 100644
--- a/spec/models/ci/runner_namespace_spec.rb
+++ b/spec/models/ci/runner_namespace_spec.rb
@@ -12,4 +12,27 @@ RSpec.describe Ci::RunnerNamespace do
let!(:parent) { model.namespace }
end
+
+ describe '.for_runner' do
+ subject(:for_runner) { described_class.for_runner(runner_ids) }
+
+ let_it_be(:group) { create(:group) }
+ let_it_be(:runners) { create_list(:ci_runner, 3, :group, groups: [group]) }
+
+ context 'with runner ids' do
+ let(:runner_ids) { runners[1..2].map(&:id) }
+
+ it 'returns requested runner namespaces' do
+ is_expected.to eq(runners[1..2].flat_map(&:runner_namespaces))
+ end
+ end
+
+ context 'with runners' do
+ let(:runner_ids) { runners.first }
+
+ it 'returns requested runner namespaces' do
+ is_expected.to eq(runners.first.runner_namespaces)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 13eb7086586..803b766c822 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Runner do
+RSpec.describe Ci::Runner, feature_category: :runner do
include StubGitlabCalls
it_behaves_like 'having unique enum values'
@@ -701,6 +701,30 @@ RSpec.describe Ci::Runner do
it { is_expected.to eq([runner1]) }
end
+ describe '.with_running_builds' do
+ subject { described_class.with_running_builds }
+
+ let_it_be(:runner1) { create(:ci_runner) }
+
+ context 'with no builds running' do
+ it { is_expected.to be_empty }
+ end
+
+ context 'with single build running on runner2' do
+ let(:runner2) { create(:ci_runner) }
+ let(:runner3) { create(:ci_runner) }
+
+ before do
+ project = create(:project, :repository)
+ pipeline = create(:ci_pipeline, project: project)
+ create(:ci_build, :running, runner: runner2, pipeline: pipeline)
+ create(:ci_build, :running, runner: runner3, pipeline: pipeline)
+ end
+
+ it { is_expected.to contain_exactly(runner2, runner3) }
+ end
+ end
+
describe '#matches_build?' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/models/ci/runner_version_spec.rb b/spec/models/ci/runner_version_spec.rb
index 7a4b2e8f21e..552b271fe85 100644
--- a/spec/models/ci/runner_version_spec.rb
+++ b/spec/models/ci/runner_version_spec.rb
@@ -2,16 +2,16 @@
require 'spec_helper'
-RSpec.describe Ci::RunnerVersion do
- it_behaves_like 'having unique enum values'
+RSpec.describe Ci::RunnerVersion, feature_category: :runner_fleet do
+ let_it_be(:runner_version_recommended) do
+ create(:ci_runner_version, version: 'abc234', status: :recommended)
+ end
let_it_be(:runner_version_not_available) do
create(:ci_runner_version, version: 'abc123', status: :not_available)
end
- let_it_be(:runner_version_recommended) do
- create(:ci_runner_version, version: 'abc234', status: :recommended)
- end
+ it_behaves_like 'having unique enum values'
describe '.not_available' do
subject { described_class.not_available }
@@ -28,11 +28,9 @@ RSpec.describe Ci::RunnerVersion do
end
it 'contains any valid or unprocessed runner version that is not already recommended' do
- is_expected.to match_array([
- runner_version_nil,
- runner_version_not_available,
- runner_version_available
- ])
+ is_expected.to match_array(
+ [runner_version_nil, runner_version_not_available, runner_version_available]
+ )
end
end
diff --git a/spec/models/ci/running_build_spec.rb b/spec/models/ci/running_build_spec.rb
index d2f74494308..1a5ea044ba3 100644
--- a/spec/models/ci/running_build_spec.rb
+++ b/spec/models/ci/running_build_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::RunningBuild do
+RSpec.describe Ci::RunningBuild, feature_category: :continuous_integration do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
@@ -50,6 +50,28 @@ RSpec.describe Ci::RunningBuild do
end
end
+ describe 'partitioning', :ci_partitionable do
+ include Ci::PartitioningHelpers
+
+ before do
+ stub_current_partition_id
+ end
+
+ let(:new_pipeline ) { create(:ci_pipeline, project: pipeline.project) }
+ let(:new_build) { create(:ci_build, :running, pipeline: new_pipeline, runner: runner) }
+
+ it 'assigns the same partition id as the one that build has', :aggregate_failures do
+ expect(new_build.partition_id).to eq ci_testing_partition_id
+ expect(new_build.partition_id).not_to eq pipeline.partition_id
+
+ described_class.upsert_shared_runner_build!(build)
+ described_class.upsert_shared_runner_build!(new_build)
+
+ expect(build.reload.runtime_metadata.partition_id).to eq pipeline.partition_id
+ expect(new_build.reload.runtime_metadata.partition_id).to eq ci_testing_partition_id
+ end
+ end
+
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:project) }
let!(:model) { create(:ci_running_build, project: parent) }
diff --git a/spec/models/ci/secure_file_spec.rb b/spec/models/ci/secure_file_spec.rb
index 4413bd8e98b..87077fe2db1 100644
--- a/spec/models/ci/secure_file_spec.rb
+++ b/spec/models/ci/secure_file_spec.rb
@@ -138,17 +138,48 @@ RSpec.describe Ci::SecureFile do
end
describe '#update_metadata!' do
- it 'assigns the expected metadata when a parsable file is supplied' do
+ it 'assigns the expected metadata when a parsable .cer file is supplied' do
file = create(:ci_secure_file, name: 'file1.cer',
file: CarrierWaveStringFile.new(fixture_file('ci_secure_files/sample.cer')))
file.update_metadata!
+ file.reload
+
expect(file.expires_at).to eq(DateTime.parse('2022-04-26 19:20:40'))
expect(file.metadata['id']).to eq('33669367788748363528491290218354043267')
expect(file.metadata['issuer']['CN']).to eq('Apple Worldwide Developer Relations Certification Authority')
expect(file.metadata['subject']['OU']).to eq('N7SYAN8PX8')
end
+ it 'assigns the expected metadata when a parsable .p12 file is supplied' do
+ file = create(:ci_secure_file, name: 'file1.p12',
+ file: CarrierWaveStringFile.new(fixture_file('ci_secure_files/sample.p12')))
+ file.update_metadata!
+
+ file.reload
+
+ expect(file.expires_at).to eq(DateTime.parse('2022-09-21 14:56:00'))
+ expect(file.metadata['id']).to eq('75949910542696343243264405377658443914')
+ expect(file.metadata['issuer']['CN']).to eq('Apple Worldwide Developer Relations Certification Authority')
+ expect(file.metadata['subject']['OU']).to eq('N7SYAN8PX8')
+ end
+
+ it 'assigns the expected metadata when a parsable .mobileprovision file is supplied' do
+ file = create(:ci_secure_file, name: 'file1.mobileprovision',
+ file: CarrierWaveStringFile.new(
+ fixture_file('ci_secure_files/sample.mobileprovision')
+ ))
+ file.update_metadata!
+
+ file.reload
+
+ expect(file.expires_at).to eq(DateTime.parse('2023-08-01 23:15:13'))
+ expect(file.metadata['id']).to eq('6b9fcce1-b9a9-4b37-b2ce-ec4da2044abf')
+ expect(file.metadata['platforms'].first).to eq('iOS')
+ expect(file.metadata['app_name']).to eq('iOS Demo')
+ expect(file.metadata['app_id']).to eq('match Development com.gitlab.ios-demo')
+ end
+
it 'logs an error when something goes wrong with the file parsing' do
corrupt_file = create(:ci_secure_file, name: 'file1.cer', file: CarrierWaveStringFile.new('11111111'))
message = 'Validation failed: Metadata must be a valid json schema - not enough data.'
diff --git a/spec/models/ci/sources/pipeline_spec.rb b/spec/models/ci/sources/pipeline_spec.rb
index fdc1c111c40..707872d0a15 100644
--- a/spec/models/ci/sources/pipeline_spec.rb
+++ b/spec/models/ci/sources/pipeline_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Sources::Pipeline do
+RSpec.describe Ci::Sources::Pipeline, feature_category: :continuous_integration do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:pipeline) }
@@ -31,4 +31,20 @@ RSpec.describe Ci::Sources::Pipeline do
let!(:model) { create(:ci_sources_pipeline, project: parent) }
end
end
+
+ describe 'partitioning', :ci_partitioning do
+ include Ci::PartitioningHelpers
+
+ let(:new_pipeline) { create(:ci_pipeline) }
+ let(:source_pipeline) { create(:ci_sources_pipeline, pipeline: new_pipeline) }
+
+ before do
+ stub_current_partition_id
+ end
+
+ it 'assigns partition_id and source_partition_id from pipeline and source_job', :aggregate_failures do
+ expect(source_pipeline.partition_id).to eq(ci_testing_partition_id)
+ expect(source_pipeline.source_partition_id).to eq(ci_testing_partition_id)
+ end
+ end
end
diff --git a/spec/models/ci/unit_test_failure_spec.rb b/spec/models/ci/unit_test_failure_spec.rb
index f9b8c66b603..7ffd103bc7d 100644
--- a/spec/models/ci/unit_test_failure_spec.rb
+++ b/spec/models/ci/unit_test_failure_spec.rb
@@ -70,4 +70,46 @@ RSpec.describe Ci::UnitTestFailure do
end
end
end
+
+ describe 'partitioning', :ci_partitionable do
+ let(:project) { FactoryBot.build(:project) }
+ let(:unit_test) { FactoryBot.build(:ci_unit_test, project: project) }
+
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+ let(:unit_test_failure) do
+ FactoryBot.build(:ci_unit_test_failure, build: build, unit_test: unit_test, failed_at: 1.day.ago)
+ end
+
+ it 'copies the partition_id from build' do
+ expect { unit_test_failure.valid? }.to change { unit_test_failure.partition_id }.to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:unit_test_failure) do
+ FactoryBot.build(
+ :ci_unit_test_failure,
+ build: build,
+ unit_test: unit_test,
+ failed_at: 1.day.ago,
+ partition_id: 125
+ )
+ end
+
+ it 'does not change the partition_id value' do
+ expect { unit_test_failure.valid? }.not_to change { unit_test_failure.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ subject(:unit_test_failure) { FactoryBot.build(:ci_unit_test_failure, build: nil, partition_id: 125) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { unit_test_failure.valid? }.not_to change { unit_test_failure.partition_id }
+ end
+ end
+ end
end