create_pipeline_service_spec.rb 28.0 KB
Newer Older
1 2
require 'spec_helper'

3
describe Ci::CreatePipelineService do
B
Bob Van Landuyt 已提交
4 5
  include ProjectForksHelper

6
  set(:project) { create(:project, :repository) }
7
  let(:user) { create(:admin) }
8
  let(:ref_name) { 'refs/heads/master' }
9 10

  before do
11
    stub_repository_ci_yaml_file(sha: anything)
12 13 14
  end

  describe '#execute' do
15 16 17 18 19
    def execute_service(
      source: :push,
      after: project.commit.id,
      message: 'Message',
      ref: ref_name,
20
      trigger_request: nil,
S
Shinya Maeda 已提交
21 22
      variables_attributes: nil,
      merge_request: nil)
23 24 25
      params = { ref: ref,
                 before: '00000000',
                 after: after,
26
                 commits: [{ message: message }],
27
                 variables_attributes: variables_attributes }
28

29
      described_class.new(project, user, params).execute(
S
Shinya Maeda 已提交
30
        source, trigger_request: trigger_request, merge_request: merge_request)
31 32
    end

33 34 35
    context 'valid params' do
      let(:pipeline) { execute_service }

36 37 38 39 40 41
      let(:pipeline_on_previous_commit) do
        execute_service(
          after: previous_commit_sha_from_ref('master')
        )
      end

42 43 44
      it 'creates a pipeline' do
        expect(pipeline).to be_kind_of(Ci::Pipeline)
        expect(pipeline).to be_valid
45
        expect(pipeline).to be_persisted
46
        expect(pipeline).to be_push
47
        expect(pipeline).to eq(project.ci_pipelines.last)
48 49
        expect(pipeline).to have_attributes(user: user)
        expect(pipeline).to have_attributes(status: 'pending')
50
        expect(pipeline.repository_source?).to be true
51 52
        expect(pipeline.builds.first).to be_kind_of(Ci::Build)
      end
53

54 55
      it 'increments the prometheus counter' do
        expect(Gitlab::Metrics).to receive(:counter)
B
Ben Kochie 已提交
56
          .with(:pipelines_created_total, "Counter of pipelines created")
57 58 59 60 61
          .and_call_original

        pipeline
      end

62
      context 'when merge requests already exist for this source branch' do
63
        let(:merge_request_1) do
S
Shinya Maeda 已提交
64
          create(:merge_request, source_branch: 'feature', target_branch: "master", source_project: project)
65 66
        end
        let(:merge_request_2) do
S
Shinya Maeda 已提交
67
          create(:merge_request, source_branch: 'feature', target_branch: "v1.1.0", source_project: project)
68
        end
69

70 71 72 73 74 75 76 77 78 79 80 81
        context 'when related merge request is already merged' do
          let!(:merged_merge_request) do
            create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project, state: 'merged')
          end

          it 'does not schedule update head pipeline job' do
            expect(UpdateHeadPipelineForMergeRequestWorker).not_to receive(:perform_async).with(merged_merge_request.id)

            execute_service
          end
        end

82 83 84 85 86
        context 'when the head pipeline sha equals merge request sha' do
          it 'updates head pipeline of each merge request' do
            merge_request_1
            merge_request_2

S
Shinya Maeda 已提交
87
            head_pipeline = execute_service(ref: 'feature', after: nil)
88

89 90 91 92 93 94
            expect(merge_request_1.reload.head_pipeline).to eq(head_pipeline)
            expect(merge_request_2.reload.head_pipeline).to eq(head_pipeline)
          end
        end

        context 'when the head pipeline sha does not equal merge request sha' do
95
          it 'does not update the head piepeline of MRs' do
96 97 98 99
            merge_request_1
            merge_request_2

            allow_any_instance_of(Ci::Pipeline).to receive(:latest?).and_return(true)
100

101
            expect { execute_service(after: 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f') }.not_to raise_error
102 103 104 105 106 107

            last_pipeline = Ci::Pipeline.last

            expect(merge_request_1.reload.head_pipeline).not_to eq(last_pipeline)
            expect(merge_request_2.reload.head_pipeline).not_to eq(last_pipeline)
          end
108 109 110 111
        end

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

116
            head_pipeline = execute_service
117 118 119 120 121 122

            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
B
Bob Van Landuyt 已提交
123
          let!(:project) { fork_project(target_project, nil, repository: true) }
124
          let!(:target_project) { create(:project, :repository) }
125

126
          it 'updates head pipeline for merge request' do
S
Shinya Maeda 已提交
127 128
            merge_request = create(:merge_request, source_branch: 'feature',
                                                   target_branch: "master",
129 130
                                                   source_project: project,
                                                   target_project: target_project)
131

S
Shinya Maeda 已提交
132
            head_pipeline = execute_service(ref: 'feature', after: nil)
133 134 135 136

            expect(merge_request.reload.head_pipeline).to eq(head_pipeline)
          end
        end
137

138
        context 'when the pipeline is not the latest for the branch' do
139
          it 'does not update merge request head pipeline' do
140 141 142
            merge_request = create(:merge_request, source_branch: 'master',
                                                   target_branch: "branch_1",
                                                   source_project: project)
143

144
            allow_any_instance_of(Ci::Pipeline).to receive(:latest?).and_return(false)
145

146
            execute_service
147 148 149 150

            expect(merge_request.reload.head_pipeline).to be_nil
          end
        end
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

        context 'when pipeline has errors' do
          before do
            stub_ci_pipeline_yaml_file('some invalid syntax')
          end

          it 'updates merge request head pipeline reference' do
            merge_request = create(:merge_request, source_branch: 'master',
                                                   target_branch: 'feature',
                                                   source_project: project)

            head_pipeline = execute_service

            expect(head_pipeline).to be_persisted
            expect(head_pipeline.yaml_errors).to be_present
            expect(merge_request.reload.head_pipeline).to eq head_pipeline
          end
        end
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

        context 'when pipeline has been skipped' do
          before do
            allow_any_instance_of(Ci::Pipeline)
              .to receive(:git_commit_message)
              .and_return('some commit [ci skip]')
          end

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

            head_pipeline = execute_service

            expect(head_pipeline).to be_skipped
            expect(head_pipeline).to be_persisted
            expect(merge_request.reload.head_pipeline).to eq head_pipeline
          end
        end
189 190
      end

191 192 193 194 195
      context 'auto-cancel enabled' do
        before do
          project.update(auto_cancel_pending_pipelines: 'enabled')
        end

196 197
        it 'does not cancel HEAD pipeline' do
          pipeline
R
Rydkin Maxim 已提交
198
          pipeline_on_previous_commit
199

R
Rydkin Maxim 已提交
200
          expect(pipeline.reload).to have_attributes(status: 'pending', auto_canceled_by_id: nil)
201
        end
202 203

        it 'auto cancel pending non-HEAD pipelines' do
R
Rydkin Maxim 已提交
204
          pipeline_on_previous_commit
205 206
          pipeline

R
Rydkin Maxim 已提交
207
          expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'canceled', auto_canceled_by_id: pipeline.id)
208 209 210
        end

        it 'does not cancel running outdated pipelines' do
R
Rydkin Maxim 已提交
211
          pipeline_on_previous_commit.run
212 213
          execute_service

R
Rydkin Maxim 已提交
214
          expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'running', auto_canceled_by_id: nil)
215 216 217
        end

        it 'cancel created outdated pipelines' do
R
Rydkin Maxim 已提交
218
          pipeline_on_previous_commit.update(status: 'created')
219 220
          pipeline

R
Rydkin Maxim 已提交
221
          expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'canceled', auto_canceled_by_id: pipeline.id)
222 223 224 225 226 227 228 229 230
        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

231
          expect(pending_pipeline.reload).to have_attributes(status: 'pending', auto_canceled_by_id: nil)
232 233
        end
      end
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251

      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
252 253
    end

254 255
    context "skip tag if there is no build for it" do
      it "creates commit if there is appropriate job" do
256
        expect(execute_service).to be_persisted
257 258 259 260 261 262
      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)

263
        expect(execute_service).to be_persisted
264 265 266 267 268 269
      end
    end

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

270
      expect(execute_service).not_to be_persisted
271 272 273
      expect(Ci::Pipeline.count).to eq(0)
    end

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
    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
300 301 302 303
    end

    context 'when commit contains a [ci skip] directive' do
      let(:message) { "some message[ci skip]" }
304 305 306 307 308 309 310 311 312 313 314

      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]"
      ]
315 316 317 318 319

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

320 321
      ci_messages.each do |ci_message|
        it "skips builds creation if the commit message is #{ci_message}" do
322
          pipeline = execute_service(message: ci_message)
323 324 325 326 327

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

330
      shared_examples 'creating a pipeline' do
R
Rydkin Maxim 已提交
331
        it 'does not skip pipeline creation' do
332
          allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { commit_message }
333

334
          pipeline = execute_service(message: commit_message)
335

336 337 338
          expect(pipeline).to be_persisted
          expect(pipeline.builds.first.name).to eq("rspec")
        end
339 340
      end

341 342
      context 'when commit message does not contain [ci skip] nor [skip ci]' do
        let(:commit_message) { 'some message' }
343

344
        it_behaves_like 'creating a pipeline'
345 346
      end

347 348
      context 'when commit message is nil' do
        let(:commit_message) { nil }
349

350
        it_behaves_like 'creating a pipeline'
351 352
      end

353 354 355 356 357
      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
358 359 360 361 362 363 364 365 366
    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
367
        result = execute_service
368 369 370 371 372 373 374 375 376 377 378 379 380 381

        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
382
        result = execute_service
383 384 385 386 387

        expect(result).to be_persisted
        expect(result.manual_actions).not_to be_empty
      end
    end
388 389 390

    context 'with environment' do
      before do
K
Kamil Trzciński 已提交
391 392 393 394 395 396 397
        config = YAML.dump(
          deploy: {
            environment: { name: "review/$CI_COMMIT_REF_NAME" },
            script: 'ls',
            tags: ['hello']
          })

398 399 400
        stub_ci_pipeline_yaml_file(config)
      end

K
Kamil Trzciński 已提交
401
      it 'creates the environment with tags' do
402
        result = execute_service
403 404

        expect(result).to be_persisted
405
        expect(Environment.find_by(name: "review/master")).to be_present
K
Kamil Trzciński 已提交
406
        expect(result.builds.first.tag_list).to contain_exactly('hello')
K
Kamil Trzciński 已提交
407 408
        expect(result.builds.first.deployment).to be_persisted
        expect(result.builds.first.deployment.deployable).to be_a(Ci::Build)
K
Kamil Trzciński 已提交
409
      end
410 411 412 413 414 415 416
    end

    context 'with environment name including persisted variables' do
      before do
        config = YAML.dump(
          deploy: {
            environment: { name: "review/id1$CI_PIPELINE_ID/id2$CI_BUILD_ID" },
417 418
            script: 'ls'
          }
419 420 421 422 423 424 425 426 427 428
        )

        stub_ci_pipeline_yaml_file(config)
      end

      it 'skipps persisted variables in environment name' do
        result = execute_service

        expect(result).to be_persisted
        expect(Environment.find_by(name: "review/id1/id2")).to be_present
429 430
      end
    end
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445

    context 'when environment with invalid name' do
      before do
        config = YAML.dump(deploy: { environment: { name: 'name,with,commas' }, script: 'ls' })
        stub_ci_pipeline_yaml_file(config)
      end

      it 'does not create an environment' do
        expect do
          result = execute_service

          expect(result).to be_persisted
        end.not_to change { Environment.count }
      end
    end
446

447
    context 'when builds with auto-retries are configured' do
448 449 450 451 452 453 454 455 456 457 458 459 460
      context 'as an integer' do
        before do
          config = YAML.dump(rspec: { script: 'rspec', retry: 2 })
          stub_ci_pipeline_yaml_file(config)
        end

        it 'correctly creates builds with auto-retry value configured' do
          pipeline = execute_service

          expect(pipeline).to be_persisted
          expect(pipeline.builds.find_by(name: 'rspec').retries_max).to eq 2
          expect(pipeline.builds.find_by(name: 'rspec').retry_when).to eq ['always']
        end
461 462
      end

463 464 465 466 467
      context 'as hash' do
        before do
          config = YAML.dump(rspec: { script: 'rspec', retry: { max: 2, when: 'runner_system_failure' } })
          stub_ci_pipeline_yaml_file(config)
        end
468

469 470 471 472 473 474 475
        it 'correctly creates builds with auto-retry value configured' do
          pipeline = execute_service

          expect(pipeline).to be_persisted
          expect(pipeline.builds.find_by(name: 'rspec').retries_max).to eq 2
          expect(pipeline.builds.find_by(name: 'rspec').retry_when).to eq ['runner_system_failure']
        end
476 477
      end
    end
478

479 480 481 482 483 484 485 486 487 488 489 490 491 492
    shared_examples 'when ref is protected' do
      let(:user) { create(:user) }

      context 'when user is developer' do
        before do
          project.add_developer(user)
        end

        it 'does not create a pipeline' do
          expect(execute_service).not_to be_persisted
          expect(Ci::Pipeline.count).to eq(0)
        end
      end

493
      context 'when user is maintainer' do
S
Shinya Maeda 已提交
494 495
        let(:pipeline) { execute_service }

496
        before do
497
          project.add_maintainer(user)
498 499
        end

S
Shinya Maeda 已提交
500 501 502
        it 'creates a protected pipeline' do
          expect(pipeline).to be_persisted
          expect(pipeline).to be_protected
503 504 505
          expect(Ci::Pipeline.count).to eq(1)
        end
      end
506 507 508 509 510 511 512 513 514 515 516 517 518

      context 'when trigger belongs to no one' do
        let(:user) {}
        let(:trigger_request) { create(:ci_trigger_request) }

        it 'does not create a pipeline' do
          expect(execute_service(trigger_request: trigger_request))
            .not_to be_persisted
          expect(Ci::Pipeline.count).to eq(0)
        end
      end

      context 'when trigger belongs to a developer' do
S
Shinya Maeda 已提交
519 520 521
        let(:user) { create(:user) }
        let(:trigger) { create(:ci_trigger, owner: user) }
        let(:trigger_request) { create(:ci_trigger_request, trigger: trigger) }
522

S
Shinya Maeda 已提交
523 524
        before do
          project.add_developer(user)
525 526 527 528 529 530 531 532 533
        end

        it 'does not create a pipeline' do
          expect(execute_service(trigger_request: trigger_request))
            .not_to be_persisted
          expect(Ci::Pipeline.count).to eq(0)
        end
      end

534
      context 'when trigger belongs to a maintainer' do
S
Shinya Maeda 已提交
535 536 537
        let(:user) { create(:user) }
        let(:trigger) { create(:ci_trigger, owner: user) }
        let(:trigger_request) { create(:ci_trigger_request, trigger: trigger) }
538

S
Shinya Maeda 已提交
539
        before do
540
          project.add_maintainer(user)
541 542
        end

S
Shinya Maeda 已提交
543
        it 'creates a pipeline' do
544 545 546 547 548
          expect(execute_service(trigger_request: trigger_request))
            .to be_persisted
          expect(Ci::Pipeline.count).to eq(1)
        end
      end
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
    end

    context 'when ref is a protected branch' do
      before do
        create(:protected_branch, project: project, name: 'master')
      end

      it_behaves_like 'when ref is protected'
    end

    context 'when ref is a protected tag' do
      let(:ref_name) { 'refs/tags/v1.0.0' }

      before do
        create(:protected_tag, project: project, name: '*')
      end

      it_behaves_like 'when ref is protected'
    end
568 569 570 571

    context 'when ref is not protected' do
      context 'when trigger belongs to no one' do
        let(:user) {}
572 573
        let(:trigger) { create(:ci_trigger, owner: nil) }
        let(:trigger_request) { create(:ci_trigger_request, trigger: trigger) }
S
Shinya Maeda 已提交
574
        let(:pipeline) { execute_service(trigger_request: trigger_request) }
575

S
Shinya Maeda 已提交
576 577 578
        it 'creates an unprotected pipeline' do
          expect(pipeline).to be_persisted
          expect(pipeline).not_to be_protected
579
          expect(Ci::Pipeline.count).to eq(1)
580 581 582
        end
      end
    end
583

K
Kamil Trzcinski 已提交
584
    context 'when pipeline is running for a tag' do
585 586
      before do
        config = YAML.dump(test: { script: 'test', only: ['branches'] },
K
Kamil Trzcinski 已提交
587
                           deploy: { script: 'deploy', only: ['tags'] })
588 589 590 591 592

        stub_ci_pipeline_yaml_file(config)
      end

      it 'creates a tagged pipeline' do
K
Kamil Trzcinski 已提交
593
        pipeline = execute_service(ref: 'v1.0.0')
594 595 596 597

        expect(pipeline.tag?).to be true
      end
    end
598 599

    context 'when pipeline variables are specified' do
600
      let(:variables_attributes) do
601 602 603 604
        [{ key: 'first', secret_value: 'world' },
         { key: 'second', secret_value: 'second_world' }]
      end

605
      subject { execute_service(variables_attributes: variables_attributes) }
606 607

      it 'creates a pipeline with specified variables' do
M
Matija Čupić 已提交
608 609
        expect(subject.variables.map { |var| var.slice(:key, :secret_value) })
          .to eq variables_attributes.map(&:with_indifferent_access)
610 611
      end
    end
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659

    context 'when pipeline has a job with environment' do
      let(:pipeline) { execute_service }

      before do
        stub_ci_pipeline_yaml_file(YAML.dump(config))
      end

      context 'when environment name is valid' do
        let(:config) do
          {
            review_app: {
              script: 'deploy',
              environment: {
                name: 'review/${CI_COMMIT_REF_NAME}',
                url: 'http://${CI_COMMIT_REF_SLUG}-staging.example.com'
              }
            }
          }
        end

        it 'has a job with environment' do
          expect(pipeline.builds.count).to eq(1)
          expect(pipeline.builds.first.persisted_environment.name).to eq('review/master')
          expect(pipeline.builds.first.deployment).to be_created
        end
      end

      context 'when environment name is invalid' do
        let(:config) do
          {
            'job:deploy-to-test-site': {
              script: 'deploy',
              environment: {
                name: '${CI_JOB_NAME}',
                url: 'https://$APP_URL'
              }
            }
          }
        end

        it 'has a job without environment' do
          expect(pipeline.builds.count).to eq(1)
          expect(pipeline.builds.first.persisted_environment).to be_nil
          expect(pipeline.builds.first.deployment).to be_nil
        end
      end
    end
S
Shinya Maeda 已提交
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733

    describe 'Merge request pipelines' do
      let(:pipeline) do
        execute_service(source: source, merge_request: merge_request, ref: ref_name)
      end

      before do
        stub_ci_pipeline_yaml_file(YAML.dump(config))
      end

      let(:ref_name) { 'feature' }

      context 'when source is merge request' do
        let(:source) { :merge_request }

        context "when config has merge_requests keywords" do
          let(:config) do
            {
              build: {
                stage: 'build',
                script: 'echo'
              },
              test: {
                stage: 'test',
                script: 'echo',
                only: ['merge_requests']
              },
              pages: {
                stage: 'deploy',
                script: 'echo',
                except: ['merge_requests']
              }
            }
          end

          context 'when merge request is specified' do
            let(:merge_request) do
              create(:merge_request,
                source_project: project,
                source_branch: ref_name,
                target_project: project,
                target_branch: 'master')
            end

            it 'creates a merge request pipeline' do
              expect(pipeline).to be_persisted
              expect(pipeline).to be_merge_request
              expect(pipeline.merge_request).to eq(merge_request)
              expect(pipeline.builds.order(:stage_id).map(&:name)).to eq(%w[test])
            end

            context 'when ref is tag' do
              let(:ref_name) { 'v1.1.0' }

              it 'does not create a merge request pipeline' do
                expect(pipeline).not_to be_persisted
                expect(pipeline.errors[:tag]).to eq(["is not included in the list"])
              end
            end

            context 'when merge request is created from a forked project' do
              let(:merge_request) do
                create(:merge_request,
                  source_project: project,
                  source_branch: ref_name,
                  target_project: target_project,
                  target_branch: 'master')
              end

              let!(:project) { fork_project(target_project, nil, repository: true) }
              let!(:target_project) { create(:project, :repository) }

              it 'creates a merge request pipeline in the forked project' do
                expect(pipeline).to be_persisted
734 735
                expect(project.ci_pipelines).to eq([pipeline])
                expect(target_project.ci_pipelines).to be_empty
S
Shinya Maeda 已提交
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
              end
            end

            context "when there are no matched jobs" do
              let(:config) do
                {
                  test: {
                    stage: 'test',
                    script: 'echo',
                    except: ['merge_requests']
                  }
                }
              end

              it 'does not create a merge request pipeline' do
                expect(pipeline).not_to be_persisted
                expect(pipeline.errors[:base]).to eq(["No stages / jobs for this pipeline."])
              end
            end
          end

          context 'when merge request is not specified' do
            let(:merge_request) { nil }

            it 'does not create a merge request pipeline' do
              expect(pipeline).not_to be_persisted
              expect(pipeline.errors[:merge_request]).to eq(["can't be blank"])
            end
          end
        end

        context "when config does not have merge_requests keywords" do
          let(:config) do
            {
              build: {
                stage: 'build',
                script: 'echo'
              },
              test: {
                stage: 'test',
                script: 'echo'
              },
              pages: {
                stage: 'deploy',
                script: 'echo'
              }
            }
          end

          context 'when merge request is specified' do
            let(:merge_request) do
              create(:merge_request,
                source_project: project,
                source_branch: ref_name,
                target_project: project,
                target_branch: 'master')
            end

            it 'does not create a merge request pipeline' do
              expect(pipeline).not_to be_persisted

              expect(pipeline.errors[:base])
                .to eq(['No stages / jobs for this pipeline.'])
            end
          end

          context 'when merge request is not specified' do
            let(:merge_request) { nil }

            it 'does not create a merge request pipeline' do
              expect(pipeline).not_to be_persisted

              expect(pipeline.errors[:base])
                .to eq(['No stages / jobs for this pipeline.'])
            end
          end
        end
      end

      context 'when source is web' do
        let(:source) { :web }

        context "when config has merge_requests keywords" do
          let(:config) do
            {
              build: {
                stage: 'build',
                script: 'echo'
              },
              test: {
                stage: 'test',
                script: 'echo',
                only: ['merge_requests']
              },
              pages: {
                stage: 'deploy',
                script: 'echo',
                except: ['merge_requests']
              }
            }
          end

          context 'when merge request is specified' do
            let(:merge_request) do
              create(:merge_request,
                source_project: project,
                source_branch: ref_name,
                target_project: project,
                target_branch: 'master')
            end

            it 'does not create a merge request pipeline' do
              expect(pipeline).not_to be_persisted
              expect(pipeline.errors[:merge_request]).to eq(["must be blank"])
            end
          end

          context 'when merge request is not specified' do
            let(:merge_request) { nil }

            it 'creates a branch pipeline' do
              expect(pipeline).to be_persisted
              expect(pipeline).to be_web
              expect(pipeline.merge_request).to be_nil
              expect(pipeline.builds.order(:stage_id).map(&:name)).to eq(%w[build pages])
            end
          end
        end
      end
    end
866
  end
S
Shinya Maeda 已提交
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899

  describe '#execute!' do
    subject { service.execute!(*args) }

    let(:service) { described_class.new(project, user, ref: ref_name) }
    let(:args) { [:push] }

    context 'when user has a permission to create a pipeline' do
      let(:user) { create(:user) }

      before do
        project.add_developer(user)
      end

      it 'does not raise an error' do
        expect { subject }.not_to raise_error
      end

      it 'creates a pipeline' do
        expect { subject }.to change { Ci::Pipeline.count }.by(1)
      end
    end

    context 'when user does not have a permission to create a pipeline' do
      let(:user) { create(:user) }

      it 'raises an error' do
        expect { subject }
          .to raise_error(described_class::CreateError)
          .with_message('Insufficient permissions to create a new pipeline')
      end
    end
  end
900
end