create_pipeline_service_spec.rb 9.5 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 13 14 15 16 17
    def execute_service(after: project.commit.id, message: 'Message', ref: 'refs/heads/master')
      params = { ref: ref,
                 before: '00000000',
                 after: after,
                 commits: [{ message: message }] }

18 19 20
      described_class.new(project, user, params).execute
    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 33 34 35 36 37
      it 'creates a pipeline' do
        expect(pipeline).to be_kind_of(Ci::Pipeline)
        expect(pipeline).to be_valid
        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
38

39
      context 'when merge requests already exist for this source branch' do
40 41 42
        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)
43

44
          head_pipeline = pipeline
45

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
          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
61
          let!(:target_project) { create(:project) }
62 63 64 65 66 67 68 69 70 71 72
          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
73 74 75 76 77 78 79 80 81 82 83

        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
84 85
      end

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

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

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

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

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

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

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

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

R
Rydkin Maxim 已提交
116
          expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'canceled', auto_canceled_by_id: pipeline.id)
117 118 119 120 121 122 123 124 125
        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

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

      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
147 148
    end

149 150
    context "skip tag if there is no build for it" do
      it "creates commit if there is appropriate job" do
151
        expect(execute_service).to be_persisted
152 153 154 155 156 157
      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)

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

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

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

169 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
    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
195 196 197 198
    end

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

      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]"
      ]
210 211 212 213 214

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

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

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

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

229
          pipeline = execute_service(message: commit_message)
230

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

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

239
        it_behaves_like 'creating a pipeline'
240 241
      end

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

245
        it_behaves_like 'creating a pipeline'
246 247
      end

248 249 250 251 252
      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
253 254 255 256 257 258 259 260 261
    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
262
        result = execute_service
263 264 265 266 267 268 269 270 271 272 273 274 275 276

        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
277
        result = execute_service
278 279 280 281 282

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

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

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

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