create_pipeline_service_spec.rb 9.6 KB
Newer Older
1 2 3
require 'spec_helper'

describe Ci::CreatePipelineService, services: true do
4
  let(:project) { create(:project, :repository) }
5 6 7 8 9 10 11
  let(:user) { create(:admin) }

  before do
    stub_ci_pipeline_to_return_yaml_file
  end

  describe '#execute' do
12
    def execute_service(source: :push, after: project.commit.id, message: 'Message', ref: 'refs/heads/master')
13 14 15 16 17
      params = { ref: ref,
                 before: '00000000',
                 after: after,
                 commits: [{ message: message }] }

18
      described_class.new(project, user, params).execute(source)
19 20
    end

21 22 23
    context 'valid params' do
      let(:pipeline) { execute_service }

24 25 26 27 28 29
      let(:pipeline_on_previous_commit) do
        execute_service(
          after: previous_commit_sha_from_ref('master')
        )
      end

30 31 32
      it 'creates a pipeline' do
        expect(pipeline).to be_kind_of(Ci::Pipeline)
        expect(pipeline).to be_valid
33
        expect(pipeline).to be_push
34 35 36 37 38
        expect(pipeline).to eq(project.pipelines.last)
        expect(pipeline).to have_attributes(user: user)
        expect(pipeline).to have_attributes(status: 'pending')
        expect(pipeline.builds.first).to be_kind_of(Ci::Build)
      end
39

40
      context 'when merge requests already exist for this source branch' do
41 42 43
        it 'updates head pipeline of each merge request' do
          merge_request_1 = create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project)
          merge_request_2 = create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project)
44

45
          head_pipeline = pipeline
46

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
          expect(merge_request_1.reload.head_pipeline).to eq(head_pipeline)
          expect(merge_request_2.reload.head_pipeline).to eq(head_pipeline)
        end

        context 'when there is no pipeline for source branch' do
          it "does not update merge request head pipeline" do
            merge_request = create(:merge_request, source_branch: 'other_branch', target_branch: "branch_1", source_project: project)

            head_pipeline = pipeline

            expect(merge_request.reload.head_pipeline).not_to eq(head_pipeline)
          end
        end

        context 'when merge request target project is different from source project' do
62
          let!(:target_project) { create(:project) }
63 64 65 66 67 68 69 70 71 72 73
          let!(:forked_project_link) { create(:forked_project_link, forked_to_project: project, forked_from_project: target_project) }

          it 'updates head pipeline for merge request' do
            merge_request =
              create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project, target_project: target_project)

            head_pipeline = pipeline

            expect(merge_request.reload.head_pipeline).to eq(head_pipeline)
          end
        end
74 75 76 77 78 79 80 81 82 83 84

        context 'when merge request head commit sha does not match pipeline sha' do
          it 'does not update merge request head pipeline' do
            merge_request = create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project)
            allow_any_instance_of(MergeRequestDiff).to receive(:head_commit).and_return(double(id: 1234))

            pipeline

            expect(merge_request.reload.head_pipeline).to be_nil
          end
        end
85 86
      end

87 88 89 90 91
      context 'auto-cancel enabled' do
        before do
          project.update(auto_cancel_pending_pipelines: 'enabled')
        end

92 93
        it 'does not cancel HEAD pipeline' do
          pipeline
R
Rydkin Maxim 已提交
94
          pipeline_on_previous_commit
95

R
Rydkin Maxim 已提交
96
          expect(pipeline.reload).to have_attributes(status: 'pending', auto_canceled_by_id: nil)
97
        end
98 99

        it 'auto cancel pending non-HEAD pipelines' do
R
Rydkin Maxim 已提交
100
          pipeline_on_previous_commit
101 102
          pipeline

R
Rydkin Maxim 已提交
103
          expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'canceled', auto_canceled_by_id: pipeline.id)
104 105 106
        end

        it 'does not cancel running outdated pipelines' do
R
Rydkin Maxim 已提交
107
          pipeline_on_previous_commit.run
108 109
          execute_service

R
Rydkin Maxim 已提交
110
          expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'running', auto_canceled_by_id: nil)
111 112 113
        end

        it 'cancel created outdated pipelines' do
R
Rydkin Maxim 已提交
114
          pipeline_on_previous_commit.update(status: 'created')
115 116
          pipeline

R
Rydkin Maxim 已提交
117
          expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'canceled', auto_canceled_by_id: pipeline.id)
118 119 120 121 122 123 124 125 126
        end

        it 'does not cancel pipelines from the other branches' do
          pending_pipeline = execute_service(
            ref: 'refs/heads/feature',
            after: previous_commit_sha_from_ref('feature')
          )
          pipeline

127
          expect(pending_pipeline.reload).to have_attributes(status: 'pending', auto_canceled_by_id: nil)
128 129
        end
      end
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

      context 'auto-cancel disabled' do
        before do
          project.update(auto_cancel_pending_pipelines: 'disabled')
        end

        it 'does not auto cancel pending non-HEAD pipelines' do
          pipeline_on_previous_commit
          pipeline

          expect(pipeline_on_previous_commit.reload)
            .to have_attributes(status: 'pending', auto_canceled_by_id: nil)
        end
      end

      def previous_commit_sha_from_ref(ref)
        project.commit(ref).parent.sha
      end
148 149
    end

150 151
    context "skip tag if there is no build for it" do
      it "creates commit if there is appropriate job" do
152
        expect(execute_service).to be_persisted
153 154 155 156 157 158
      end

      it "creates commit if there is no appropriate job but deploy job has right ref setting" do
        config = YAML.dump({ deploy: { script: "ls", only: ["master"] } })
        stub_ci_pipeline_yaml_file(config)

159
        expect(execute_service).to be_persisted
160 161 162 163 164 165
      end
    end

    it 'skips creating pipeline for refs without .gitlab-ci.yml' do
      stub_ci_pipeline_yaml_file(nil)

166
      expect(execute_service).not_to be_persisted
167 168 169
      expect(Ci::Pipeline.count).to eq(0)
    end

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
    shared_examples 'a failed pipeline' do
      it 'creates failed pipeline' do
        stub_ci_pipeline_yaml_file(ci_yaml)

        pipeline = execute_service(message: message)

        expect(pipeline).to be_persisted
        expect(pipeline.builds.any?).to be false
        expect(pipeline.status).to eq('failed')
        expect(pipeline.yaml_errors).not_to be_nil
      end
    end

    context 'when yaml is invalid' do
      let(:ci_yaml) { 'invalid: file: fiile' }
      let(:message) { 'Message' }

      it_behaves_like 'a failed pipeline'

      context 'when receive git commit' do
        before do
          allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message }
        end

        it_behaves_like 'a failed pipeline'
      end
196 197 198 199
    end

    context 'when commit contains a [ci skip] directive' do
      let(:message) { "some message[ci skip]" }
200 201 202 203 204 205 206 207 208 209 210

      ci_messages = [
        "some message[ci skip]",
        "some message[skip ci]",
        "some message[CI SKIP]",
        "some message[SKIP CI]",
        "some message[ci_skip]",
        "some message[skip_ci]",
        "some message[ci-skip]",
        "some message[skip-ci]"
      ]
211 212 213 214 215

      before do
        allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message }
      end

216 217
      ci_messages.each do |ci_message|
        it "skips builds creation if the commit message is #{ci_message}" do
218
          pipeline = execute_service(message: ci_message)
219 220 221 222 223

          expect(pipeline).to be_persisted
          expect(pipeline.builds.any?).to be false
          expect(pipeline.status).to eq("skipped")
        end
224 225
      end

226
      shared_examples 'creating a pipeline' do
R
Rydkin Maxim 已提交
227
        it 'does not skip pipeline creation' do
228
          allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { commit_message }
229

230
          pipeline = execute_service(message: commit_message)
231

232 233 234
          expect(pipeline).to be_persisted
          expect(pipeline.builds.first.name).to eq("rspec")
        end
235 236
      end

237 238
      context 'when commit message does not contain [ci skip] nor [skip ci]' do
        let(:commit_message) { 'some message' }
239

240
        it_behaves_like 'creating a pipeline'
241 242
      end

243 244
      context 'when commit message is nil' do
        let(:commit_message) { nil }
245

246
        it_behaves_like 'creating a pipeline'
247 248
      end

249 250 251 252 253
      context 'when there is [ci skip] tag in commit message and yaml is invalid' do
        let(:ci_yaml) { 'invalid: file: fiile' }

        it_behaves_like 'a failed pipeline'
      end
254 255 256 257 258 259 260 261 262
    end

    context 'when there are no jobs for this pipeline' do
      before do
        config = YAML.dump({ test: { script: 'ls', only: ['feature'] } })
        stub_ci_pipeline_yaml_file(config)
      end

      it 'does not create a new pipeline' do
263
        result = execute_service
264 265 266 267 268 269 270 271 272 273 274 275 276 277

        expect(result).not_to be_persisted
        expect(Ci::Build.all).to be_empty
        expect(Ci::Pipeline.count).to eq(0)
      end
    end

    context 'with manual actions' do
      before do
        config = YAML.dump({ deploy: { script: 'ls', when: 'manual' } })
        stub_ci_pipeline_yaml_file(config)
      end

      it 'does not create a new pipeline' do
278
        result = execute_service
279 280 281 282 283

        expect(result).to be_persisted
        expect(result.manual_actions).not_to be_empty
      end
    end
284 285 286

    context 'with environment' do
      before do
Z
Z.J. van de Weg 已提交
287
        config = YAML.dump(deploy: { environment: { name: "review/$CI_COMMIT_REF_NAME" }, script: 'ls' })
288 289 290 291
        stub_ci_pipeline_yaml_file(config)
      end

      it 'creates the environment' do
292
        result = execute_service
293 294 295 296 297

        expect(result).to be_persisted
        expect(Environment.find_by(name: "review/master")).not_to be_nil
      end
    end
298 299
  end
end