summaryrefslogtreecommitdiff
path: root/spec/models/ci
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 13:18:24 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 13:18:24 +0000
commit0653e08efd039a5905f3fa4f6e9cef9f5d2f799c (patch)
tree4dcc884cf6d81db44adae4aa99f8ec1233a41f55 /spec/models/ci
parent744144d28e3e7fddc117924fef88de5d9674fe4c (diff)
downloadgitlab-ce-0653e08efd039a5905f3fa4f6e9cef9f5d2f799c.tar.gz
Add latest changes from gitlab-org/gitlab@14-3-stable-eev14.3.0-rc42
Diffstat (limited to 'spec/models/ci')
-rw-r--r--spec/models/ci/bridge_spec.rb8
-rw-r--r--spec/models/ci/build_spec.rb57
-rw-r--r--spec/models/ci/build_trace_chunks/fog_spec.rb42
-rw-r--r--spec/models/ci/build_trace_metadata_spec.rb124
-rw-r--r--spec/models/ci/pending_build_spec.rb111
-rw-r--r--spec/models/ci/pipeline_schedule_spec.rb44
-rw-r--r--spec/models/ci/pipeline_spec.rb77
-rw-r--r--spec/models/ci/pipeline_variable_spec.rb2
-rw-r--r--spec/models/ci/runner_spec.rb4
9 files changed, 345 insertions, 124 deletions
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index db956b26b6b..6dd3c40f228 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -74,18 +74,18 @@ RSpec.describe Ci::Bridge do
it "schedules downstream pipeline creation when the status is #{status}" do
bridge.status = status
- expect(bridge).to receive(:schedule_downstream_pipeline!)
-
bridge.enqueue!
+
+ expect(::Ci::CreateCrossProjectPipelineWorker.jobs.last['args']).to eq([bridge.id])
end
end
it "schedules downstream pipeline creation when the status is waiting for resource" do
bridge.status = :waiting_for_resource
- expect(bridge).to receive(:schedule_downstream_pipeline!)
-
bridge.enqueue_waiting_for_resource!
+
+ expect(::Ci::CreateCrossProjectPipelineWorker.jobs.last['args']).to eq([bridge.id])
end
it 'raises error when the status is failed' do
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 26abc98656e..1e06d566c80 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1307,7 +1307,9 @@ RSpec.describe Ci::Build do
shared_examples_for 'avoid deadlock' do
it 'executes UPDATE in the right order' do
- recorded = ActiveRecord::QueryRecorder.new { subject }
+ recorded = with_cross_database_modification_prevented do
+ ActiveRecord::QueryRecorder.new { subject }
+ end
index_for_build = recorded.log.index { |l| l.include?("UPDATE \"ci_builds\"") }
index_for_deployment = recorded.log.index { |l| l.include?("UPDATE \"deployments\"") }
@@ -1322,7 +1324,9 @@ RSpec.describe Ci::Build do
it_behaves_like 'avoid deadlock'
it 'transits deployment status to running' do
- subject
+ with_cross_database_modification_prevented do
+ subject
+ end
expect(deployment).to be_running
end
@@ -1340,7 +1344,9 @@ RSpec.describe Ci::Build do
it_behaves_like 'calling proper BuildFinishedWorker'
it 'transits deployment status to success' do
- subject
+ with_cross_database_modification_prevented do
+ subject
+ end
expect(deployment).to be_success
end
@@ -1353,7 +1359,9 @@ RSpec.describe Ci::Build do
it_behaves_like 'calling proper BuildFinishedWorker'
it 'transits deployment status to failed' do
- subject
+ with_cross_database_modification_prevented do
+ subject
+ end
expect(deployment).to be_failed
end
@@ -1365,7 +1373,9 @@ RSpec.describe Ci::Build do
it_behaves_like 'avoid deadlock'
it 'transits deployment status to skipped' do
- subject
+ with_cross_database_modification_prevented do
+ subject
+ end
expect(deployment).to be_skipped
end
@@ -1378,7 +1388,9 @@ RSpec.describe Ci::Build do
it_behaves_like 'calling proper BuildFinishedWorker'
it 'transits deployment status to canceled' do
- subject
+ with_cross_database_modification_prevented do
+ subject
+ end
expect(deployment).to be_canceled
end
@@ -2632,6 +2644,10 @@ RSpec.describe Ci::Build do
value: "#{Gitlab.host_with_port}/#{project.namespace.root_ancestor.path.downcase}#{DependencyProxy::URL_SUFFIX}",
public: true,
masked: false },
+ { key: 'CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX',
+ value: "#{Gitlab.host_with_port}/#{project.namespace.full_path.downcase}#{DependencyProxy::URL_SUFFIX}",
+ public: true,
+ masked: false },
{ key: 'CI_API_V4_URL', value: 'http://localhost/api/v4', public: true, masked: false },
{ key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true, masked: false },
{ key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true, masked: false },
@@ -5243,4 +5259,33 @@ RSpec.describe Ci::Build do
expect(described_class.with_coverage_regex).to eq([build_with_coverage_regex])
end
end
+
+ describe '#ensure_trace_metadata!' do
+ it 'delegates to Ci::BuildTraceMetadata' do
+ expect(Ci::BuildTraceMetadata)
+ .to receive(:find_or_upsert_for!)
+ .with(build.id)
+
+ build.ensure_trace_metadata!
+ end
+ end
+
+ describe '#doom!' do
+ subject { build.doom! }
+
+ let_it_be(:build) { create(:ci_build, :queued) }
+
+ it 'updates status and failure_reason', :aggregate_failures do
+ subject
+
+ expect(build.status).to eq("failed")
+ expect(build.failure_reason).to eq("data_integrity_failure")
+ end
+
+ it 'drops associated pending build' do
+ subject
+
+ expect(build.reload.queuing_entry).not_to be_present
+ end
+ end
end
diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb
index 21dab6fad60..bbf04ef9430 100644
--- a/spec/models/ci/build_trace_chunks/fog_spec.rb
+++ b/spec/models/ci/build_trace_chunks/fog_spec.rb
@@ -107,37 +107,22 @@ RSpec.describe Ci::BuildTraceChunks::Fog do
let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: initial_data) }
let(:data) { data_store.data(model) }
- context 'when ci_job_trace_force_encode is enabled' do
- it 'appends ASCII data' do
- data_store.append_data(model, +'hello world', 4)
+ it 'appends ASCII data' do
+ data_store.append_data(model, +'hello world', 4)
- expect(data.encoding).to eq(Encoding::ASCII_8BIT)
- expect(data.force_encoding(Encoding::UTF_8)).to eq('😺hello world')
- end
-
- it 'appends UTF-8 data' do
- data_store.append_data(model, +'Résumé', 4)
-
- expect(data.encoding).to eq(Encoding::ASCII_8BIT)
- expect(data.force_encoding(Encoding::UTF_8)).to eq("😺Résumé")
- end
-
- context 'when initial data is UTF-8' do
- let(:initial_data) { +'😺' }
+ expect(data.encoding).to eq(Encoding::ASCII_8BIT)
+ expect(data.force_encoding(Encoding::UTF_8)).to eq('😺hello world')
+ end
- it 'appends ASCII data' do
- data_store.append_data(model, +'hello world', 4)
+ it 'appends UTF-8 data' do
+ data_store.append_data(model, +'Résumé', 4)
- expect(data.encoding).to eq(Encoding::ASCII_8BIT)
- expect(data.force_encoding(Encoding::UTF_8)).to eq('😺hello world')
- end
- end
+ expect(data.encoding).to eq(Encoding::ASCII_8BIT)
+ expect(data.force_encoding(Encoding::UTF_8)).to eq("😺Résumé")
end
- context 'when ci_job_trace_force_encode is disabled' do
- before do
- stub_feature_flags(ci_job_trace_force_encode: false)
- end
+ context 'when initial data is UTF-8' do
+ let(:initial_data) { +'😺' }
it 'appends ASCII data' do
data_store.append_data(model, +'hello world', 4)
@@ -145,11 +130,6 @@ RSpec.describe Ci::BuildTraceChunks::Fog do
expect(data.encoding).to eq(Encoding::ASCII_8BIT)
expect(data.force_encoding(Encoding::UTF_8)).to eq('😺hello world')
end
-
- it 'throws an exception when appending UTF-8 data' do
- expect(Gitlab::ErrorTracking).to receive(:track_and_raise_exception).and_call_original
- expect { data_store.append_data(model, +'Résumé', 4) }.to raise_exception(Encoding::CompatibilityError)
- end
end
end
diff --git a/spec/models/ci/build_trace_metadata_spec.rb b/spec/models/ci/build_trace_metadata_spec.rb
index 42b9d5d34b6..5e4645c5dc4 100644
--- a/spec/models/ci/build_trace_metadata_spec.rb
+++ b/spec/models/ci/build_trace_metadata_spec.rb
@@ -7,4 +7,128 @@ RSpec.describe Ci::BuildTraceMetadata do
it { is_expected.to belong_to(:trace_artifact) }
it { is_expected.to validate_presence_of(:build) }
+ it { is_expected.to validate_presence_of(:archival_attempts) }
+
+ describe '#can_attempt_archival_now?' do
+ let(:metadata) do
+ build(:ci_build_trace_metadata,
+ archival_attempts: archival_attempts,
+ last_archival_attempt_at: last_archival_attempt_at)
+ end
+
+ subject { metadata.can_attempt_archival_now? }
+
+ context 'when archival_attempts is over the limit' do
+ let(:archival_attempts) { described_class::MAX_ATTEMPTS + 1 }
+ let(:last_archival_attempt_at) {}
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when last_archival_attempt_at is not set' do
+ let(:archival_attempts) { described_class::MAX_ATTEMPTS }
+ let(:last_archival_attempt_at) {}
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when last_archival_attempt_at is set' do
+ let(:archival_attempts) { described_class::MAX_ATTEMPTS }
+ let(:last_archival_attempt_at) { 6.days.ago }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when last_archival_attempt_at is too close' do
+ let(:archival_attempts) { described_class::MAX_ATTEMPTS }
+ let(:last_archival_attempt_at) { 1.hour.ago }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#archival_attempts_available?' do
+ let(:metadata) do
+ build(:ci_build_trace_metadata, archival_attempts: archival_attempts)
+ end
+
+ subject { metadata.archival_attempts_available? }
+
+ context 'when archival_attempts is over the limit' do
+ let(:archival_attempts) { described_class::MAX_ATTEMPTS + 1 }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when archival_attempts is at the limit' do
+ let(:archival_attempts) { described_class::MAX_ATTEMPTS }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe '#increment_archival_attempts!' do
+ let_it_be(:metadata) do
+ create(:ci_build_trace_metadata,
+ archival_attempts: 2,
+ last_archival_attempt_at: 1.day.ago)
+ end
+
+ it 'increments the attempts' do
+ expect { metadata.increment_archival_attempts! }
+ .to change { metadata.reload.archival_attempts }
+ end
+
+ it 'updates the last_archival_attempt_at timestamp' do
+ expect { metadata.increment_archival_attempts! }
+ .to change { metadata.reload.last_archival_attempt_at }
+ end
+ end
+
+ describe '#track_archival!' do
+ let(:trace_artifact) { create(:ci_job_artifact) }
+ let(:metadata) { create(:ci_build_trace_metadata) }
+
+ it 'stores the artifact id and timestamp' do
+ expect(metadata.trace_artifact_id).to be_nil
+
+ metadata.track_archival!(trace_artifact.id)
+ metadata.reload
+
+ expect(metadata.trace_artifact_id).to eq(trace_artifact.id)
+ expect(metadata.archived_at).to be_like_time(Time.current)
+ end
+ end
+
+ describe '.find_or_upsert_for!' do
+ let_it_be(:build) { create(:ci_build) }
+
+ subject(:execute) do
+ described_class.find_or_upsert_for!(build.id)
+ end
+
+ it 'creates a new record' do
+ metadata = execute
+
+ expect(metadata).to be_a(described_class)
+ expect(metadata.id).to eq(build.id)
+ expect(metadata.archival_attempts).to eq(0)
+ end
+
+ context 'with existing records' do
+ before do
+ create(:ci_build_trace_metadata,
+ build: build,
+ archival_attempts: described_class::MAX_ATTEMPTS)
+ end
+
+ it 'returns the existing record' do
+ metadata = execute
+
+ expect(metadata).to be_a(described_class)
+ expect(metadata.id).to eq(build.id)
+ expect(metadata.archival_attempts).to eq(described_class::MAX_ATTEMPTS)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/pending_build_spec.rb b/spec/models/ci/pending_build_spec.rb
index 0518c9a1652..ad711f5622f 100644
--- a/spec/models/ci/pending_build_spec.rb
+++ b/spec/models/ci/pending_build_spec.rb
@@ -34,6 +34,47 @@ RSpec.describe Ci::PendingBuild do
end
end
end
+
+ describe '.for_tags' do
+ subject(:pending_builds) { described_class.for_tags(tag_ids) }
+
+ let_it_be(:pending_build_with_tags) { create(:ci_pending_build, tag_ids: [1, 2]) }
+ let_it_be(:pending_build_without_tags) { create(:ci_pending_build) }
+
+ context 'when tag_ids match pending builds' do
+ let(:tag_ids) { [1, 2] }
+
+ it 'returns matching pending builds' do
+ expect(pending_builds).to contain_exactly(pending_build_with_tags, pending_build_without_tags)
+ end
+ end
+
+ context 'when tag_ids does not match pending builds' do
+ let(:tag_ids) { [non_existing_record_id] }
+
+ it 'returns matching pending builds without tags' do
+ expect(pending_builds).to contain_exactly(pending_build_without_tags)
+ end
+ end
+
+ context 'when tag_ids is not provided' do
+ context 'with a nil value' do
+ let(:tag_ids) { nil }
+
+ it 'returns matching pending builds without tags' do
+ expect(pending_builds).to contain_exactly(pending_build_without_tags)
+ end
+ end
+
+ context 'with an empty array' do
+ let(:tag_ids) { [] }
+
+ it 'returns matching pending builds without tags' do
+ expect(pending_builds).to contain_exactly(pending_build_without_tags)
+ end
+ end
+ end
+ end
end
describe '.upsert_from_build!' do
@@ -58,7 +99,11 @@ RSpec.describe Ci::PendingBuild do
end
end
- context 'when project does not have shared runner' do
+ context 'when project does not have shared runners enabled' do
+ before do
+ project.shared_runners_enabled = false
+ end
+
it 'sets instance_runners_enabled to false' do
described_class.upsert_from_build!(build)
@@ -69,6 +114,10 @@ RSpec.describe Ci::PendingBuild do
context 'when project has shared runner' do
let_it_be(:runner) { create(:ci_runner, :instance) }
+ before do
+ project.shared_runners_enabled = true
+ end
+
context 'when ci_pending_builds_maintain_shared_runners_data is enabled' do
it 'sets instance_runners_enabled to true' do
described_class.upsert_from_build!(build)
@@ -113,5 +162,65 @@ RSpec.describe Ci::PendingBuild do
end
end
end
+
+ context 'when build has tags' do
+ let!(:build) { create(:ci_build, :tags) }
+
+ subject(:ci_pending_build) { described_class.last }
+
+ context 'when ci_pending_builds_maintain_tags_data is enabled' do
+ it 'sets tag_ids' do
+ described_class.upsert_from_build!(build)
+
+ expect(ci_pending_build.tag_ids).to eq(build.tags_ids)
+ end
+ end
+
+ context 'when ci_pending_builds_maintain_tags_data is disabled' do
+ before do
+ stub_feature_flags(ci_pending_builds_maintain_tags_data: false)
+ end
+
+ it 'does not set tag_ids' do
+ described_class.upsert_from_build!(build)
+
+ expect(ci_pending_build.tag_ids).to be_empty
+ end
+ end
+ end
+
+ context 'when a build project is nested in a subgroup' do
+ let(:group) { create(:group, :with_hierarchy, depth: 2, children: 1) }
+ let(:project) { create(:project, namespace: group.descendants.first) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, :created, pipeline: pipeline) }
+
+ subject { described_class.last }
+
+ context 'when build can be picked by a group runner' do
+ before do
+ project.group_runners_enabled = true
+ end
+
+ it 'denormalizes namespace traversal ids' do
+ described_class.upsert_from_build!(build)
+
+ expect(subject.namespace_traversal_ids).not_to be_empty
+ expect(subject.namespace_traversal_ids).to eq [group.id, project.namespace.id]
+ end
+ end
+
+ context 'when build can not be picked by a group runner' do
+ before do
+ project.group_runners_enabled = false
+ end
+
+ it 'creates an empty namespace traversal ids array' do
+ described_class.upsert_from_build!(build)
+
+ expect(subject.namespace_traversal_ids).to be_empty
+ end
+ end
+ end
end
end
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index 8de3ebb18b9..c7e1fe91b1e 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -107,31 +107,24 @@ RSpec.describe Ci::PipelineSchedule do
describe '#set_next_run_at' do
using RSpec::Parameterized::TableSyntax
- where(:worker_cron, :schedule_cron, :plan_limit, :ff_enabled, :now, :result) do
- '0 1 2 3 *' | '0 1 * * *' | nil | true | 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 | true | 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 | false | Time.zone.local(2021, 3, 2, 1, 0) | Time.zone.local(2022, 3, 2, 1, 0)
- '*/5 * * * *' | '*/1 * * * *' | nil | true | 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 | true | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
- '*/5 * * * *' | '*/1 * * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | false | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 5)
- '*/5 * * * *' | '*/1 * * * *' | (1.day.in_minutes / 10).to_i | true | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10)
- '*/5 * * * *' | '*/1 * * * *' | 200 | true | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 10)
- '*/5 * * * *' | '*/1 * * * *' | 200 | false | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 11, 5)
- '*/5 * * * *' | '0 * * * *' | nil | true | 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 | true | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 0)
- '*/5 * * * *' | '0 * * * *' | (1.day.in_minutes / 10).to_i | false | Time.zone.local(2021, 5, 27, 11, 0) | Time.zone.local(2021, 5, 27, 12, 5)
- '*/5 * * * *' | '0 * * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | true | 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 | true | 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 | true | 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 | true | 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 | true | 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 | true | 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 | true | Time.zone.local(2021, 5, 1, 1, 9) | Time.zone.local(2021, 6, 1, 1, 0)
- '*/9 * * * *' | '0 1 1 * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | false | Time.zone.local(2021, 5, 1, 1, 9) | Time.zone.local(2021, 6, 1, 1, 9)
- '*/5 * * * *' | '59 14 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | true | Time.zone.local(2021, 5, 1, 15, 0) | Time.zone.local(2021, 5, 2, 15, 0)
- '*/5 * * * *' | '59 14 * * *' | (1.day.in_minutes / 1.hour.in_minutes).to_i | false | 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 | true | Time.zone.local(2021, 2, 1, 21, 45) | Time.zone.local(2022, 2, 1, 21, 45)
- '*/5 * * * *' | '45 21 1 2 *' | (1.day.in_minutes / 5).to_i | false | Time.zone.local(2021, 2, 1, 21, 45) | Time.zone.local(2022, 2, 1, 21, 50)
+ 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
with_them do
@@ -143,7 +136,6 @@ RSpec.describe Ci::PipelineSchedule do
end
create(:plan_limits, :default_plan, ci_daily_pipeline_schedule_triggers: plan_limit) if plan_limit
- stub_feature_flags(ci_daily_limit_for_pipeline_schedules: false) unless ff_enabled
# Setting this here to override initial save with the current time
pipeline_schedule.next_run_at = now
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index da89eccc3b2..1007d64438f 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -183,6 +183,28 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '.where_not_sha' do
+ let_it_be(:pipeline) { create(:ci_pipeline, sha: 'abcx') }
+ let_it_be(:pipeline_2) { create(:ci_pipeline, sha: 'abc') }
+
+ let(:sha) { 'abc' }
+
+ subject { described_class.where_not_sha(sha) }
+
+ it 'returns the pipeline without the specified sha' do
+ is_expected.to contain_exactly(pipeline)
+ end
+
+ context 'when argument is array' do
+ let(:sha) { %w[abc abcx] }
+
+ it 'returns the pipelines without the specified shas' do
+ pipeline_3 = create(:ci_pipeline, sha: 'abcy')
+ is_expected.to contain_exactly(pipeline_3)
+ end
+ end
+ end
+
describe '.for_source_sha' do
subject { described_class.for_source_sha(source_sha) }
@@ -2015,16 +2037,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it 'returns external pull request modified paths' do
expect(pipeline.modified_paths).to match(external_pull_request.modified_paths)
end
-
- context 'when the FF ci_modified_paths_of_external_prs is disabled' do
- before do
- stub_feature_flags(ci_modified_paths_of_external_prs: false)
- end
-
- it 'returns nil' do
- expect(pipeline.modified_paths).to be_nil
- end
- end
end
end
@@ -4524,51 +4536,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
subject(:reset_bridge) { pipeline.reset_source_bridge!(project.owner) }
- # This whole block will be removed by https://gitlab.com/gitlab-org/gitlab/-/issues/329194
- # It contains some duplicate checks.
- context 'when the FF ci_reset_bridge_with_subsequent_jobs is disabled' do
- before do
- stub_feature_flags(ci_reset_bridge_with_subsequent_jobs: false)
- end
-
- context 'when the pipeline is a child pipeline and the bridge is depended' do
- let!(:parent_pipeline) { create(:ci_pipeline) }
- let!(:bridge) { create_bridge(parent_pipeline, pipeline, true) }
-
- it 'marks source bridge as pending' do
- reset_bridge
-
- expect(bridge.reload).to be_pending
- end
-
- context 'when the parent pipeline has subsequent jobs after the bridge' do
- let!(:after_bridge_job) { create(:ci_build, :skipped, pipeline: parent_pipeline, stage_idx: bridge.stage_idx + 1) }
-
- it 'does not touch subsequent jobs of the bridge' do
- reset_bridge
-
- expect(after_bridge_job.reload).to be_skipped
- end
- end
-
- context 'when the parent pipeline has a dependent upstream pipeline' do
- let(:upstream_pipeline) { create(:ci_pipeline, project: create(:project)) }
- let!(:upstream_bridge) { create_bridge(upstream_pipeline, parent_pipeline, true) }
-
- let(:upstream_upstream_pipeline) { create(:ci_pipeline, project: create(:project)) }
- let!(:upstream_upstream_bridge) { create_bridge(upstream_upstream_pipeline, upstream_pipeline, true) }
-
- it 'marks all source bridges as pending' do
- reset_bridge
-
- expect(bridge.reload).to be_pending
- expect(upstream_bridge.reload).to be_pending
- expect(upstream_upstream_bridge.reload).to be_pending
- end
- end
- end
- end
-
context 'when the pipeline is a child pipeline and the bridge is depended' do
let!(:parent_pipeline) { create(:ci_pipeline) }
let!(:bridge) { create_bridge(parent_pipeline, pipeline, true) }
diff --git a/spec/models/ci/pipeline_variable_spec.rb b/spec/models/ci/pipeline_variable_spec.rb
index 04fcaab4c2d..4e8d49585d0 100644
--- a/spec/models/ci/pipeline_variable_spec.rb
+++ b/spec/models/ci/pipeline_variable_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Ci::PipelineVariable do
it_behaves_like "CI variable"
- it { is_expected.to validate_uniqueness_of(:key).scoped_to(:pipeline_id) }
+ it { is_expected.to validate_presence_of(:key) }
describe '#hook_attrs' do
let(:variable) { create(:ci_pipeline_variable, key: 'foo', value: 'bar') }
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index ffc8ab4cf8b..31e854c852e 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -531,6 +531,10 @@ RSpec.describe Ci::Runner do
it 'can handle builds' do
expect(runner.can_pick?(build)).to be_truthy
end
+
+ it 'knows namespace id it is assigned to' do
+ expect(runner.namespace_ids).to eq [group.id]
+ end
end
end