diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-10 12:08:59 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-10 12:08:59 +0000 |
commit | 7351a484d79236b7e9d47c86f2fcc970b7ae10b0 (patch) | |
tree | 651b5fca7ea0460e3ce7c687cfa9e3a3b37eefc8 /spec/services | |
parent | b4ded0ba7b4d2cdbed5b1f331cf2083a25ee4d7c (diff) | |
download | gitlab-ce-7351a484d79236b7e9d47c86f2fcc970b7ae10b0.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/services')
-rw-r--r-- | spec/services/ci/create_cross_project_pipeline_service_spec.rb | 368 | ||||
-rw-r--r-- | spec/services/merge_requests/merge_service_spec.rb | 5 | ||||
-rw-r--r-- | spec/services/projects/create_service_spec.rb | 6 |
3 files changed, 379 insertions, 0 deletions
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 |