summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-10 12:08:59 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-10 12:08:59 +0000
commit7351a484d79236b7e9d47c86f2fcc970b7ae10b0 (patch)
tree651b5fca7ea0460e3ce7c687cfa9e3a3b37eefc8 /spec
parentb4ded0ba7b4d2cdbed5b1f331cf2083a25ee4d7c (diff)
downloadgitlab-ce-7351a484d79236b7e9d47c86f2fcc970b7ae10b0.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb14
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb16
-rw-r--r--spec/features/admin/admin_disables_git_access_protocol_spec.rb2
-rw-r--r--spec/features/admin/admin_mode_spec.rb2
-rw-r--r--spec/features/admin/admin_settings_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb24
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb9
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml3
-rw-r--r--spec/migrations/20200122123016_backfill_project_settings_spec.rb32
-rw-r--r--spec/models/ci/bridge_spec.rb10
-rw-r--r--spec/models/clusters/cluster_spec.rb1
-rw-r--r--spec/models/deployment_cluster_spec.rb22
-rw-r--r--spec/models/deployment_spec.rb1
-rw-r--r--spec/models/project_setting_spec.rb7
-rw-r--r--spec/models/project_spec.rb6
-rw-r--r--spec/rubocop/cop/scalability/cron_worker_context_spec.rb8
-rw-r--r--spec/services/ci/create_cross_project_pipeline_service_spec.rb368
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb5
-rw-r--r--spec/services/projects/create_service_spec.rb6
-rw-r--r--spec/workers/ci/create_cross_project_pipeline_worker_spec.rb36
20 files changed, 555 insertions, 21 deletions
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index fa575ba2eae..66deb2359db 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -56,49 +56,49 @@ describe Admin::ApplicationSettingsController do
it 'updates the password_authentication_enabled_for_git setting' do
put :update, params: { application_setting: { password_authentication_enabled_for_git: "0" } }
- expect(response).to redirect_to(admin_application_settings_path)
+ expect(response).to redirect_to(general_admin_application_settings_path)
expect(ApplicationSetting.current.password_authentication_enabled_for_git).to eq(false)
end
it 'updates the default_project_visibility for string value' do
put :update, params: { application_setting: { default_project_visibility: "20" } }
- expect(response).to redirect_to(admin_application_settings_path)
+ expect(response).to redirect_to(general_admin_application_settings_path)
expect(ApplicationSetting.current.default_project_visibility).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
it 'update the restricted levels for string values' do
put :update, params: { application_setting: { restricted_visibility_levels: %w[10 20] } }
- expect(response).to redirect_to(admin_application_settings_path)
+ expect(response).to redirect_to(general_admin_application_settings_path)
expect(ApplicationSetting.current.restricted_visibility_levels).to eq([10, 20])
end
it 'updates the restricted_visibility_levels when empty array is passed' do
put :update, params: { application_setting: { restricted_visibility_levels: [""] } }
- expect(response).to redirect_to(admin_application_settings_path)
+ expect(response).to redirect_to(general_admin_application_settings_path)
expect(ApplicationSetting.current.restricted_visibility_levels).to be_empty
end
it 'updates the receive_max_input_size setting' do
put :update, params: { application_setting: { receive_max_input_size: "1024" } }
- expect(response).to redirect_to(admin_application_settings_path)
+ expect(response).to redirect_to(general_admin_application_settings_path)
expect(ApplicationSetting.current.receive_max_input_size).to eq(1024)
end
it 'updates the default_project_creation for string value' do
put :update, params: { application_setting: { default_project_creation: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS } }
- expect(response).to redirect_to(admin_application_settings_path)
+ expect(response).to redirect_to(general_admin_application_settings_path)
expect(ApplicationSetting.current.default_project_creation).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS)
end
it 'updates minimum_password_length setting' do
put :update, params: { application_setting: { minimum_password_length: 10 } }
- expect(response).to redirect_to(admin_application_settings_path)
+ expect(response).to redirect_to(general_admin_application_settings_path)
expect(ApplicationSetting.current.minimum_password_length).to eq(10)
end
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index d464827d63a..b12af198986 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -29,7 +29,7 @@ describe Projects::PipelinesController do
stub_feature_flags(ci_pipeline_persisted_stages: true)
end
- it 'returns serialized pipelines', :request_store do
+ it 'returns serialized pipelines' do
expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
get_pipelines_index_json
@@ -60,7 +60,6 @@ describe Projects::PipelinesController do
# There appears to be one extra query for Pipelines#has_warnings? for some reason
expect { get_pipelines_index_json }.not_to exceed_query_limit(control_count + 1)
-
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['pipelines'].count).to eq 10
end
@@ -90,11 +89,18 @@ describe Projects::PipelinesController do
end
it 'does not execute N+1 queries' do
- queries = ActiveRecord::QueryRecorder.new do
+ get_pipelines_index_json
+
+ control_count = ActiveRecord::QueryRecorder.new do
get_pipelines_index_json
- end
+ end.count
- expect(queries.count).to be <= 36
+ create_all_pipeline_types
+
+ # There appears to be one extra query for Pipelines#has_warnings? for some reason
+ expect { get_pipelines_index_json }.not_to exceed_query_limit(control_count + 1)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['pipelines'].count).to eq 10
end
end
diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
index bc757d72a49..05ebb7e90d2 100644
--- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb
+++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
@@ -121,7 +121,7 @@ describe 'Admin disables Git access protocol', :js do
end
def switch_git_protocol(value)
- visit admin_application_settings_path
+ visit general_admin_application_settings_path
page.within('.as-visibility-access') do
find('#application_setting_enabled_git_access_protocol').find(:xpath, "option[#{value}]").select_option
diff --git a/spec/features/admin/admin_mode_spec.rb b/spec/features/admin/admin_mode_spec.rb
index 016147e6508..7b8990aceef 100644
--- a/spec/features/admin/admin_mode_spec.rb
+++ b/spec/features/admin/admin_mode_spec.rb
@@ -37,7 +37,7 @@ describe 'Admin mode', :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode
end
it 'is necessary to provide credentials again before opening pages in admin scope' do
- visit admin_application_settings_path # admin logged out because not in admin_mode
+ visit general_admin_application_settings_path # admin logged out because not in admin_mode
expect(page).to have_current_path(new_admin_session_path)
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index d0ed2c3210b..7bc904be69d 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -458,11 +458,11 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin)
- visit admin_application_settings_path
+ visit general_admin_application_settings_path
end
it 'loads admin settings page without redirect for reauthentication' do
- expect(current_path).to eq admin_application_settings_path
+ expect(current_path).to eq general_admin_application_settings_path
end
end
diff --git a/spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb
new file mode 100644
index 00000000000..718109bb720
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::BackfillProjectSettings, :migration, schema: 20200114113341 do
+ let(:projects) { table(:projects) }
+ let(:project_settings) { table(:project_settings) }
+ let(:namespace) { table(:namespaces).create(name: 'user', path: 'user') }
+ let(:project) { projects.create(namespace_id: namespace.id) }
+
+ subject { described_class.new }
+
+ describe '#perform' do
+ it 'creates settings for all projects in range' do
+ projects.create(id: 5, namespace_id: namespace.id)
+ projects.create(id: 7, namespace_id: namespace.id)
+ projects.create(id: 8, namespace_id: namespace.id)
+
+ subject.perform(5, 7)
+
+ expect(project_settings.all.pluck(:project_id)).to contain_exactly(5, 7)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
index 90f4b06cea0..c5c91135f60 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
@@ -33,13 +33,18 @@ describe Gitlab::Ci::Pipeline::Seed::Deployment do
expect(subject.iid).to be_present
expect(subject.environment.name).to eq('production')
expect(subject.cluster).to be_nil
+ expect(subject.deployment_cluster).to be_nil
end
context 'when environment has deployment platform' do
let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
- it 'returns a deployment with cluster id' do
- expect(subject.cluster).to eq(cluster)
+ it 'sets the cluster and deployment_cluster' do
+ expect(subject.cluster).to eq(cluster) # until we stop double writing in 12.9: https://gitlab.com/gitlab-org/gitlab/issues/202628
+ expect(subject.deployment_cluster).to have_attributes(
+ cluster_id: cluster.id,
+ kubernetes_namespace: subject.environment.deployment_namespace
+ )
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 81f6138e2bf..4c521ae7f07 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -452,6 +452,7 @@ project:
- package_files
- tracing_setting
- alerting_setting
+- project_setting
- webide_pipelines
- reviews
- incident_management_setting
@@ -613,4 +614,4 @@ epic:
- due_date_sourcing_epic
- events
- resource_label_events
-- user_mentions \ No newline at end of file
+- user_mentions
diff --git a/spec/migrations/20200122123016_backfill_project_settings_spec.rb b/spec/migrations/20200122123016_backfill_project_settings_spec.rb
new file mode 100644
index 00000000000..fec18d6d52b
--- /dev/null
+++ b/spec/migrations/20200122123016_backfill_project_settings_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20200122123016_backfill_project_settings.rb')
+
+describe BackfillProjectSettings, :migration, :sidekiq, schema: 20200114113341 do
+ let(:projects) { table(:projects) }
+ let(:namespace) { table(:namespaces).create(name: 'user', path: 'user') }
+ let(:project) { projects.create(namespace_id: namespace.id) }
+
+ describe '#up' do
+ before do
+ stub_const("#{described_class}::BATCH_SIZE", 2)
+
+ projects.create(id: 1, namespace_id: namespace.id)
+ projects.create(id: 2, namespace_id: namespace.id)
+ projects.create(id: 3, namespace_id: namespace.id)
+ end
+
+ it 'schedules BackfillProjectSettings background jobs' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ migrate!
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 1, 2)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(4.minutes, 3, 3)
+ expect(BackgroundMigrationWorker.jobs.size).to eq(2)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index 43c843b3420..1a97dd60c0e 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -53,6 +53,16 @@ describe Ci::Bridge do
end
end
+ describe 'state machine transitions' do
+ context 'when bridge points towards downstream' do
+ it 'schedules downstream pipeline creation' do
+ expect(bridge).to receive(:schedule_downstream_pipeline!)
+
+ bridge.enqueue!
+ end
+ end
+ end
+
describe '#inherit_status_from_downstream!' do
let(:downstream_pipeline) { build(:ci_pipeline, status: downstream_status) }
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 85860da38ad..23592cb0c70 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -26,6 +26,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
it { is_expected.to have_one(:application_runner) }
it { is_expected.to have_many(:kubernetes_namespaces) }
it { is_expected.to have_one(:cluster_project) }
+ it { is_expected.to have_many(:deployment_clusters) }
it { is_expected.to delegate_method(:status).to(:provider) }
it { is_expected.to delegate_method(:status_reason).to(:provider) }
diff --git a/spec/models/deployment_cluster_spec.rb b/spec/models/deployment_cluster_spec.rb
new file mode 100644
index 00000000000..8bb09e9a510
--- /dev/null
+++ b/spec/models/deployment_cluster_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe DeploymentCluster do
+ let(:cluster) { create(:cluster) }
+ let(:deployment) { create(:deployment) }
+ let(:kubernetes_namespace) { 'an-example-namespace' }
+
+ subject { described_class.new(deployment: deployment, cluster: cluster, kubernetes_namespace: kubernetes_namespace) }
+
+ it { is_expected.to belong_to(:deployment).required }
+ it { is_expected.to belong_to(:cluster).required }
+
+ it do
+ is_expected.to have_attributes(
+ cluster_id: cluster.id,
+ deployment_id: deployment.id,
+ kubernetes_namespace: kubernetes_namespace
+ )
+ end
+end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index c1beef0b759..bdbe38afc56 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -10,6 +10,7 @@ describe Deployment do
it { is_expected.to belong_to(:cluster).class_name('Clusters::Cluster') }
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:deployable) }
+ it { is_expected.to have_one(:deployment_cluster) }
it { is_expected.to have_many(:deployment_merge_requests) }
it { is_expected.to have_many(:merge_requests).through(:deployment_merge_requests) }
diff --git a/spec/models/project_setting_spec.rb b/spec/models/project_setting_spec.rb
new file mode 100644
index 00000000000..5cfb932eb2a
--- /dev/null
+++ b/spec/models/project_setting_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProjectSetting, type: :model do
+ it { is_expected.to belong_to(:project) }
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 924cc7169ea..f847cb63ddc 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -69,6 +69,7 @@ describe Project do
it { is_expected.to have_one(:forked_from_project).through(:fork_network_member) }
it { is_expected.to have_one(:auto_devops).class_name('ProjectAutoDevops') }
it { is_expected.to have_one(:error_tracking_setting).class_name('ErrorTracking::ProjectErrorTrackingSetting') }
+ it { is_expected.to have_one(:project_setting) }
it { is_expected.to have_many(:commit_statuses) }
it { is_expected.to have_many(:ci_pipelines) }
it { is_expected.to have_many(:builds) }
@@ -155,6 +156,11 @@ describe Project do
expect(project.pages_metadatum).to be_an_instance_of(ProjectPagesMetadatum)
expect(project.pages_metadatum).to be_persisted
end
+
+ it 'automatically creates a project setting row' do
+ expect(project.project_setting).to be_an_instance_of(ProjectSetting)
+ expect(project.project_setting).to be_persisted
+ end
end
context 'updating cd_cd_settings' do
diff --git a/spec/rubocop/cop/scalability/cron_worker_context_spec.rb b/spec/rubocop/cop/scalability/cron_worker_context_spec.rb
index bf10b8dc02c..460514d9bed 100644
--- a/spec/rubocop/cop/scalability/cron_worker_context_spec.rb
+++ b/spec/rubocop/cop/scalability/cron_worker_context_spec.rb
@@ -58,7 +58,9 @@ describe RuboCop::Cop::Scalability::CronWorkerContext do
include CronjobQueue
def perform
- SomeOtherWorker.bulk_perform_async_with_contexts(contexts_for_arguments)
+ SomeOtherWorker.bulk_perform_async_with_contexts(things,
+ arguments_proc: -> (thing) { thing.id },
+ context_proc: -> (thing) { { project: thing.project } })
end
end
CODE
@@ -70,7 +72,9 @@ describe RuboCop::Cop::Scalability::CronWorkerContext do
include CronjobQueue
def perform
- SomeOtherWorker.bulk_perform_in_with_contexts(contexts_for_arguments)
+ SomeOtherWorker.bulk_perform_in_with_contexts(10.minutes, things,
+ arguments_proc: -> (thing) { thing.id },
+ context_proc: -> (thing) { { project: thing.project } })
end
end
CODE
diff --git a/spec/services/ci/create_cross_project_pipeline_service_spec.rb b/spec/services/ci/create_cross_project_pipeline_service_spec.rb
new file mode 100644
index 00000000000..f90cdb55a7a
--- /dev/null
+++ b/spec/services/ci/create_cross_project_pipeline_service_spec.rb
@@ -0,0 +1,368 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::CreateCrossProjectPipelineService, '#execute' do
+ let_it_be(:user) { create(:user) }
+ let(:upstream_project) { create(:project, :repository) }
+ let_it_be(:downstream_project) { create(:project, :repository) }
+
+ let!(:upstream_pipeline) do
+ create(:ci_pipeline, :running, project: upstream_project)
+ end
+
+ let(:trigger) do
+ {
+ trigger: {
+ project: downstream_project.full_path,
+ branch: 'feature'
+ }
+ }
+ end
+
+ let(:bridge) do
+ create(:ci_bridge, status: :pending,
+ user: user,
+ options: trigger,
+ pipeline: upstream_pipeline)
+ end
+
+ let(:service) { described_class.new(upstream_project, user) }
+
+ before do
+ upstream_project.add_developer(user)
+ end
+
+ context 'when downstream project has not been found' do
+ let(:trigger) do
+ { trigger: { project: 'unknown/project' } }
+ end
+
+ it 'does not create a pipeline' do
+ expect { service.execute(bridge) }
+ .not_to change { Ci::Pipeline.count }
+ end
+
+ it 'changes pipeline bridge job status to failed' do
+ service.execute(bridge)
+
+ expect(bridge.reload).to be_failed
+ expect(bridge.failure_reason)
+ .to eq 'downstream_bridge_project_not_found'
+ end
+ end
+
+ context 'when user can not access downstream project' do
+ it 'does not create a new pipeline' do
+ expect { service.execute(bridge) }
+ .not_to change { Ci::Pipeline.count }
+ end
+
+ it 'changes status of the bridge build' do
+ service.execute(bridge)
+
+ expect(bridge.reload).to be_failed
+ expect(bridge.failure_reason)
+ .to eq 'downstream_bridge_project_not_found'
+ end
+ end
+
+ context 'when user does not have access to create pipeline' do
+ before do
+ downstream_project.add_guest(user)
+ end
+
+ it 'does not create a new pipeline' do
+ expect { service.execute(bridge) }
+ .not_to change { Ci::Pipeline.count }
+ end
+
+ it 'changes status of the bridge build' do
+ service.execute(bridge)
+
+ expect(bridge.reload).to be_failed
+ expect(bridge.failure_reason).to eq 'insufficient_bridge_permissions'
+ end
+ end
+
+ context 'when user can create pipeline in a downstream project' do
+ let(:stub_config) { true }
+
+ before do
+ downstream_project.add_developer(user)
+ stub_ci_pipeline_yaml_file(YAML.dump(rspec: { script: 'rspec' })) if stub_config
+ end
+
+ it 'creates only one new pipeline' do
+ expect { service.execute(bridge) }
+ .to change { Ci::Pipeline.count }.by(1)
+ end
+
+ it 'creates a new pipeline in a downstream project' do
+ pipeline = service.execute(bridge)
+
+ expect(pipeline.user).to eq bridge.user
+ expect(pipeline.project).to eq downstream_project
+ expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline
+ expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline
+ expect(pipeline.source_bridge).to eq bridge
+ expect(pipeline.source_bridge).to be_a ::Ci::Bridge
+ end
+
+ it 'updates bridge status when downstream pipeline gets proceesed' do
+ pipeline = service.execute(bridge)
+
+ expect(pipeline.reload).to be_pending
+ expect(bridge.reload).to be_success
+ end
+
+ context 'when target ref is not specified' do
+ let(:trigger) do
+ { trigger: { project: downstream_project.full_path } }
+ end
+
+ it 'is using default branch name' do
+ pipeline = service.execute(bridge)
+
+ expect(pipeline.ref).to eq 'master'
+ end
+ end
+
+ context 'when downstream project is the same as the job project' do
+ let(:trigger) do
+ { trigger: { project: upstream_project.full_path } }
+ end
+
+ context 'detects a circular dependency' do
+ it 'does not create a new pipeline' do
+ expect { service.execute(bridge) }
+ .not_to change { Ci::Pipeline.count }
+ end
+
+ it 'changes status of the bridge build' do
+ service.execute(bridge)
+
+ expect(bridge.reload).to be_failed
+ expect(bridge.failure_reason).to eq 'invalid_bridge_trigger'
+ end
+ end
+
+ context 'when "include" is provided' do
+ shared_examples 'creates a child pipeline' do
+ it 'creates only one new pipeline' do
+ expect { service.execute(bridge) }
+ .to change { Ci::Pipeline.count }.by(1)
+ end
+
+ it 'creates a child pipeline in the same project' do
+ pipeline = service.execute(bridge)
+ pipeline.reload
+
+ expect(pipeline.builds.map(&:name)).to eq %w[rspec echo]
+ expect(pipeline.user).to eq bridge.user
+ expect(pipeline.project).to eq bridge.project
+ expect(bridge.sourced_pipelines.first.pipeline).to eq pipeline
+ expect(pipeline.triggered_by_pipeline).to eq upstream_pipeline
+ expect(pipeline.source_bridge).to eq bridge
+ expect(pipeline.source_bridge).to be_a ::Ci::Bridge
+ end
+
+ it 'updates bridge status when downstream pipeline gets proceesed' do
+ pipeline = service.execute(bridge)
+
+ expect(pipeline.reload).to be_pending
+ expect(bridge.reload).to be_success
+ end
+
+ it 'propagates parent pipeline settings to the child pipeline' do
+ pipeline = service.execute(bridge)
+ pipeline.reload
+
+ expect(pipeline.ref).to eq(upstream_pipeline.ref)
+ expect(pipeline.sha).to eq(upstream_pipeline.sha)
+ expect(pipeline.source_sha).to eq(upstream_pipeline.source_sha)
+ expect(pipeline.target_sha).to eq(upstream_pipeline.target_sha)
+ expect(pipeline.target_sha).to eq(upstream_pipeline.target_sha)
+
+ expect(pipeline.trigger_requests.last).to eq(bridge.trigger_request)
+ end
+ end
+
+ before do
+ file_content = YAML.dump(
+ rspec: { script: 'rspec' },
+ echo: { script: 'echo' })
+ upstream_project.repository.create_file(
+ user, 'child-pipeline.yml', file_content, message: 'message', branch_name: 'master')
+
+ upstream_pipeline.update!(sha: upstream_project.commit.id)
+ end
+
+ let(:stub_config) { false }
+
+ let(:trigger) do
+ {
+ trigger: { include: 'child-pipeline.yml' }
+ }
+ end
+
+ it_behaves_like 'creates a child pipeline'
+
+ context 'when latest sha for the ref changed in the meantime' do
+ before do
+ upstream_project.repository.create_file(
+ user, 'another-change', 'test', message: 'message', branch_name: 'master')
+ end
+
+ # it does not auto-cancel pipelines from the same family
+ it_behaves_like 'creates a child pipeline'
+ end
+
+ context 'when upstream pipeline is a child pipeline' do
+ let!(:pipeline_source) do
+ create(:ci_sources_pipeline,
+ source_pipeline: create(:ci_pipeline, project: upstream_pipeline.project),
+ pipeline: upstream_pipeline
+ )
+ end
+
+ before do
+ upstream_pipeline.update!(source: :parent_pipeline)
+ end
+
+ it 'does not create a further child pipeline' do
+ expect { service.execute(bridge) }
+ .not_to change { Ci::Pipeline.count }
+
+ expect(bridge.reload).to be_failed
+ expect(bridge.failure_reason).to eq 'bridge_pipeline_is_child_pipeline'
+ end
+ end
+ end
+ end
+
+ context 'when downstream pipeline creation errors out' do
+ let(:stub_config) { false }
+
+ before do
+ stub_ci_pipeline_yaml_file(YAML.dump(invalid: { yaml: 'error' }))
+ end
+
+ it 'creates only one new pipeline' do
+ expect { service.execute(bridge) }
+ .to change { Ci::Pipeline.count }.by(1)
+ end
+
+ it 'creates a new pipeline in the downstream project' do
+ pipeline = service.execute(bridge)
+
+ expect(pipeline.user).to eq bridge.user
+ expect(pipeline.project).to eq downstream_project
+ end
+
+ it 'drops the bridge' do
+ pipeline = service.execute(bridge)
+
+ expect(pipeline.reload).to be_failed
+ expect(bridge.reload).to be_failed
+ expect(bridge.failure_reason).to eq('downstream_pipeline_creation_failed')
+ end
+ end
+
+ context 'when bridge job has YAML variables defined' do
+ before do
+ bridge.yaml_variables = [{ key: 'BRIDGE', value: 'var', public: true }]
+ end
+
+ it 'passes bridge variables to downstream pipeline' do
+ pipeline = service.execute(bridge)
+
+ expect(pipeline.variables.first)
+ .to have_attributes(key: 'BRIDGE', value: 'var')
+ end
+ end
+
+ context 'when pipeline variables are defined' do
+ before do
+ upstream_pipeline.variables.create(key: 'PIPELINE_VARIABLE', value: 'my-value')
+ end
+
+ it 'does not pass pipeline variables directly downstream' do
+ pipeline = service.execute(bridge)
+
+ pipeline.variables.map(&:key).tap do |variables|
+ expect(variables).not_to include 'PIPELINE_VARIABLE'
+ end
+ end
+
+ context 'when using YAML variables interpolation' do
+ before do
+ bridge.yaml_variables = [{ key: 'BRIDGE', value: '$PIPELINE_VARIABLE-var', public: true }]
+ end
+
+ it 'makes it possible to pass pipeline variable downstream' do
+ pipeline = service.execute(bridge)
+
+ pipeline.variables.find_by(key: 'BRIDGE').tap do |variable|
+ expect(variable.value).to eq 'my-value-var'
+ end
+ end
+ end
+ end
+
+ # TODO: Move this context into a feature spec that uses
+ # multiple pipeline processing services. Location TBD in:
+ # https://gitlab.com/gitlab-org/gitlab/issues/36216
+ context 'when configured with bridge job rules' do
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ downstream_project.add_maintainer(upstream_project.owner)
+ end
+
+ let(:config) do
+ <<-EOY
+ hello:
+ script: echo world
+
+ bridge-job:
+ rules:
+ - if: $CI_COMMIT_REF_NAME == "master"
+ trigger:
+ project: #{downstream_project.full_path}
+ branch: master
+ EOY
+ end
+
+ let(:primary_pipeline) do
+ Ci::CreatePipelineService.new(upstream_project, upstream_project.owner, { ref: 'master' })
+ .execute(:push, save_on_errors: false)
+ end
+
+ let(:bridge) { primary_pipeline.processables.find_by(name: 'bridge-job') }
+ let(:service) { described_class.new(upstream_project, upstream_project.owner) }
+
+ context 'that include the bridge job' do
+ # TODO: this is skipped because `trigger` keyword does not exist yet.
+ # enabling it in the next MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24393
+ xit 'creates the downstream pipeline' do
+ expect { service.execute(bridge) }
+ .to change(downstream_project.ci_pipelines, :count).by(1)
+ end
+ end
+ end
+
+ context 'when user does not have access to push protected branch of downstream project' do
+ before do
+ create(:protected_branch, :maintainers_can_push,
+ project: downstream_project, name: 'feature')
+ end
+
+ it 'changes status of the bridge build' do
+ service.execute(bridge)
+
+ expect(bridge.reload).to be_failed
+ expect(bridge.failure_reason).to eq 'insufficient_bridge_permissions'
+ end
+ end
+ end
+end
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index fa1a8f60256..496b08799f2 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -31,6 +31,11 @@ describe MergeRequests::MergeService do
it { expect(merge_request).to be_valid }
it { expect(merge_request).to be_merged }
+ it 'persists merge_commit_sha and nullifies in_progress_merge_commit_sha' do
+ expect(merge_request.merge_commit_sha).not_to be_nil
+ expect(merge_request.in_progress_merge_commit_sha).to be_nil
+ end
+
it 'sends email to user2 about merge of new merge_request' do
email = ActionMailer::Base.deliveries.last
expect(email.to.first).to eq(user2.email)
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 24781ac86be..9d23556efda 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -43,6 +43,12 @@ describe Projects::CreateService, '#execute' do
create_project(user, opts)
end
+
+ it 'creates associated project settings' do
+ project = create_project(user, opts)
+
+ expect(project.project_setting).to be_persisted
+ end
end
context "admin creates project with other user's namespace_id" do
diff --git a/spec/workers/ci/create_cross_project_pipeline_worker_spec.rb b/spec/workers/ci/create_cross_project_pipeline_worker_spec.rb
new file mode 100644
index 00000000000..22eab1d20f7
--- /dev/null
+++ b/spec/workers/ci/create_cross_project_pipeline_worker_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::CreateCrossProjectPipelineWorker do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:bridge) { create(:ci_bridge, user: user, pipeline: pipeline) }
+
+ let(:service) { double('pipeline creation service') }
+
+ describe '#perform' do
+ context 'when bridge exists' do
+ it 'calls cross project pipeline creation service' do
+ expect(Ci::CreateCrossProjectPipelineService)
+ .to receive(:new)
+ .with(project, user)
+ .and_return(service)
+
+ expect(service).to receive(:execute).with(bridge)
+
+ described_class.new.perform(bridge.id)
+ end
+ end
+
+ context 'when bridge does not exist' do
+ it 'does nothing' do
+ expect(Ci::CreateCrossProjectPipelineService)
+ .not_to receive(:new)
+
+ described_class.new.perform(1234)
+ end
+ end
+ end
+end