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:
Diffstat (limited to 'spec/models/ci')
-rw-r--r--spec/models/ci/bridge_spec.rb26
-rw-r--r--spec/models/ci/build_dependencies_spec.rb4
-rw-r--r--spec/models/ci/build_spec.rb136
-rw-r--r--spec/models/ci/job_artifact_spec.rb13
-rw-r--r--spec/models/ci/namespace_mirror_spec.rb47
-rw-r--r--spec/models/ci/pipeline_spec.rb73
-rw-r--r--spec/models/ci/processable_spec.rb94
-rw-r--r--spec/models/ci/runner_spec.rb8
-rw-r--r--spec/models/ci/secure_file_spec.rb32
9 files changed, 306 insertions, 127 deletions
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index 7c3c02a5ab7..5ee560c4925 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -30,6 +30,12 @@ RSpec.describe Ci::Bridge do
expect(bridge).to have_one(:downstream_pipeline)
end
+ describe '#retryable?' do
+ it 'returns false' do
+ expect(bridge.retryable?).to eq(false)
+ end
+ end
+
describe '#tags' do
it 'only has a bridge tag' do
expect(bridge.tags).to eq [:bridge]
@@ -282,6 +288,26 @@ RSpec.describe Ci::Bridge do
)
end
end
+
+ context 'when the pipeline runs from a pipeline schedule' do
+ let(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project ) }
+ let(:pipeline) { create(:ci_pipeline, pipeline_schedule: pipeline_schedule) }
+
+ let(:options) do
+ { trigger: { project: 'my/project', forward: { pipeline_variables: true } } }
+ end
+
+ before do
+ pipeline_schedule.variables.create!(key: 'schedule_var_key', value: 'schedule var value')
+ end
+
+ it 'adds the schedule variable' do
+ expect(bridge.downstream_variables).to contain_exactly(
+ { key: 'BRIDGE', value: 'cross' },
+ { key: 'schedule_var_key', value: 'schedule var value' }
+ )
+ end
+ end
end
end
diff --git a/spec/models/ci/build_dependencies_spec.rb b/spec/models/ci/build_dependencies_spec.rb
index cd330324840..91048cae064 100644
--- a/spec/models/ci/build_dependencies_spec.rb
+++ b/spec/models/ci/build_dependencies_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Ci::BuildDependencies do
end
let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, stage: 'build') }
- let!(:rspec_test) { create(:ci_build, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') }
+ let!(:rspec_test) { create(:ci_build, :success, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') }
let!(:rubocop_test) { create(:ci_build, pipeline: pipeline, name: 'rubocop', stage_idx: 1, stage: 'test') }
let!(:staging) { create(:ci_build, pipeline: pipeline, name: 'staging', stage_idx: 2, stage: 'deploy') }
@@ -48,7 +48,7 @@ RSpec.describe Ci::BuildDependencies do
project.add_developer(user)
end
- let!(:retried_job) { Ci::Build.retry(rspec_test, user) }
+ let!(:retried_job) { Ci::RetryJobService.new(rspec_test.project, user).execute(rspec_test)[:job] }
it 'contains the retried job instead of the original one' do
is_expected.to contain_exactly(build, retried_job, rubocop_test)
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 240b932638a..fd87a388442 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1585,6 +1585,31 @@ RSpec.describe Ci::Build do
it { is_expected.to eq('review/x') }
end
+
+ context 'when environment name uses a nested variable' do
+ let(:yaml_variables) do
+ [
+ { key: 'ENVIRONMENT_NAME', value: '${CI_COMMIT_REF_NAME}' }
+ ]
+ end
+
+ let(:build) do
+ create(:ci_build,
+ ref: 'master',
+ yaml_variables: yaml_variables,
+ environment: 'review/$ENVIRONMENT_NAME')
+ end
+
+ it { is_expected.to eq('review/master') }
+
+ context 'when the FF ci_expand_environment_name_and_url is disabled' do
+ before do
+ stub_feature_flags(ci_expand_environment_name_and_url: false)
+ end
+
+ it { is_expected.to eq('review/${CI_COMMIT_REF_NAME}') }
+ end
+ end
end
describe '#expanded_kubernetes_namespace' do
@@ -1951,90 +1976,6 @@ RSpec.describe Ci::Build do
end
end
- describe '#retryable?' do
- subject { build }
-
- context 'when build is retryable' do
- context 'when build is successful' do
- before do
- build.success!
- end
-
- it { is_expected.to be_retryable }
- end
-
- context 'when build is failed' do
- before do
- build.drop!
- end
-
- it { is_expected.to be_retryable }
- end
-
- context 'when build is canceled' do
- before do
- build.cancel!
- end
-
- it { is_expected.to be_retryable }
- end
- end
-
- context 'when build is not retryable' do
- context 'when build is running' do
- before do
- build.run!
- end
-
- it { is_expected.not_to be_retryable }
- end
-
- context 'when build is skipped' do
- before do
- build.skip!
- end
-
- it { is_expected.not_to be_retryable }
- end
-
- context 'when build is degenerated' do
- before do
- build.degenerate!
- end
-
- it { is_expected.not_to be_retryable }
- end
-
- context 'when a canceled build has been retried already' do
- before do
- project.add_developer(user)
- build.cancel!
- described_class.retry(build, user)
- end
-
- 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
-
- context 'when build is waiting for deployment approval' do
- subject { build_stubbed(:ci_build, :manual, environment: 'production') }
-
- before do
- create(:deployment, :blocked, deployable: subject)
- end
-
- it { is_expected.not_to be_retryable }
- end
- end
- end
-
describe '#action?' do
before do
build.update!(when: value)
@@ -2308,7 +2249,7 @@ RSpec.describe Ci::Build do
describe '#options' do
let(:options) do
{
- image: "ruby:2.7",
+ image: "image:1.0",
services: ["postgres"],
script: ["ls -a"]
}
@@ -2319,7 +2260,7 @@ RSpec.describe Ci::Build do
end
it 'allows to access with symbolized keys' do
- expect(build.options[:image]).to eq('ruby:2.7')
+ expect(build.options[:image]).to eq('image:1.0')
end
it 'rejects access with string keys' do
@@ -2358,24 +2299,12 @@ RSpec.describe Ci::Build do
end
context 'when build is retried' do
- let!(:new_build) { described_class.retry(build, user) }
+ let!(:new_build) { Ci::RetryJobService.new(project, user).execute(build)[:job] }
it 'does not return any of them' do
is_expected.not_to include(build, new_build)
end
end
-
- context 'when other build is retried' do
- let!(:retried_build) { described_class.retry(other_build, user) }
-
- before do
- retried_build.success
- end
-
- it 'returns a retried build' do
- is_expected.to contain_exactly(retried_build)
- end
- end
end
describe '#other_scheduled_actions' do
@@ -3962,8 +3891,9 @@ RSpec.describe Ci::Build do
subject { create(:ci_build, :running, options: { script: ["ls -al"], retry: 3 }, project: project, user: user) }
it 'retries build and assigns the same user to it' do
- expect(described_class).to receive(:retry)
- .with(subject, user)
+ expect_next_instance_of(::Ci::RetryJobService) do |service|
+ expect(service).to receive(:execute).with(subject)
+ end
subject.drop!
end
@@ -3977,10 +3907,10 @@ RSpec.describe Ci::Build do
end
context 'when retry service raises Gitlab::Access::AccessDeniedError exception' do
- let(:retry_service) { Ci::RetryBuildService.new(subject.project, subject.user) }
+ let(:retry_service) { Ci::RetryJobService.new(subject.project, subject.user) }
before do
- allow_any_instance_of(Ci::RetryBuildService)
+ allow_any_instance_of(Ci::RetryJobService)
.to receive(:execute)
.with(subject)
.and_raise(Gitlab::Access::AccessDeniedError)
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index bd0397e0396..24c318d0218 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -279,6 +279,15 @@ RSpec.describe Ci::JobArtifact do
end
end
+ describe '.order_expired_asc' do
+ let_it_be(:first_artifact) { create(:ci_job_artifact, expire_at: 2.days.ago) }
+ let_it_be(:second_artifact) { create(:ci_job_artifact, expire_at: 1.day.ago) }
+
+ it 'returns ordered artifacts' do
+ expect(described_class.order_expired_asc).to eq([first_artifact, second_artifact])
+ end
+ end
+
describe '.for_project' do
it 'returns artifacts only for given project(s)', :aggregate_failures do
artifact1 = create(:ci_job_artifact)
@@ -700,10 +709,6 @@ RSpec.describe Ci::JobArtifact do
MSG
end
- it_behaves_like 'it has loose foreign keys' do
- let(:factory_name) { :ci_job_artifact }
- end
-
context 'loose foreign key on ci_job_artifacts.project_id' do
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:project) }
diff --git a/spec/models/ci/namespace_mirror_spec.rb b/spec/models/ci/namespace_mirror_spec.rb
index 38471f15849..9b4e86916b8 100644
--- a/spec/models/ci/namespace_mirror_spec.rb
+++ b/spec/models/ci/namespace_mirror_spec.rb
@@ -44,6 +44,53 @@ RSpec.describe Ci::NamespaceMirror do
end
end
+ describe '.contains_traversal_ids' do
+ let!(:other_group1) { create(:group) }
+ let!(:other_group2) { create(:group, parent: other_group1) }
+ let!(:other_group3) { create(:group, parent: other_group2) }
+ let!(:other_group4) { create(:group) }
+
+ subject(:result) { described_class.contains_traversal_ids(all_traversal_ids) }
+
+ context 'when passing a top-level group' do
+ let(:all_traversal_ids) do
+ [
+ [other_group1.id]
+ ]
+ end
+
+ it 'returns only itself and children of that group' do
+ expect(result.map(&:namespace)).to contain_exactly(other_group1, other_group2, other_group3)
+ end
+ end
+
+ context 'when passing many levels of groups' do
+ let(:all_traversal_ids) do
+ [
+ [other_group2.parent_id, other_group2.id],
+ [other_group3.parent_id, other_group3.id],
+ [other_group4.id]
+ ]
+ end
+
+ it 'returns only the asked group' do
+ expect(result.map(&:namespace)).to contain_exactly(other_group2, other_group3, other_group4)
+ end
+ end
+
+ context 'when passing invalid data ' do
+ let(:all_traversal_ids) do
+ [
+ ["; UPDATE"]
+ ]
+ end
+
+ it 'data is properly sanitised' do
+ expect(result.to_sql).to include "((traversal_ids[1])) IN (('; UPDATE'))"
+ end
+ end
+ end
+
describe '.by_namespace_id' do
subject(:result) { described_class.by_namespace_id(group2.id) }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 294ec07ee3e..45b51d5bf44 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1146,6 +1146,50 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
end
+
+ describe 'variable CI_GITLAB_FIPS_MODE' do
+ context 'when FIPS flag is enabled' do
+ before do
+ allow(Gitlab::FIPS).to receive(:enabled?).and_return(true)
+ end
+
+ it "is included with value 'true'" do
+ expect(subject.to_hash).to include('CI_GITLAB_FIPS_MODE' => 'true')
+ end
+ end
+
+ context 'when FIPS flag is disabled' do
+ before do
+ allow(Gitlab::FIPS).to receive(:enabled?).and_return(false)
+ end
+
+ it 'is not included' do
+ expect(subject.to_hash).not_to have_key('CI_GITLAB_FIPS_MODE')
+ end
+ end
+ end
+
+ context 'without a commit' do
+ let(:pipeline) { build(:ci_empty_pipeline, :created, sha: nil) }
+
+ it 'does not expose commit variables' do
+ expect(subject.to_hash.keys)
+ .not_to include(
+ 'CI_COMMIT_SHA',
+ 'CI_COMMIT_SHORT_SHA',
+ 'CI_COMMIT_BEFORE_SHA',
+ 'CI_COMMIT_REF_NAME',
+ 'CI_COMMIT_REF_SLUG',
+ 'CI_COMMIT_BRANCH',
+ 'CI_COMMIT_TAG',
+ 'CI_COMMIT_MESSAGE',
+ 'CI_COMMIT_TITLE',
+ 'CI_COMMIT_DESCRIPTION',
+ 'CI_COMMIT_REF_PROTECTED',
+ 'CI_COMMIT_TIMESTAMP',
+ 'CI_COMMIT_AUTHOR')
+ end
+ end
end
describe '#protected_ref?' do
@@ -1663,7 +1707,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
expect(upstream_pipeline.reload).to be_failed
Sidekiq::Testing.inline! do
- new_job = Ci::Build.retry(job, project.users.first)
+ new_job = Ci::RetryJobService.new(project, project.users.first).execute(job)[:job]
expect(downstream_pipeline.reload).to be_running
expect(upstream_pipeline.reload).to be_running
@@ -1684,7 +1728,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
expect(upstream_pipeline.reload).to be_success
Sidekiq::Testing.inline! do
- new_job = Ci::Build.retry(job, project.users.first)
+ new_job = Ci::RetryJobService.new(project, project.users.first).execute(job)[:job]
expect(downstream_pipeline.reload).to be_running
expect(upstream_pipeline.reload).to be_running
@@ -1715,7 +1759,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
expect(upstream_of_upstream_pipeline.reload).to be_failed
Sidekiq::Testing.inline! do
- new_job = Ci::Build.retry(job, project.users.first)
+ new_job = Ci::RetryJobService.new(project, project.users.first).execute(job)[:job]
expect(downstream_pipeline.reload).to be_running
expect(upstream_pipeline.reload).to be_running
@@ -2583,8 +2627,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
build.drop
project.add_developer(user)
-
- Ci::Build.retry(build, user)
+ ::Ci::RetryJobService.new(project, user).execute(build)[:job]
end
# We are changing a state: created > failed > running
@@ -4688,7 +4731,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
project.add_developer(user)
retried_build.cancel!
- ::Ci::Build.retry(retried_build, user)
+ Ci::RetryJobService.new(project, user).execute(retried_build)[:job]
end
it 'does not include retried builds' do
@@ -4714,6 +4757,24 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '#has_expired_test_reports?' do
+ subject { pipeline_with_test_report.has_expired_test_reports? }
+
+ let(:pipeline_with_test_report) { create(:ci_pipeline, :with_test_reports) }
+
+ context 'when artifacts are not expired' do
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when artifacts are expired' do
+ before do
+ pipeline_with_test_report.job_artifacts.first.update!(expire_at: Date.yesterday)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
it_behaves_like 'it has loose foreign keys' do
let(:factory_name) { :ci_pipeline }
end
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index ac1a8247aaa..71fef3c1b5b 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -14,6 +14,100 @@ RSpec.describe Ci::Processable do
it { is_expected.to delegate_method(:legacy_detached_merge_request_pipeline?).to(:pipeline) }
end
+ describe '#retryable' do
+ shared_examples_for 'retryable processable' do
+ context 'when processable is successful' do
+ before do
+ processable.success!
+ end
+
+ it { is_expected.to be_retryable }
+ end
+
+ context 'when processable is failed' do
+ before do
+ processable.drop!
+ end
+
+ it { is_expected.to be_retryable }
+ end
+
+ context 'when processable is canceled' do
+ before do
+ processable.cancel!
+ end
+
+ it { is_expected.to be_retryable }
+ end
+ end
+
+ shared_examples_for 'non-retryable processable' do
+ context 'when processable is skipped' do
+ before do
+ processable.skip!
+ end
+
+ it { is_expected.not_to be_retryable }
+ end
+
+ context 'when processable is degenerated' do
+ before do
+ processable.degenerate!
+ end
+
+ it { is_expected.not_to be_retryable }
+ end
+
+ context 'when a canceled processable has been retried already' do
+ before do
+ project.add_developer(create(:user))
+ processable.cancel!
+ processable.update!(retried: true)
+ end
+
+ it { is_expected.not_to be_retryable }
+ end
+ end
+
+ context 'when the processable is a build' do
+ subject(:processable) { create(:ci_build, pipeline: pipeline) }
+
+ context 'when the processable is retryable' do
+ it_behaves_like 'retryable processable'
+
+ context 'when deployment is rejected' do
+ before do
+ processable.drop!(:deployment_rejected)
+ end
+
+ it { is_expected.not_to be_retryable }
+ end
+
+ context 'when build is waiting for deployment approval' do
+ subject { build_stubbed(:ci_build, :manual, environment: 'production') }
+
+ before do
+ create(:deployment, :blocked, deployable: subject)
+ end
+
+ it { is_expected.not_to be_retryable }
+ end
+ end
+
+ context 'when the processable is non-retryable' do
+ it_behaves_like 'non-retryable processable'
+
+ context 'when processable is running' do
+ before do
+ processable.run!
+ end
+
+ it { is_expected.not_to be_retryable }
+ end
+ end
+ end
+ end
+
describe '#aggregated_needs_names' do
let(:with_aggregated_needs) { pipeline.processables.select_with_aggregated_needs(project) }
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 42187c3ef99..05b7bc39a74 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -134,28 +134,28 @@ RSpec.describe Ci::Runner do
end
context 'cost factors validations' do
- it 'dissalows :private_projects_minutes_cost_factor being nil' do
+ it 'disallows :private_projects_minutes_cost_factor being nil' do
runner = build(:ci_runner, private_projects_minutes_cost_factor: nil)
expect(runner).to be_invalid
expect(runner.errors.full_messages).to include('Private projects minutes cost factor needs to be non-negative')
end
- it 'dissalows :public_projects_minutes_cost_factor being nil' do
+ it 'disallows :public_projects_minutes_cost_factor being nil' do
runner = build(:ci_runner, public_projects_minutes_cost_factor: nil)
expect(runner).to be_invalid
expect(runner.errors.full_messages).to include('Public projects minutes cost factor needs to be non-negative')
end
- it 'dissalows :private_projects_minutes_cost_factor being negative' do
+ it 'disallows :private_projects_minutes_cost_factor being negative' do
runner = build(:ci_runner, private_projects_minutes_cost_factor: -1.1)
expect(runner).to be_invalid
expect(runner.errors.full_messages).to include('Private projects minutes cost factor needs to be non-negative')
end
- it 'dissalows :public_projects_minutes_cost_factor being negative' do
+ it 'disallows :public_projects_minutes_cost_factor being negative' do
runner = build(:ci_runner, public_projects_minutes_cost_factor: -2.2)
expect(runner).to be_invalid
diff --git a/spec/models/ci/secure_file_spec.rb b/spec/models/ci/secure_file_spec.rb
index 4382385aaf5..f92db3fe8db 100644
--- a/spec/models/ci/secure_file_spec.rb
+++ b/spec/models/ci/secure_file_spec.rb
@@ -3,14 +3,14 @@
require 'spec_helper'
RSpec.describe Ci::SecureFile do
- let(:sample_file) { fixture_file('ci_secure_files/upload-keystore.jks') }
-
- subject { create(:ci_secure_file) }
-
before do
stub_ci_secure_file_object_storage
end
+ let(:sample_file) { fixture_file('ci_secure_files/upload-keystore.jks') }
+
+ subject { create(:ci_secure_file, file: CarrierWaveStringFile.new(sample_file)) }
+
it { is_expected.to be_a FileStoreMounter }
it { is_expected.to belong_to(:project).required }
@@ -27,6 +27,26 @@ RSpec.describe Ci::SecureFile do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_presence_of(:permissions) }
it { is_expected.to validate_presence_of(:project_id) }
+ context 'unique filename' do
+ let_it_be(:project1) { create(:project) }
+
+ it 'ensures the file name is unique within a given project' do
+ file1 = create(:ci_secure_file, project: project1, name: 'file1')
+ expect do
+ create(:ci_secure_file, project: project1, name: 'file1')
+ end.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Name has already been taken')
+
+ expect(project1.secure_files.where(name: 'file1').count).to be 1
+ expect(project1.secure_files.find_by(name: 'file1').id).to eq(file1.id)
+ end
+
+ it 'allows duplicate file names in different projects' do
+ create(:ci_secure_file, project: project1)
+ expect do
+ create(:ci_secure_file, project: create(:project))
+ end.not_to raise_error
+ end
+ end
end
describe '#permissions' do
@@ -37,8 +57,6 @@ RSpec.describe Ci::SecureFile do
describe '#checksum' do
it 'computes SHA256 checksum on the file before encrypted' do
- subject.file = CarrierWaveStringFile.new(sample_file)
- subject.save!
expect(subject.checksum).to eq(Digest::SHA256.hexdigest(sample_file))
end
end
@@ -51,8 +69,6 @@ RSpec.describe Ci::SecureFile do
describe '#file' do
it 'returns the saved file' do
- subject.file = CarrierWaveStringFile.new(sample_file)
- subject.save!
expect(Base64.encode64(subject.file.read)).to eq(Base64.encode64(sample_file))
end
end