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/pipeline_spec.rb')
-rw-r--r--spec/models/ci/pipeline_spec.rb479
1 files changed, 375 insertions, 104 deletions
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 61422978df7..5b67cbbc86b 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -19,32 +19,67 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:user) }
- it { is_expected.to belong_to(:auto_canceled_by) }
+ it { is_expected.to belong_to(:auto_canceled_by).class_name('Ci::Pipeline').inverse_of(:auto_canceled_pipelines) }
it { is_expected.to belong_to(:pipeline_schedule) }
it { is_expected.to belong_to(:merge_request) }
it { is_expected.to belong_to(:external_pull_request) }
it { is_expected.to have_many(:statuses) }
- it { is_expected.to have_many(:trigger_requests) }
+ it { is_expected.to have_many(:trigger_requests).with_foreign_key(:commit_id).inverse_of(:pipeline) }
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 do
+ is_expected.to have_many(:statuses_order_id_desc)
+ .class_name('CommitStatus').with_foreign_key(:commit_id).inverse_of(:pipeline)
+ end
+
it { is_expected.to have_many(:bridges) }
it { is_expected.to have_many(:job_artifacts).through(:builds) }
it { is_expected.to have_many(:build_trace_chunks).through(:builds) }
- it { is_expected.to have_many(:auto_canceled_pipelines) }
- it { is_expected.to have_many(:auto_canceled_jobs) }
- 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_one(:chat_data) }
+ it do
+ is_expected.to have_many(:failed_builds).class_name('Ci::Build')
+ .with_foreign_key(:commit_id).inverse_of(:pipeline)
+ end
+
+ it do
+ is_expected.to have_many(:cancelable_statuses).class_name('CommitStatus')
+ .with_foreign_key(:commit_id).inverse_of(:pipeline)
+ end
+
+ it do
+ is_expected.to have_many(:auto_canceled_pipelines).class_name('Ci::Pipeline')
+ .with_foreign_key(:auto_canceled_by_id).inverse_of(:auto_canceled_by)
+ end
+
+ it do
+ is_expected.to have_many(:auto_canceled_jobs).class_name('CommitStatus')
+ .with_foreign_key(:auto_canceled_by_id).inverse_of(:auto_canceled_by)
+ end
+
+ it do
+ is_expected.to have_many(:sourced_pipelines).class_name('Ci::Sources::Pipeline')
+ .with_foreign_key(:source_pipeline_id).inverse_of(:source_pipeline)
+ end
+
it { is_expected.to have_one(:source_pipeline) }
+ it { is_expected.to have_one(:chat_data) }
it { is_expected.to have_one(:triggered_by_pipeline) }
it { is_expected.to have_one(:source_job) }
it { is_expected.to have_one(:pipeline_config) }
it { is_expected.to have_one(:pipeline_metadata) }
+ it do
+ is_expected.to have_many(:daily_build_group_report_results).class_name('Ci::DailyBuildGroupReportResult')
+ .with_foreign_key(:last_pipeline_id).inverse_of(:last_pipeline)
+ end
+
+ it { is_expected.to have_many(:latest_builds_report_results).through(:latest_builds).source(:report_results) }
+
it { is_expected.to respond_to :git_author_name }
it { is_expected.to respond_to :git_author_email }
it { is_expected.to respond_to :git_author_full_text }
@@ -409,6 +444,16 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
end
end
+ describe '.preload_pipeline_metadata' do
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, project: project, user: user, name: 'Chatops pipeline') }
+
+ it 'loads associations' do
+ result = described_class.preload_pipeline_metadata.first
+
+ expect(result.association(:pipeline_metadata).loaded?).to be(true)
+ end
+ end
+
describe '.ci_sources' do
subject { described_class.ci_sources }
@@ -462,11 +507,13 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
let!(:other_pipeline) { create(:ci_pipeline, project: project) }
before do
- create(:ci_sources_pipeline,
- source_job: create(:ci_build, pipeline: upstream_pipeline),
- source_project: project,
- pipeline: child_pipeline,
- project: project)
+ create(
+ :ci_sources_pipeline,
+ source_job: create(:ci_build, pipeline: upstream_pipeline),
+ source_project: project,
+ pipeline: child_pipeline,
+ project: project
+ )
end
it 'only returns pipelines outside pipeline family' do
@@ -485,11 +532,13 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
let!(:other_pipeline) { create(:ci_pipeline, project: project) }
before do
- create(:ci_sources_pipeline,
- source_job: create(:ci_build, pipeline: upstream_pipeline),
- source_project: project,
- pipeline: child_pipeline,
- project: project)
+ create(
+ :ci_sources_pipeline,
+ source_job: create(:ci_build, pipeline: upstream_pipeline),
+ source_project: project,
+ pipeline: child_pipeline,
+ project: project
+ )
end
it 'only returns older pipelines outside pipeline family' do
@@ -497,6 +546,17 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
end
end
+ describe '.order_id_desc' do
+ subject(:pipelines_ordered_by_id) { described_class.order_id_desc }
+
+ let(:older_pipeline) { create(:ci_pipeline, id: 99, project: project) }
+ let(:newest_pipeline) { create(:ci_pipeline, id: 100, project: project) }
+
+ it 'only returns the pipelines ordered by id' do
+ expect(pipelines_ordered_by_id).to eq([newest_pipeline, older_pipeline])
+ end
+ end
+
describe '.jobs_count_in_alive_pipelines' do
before do
::Ci::HasStatus::ALIVE_STATUSES.each do |status|
@@ -872,6 +932,26 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
expect(pipeline).to be_valid
end
end
+
+ context 'when source is unknown' do
+ subject(:pipeline) { create(:ci_empty_pipeline, :created) }
+
+ let(:attr) { :source }
+ let(:attr_value) { :unknown }
+
+ it_behaves_like 'having enum with nil value'
+ end
+ end
+
+ describe '#config_source' do
+ context 'when source is unknown' do
+ subject(:pipeline) { create(:ci_empty_pipeline, :created) }
+
+ let(:attr) { :config_source }
+ let(:attr_value) { :unknown_source }
+
+ it_behaves_like 'having enum with nil value'
+ end
end
describe '#block' do
@@ -1131,29 +1211,41 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
describe 'legacy stages' do
before do
- create(:commit_status, pipeline: pipeline,
- stage: 'build',
- name: 'linux',
- stage_idx: 0,
- status: 'success')
-
- create(:commit_status, pipeline: pipeline,
- stage: 'build',
- name: 'mac',
- stage_idx: 0,
- status: 'failed')
-
- create(:commit_status, pipeline: pipeline,
- stage: 'deploy',
- name: 'staging',
- stage_idx: 2,
- status: 'running')
-
- create(:commit_status, pipeline: pipeline,
- stage: 'test',
- name: 'rspec',
- stage_idx: 1,
- status: 'success')
+ create(
+ :commit_status,
+ pipeline: pipeline,
+ stage: 'build',
+ name: 'linux',
+ stage_idx: 0,
+ status: 'success'
+ )
+
+ create(
+ :commit_status,
+ pipeline: pipeline,
+ stage: 'build',
+ name: 'mac',
+ stage_idx: 0,
+ status: 'failed'
+ )
+
+ create(
+ :commit_status,
+ pipeline: pipeline,
+ stage: 'deploy',
+ name: 'staging',
+ stage_idx: 2,
+ status: 'running'
+ )
+
+ create(
+ :commit_status,
+ pipeline: pipeline,
+ stage: 'test',
+ name: 'rspec',
+ stage_idx: 1,
+ status: 'success'
+ )
end
describe '#stages_count' do
@@ -1604,8 +1696,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
before do
upstream_bridge = create(:ci_bridge, :strategy_depend, pipeline: upstream_of_upstream_pipeline)
- create(:ci_sources_pipeline, pipeline: upstream_pipeline,
- source_job: upstream_bridge)
+ create(:ci_sources_pipeline, pipeline: upstream_pipeline, source_job: upstream_bridge)
end
context 'when the downstream pipeline first fails then retries and succeeds' do
@@ -1661,13 +1752,163 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
end
end
+ describe 'merge status subscription trigger' do
+ shared_examples 'state transition not triggering GraphQL subscription mergeRequestMergeStatusUpdated' do
+ context 'when state transitions to running' do
+ it_behaves_like 'does not trigger GraphQL subscription mergeRequestMergeStatusUpdated' do
+ let(:action) { pipeline.run }
+ end
+ end
+
+ context 'when state transitions to success' do
+ it_behaves_like 'does not trigger GraphQL subscription mergeRequestMergeStatusUpdated' do
+ let(:action) { pipeline.succeed }
+ end
+ end
+
+ context 'when state transitions to failed' do
+ it_behaves_like 'does not trigger GraphQL subscription mergeRequestMergeStatusUpdated' do
+ let(:action) { pipeline.drop }
+ end
+ end
+
+ context 'when state transitions to canceled' do
+ it_behaves_like 'does not trigger GraphQL subscription mergeRequestMergeStatusUpdated' do
+ let(:action) { pipeline.cancel }
+ end
+ end
+
+ context 'when state transitions to skipped' do
+ it_behaves_like 'does not trigger GraphQL subscription mergeRequestMergeStatusUpdated' do
+ let(:action) { pipeline.skip }
+ end
+ end
+ end
+
+ shared_examples 'state transition triggering GraphQL subscription mergeRequestMergeStatusUpdated' do
+ context 'when state transitions to running' do
+ it_behaves_like 'triggers GraphQL subscription mergeRequestMergeStatusUpdated' do
+ let(:action) { pipeline.run }
+ end
+ end
+
+ context 'when state transitions to success' do
+ it_behaves_like 'triggers GraphQL subscription mergeRequestMergeStatusUpdated' do
+ let(:action) { pipeline.succeed }
+ end
+ end
+
+ context 'when state transitions to failed' do
+ it_behaves_like 'triggers GraphQL subscription mergeRequestMergeStatusUpdated' do
+ let(:action) { pipeline.drop }
+ end
+ end
+
+ context 'when state transitions to canceled' do
+ it_behaves_like 'triggers GraphQL subscription mergeRequestMergeStatusUpdated' do
+ let(:action) { pipeline.cancel }
+ end
+ end
+
+ context 'when state transitions to skipped' do
+ it_behaves_like 'triggers GraphQL subscription mergeRequestMergeStatusUpdated' do
+ let(:action) { pipeline.skip }
+ end
+ end
+
+ context 'when only_allow_merge_if_pipeline_succeeds? returns false' do
+ let(:only_allow_merge_if_pipeline_succeeds?) { false }
+
+ it_behaves_like 'state transition not triggering GraphQL subscription mergeRequestMergeStatusUpdated'
+ end
+
+ context 'when pipeline_trigger_merge_status feature flag is disabled' do
+ before do
+ stub_feature_flags(pipeline_trigger_merge_status: false)
+ end
+
+ it_behaves_like 'state transition not triggering GraphQL subscription mergeRequestMergeStatusUpdated'
+ end
+ end
+
+ context 'when pipeline has merge requests' do
+ let(:merge_request) do
+ create(
+ :merge_request,
+ :simple,
+ source_project: project,
+ target_project: project
+ )
+ end
+
+ let(:only_allow_merge_if_pipeline_succeeds?) { true }
+
+ before do
+ allow(project)
+ .to receive(:only_allow_merge_if_pipeline_succeeds?)
+ .and_return(only_allow_merge_if_pipeline_succeeds?)
+ end
+
+ context 'when for a specific merge request' do
+ let(:pipeline) do
+ create(
+ :ci_pipeline,
+ project: project,
+ merge_request: merge_request
+ )
+ end
+
+ it_behaves_like 'state transition triggering GraphQL subscription mergeRequestMergeStatusUpdated'
+
+ context 'when pipeline is a child' do
+ let(:parent_pipeline) do
+ create(
+ :ci_pipeline,
+ project: project,
+ merge_request: merge_request
+ )
+ end
+
+ let(:pipeline) do
+ create(
+ :ci_pipeline,
+ child_of: parent_pipeline,
+ merge_request: merge_request
+ )
+ end
+
+ it_behaves_like 'state transition not triggering GraphQL subscription mergeRequestMergeStatusUpdated'
+ end
+ end
+
+ context 'when for merge requests matching the source branch and SHA' do
+ let(:pipeline) do
+ create(
+ :ci_pipeline,
+ project: project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha
+ )
+ end
+
+ it_behaves_like 'state transition triggering GraphQL subscription mergeRequestMergeStatusUpdated'
+ end
+ end
+
+ context 'when pipeline has no merge requests' do
+ it_behaves_like 'state transition not triggering GraphQL subscription mergeRequestMergeStatusUpdated'
+ end
+ end
+
def create_build(name, *traits, queued_at: current, started_from: 0, **opts)
- create(:ci_build, *traits,
- name: name,
- pipeline: pipeline,
- queued_at: queued_at,
- started_at: queued_at + started_from,
- **opts)
+ create(
+ :ci_build, *traits,
+ name: name,
+ pipeline: pipeline,
+ queued_at: queued_at,
+ started_at: queued_at + started_from,
+ **opts
+ )
end
end
@@ -1715,9 +1956,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
let(:pipeline) { build(:ci_pipeline, merge_request: merge_request) }
let(:merge_request) do
- create(:merge_request, :simple,
- source_project: project,
- target_project: project)
+ create(:merge_request, :simple, source_project: project, target_project: project)
end
it 'returns false' do
@@ -1758,17 +1997,17 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
context 'when ref is merge request' do
let(:pipeline) do
- create(:ci_pipeline,
- source: :merge_request_event,
- merge_request: merge_request)
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
end
let(:merge_request) do
- create(:merge_request,
- source_project: project,
- source_branch: 'feature',
- target_project: project,
- target_branch: 'master')
+ create(
+ :merge_request,
+ source_project: project,
+ source_branch: 'feature',
+ target_project: project,
+ target_branch: 'master'
+ )
end
it 'returns branch ref' do
@@ -1812,35 +2051,63 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
context 'with non-empty project' do
let(:pipeline) do
- create(:ci_pipeline,
- ref: project.default_branch,
- sha: project.commit.sha)
+ create(
+ :ci_pipeline,
+ project: project,
+ ref: project.default_branch,
+ sha: project.commit.sha
+ )
end
describe '#lazy_ref_commit' do
let(:another) do
- create(:ci_pipeline,
- ref: 'feature',
- sha: project.commit('feature').sha)
+ create(
+ :ci_pipeline,
+ project: project,
+ ref: 'feature',
+ sha: project.commit('feature').sha
+ )
end
let(:unicode) do
- create(:ci_pipeline,
- ref: 'ü/unicode/multi-byte')
+ create(
+ :ci_pipeline,
+ project: project,
+ ref: 'ü/unicode/multi-byte'
+ )
+ end
+
+ let(:in_another_project) do
+ other_project = create(:project, :repository)
+ create(
+ :ci_pipeline,
+ project: other_project,
+ ref: other_project.default_branch,
+ sha: other_project.commit.sha
+ )
end
- it 'returns the latest commit for a ref lazily' do
+ it 'returns the latest commit for a ref lazily', :aggregate_failures do
expect(project.repository)
.to receive(:list_commits_by_ref_name).once
.and_call_original
+ requests_before = Gitlab::GitalyClient.get_request_count
pipeline.lazy_ref_commit
another.lazy_ref_commit
unicode.lazy_ref_commit
+ in_another_project.lazy_ref_commit
+ requests_after = Gitlab::GitalyClient.get_request_count
+
+ expect(requests_after - requests_before).to eq(0)
expect(pipeline.lazy_ref_commit.id).to eq pipeline.sha
expect(another.lazy_ref_commit.id).to eq another.sha
- expect(unicode.lazy_ref_commit).to be_nil
+ expect(unicode.lazy_ref_commit.itself).to be_nil
+ expect(in_another_project.lazy_ref_commit.id).to eq in_another_project.sha
+
+ expect(pipeline.lazy_ref_commit.repository.container).to eq project
+ expect(in_another_project.lazy_ref_commit.repository.container).to eq in_another_project.project
end
end
@@ -1969,9 +2236,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
end
let(:merge_request) do
- create(:merge_request, :simple,
- source_project: project,
- target_project: project)
+ create(:merge_request, :simple, source_project: project, target_project: project)
end
it 'returns merge request modified paths' do
@@ -1996,8 +2261,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
describe '#modified_paths_since' do
let(:project) do
- create(:project, :custom_repo,
- files: { 'file1.txt' => 'file 1' })
+ create(:project, :custom_repo, files: { 'file1.txt' => 'file 1' })
end
let(:user) { project.owner }
@@ -3270,19 +3534,23 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
let(:target_branch) { 'master' }
let!(:pipeline) do
- create(:ci_pipeline,
- source: :merge_request_event,
- project: pipeline_project,
- ref: source_branch,
- merge_request: merge_request)
+ create(
+ :ci_pipeline,
+ source: :merge_request_event,
+ project: pipeline_project,
+ ref: source_branch,
+ merge_request: merge_request
+ )
end
let(:merge_request) do
- create(:merge_request,
- source_project: pipeline_project,
- source_branch: source_branch,
- target_project: project,
- target_branch: target_branch)
+ create(
+ :merge_request,
+ source_project: pipeline_project,
+ source_branch: source_branch,
+ target_project: project,
+ target_branch: target_branch
+ )
end
it 'returns an associated merge request' do
@@ -3293,19 +3561,23 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
let(:target_branch_2) { 'merge-test' }
let!(:pipeline_2) do
- create(:ci_pipeline,
- source: :merge_request_event,
- project: pipeline_project,
- ref: source_branch,
- merge_request: merge_request_2)
+ create(
+ :ci_pipeline,
+ source: :merge_request_event,
+ project: pipeline_project,
+ ref: source_branch,
+ merge_request: merge_request_2
+ )
end
let(:merge_request_2) do
- create(:merge_request,
- source_project: pipeline_project,
- source_branch: source_branch,
- target_project: project,
- target_branch: target_branch_2)
+ create(
+ :merge_request,
+ source_project: pipeline_project,
+ source_branch: source_branch,
+ target_project: project,
+ target_branch: target_branch_2
+ )
end
it 'does not return an associated merge request' do
@@ -3701,10 +3973,12 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
let(:project) { create(:project, :repository, namespace: namespace) }
let(:pipeline) do
- create(:ci_pipeline,
- project: project,
- sha: project.commit('master').sha,
- user: project.first_owner)
+ create(
+ :ci_pipeline,
+ project: project,
+ sha: project.commit('master').sha,
+ user: project.first_owner
+ )
end
before do
@@ -4488,10 +4762,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
let(:stage_name) { 'test' }
let(:stage) do
- create(:ci_stage,
- pipeline: pipeline,
- project: pipeline.project,
- name: 'test')
+ create(:ci_stage, pipeline: pipeline, project: pipeline.project, name: 'test')
end
before do
@@ -5208,11 +5479,11 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
describe '#cluster_agent_authorizations' do
let(:pipeline) { create(:ci_empty_pipeline, :created) }
- let(:authorization) { instance_double(Clusters::Agents::GroupAuthorization) }
+ let(:authorization) { instance_double(Clusters::Agents::Authorizations::CiAccess::GroupAuthorization) }
let(:finder) { double(execute: [authorization]) }
it 'retrieves authorization records from the finder and caches the result' do
- expect(Clusters::AgentAuthorizationsFinder).to receive(:new).once
+ expect(Clusters::Agents::Authorizations::CiAccess::Finder).to receive(:new).once
.with(pipeline.project)
.and_return(finder)