diff options
author | Kamil Trzciński <ayufan@ayufan.eu> | 2017-09-27 14:44:28 +0000 |
---|---|---|
committer | Kamil Trzciński <ayufan@ayufan.eu> | 2017-09-27 14:44:28 +0000 |
commit | 0bba522f74dbb70160f81d23c580e8928d36c33b (patch) | |
tree | a49b71c0efce6172ced5a46e2831399a7f7f427c /spec/lib | |
parent | 024249621fd1efbad611bad32f0e171854a4cdb4 (diff) | |
parent | 26e73c2e8fa3d6cdd85ba82628981d3334445aeb (diff) | |
download | gitlab-ce-0bba522f74dbb70160f81d23c580e8928d36c33b.tar.gz |
Merge branch 'backstage/gb/refactor-pipeline-create-service' into 'master'
Refactor a service responsible for creating a pipeline
Closes #37563 and #34415
See merge request gitlab-org/gitlab-ce!14482
Diffstat (limited to 'spec/lib')
-rw-r--r-- | spec/lib/gitlab/ci/pipeline/chain/create_spec.rb | 66 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb | 55 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb | 85 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb | 142 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb | 126 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb | 60 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/pipeline/duration_spec.rb (renamed from spec/lib/gitlab/ci/pipeline_duration_spec.rb) | 6 |
7 files changed, 537 insertions, 3 deletions
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb new file mode 100644 index 00000000000..f54e2326b06 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Create do + set(:project) { create(:project) } + set(:user) { create(:user) } + + let(:pipeline) do + build(:ci_pipeline_with_one_job, project: project, + ref: 'master') + end + + let(:command) do + double('command', project: project, + current_user: user, + seeds_block: nil) + end + + let(:step) { described_class.new(pipeline, command) } + + before do + step.perform! + end + + context 'when pipeline is ready to be saved' do + it 'saves a pipeline' do + expect(pipeline).to be_persisted + end + + it 'does not break the chain' do + expect(step.break?).to be false + end + + it 'creates stages' do + expect(pipeline.reload.stages).to be_one + end + end + + context 'when pipeline has validation errors' do + let(:pipeline) do + build(:ci_pipeline, project: project, ref: nil) + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + it 'appends validation error' do + expect(pipeline.errors.to_a) + .to include /Failed to persist the pipeline/ + end + end + + context 'when there is a seed block present' do + let(:seeds) { spy('pipeline seeds') } + + let(:command) do + double('command', project: project, + current_user: user, + seeds_block: seeds) + end + + it 'executes the block' do + expect(seeds).to have_received(:call).with(pipeline) + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb new file mode 100644 index 00000000000..e165e0fac2a --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Sequence do + set(:project) { create(:project) } + set(:user) { create(:user) } + + let(:pipeline) { build_stubbed(:ci_pipeline) } + let(:command) { double('command' ) } + let(:first_step) { spy('first step') } + let(:second_step) { spy('second step') } + let(:sequence) { [first_step, second_step] } + + subject do + described_class.new(pipeline, command, sequence) + end + + context 'when one of steps breaks the chain' do + before do + allow(first_step).to receive(:break?).and_return(true) + end + + it 'does not process the second step' do + subject.build! do |pipeline, sequence| + expect(sequence).not_to be_complete + end + + expect(second_step).not_to have_received(:perform!) + end + + it 'returns a pipeline object' do + expect(subject.build!).to eq pipeline + end + end + + context 'when all chains are executed correctly' do + before do + sequence.each do |step| + allow(step).to receive(:break?).and_return(false) + end + end + + it 'iterates through entire sequence' do + subject.build! do |pipeline, sequence| + expect(sequence).to be_complete + end + + expect(first_step).to have_received(:perform!) + expect(second_step).to have_received(:perform!) + end + + it 'returns a pipeline object' do + expect(subject.build!).to eq pipeline + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb new file mode 100644 index 00000000000..32bd5de829b --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Skip do + set(:project) { create(:project) } + set(:user) { create(:user) } + set(:pipeline) { create(:ci_pipeline, project: project) } + + let(:command) do + double('command', project: project, + current_user: user, + ignore_skip_ci: false, + save_incompleted: true) + end + + let(:step) { described_class.new(pipeline, command) } + + context 'when pipeline has been skipped by a user' do + before do + allow(pipeline).to receive(:git_commit_message) + .and_return('commit message [ci skip]') + + step.perform! + end + + it 'should break the chain' do + expect(step.break?).to be true + end + + it 'skips the pipeline' do + expect(pipeline.reload).to be_skipped + end + end + + context 'when pipeline has not been skipped' do + before do + step.perform! + end + + it 'should not break the chain' do + expect(step.break?).to be false + end + + it 'should not skip a pipeline chain' do + expect(pipeline.reload).not_to be_skipped + end + end + + context 'when [ci skip] should be ignored' do + let(:command) do + double('command', project: project, + current_user: user, + ignore_skip_ci: true) + end + + it 'does not break the chain' do + step.perform! + + expect(step.break?).to be false + end + end + + context 'when pipeline should be skipped but not persisted' do + let(:command) do + double('command', project: project, + current_user: user, + ignore_skip_ci: false, + save_incompleted: false) + end + + before do + allow(pipeline).to receive(:git_commit_message) + .and_return('commit message [ci skip]') + + step.perform! + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + it 'does not skip pipeline' do + expect(pipeline.reload).not_to be_skipped + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb new file mode 100644 index 00000000000..0bbdd23f4d6 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb @@ -0,0 +1,142 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do + set(:project) { create(:project, :repository) } + set(:user) { create(:user) } + + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: ref, project: project) + end + + let(:command) do + double('command', project: project, current_user: user) + end + + let(:step) { described_class.new(pipeline, command) } + + let(:ref) { 'master' } + + context 'when users has no ability to run a pipeline' do + before do + step.perform! + end + + it 'adds an error about insufficient permissions' do + expect(pipeline.errors.to_a) + .to include /Insufficient permissions/ + end + + it 'breaks the pipeline builder chain' do + expect(step.break?).to eq true + end + end + + context 'when user has ability to create a pipeline' do + before do + project.add_developer(user) + + step.perform! + end + + it 'does not invalidate the pipeline' do + expect(pipeline).to be_valid + end + + it 'does not break the chain' do + expect(step.break?).to eq false + end + end + + describe '#allowed_to_create?' do + subject { step.allowed_to_create? } + + context 'when user is a developer' do + before do + project.add_developer(user) + end + + it { is_expected.to be_truthy } + + context 'when the branch is protected' do + let!(:protected_branch) do + create(:protected_branch, project: project, name: ref) + end + + it { is_expected.to be_falsey } + + context 'when developers are allowed to merge' do + let!(:protected_branch) do + create(:protected_branch, + :developers_can_merge, + project: project, + name: ref) + end + + it { is_expected.to be_truthy } + end + end + + context 'when the tag is protected' do + let(:ref) { 'v1.0.0' } + + let!(:protected_tag) do + create(:protected_tag, project: project, name: ref) + end + + it { is_expected.to be_falsey } + + context 'when developers are allowed to create the tag' do + let!(:protected_tag) do + create(:protected_tag, + :developers_can_create, + project: project, + name: ref) + end + + it { is_expected.to be_truthy } + end + end + end + + context 'when user is a master' do + before do + project.add_master(user) + end + + it { is_expected.to be_truthy } + + context 'when the branch is protected' do + let!(:protected_branch) do + create(:protected_branch, project: project, name: ref) + end + + it { is_expected.to be_truthy } + end + + context 'when the tag is protected' do + let(:ref) { 'v1.0.0' } + + let!(:protected_tag) do + create(:protected_tag, project: project, name: ref) + end + + it { is_expected.to be_truthy } + + context 'when no one can create the tag' do + let!(:protected_tag) do + create(:protected_tag, + :no_one_can_create, + project: project, + name: ref) + end + + it { is_expected.to be_falsey } + end + end + end + + context 'when owner cannot create pipeline' do + it { is_expected.to be_falsey } + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb new file mode 100644 index 00000000000..3740df88f42 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb @@ -0,0 +1,126 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Validate::Config do + set(:project) { create(:project) } + set(:user) { create(:user) } + + let(:command) do + double('command', project: project, + current_user: user, + save_incompleted: true) + end + + let!(:step) { described_class.new(pipeline, command) } + + before do + step.perform! + end + + context 'when pipeline has no YAML configuration' do + let(:pipeline) do + build_stubbed(:ci_pipeline, project: project) + end + + it 'appends errors about missing configuration' do + expect(pipeline.errors.to_a) + .to include 'Missing .gitlab-ci.yml file' + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + end + + context 'when YAML configuration contains errors' do + let(:pipeline) do + build(:ci_pipeline, project: project, config: 'invalid YAML') + end + + it 'appends errors about YAML errors' do + expect(pipeline.errors.to_a) + .to include 'Invalid configuration format' + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + context 'when saving incomplete pipeline is allowed' do + let(:command) do + double('command', project: project, + current_user: user, + save_incompleted: true) + end + + it 'fails the pipeline' do + expect(pipeline.reload).to be_failed + end + end + + context 'when saving incomplete pipeline is not allowed' do + let(:command) do + double('command', project: project, + current_user: user, + save_incompleted: false) + end + + it 'does not drop pipeline' do + expect(pipeline).not_to be_failed + expect(pipeline).not_to be_persisted + end + end + end + + context 'when pipeline has no stages / jobs' do + let(:config) do + { rspec: { + script: 'ls', + only: ['something'] + } } + end + + let(:pipeline) do + build(:ci_pipeline, project: project, config: config) + end + + it 'appends an error about missing stages' do + expect(pipeline.errors.to_a) + .to include 'No stages / jobs for this pipeline.' + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + end + + context 'when pipeline contains configuration validation errors' do + let(:config) { { rspec: {} } } + + let(:pipeline) do + build(:ci_pipeline, project: project, config: config) + end + + it 'appends configuration validation errors to pipeline errors' do + expect(pipeline.errors.to_a) + .to include "jobs:rspec config can't be blank" + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + end + + context 'when pipeline is correct and complete' do + let(:pipeline) do + build(:ci_pipeline_with_one_job, project: project) + end + + it 'does not invalidate the pipeline' do + expect(pipeline).to be_valid + end + + it 'does not break the chain' do + expect(step.break?).to be false + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb new file mode 100644 index 00000000000..bb356efe9ad --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do + set(:project) { create(:project, :repository) } + set(:user) { create(:user) } + + let(:command) do + double('command', project: project, current_user: user) + end + + let!(:step) { described_class.new(pipeline, command) } + + before do + step.perform! + end + + context 'when pipeline ref and sha exists' do + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: 'master', sha: '123', project: project) + end + + it 'does not break the chain' do + expect(step.break?).to be false + end + + it 'does not append pipeline errors' do + expect(pipeline.errors).to be_empty + end + end + + context 'when pipeline ref does not exist' do + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: 'something', project: project) + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + it 'adds an error about missing ref' do + expect(pipeline.errors.to_a) + .to include 'Reference not found' + end + end + + context 'when pipeline does not have SHA set' do + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: 'master', sha: nil, project: project) + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + it 'adds an error about missing SHA' do + expect(pipeline.errors.to_a) + .to include 'Commit not found' + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline_duration_spec.rb b/spec/lib/gitlab/ci/pipeline/duration_spec.rb index b26728a843c..7c9836e2da6 100644 --- a/spec/lib/gitlab/ci/pipeline_duration_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/duration_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::PipelineDuration do +describe Gitlab::Ci::Pipeline::Duration do let(:calculated_duration) { calculate(data) } shared_examples 'calculating duration' do @@ -107,9 +107,9 @@ describe Gitlab::Ci::PipelineDuration do def calculate(data) periods = data.shuffle.map do |(first, last)| - Gitlab::Ci::PipelineDuration::Period.new(first, last) + described_class::Period.new(first, last) end - Gitlab::Ci::PipelineDuration.from_periods(periods.sort_by(&:first)) + described_class.from_periods(periods.sort_by(&:first)) end end |