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>2021-12-20 16:37:47 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 16:37:47 +0300
commitaee0a117a889461ce8ced6fcf73207fe017f1d99 (patch)
tree891d9ef189227a8445d83f35c1b0fc99573f4380 /spec/models/ci
parent8d46af3258650d305f53b819eabf7ab18d22f59e (diff)
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'spec/models/ci')
-rw-r--r--spec/models/ci/build_spec.rb49
-rw-r--r--spec/models/ci/job_artifact_spec.rb4
-rw-r--r--spec/models/ci/namespace_mirror_spec.rb94
-rw-r--r--spec/models/ci/pending_build_spec.rb12
-rw-r--r--spec/models/ci/pipeline_spec.rb96
-rw-r--r--spec/models/ci/project_mirror_spec.rb36
-rw-r--r--spec/models/ci/runner_namespace_spec.rb6
-rw-r--r--spec/models/ci/runner_project_spec.rb6
-rw-r--r--spec/models/ci/runner_spec.rb271
-rw-r--r--spec/models/ci/stage_spec.rb12
10 files changed, 483 insertions, 103 deletions
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index b7de8ca4337..b9a12339e61 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe Ci::Build do
it { is_expected.to have_one(:deployment) }
it { is_expected.to have_one(:runner_session) }
it { is_expected.to have_one(:trace_metadata) }
- it { is_expected.to have_many(:terraform_state_versions).dependent(:nullify).inverse_of(:build) }
+ it { is_expected.to have_many(:terraform_state_versions).inverse_of(:build) }
it { is_expected.to validate_presence_of(:ref) }
@@ -1994,6 +1994,14 @@ RSpec.describe Ci::Build do
it { is_expected.not_to be_retryable }
end
+
+ context 'when deployment is rejected' do
+ before do
+ build.drop!(:deployment_rejected)
+ end
+
+ it { is_expected.not_to be_retryable }
+ end
end
end
@@ -2498,7 +2506,7 @@ RSpec.describe Ci::Build do
it { is_expected.to start_with(project.web_url[0..6]) }
it { is_expected.to include(build.token) }
it { is_expected.to include('gitlab-ci-token') }
- it { is_expected.to include(project.web_url[7..-1]) }
+ it { is_expected.to include(project.web_url[7..]) }
end
context 'when token is empty' do
@@ -3421,10 +3429,6 @@ RSpec.describe Ci::Build do
end
describe '#scoped_variables' do
- before do
- pipeline.clear_memoization(:predefined_vars_in_builder_enabled)
- end
-
it 'records a prometheus metric' do
histogram = double(:histogram)
expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_builder_scoped_variables_histogram)
@@ -3522,22 +3526,6 @@ RSpec.describe Ci::Build do
build.scoped_variables
end
-
- context 'when ci builder feature flag is disabled' do
- before do
- stub_feature_flags(ci_predefined_vars_in_builder: false)
- end
-
- it 'does not delegate to the variable builders' do
- expect_next_instance_of(Gitlab::Ci::Variables::Builder) do |builder|
- expect(builder).not_to receive(:predefined_variables)
- end
-
- build.scoped_variables
- end
-
- it_behaves_like 'calculates scoped_variables'
- end
end
describe '#simple_variables_without_dependencies' do
@@ -3782,6 +3770,12 @@ RSpec.describe Ci::Build do
build.enqueue
end
+
+ it 'queues BuildHooksWorker' do
+ expect(BuildHooksWorker).to receive(:perform_async).with(build.id)
+
+ build.enqueue
+ end
end
describe 'state transition: pending: :running' do
@@ -4474,7 +4468,7 @@ RSpec.describe Ci::Build do
'create' => 0,
'update' => 1,
'delete' => 0,
- 'job_name' => build.options.dig(:artifacts, :name).to_s
+ 'job_name' => build.name
)
)
)
@@ -5423,4 +5417,13 @@ RSpec.describe Ci::Build do
expect(subject).to be true
end
end
+
+ it_behaves_like 'it has loose foreign keys' do
+ let(:factory_name) { :ci_build }
+ end
+
+ it_behaves_like 'cleanup by a loose foreign key' do
+ let!(:model) { create(:ci_build, user: create(:user)) }
+ let!(:parent) { model.user }
+ end
end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index d63f87e8943..38061e0975f 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -700,4 +700,8 @@ RSpec.describe Ci::JobArtifact do
when changes or new entries are made.
MSG
end
+
+ it_behaves_like 'it has loose foreign keys' do
+ let(:factory_name) { :ci_job_artifact }
+ end
end
diff --git a/spec/models/ci/namespace_mirror_spec.rb b/spec/models/ci/namespace_mirror_spec.rb
new file mode 100644
index 00000000000..b4c71f51377
--- /dev/null
+++ b/spec/models/ci/namespace_mirror_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::NamespaceMirror do
+ let!(:group1) { create(:group) }
+ let!(:group2) { create(:group, parent: group1) }
+ let!(:group3) { create(:group, parent: group2) }
+ let!(:group4) { create(:group, parent: group3) }
+
+ describe '.sync!' do
+ let!(:event) { namespace.sync_events.create! }
+
+ subject(:sync) { described_class.sync!(event.reload) }
+
+ context 'when namespace hierarchy does not exist in the first place' do
+ let(:namespace) { group3 }
+
+ it 'creates the hierarchy' do
+ expect { sync }.to change { described_class.count }.from(0).to(1)
+
+ expect(namespace.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id, group2.id, group3.id])
+ end
+ end
+
+ context 'when namespace hierarchy does already exist' do
+ let(:namespace) { group3 }
+
+ before do
+ described_class.create!(namespace: namespace, traversal_ids: [namespace.id])
+ end
+
+ it 'updates the hierarchy' do
+ expect { sync }.not_to change { described_class.count }
+
+ expect(namespace.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id, group2.id, group3.id])
+ end
+ end
+
+ # I did not extract this context to a `shared_context` because the behavior will change
+ # after implementing the TODO in `Ci::NamespaceMirror.sync!`
+ context 'changing the middle namespace' do
+ let(:namespace) { group2 }
+
+ before do
+ described_class.create!(namespace_id: group1.id, traversal_ids: [group1.id])
+ described_class.create!(namespace_id: group2.id, traversal_ids: [group1.id, group2.id])
+ described_class.create!(namespace_id: group3.id, traversal_ids: [group1.id, group2.id, group3.id])
+ described_class.create!(namespace_id: group4.id, traversal_ids: [group1.id, group2.id, group3.id, group4.id])
+
+ group2.update!(parent: nil)
+ end
+
+ it 'updates hierarchies for the base but wait for events for the children' do
+ expect { sync }.not_to change { described_class.count }
+
+ expect(group1.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id])
+ expect(group2.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group2.id])
+ expect(group3.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group2.id, group3.id])
+ expect(group4.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group2.id, group3.id, group4.id])
+ end
+ end
+
+ context 'when the FFs sync_traversal_ids, use_traversal_ids and use_traversal_ids_for_ancestors are disabled' do
+ before do
+ stub_feature_flags(sync_traversal_ids: false,
+ use_traversal_ids: false,
+ use_traversal_ids_for_ancestors: false)
+ end
+
+ context 'changing the middle namespace' do
+ let(:namespace) { group2 }
+
+ before do
+ described_class.create!(namespace_id: group1.id, traversal_ids: [group1.id])
+ described_class.create!(namespace_id: group2.id, traversal_ids: [group1.id, group2.id])
+ described_class.create!(namespace_id: group3.id, traversal_ids: [group1.id, group2.id, group3.id])
+ described_class.create!(namespace_id: group4.id, traversal_ids: [group1.id, group2.id, group3.id, group4.id])
+
+ group2.update!(parent: nil)
+ end
+
+ it 'updates hierarchies for the base and descendants' do
+ expect { sync }.not_to change { described_class.count }
+
+ expect(group1.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id])
+ expect(group2.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group2.id])
+ expect(group3.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group2.id, group3.id])
+ expect(group4.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group2.id, group3.id, group4.id])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/pending_build_spec.rb b/spec/models/ci/pending_build_spec.rb
index ad711f5622f..abf0fb443bb 100644
--- a/spec/models/ci/pending_build_spec.rb
+++ b/spec/models/ci/pending_build_spec.rb
@@ -118,7 +118,7 @@ RSpec.describe Ci::PendingBuild do
project.shared_runners_enabled = true
end
- context 'when ci_pending_builds_maintain_shared_runners_data is enabled' do
+ context 'when ci_pending_builds_maintain_denormalized_data is enabled' do
it 'sets instance_runners_enabled to true' do
described_class.upsert_from_build!(build)
@@ -150,9 +150,9 @@ RSpec.describe Ci::PendingBuild do
end
end
- context 'when ci_pending_builds_maintain_shared_runners_data is disabled' do
+ context 'when ci_pending_builds_maintain_denormalized_data is disabled' do
before do
- stub_feature_flags(ci_pending_builds_maintain_shared_runners_data: false)
+ stub_feature_flags(ci_pending_builds_maintain_denormalized_data: false)
end
it 'sets instance_runners_enabled to false' do
@@ -168,7 +168,7 @@ RSpec.describe Ci::PendingBuild do
subject(:ci_pending_build) { described_class.last }
- context 'when ci_pending_builds_maintain_tags_data is enabled' do
+ context 'when ci_pending_builds_maintain_denormalized_data is enabled' do
it 'sets tag_ids' do
described_class.upsert_from_build!(build)
@@ -176,9 +176,9 @@ RSpec.describe Ci::PendingBuild do
end
end
- context 'when ci_pending_builds_maintain_tags_data is disabled' do
+ context 'when ci_pending_builds_maintain_denormalized_data is disabled' do
before do
- stub_feature_flags(ci_pending_builds_maintain_tags_data: false)
+ stub_feature_flags(ci_pending_builds_maintain_denormalized_data: false)
end
it 'does not set tag_ids' do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index e573a6ef780..fd9970699d7 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -28,6 +28,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it { is_expected.to have_many(:trigger_requests) }
it { is_expected.to have_many(:variables) }
it { is_expected.to have_many(:builds) }
+ it { is_expected.to have_many(:statuses_order_id_desc) }
it { is_expected.to have_many(:bridges) }
it { is_expected.to have_many(:job_artifacts).through(:builds) }
it { is_expected.to have_many(:auto_canceled_pipelines) }
@@ -35,8 +36,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it { is_expected.to have_many(:sourced_pipelines) }
it { is_expected.to have_many(:triggered_pipelines) }
it { is_expected.to have_many(:pipeline_artifacts) }
- it { is_expected.to have_many(:package_build_infos).dependent(:nullify).inverse_of(:pipeline) }
- it { is_expected.to have_many(:package_file_build_infos).dependent(:nullify).inverse_of(:pipeline) }
it { is_expected.to have_one(:chat_data) }
it { is_expected.to have_one(:source_pipeline) }
@@ -757,23 +756,23 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'with multiple pipelines' do
before_all do
create(:ci_build, name: "rspec", coverage: 30, pipeline: pipeline)
- create(:ci_build, name: "rubocop", coverage: 40, pipeline: pipeline)
+ create(:ci_build, name: "rubocop", coverage: 35, pipeline: pipeline)
end
it "calculates average when there are two builds with coverage" do
- expect(pipeline.coverage).to eq("35.00")
+ expect(pipeline.coverage).to be_within(0.001).of(32.5)
end
it "calculates average when there are two builds with coverage and one with nil" do
create(:ci_build, pipeline: pipeline)
- expect(pipeline.coverage).to eq("35.00")
+ expect(pipeline.coverage).to be_within(0.001).of(32.5)
end
it "calculates average when there are two builds with coverage and one is retried" do
create(:ci_build, name: "rubocop", coverage: 30, pipeline: pipeline, retried: true)
- expect(pipeline.coverage).to eq("35.00")
+ expect(pipeline.coverage).to be_within(0.001).of(32.5)
end
end
@@ -1358,12 +1357,26 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe 'synching status to Jira' do
let(:worker) { ::JiraConnect::SyncBuildsWorker }
- %i[prepare! run! skip! drop! succeed! cancel! block! delay!].each do |event|
- context "when we call pipeline.#{event}" do
- it 'triggers a Jira synch worker' do
- expect(worker).to receive(:perform_async).with(pipeline.id, Integer)
+ context 'when Jira Connect subscription does not exist' do
+ it 'does not trigger a Jira synch worker' do
+ expect(worker).not_to receive(:perform_async)
- pipeline.send(event)
+ pipeline.prepare!
+ end
+ end
+
+ context 'when Jira Connect subscription exists' do
+ before_all do
+ create(:jira_connect_subscription, namespace: project.namespace)
+ end
+
+ %i[prepare! run! skip! drop! succeed! cancel! block! delay!].each do |event|
+ context "when we call pipeline.#{event}" do
+ it 'triggers a Jira synch worker' do
+ expect(worker).to receive(:perform_async).with(pipeline.id, Integer)
+
+ pipeline.send(event)
+ end
end
end
end
@@ -1503,10 +1516,30 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe 'pipeline caching' do
- it 'performs ExpirePipelinesCacheWorker' do
- expect(ExpirePipelineCacheWorker).to receive(:perform_async).with(pipeline.id)
+ context 'when expire_job_and_pipeline_cache_synchronously is enabled' do
+ before do
+ stub_feature_flags(expire_job_and_pipeline_cache_synchronously: true)
+ end
- pipeline.cancel
+ it 'executes Ci::ExpirePipelineCacheService' do
+ expect_next_instance_of(Ci::ExpirePipelineCacheService) do |service|
+ expect(service).to receive(:execute).with(pipeline)
+ end
+
+ pipeline.cancel
+ end
+ end
+
+ context 'when expire_job_and_pipeline_cache_synchronously is disabled' do
+ before do
+ stub_feature_flags(expire_job_and_pipeline_cache_synchronously: false)
+ end
+
+ it 'performs ExpirePipelinesCacheWorker' do
+ expect(ExpirePipelineCacheWorker).to receive(:perform_async).with(pipeline.id)
+
+ pipeline.cancel
+ end
end
end
@@ -3173,11 +3206,35 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline is not child nor parent' do
let_it_be(:pipeline) { create(:ci_pipeline, :created) }
- let_it_be(:build) { create(:ci_build, :with_deployment, :deploy_to_production, pipeline: pipeline) }
+ let_it_be(:build, refind: true) { create(:ci_build, :with_deployment, :deploy_to_production, pipeline: pipeline) }
it 'returns just the pipeline environment' do
expect(subject).to contain_exactly(build.deployment.environment)
end
+
+ context 'when deployment SHA is not matched' do
+ before do
+ build.deployment.update!(sha: 'old-sha')
+ end
+
+ it 'does not return environments' do
+ expect(subject).to be_empty
+ end
+ end
+ end
+
+ context 'when an associated environment does not have deployments' do
+ let_it_be(:pipeline) { create(:ci_pipeline, :created) }
+ let_it_be(:build) { create(:ci_build, :stop_review_app, pipeline: pipeline) }
+ let_it_be(:environment) { create(:environment, project: pipeline.project) }
+
+ before_all do
+ build.metadata.update!(expanded_environment_name: environment.name)
+ end
+
+ it 'does not return environments' do
+ expect(subject).to be_empty
+ end
end
context 'when pipeline is in extended family' do
@@ -4611,4 +4668,13 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
expect(pipeline.authorized_cluster_agents).to contain_exactly(agent) # cached
end
end
+
+ it_behaves_like 'it has loose foreign keys' do
+ let(:factory_name) { :ci_pipeline }
+ end
+
+ it_behaves_like 'cleanup by a loose foreign key' do
+ let!(:model) { create(:ci_pipeline, user: create(:user)) }
+ let!(:parent) { model.user }
+ end
end
diff --git a/spec/models/ci/project_mirror_spec.rb b/spec/models/ci/project_mirror_spec.rb
new file mode 100644
index 00000000000..199285b036c
--- /dev/null
+++ b/spec/models/ci/project_mirror_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::ProjectMirror do
+ let_it_be(:group1) { create(:group) }
+ let_it_be(:group2) { create(:group) }
+
+ let!(:project) { create(:project, namespace: group2) }
+
+ describe '.sync!' do
+ let!(:event) { Projects::SyncEvent.create!(project: project) }
+
+ subject(:sync) { described_class.sync!(event.reload) }
+
+ context 'when project hierarchy does not exist in the first place' do
+ it 'creates a ci_projects record' do
+ expect { sync }.to change { described_class.count }.from(0).to(1)
+
+ expect(project.ci_project_mirror).to have_attributes(namespace_id: group2.id)
+ end
+ end
+
+ context 'when project hierarchy does already exist' do
+ before do
+ described_class.create!(project_id: project.id, namespace_id: group1.id)
+ end
+
+ it 'updates the related ci_projects record' do
+ expect { sync }.not_to change { described_class.count }
+
+ expect(project.ci_project_mirror).to have_attributes(namespace_id: group2.id)
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/runner_namespace_spec.rb b/spec/models/ci/runner_namespace_spec.rb
index 4e7cf7a3cb3..41d805adb9f 100644
--- a/spec/models/ci/runner_namespace_spec.rb
+++ b/spec/models/ci/runner_namespace_spec.rb
@@ -4,12 +4,6 @@ require 'spec_helper'
RSpec.describe Ci::RunnerNamespace do
it_behaves_like 'includes Limitable concern' do
- before do
- skip_default_enabled_yaml_check
-
- stub_feature_flags(ci_runner_limits_override: false)
- end
-
subject { build(:ci_runner_namespace, group: create(:group, :nested), runner: create(:ci_runner, :group)) }
end
end
diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb
index fef1416a84a..13369dba2cf 100644
--- a/spec/models/ci/runner_project_spec.rb
+++ b/spec/models/ci/runner_project_spec.rb
@@ -4,12 +4,6 @@ require 'spec_helper'
RSpec.describe Ci::RunnerProject do
it_behaves_like 'includes Limitable concern' do
- before do
- skip_default_enabled_yaml_check
-
- stub_feature_flags(ci_runner_limits_override: false)
- end
-
subject { build(:ci_runner_project, project: create(:project), runner: create(:ci_runner, :project)) }
end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 2e79159cc60..5142f70fa2c 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -7,10 +7,6 @@ RSpec.describe Ci::Runner do
it_behaves_like 'it has loose foreign keys' do
let(:factory_name) { :ci_runner }
-
- before do
- Clusters::Applications::Runner # ensure that the referenced model is loaded
- end
end
describe 'groups association' do
@@ -298,26 +294,134 @@ RSpec.describe Ci::Runner do
describe '.recent' do
subject { described_class.recent }
+ let!(:runner1) { create(:ci_runner, :instance, contacted_at: nil, created_at: 2.months.ago) }
+ let!(:runner2) { create(:ci_runner, :instance, contacted_at: nil, created_at: 3.months.ago) }
+ let!(:runner3) { create(:ci_runner, :instance, contacted_at: 1.month.ago, created_at: 2.months.ago) }
+ let!(:runner4) { create(:ci_runner, :instance, contacted_at: 1.month.ago, created_at: 3.months.ago) }
+
+ it { is_expected.to eq([runner1, runner3, runner4])}
+ end
+
+ describe '.active' do
+ subject { described_class.active(active_value) }
+
+ let!(:runner1) { create(:ci_runner, :instance, active: false) }
+ let!(:runner2) { create(:ci_runner, :instance) }
+
+ context 'with active_value set to false' do
+ let(:active_value) { false }
+
+ it 'returns inactive runners' do
+ is_expected.to match_array([runner1])
+ end
+ end
+
+ context 'with active_value set to true' do
+ let(:active_value) { true }
+
+ it 'returns active runners' do
+ is_expected.to match_array([runner2])
+ end
+ end
+ end
+
+ describe '.paused' do
before do
- @runner1 = create(:ci_runner, :instance, contacted_at: nil, created_at: 2.months.ago)
- @runner2 = create(:ci_runner, :instance, contacted_at: nil, created_at: 3.months.ago)
- @runner3 = create(:ci_runner, :instance, contacted_at: 1.month.ago, created_at: 2.months.ago)
- @runner4 = create(:ci_runner, :instance, contacted_at: 1.month.ago, created_at: 3.months.ago)
- @runner5 = create(:ci_runner, :instance, contacted_at: 3.months.ago, created_at: 5.months.ago)
+ expect(described_class).to receive(:active).with(false).and_call_original
end
- it { is_expected.to eq([@runner1, @runner3, @runner4])}
+ subject { described_class.paused }
+
+ let!(:runner1) { create(:ci_runner, :instance, active: false) }
+ let!(:runner2) { create(:ci_runner, :instance) }
+
+ it 'returns inactive runners' do
+ is_expected.to match_array([runner1])
+ end
end
- describe '.online' do
- subject { described_class.online }
+ describe '.stale' do
+ subject { described_class.stale }
+
+ let!(:runner1) { create(:ci_runner, :instance, created_at: 4.months.ago, contacted_at: 3.months.ago + 10.seconds) }
+ let!(:runner2) { create(:ci_runner, :instance, created_at: 4.months.ago, contacted_at: 3.months.ago - 1.second) }
+ let!(:runner3) { create(:ci_runner, :instance, created_at: 3.months.ago - 1.second, contacted_at: nil) }
+ let!(:runner4) { create(:ci_runner, :instance, created_at: 2.months.ago, contacted_at: nil) }
+
+ it 'returns stale runners' do
+ is_expected.to match_array([runner2, runner3])
+ end
+ end
+
+ describe '#stale?', :clean_gitlab_redis_cache do
+ let(:runner) { create(:ci_runner, :instance) }
+
+ subject { runner.stale? }
before do
- @runner1 = create(:ci_runner, :instance, contacted_at: 2.hours.ago)
- @runner2 = create(:ci_runner, :instance, contacted_at: 1.second.ago)
+ allow_any_instance_of(described_class).to receive(:cached_attribute).and_call_original
+ allow_any_instance_of(described_class).to receive(:cached_attribute)
+ .with(:platform).and_return("darwin")
end
- it { is_expected.to eq([@runner2])}
+ context 'table tests' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:created_at, :contacted_at, :expected_stale?) do
+ nil | nil | false
+ 3.months.ago - 1.second | 3.months.ago - 0.001.seconds | true
+ 3.months.ago - 1.second | 3.months.ago + 1.hour | false
+ 3.months.ago - 1.second | nil | true
+ 3.months.ago + 1.hour | nil | false
+ end
+
+ with_them do
+ before do
+ runner.created_at = created_at
+ end
+
+ context 'no cache value' do
+ before do
+ stub_redis_runner_contacted_at(nil)
+ runner.contacted_at = contacted_at
+ end
+
+ specify do
+ is_expected.to eq(expected_stale?)
+ end
+ end
+
+ context 'with cache value' do
+ before do
+ runner.contacted_at = contacted_at ? contacted_at + 1.week : nil
+ stub_redis_runner_contacted_at(contacted_at.to_s)
+ end
+
+ specify do
+ is_expected.to eq(expected_stale?)
+ end
+ end
+
+ def stub_redis_runner_contacted_at(value)
+ return unless created_at
+
+ Gitlab::Redis::Cache.with do |redis|
+ cache_key = runner.send(:cache_attribute_key)
+ expect(redis).to receive(:get).with(cache_key)
+ .and_return({ contacted_at: value }.to_json).at_least(:once)
+ end
+ end
+ end
+ end
+ end
+
+ describe '.online' do
+ subject { described_class.online }
+
+ let!(:runner1) { create(:ci_runner, :instance, contacted_at: 2.hours.ago) }
+ let!(:runner2) { create(:ci_runner, :instance, contacted_at: 1.second.ago) }
+
+ it { is_expected.to match_array([runner2]) }
end
describe '#online?', :clean_gitlab_redis_cache do
@@ -344,7 +448,7 @@ RSpec.describe Ci::Runner do
it { is_expected.to be_falsey }
end
- context 'contacted long time ago time' do
+ context 'contacted long time ago' do
before do
runner.contacted_at = 1.year.ago
end
@@ -362,7 +466,7 @@ RSpec.describe Ci::Runner do
end
context 'with cache value' do
- context 'contacted long time ago time' do
+ context 'contacted long time ago' do
before do
runner.contacted_at = 1.year.ago
stub_redis_runner_contacted_at(1.year.ago.to_s)
@@ -393,12 +497,10 @@ RSpec.describe Ci::Runner do
describe '.offline' do
subject { described_class.offline }
- before do
- @runner1 = create(:ci_runner, :instance, contacted_at: 2.hours.ago)
- @runner2 = create(:ci_runner, :instance, contacted_at: 1.second.ago)
- end
+ let!(:runner1) { create(:ci_runner, :instance, contacted_at: 2.hours.ago) }
+ let!(:runner2) { create(:ci_runner, :instance, contacted_at: 1.second.ago) }
- it { is_expected.to eq([@runner1])}
+ it { is_expected.to eq([runner1]) }
end
describe '#tick_runner_queue' do
@@ -626,16 +728,33 @@ RSpec.describe Ci::Runner do
end
describe '#status' do
- let(:runner) { build(:ci_runner, :instance) }
+ let(:runner) { build(:ci_runner, :instance, created_at: 4.months.ago) }
+ let(:legacy_mode) { }
- subject { runner.status }
+ subject { runner.status(legacy_mode) }
context 'never connected' do
before do
runner.contacted_at = nil
end
- it { is_expected.to eq(:not_connected) }
+ context 'with legacy_mode enabled' do
+ let(:legacy_mode) { '14.5' }
+
+ it { is_expected.to eq(:not_connected) }
+ end
+
+ context 'with legacy_mode disabled' do
+ it { is_expected.to eq(:stale) }
+ end
+
+ context 'created recently' do
+ before do
+ runner.created_at = 1.day.ago
+ end
+
+ it { is_expected.to eq(:never_contacted) }
+ end
end
context 'inactive but online' do
@@ -644,7 +763,15 @@ RSpec.describe Ci::Runner do
runner.active = false
end
- it { is_expected.to eq(:online) }
+ context 'with legacy_mode enabled' do
+ let(:legacy_mode) { '14.5' }
+
+ it { is_expected.to eq(:paused) }
+ end
+
+ context 'with legacy_mode disabled' do
+ it { is_expected.to eq(:online) }
+ end
end
context 'contacted 1s ago' do
@@ -655,13 +782,29 @@ RSpec.describe Ci::Runner do
it { is_expected.to eq(:online) }
end
- context 'contacted long time ago' do
+ context 'contacted recently' do
before do
- runner.contacted_at = 1.year.ago
+ runner.contacted_at = (3.months - 1.hour).ago
end
it { is_expected.to eq(:offline) }
end
+
+ context 'contacted long time ago' do
+ before do
+ runner.contacted_at = (3.months + 1.second).ago
+ end
+
+ context 'with legacy_mode enabled' do
+ let(:legacy_mode) { '14.5' }
+
+ it { is_expected.to eq(:offline) }
+ end
+
+ context 'with legacy_mode disabled' do
+ it { is_expected.to eq(:stale) }
+ end
+ end
end
describe '#deprecated_rest_status' do
@@ -760,8 +903,9 @@ RSpec.describe Ci::Runner do
describe '#heartbeat' do
let(:runner) { create(:ci_runner, :project) }
+ let(:executor) { 'shell' }
- subject { runner.heartbeat(architecture: '18-bit', config: { gpus: "all" }) }
+ subject { runner.heartbeat(architecture: '18-bit', config: { gpus: "all" }, executor: executor) }
context 'when database was updated recently' do
before do
@@ -797,6 +941,26 @@ RSpec.describe Ci::Runner do
expect_redis_update
does_db_update
end
+
+ %w(custom shell docker docker-windows docker-ssh ssh parallels virtualbox docker+machine docker-ssh+machine kubernetes some-unknown-type).each do |executor|
+ context "with #{executor} executor" do
+ let(:executor) { executor }
+
+ it 'updates with expected executor type' do
+ expect_redis_update
+
+ subject
+
+ expect(runner.reload.read_attribute(:executor_type)).to eq(expected_executor_type)
+ end
+
+ def expected_executor_type
+ return 'unknown' if executor == 'some-unknown-type'
+
+ executor.gsub(/[+-]/, '_')
+ end
+ end
+ end
end
def expect_redis_update
@@ -810,6 +974,7 @@ RSpec.describe Ci::Runner do
expect { subject }.to change { runner.reload.read_attribute(:contacted_at) }
.and change { runner.reload.read_attribute(:architecture) }
.and change { runner.reload.read_attribute(:config) }
+ .and change { runner.reload.read_attribute(:executor_type) }
end
end
@@ -1194,31 +1359,43 @@ RSpec.describe Ci::Runner do
end
describe '.belonging_to_group' do
- it 'returns the specific group runner' do
- group = create(:group)
- runner = create(:ci_runner, :group, groups: [group])
- unrelated_group = create(:group)
- create(:ci_runner, :group, groups: [unrelated_group])
+ shared_examples 'returns group runners' do
+ it 'returns the specific group runner' do
+ group = create(:group)
+ runner = create(:ci_runner, :group, groups: [group])
+ unrelated_group = create(:group)
+ create(:ci_runner, :group, groups: [unrelated_group])
- expect(described_class.belonging_to_group(group.id)).to contain_exactly(runner)
- end
+ expect(described_class.belonging_to_group(group.id)).to contain_exactly(runner)
+ end
- context 'runner belonging to parent group' do
- let_it_be(:parent_group) { create(:group) }
- let_it_be(:parent_runner) { create(:ci_runner, :group, groups: [parent_group]) }
- let_it_be(:group) { create(:group, parent: parent_group) }
+ context 'runner belonging to parent group' do
+ let_it_be(:parent_group) { create(:group) }
+ let_it_be(:parent_runner) { create(:ci_runner, :group, groups: [parent_group]) }
+ let_it_be(:group) { create(:group, parent: parent_group) }
- context 'when include_parent option is passed' do
- it 'returns the group runner from the parent group' do
- expect(described_class.belonging_to_group(group.id, include_ancestors: true)).to contain_exactly(parent_runner)
+ context 'when include_parent option is passed' do
+ it 'returns the group runner from the parent group' do
+ expect(described_class.belonging_to_group(group.id, include_ancestors: true)).to contain_exactly(parent_runner)
+ end
end
- end
- context 'when include_parent option is not passed' do
- it 'does not return the group runner from the parent group' do
- expect(described_class.belonging_to_group(group.id)).to be_empty
+ context 'when include_parent option is not passed' do
+ it 'does not return the group runner from the parent group' do
+ expect(described_class.belonging_to_group(group.id)).to be_empty
+ end
end
end
end
+
+ it_behaves_like 'returns group runners'
+
+ context 'when feature flag :linear_runner_ancestor_scopes is disabled' do
+ before do
+ stub_feature_flags(linear_runner_ancestor_scopes: false)
+ end
+
+ it_behaves_like 'returns group runners'
+ end
end
end
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index 5e0fcb4882f..2b6f22e68f1 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -28,6 +28,18 @@ RSpec.describe Ci::Stage, :models do
end
end
+ describe '.by_position' do
+ it 'finds stages by position' do
+ a = create(:ci_stage_entity, position: 1)
+ b = create(:ci_stage_entity, position: 2)
+ c = create(:ci_stage_entity, position: 3)
+
+ expect(described_class.by_position(1)).to contain_exactly(a)
+ expect(described_class.by_position(2)).to contain_exactly(b)
+ expect(described_class.by_position(%w[1 3])).to contain_exactly(a, c)
+ end
+ end
+
describe '.by_name' do
it 'finds stages by name' do
a = create(:ci_stage_entity, name: 'a')