BigW Consortium Gitlab

gitlab_ci_yaml_processor_spec.rb 51.1 KB
Newer Older
1 2
require 'spec_helper'

Valery Sizov committed
3
module Ci
Douwe Maan committed
4
  describe GitlabCiYamlProcessor, lib: true do
5
    let(:path) { 'path' }
6

7 8 9 10 11 12 13 14 15 16
    describe 'our current .gitlab-ci.yml' do
      let(:config) { File.read("#{Rails.root}/.gitlab-ci.yml") }

      it 'is valid' do
        error_message = described_class.validation_message(config)

        expect(error_message).to be_nil
      end
    end

17
    describe '#build_attributes' do
18
      describe 'coverage entry' do
19 20
        subject { described_class.new(config, path).build_attributes(:rspec) }

21 22 23
        describe 'code coverage regexp' do
          let(:config) do
            YAML.dump(rspec: { script: 'rspec',
24
                               coverage: '/Code coverage: \d+\.\d+/' })
25 26
          end

27 28 29
          it 'includes coverage regexp in build attributes' do
            expect(subject)
              .to include(coverage_regex: 'Code coverage: \d+\.\d+')
30 31 32 33 34
          end
        end
      end
    end

Valery Sizov committed
35 36 37 38 39 40 41 42 43
    describe "#builds_for_ref" do
      let(:type) { 'test' }

      it "returns builds if no branch specified" do
        config = YAML.dump({
          before_script: ["pwd"],
          rspec: { script: "rspec" }
        })

44
        config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
45 46 47 48

        expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
        expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({
          stage: "test",
49
          stage_idx: 1,
50
          name: "rspec",
51
          commands: "pwd\nrspec",
52
          coverage_regex: nil,
53
          tag_list: [],
Valery Sizov committed
54
          options: {},
Kamil Trzcinski committed
55
          allow_failure: false,
56 57
          when: "on_success",
          environment: nil,
58
          yaml_variables: []
Valery Sizov committed
59 60
        })
      end
61

62
      describe 'only' do
63 64 65 66 67
        it "does not return builds if only has another branch" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", only: ["deploy"] }
                             })
Valery Sizov committed
68

69
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
70

71 72
          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
        end
Valery Sizov committed
73

74 75 76 77 78
        it "does not return builds if only has regexp with another branch" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", only: ["/^deploy$/"] }
                             })
Valery Sizov committed
79

80
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
81

82 83
          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
        end
Valery Sizov committed
84

85 86 87 88 89
        it "returns builds if only has specified this branch" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", only: ["master"] }
                             })
Valery Sizov committed
90

91
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
92

93 94
          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
        end
Valery Sizov committed
95

96 97 98 99 100
        it "returns builds if only has a list of branches including specified" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, only: ["master", "deploy"] }
                             })
Valery Sizov committed
101

102
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
103

104 105
          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
        end
Valery Sizov committed
106

107 108 109 110 111
        it "returns builds if only has a branches keyword specified" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, only: ["branches"] }
                             })
Valery Sizov committed
112

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
        end

        it "does not return builds if only has a tags keyword" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, only: ["tags"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
        end

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
        it "returns builds if only has a triggers keyword specified and a trigger is provided" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, only: ["triggers"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, true).size).to eq(1)
        end

        it "does not return builds if only has a triggers keyword specified and no trigger is provided" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, only: ["triggers"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
        end

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
        it "returns builds if only has current repository path" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, only: ["branches@path"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
        end

        it "does not return builds if only has different repository path" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, only: ["branches@fork"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
        end

        it "returns build only for specified type" do
          config = YAML.dump({
                               before_script: ["pwd"],
Kamil Trzcinski committed
176
                               rspec: { script: "rspec", type: "test", only: ["master", "deploy"] },
177 178 179
                               staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] },
                               production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] },
                             })
Valery Sizov committed
180

Kamil Trzcinski committed
181
          config_processor = GitlabCiYamlProcessor.new(config, 'fork')
Valery Sizov committed
182

183
          expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2)
Kamil Trzcinski committed
184 185
          expect(config_processor.builds_for_stage_and_ref("test", "deploy").size).to eq(1)
          expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(1)
186
        end
187 188 189 190 191 192 193

        context 'for invalid value' do
          let(:config) { { rspec: { script: "rspec", type: "test", only: only } } }
          let(:processor) { GitlabCiYamlProcessor.new(YAML.dump(config)) }

          shared_examples 'raises an error' do
            it do
194
              expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError, 'jobs:rspec:only config should be an array of strings or regexps')
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
            end
          end

          context 'when it is integer' do
            let(:only) { 1 }

            it_behaves_like 'raises an error'
          end

          context 'when it is an array of integers' do
            let(:only) { [1, 1] }

            it_behaves_like 'raises an error'
          end

          context 'when it is invalid regex' do
            let(:only) { ["/*invalid/"] }

            it_behaves_like 'raises an error'
          end
        end
Valery Sizov committed
216 217
      end

218
      describe 'except' do
219 220 221 222 223
        it "returns builds if except has another branch" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", except: ["deploy"] }
                             })
Valery Sizov committed
224

225
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
226

227 228 229 230 231 232 233 234 235 236 237 238 239
          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
        end

        it "returns builds if except has regexp with another branch" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", except: ["/^deploy$/"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
        end
Valery Sizov committed
240

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
        it "does not return builds if except has specified this branch" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", except: ["master"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
        end

        it "does not return builds if except has a list of branches including specified" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, except: ["master", "deploy"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
        end

        it "does not return builds if except has a branches keyword specified" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, except: ["branches"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
        end

        it "returns builds if except has a tags keyword" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, except: ["tags"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
        end

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
        it "does not return builds if except has a triggers keyword specified and a trigger is provided" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, except: ["triggers"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, true).size).to eq(0)
        end

        it "returns builds if except has a triggers keyword specified and no trigger is provided" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, except: ["triggers"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
        end

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
        it "does not return builds if except has current repository path" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, except: ["branches@path"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
        end

        it "returns builds if except has different repository path" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", type: type, except: ["branches@fork"] }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)

          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
        end

        it "returns build except specified type" do
          config = YAML.dump({
                               before_script: ["pwd"],
Kamil Trzcinski committed
332 333 334
                               rspec: { script: "rspec", type: "test", except: ["master", "deploy", "test@fork"] },
                               staging: { script: "deploy", type: "deploy", except: ["master"] },
                               production: { script: "deploy", type: "deploy", except: ["master@fork"] },
335 336
                             })

Kamil Trzcinski committed
337
          config_processor = GitlabCiYamlProcessor.new(config, 'fork')
338

Kamil Trzcinski committed
339 340 341
          expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2)
          expect(config_processor.builds_for_stage_and_ref("test", "test").size).to eq(0)
          expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(0)
342 343
        end

344 345 346 347 348 349
        context 'for invalid value' do
          let(:config) { { rspec: { script: "rspec", except: except } } }
          let(:processor) { GitlabCiYamlProcessor.new(YAML.dump(config)) }

          shared_examples 'raises an error' do
            it do
350
              expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError, 'jobs:rspec:except config should be an array of strings or regexps')
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
            end
          end

          context 'when it is integer' do
            let(:except) { 1 }

            it_behaves_like 'raises an error'
          end

          context 'when it is an array of integers' do
            let(:except) { [1, 1] }

            it_behaves_like 'raises an error'
          end

          context 'when it is invalid regex' do
            let(:except) { ["/*invalid/"] }

            it_behaves_like 'raises an error'
          end
        end
      end
373
    end
374

375 376 377
    describe "Scripts handling" do
      let(:config_data) { YAML.dump(config) }
      let(:config_processor) { GitlabCiYamlProcessor.new(config_data, path) }
378

379
      subject { config_processor.builds_for_stage_and_ref("test", "master").first }
380

381 382
      describe "before_script" do
        context "in global context" do
Kamil Trzcinski committed
383
          let(:config) do
384 385 386 387
            {
              before_script: ["global script"],
              test: { script: ["script"] }
            }
Kamil Trzcinski committed
388
          end
389

390 391 392 393
          it "return commands with scripts concencaced" do
            expect(subject[:commands]).to eq("global script\nscript")
          end
        end
394

395
        context "overwritten in local context" do
Kamil Trzcinski committed
396
          let(:config) do
397 398 399 400
            {
              before_script: ["global script"],
              test: { before_script: ["local script"], script: ["script"] }
            }
Kamil Trzcinski committed
401
          end
402 403 404 405 406 407 408 409

          it "return commands with scripts concencaced" do
            expect(subject[:commands]).to eq("local script\nscript")
          end
        end
      end

      describe "script" do
Kamil Trzcinski committed
410
        let(:config) do
411 412 413
          {
            test: { script: ["script"] }
          }
Kamil Trzcinski committed
414
        end
415 416 417 418 419 420

        it "return commands with scripts concencaced" do
          expect(subject[:commands]).to eq("script")
        end
      end

421
      describe "after_script" do
422
        context "in global context" do
423
          let(:config) do
424
            {
425
              after_script: ["after_script"],
426 427
              test: { script: ["script"] }
            }
428
          end
429

430 431
          it "return after_script in options" do
            expect(subject[:options][:after_script]).to eq(["after_script"])
432 433
          end
        end
434 435

        context "overwritten in local context" do
Kamil Trzcinski committed
436
          let(:config) do
437
            {
438 439
              after_script: ["local after_script"],
              test: { after_script: ["local after_script"], script: ["script"] }
440
            }
Kamil Trzcinski committed
441
          end
442

443 444
          it "return after_script in options" do
            expect(subject[:options][:after_script]).to eq(["local after_script"])
445 446
          end
        end
447 448
      end
    end
449

Valery Sizov committed
450 451 452 453 454 455 456 457 458
    describe "Image and service handling" do
      it "returns image and service when defined" do
        config = YAML.dump({
                             image: "ruby:2.1",
                             services: ["mysql"],
                             before_script: ["pwd"],
                             rspec: { script: "rspec" }
                           })

459
        config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
460 461 462 463

        expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
        expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
          stage: "test",
464
          stage_idx: 1,
465
          name: "rspec",
466
          commands: "pwd\nrspec",
467
          coverage_regex: nil,
468
          tag_list: [],
Valery Sizov committed
469 470 471 472
          options: {
            image: "ruby:2.1",
            services: ["mysql"]
          },
473
          allow_failure: false,
474 475
          when: "on_success",
          environment: nil,
476
          yaml_variables: []
Valery Sizov committed
477 478 479 480 481 482 483 484 485 486 487
        })
      end

      it "returns image and service when overridden for job" do
        config = YAML.dump({
                             image:         "ruby:2.1",
                             services:      ["mysql"],
                             before_script: ["pwd"],
                             rspec:         { image: "ruby:2.5", services: ["postgresql"], script: "rspec" }
                           })

488
        config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
489 490 491 492

        expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
        expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
          stage: "test",
493
          stage_idx: 1,
494
          name: "rspec",
495
          commands: "pwd\nrspec",
496
          coverage_regex: nil,
497
          tag_list: [],
Valery Sizov committed
498 499 500 501
          options: {
            image: "ruby:2.5",
            services: ["postgresql"]
          },
502
          allow_failure: false,
503 504
          when: "on_success",
          environment: nil,
505
          yaml_variables: []
Valery Sizov committed
506 507
        })
      end
508 509
    end

510
    describe 'Variables' do
511
      let(:config_processor) { GitlabCiYamlProcessor.new(YAML.dump(config), path) }
Valery Sizov committed
512

513 514 515 516
      subject { config_processor.builds.first[:yaml_variables] }

      context 'when global variables are defined' do
        let(:variables) do
517
          { 'VAR1' => 'value1', 'VAR2' => 'value2' }
518 519 520
        end
        let(:config) do
          {
521 522 523
            variables: variables,
            before_script: ['pwd'],
            rspec: { script: 'rspec' }
524 525
          }
        end
Valery Sizov committed
526

527 528
        it 'returns global variables' do
          expect(subject).to contain_exactly(
529 530
            { key: 'VAR1', value: 'value1', public: true },
            { key: 'VAR2', value: 'value2', public: true }
531 532 533
          )
        end
      end
534

535 536
      context 'when job and global variables are defined' do
        let(:global_variables) do
537
          { 'VAR1' => 'global1', 'VAR3' => 'global3' }
538 539
        end
        let(:job_variables) do
540
          { 'VAR1' => 'value1', 'VAR2' => 'value2' }
541 542 543 544 545 546 547 548
        end
        let(:config) do
          {
            before_script: ['pwd'],
            variables: global_variables,
            rspec: { script: 'rspec', variables: job_variables }
          }
        end
549

550 551
        it 'returns all unique variables' do
          expect(subject).to contain_exactly(
552 553 554
            { key: 'VAR3', value: 'global3', public: true },
            { key: 'VAR1', value: 'value1', public: true },
            { key: 'VAR2', value: 'value2', public: true }
555
          )
556 557 558 559
        end
      end

      context 'when job variables are defined' do
560 561 562 563 564 565
        let(:config) do
          {
            before_script: ['pwd'],
            rspec: { script: 'rspec', variables: variables }
          }
        end
566

567 568
        context 'when syntax is correct' do
          let(:variables) do
569
            { 'VAR1' => 'value1', 'VAR2' => 'value2' }
570
          end
571

572 573
          it 'returns job variables' do
            expect(subject).to contain_exactly(
574 575
              { key: 'VAR1', value: 'value1', public: true },
              { key: 'VAR2', value: 'value2', public: true }
576
            )
577 578
          end
        end
579

580
        context 'when syntax is incorrect' do
581
          context 'when variables defined but invalid' do
582
            let(:variables) do
583
              [ 'VAR1', 'value1', 'VAR2', 'value2' ]
584
            end
585

586 587
            it 'raises error' do
              expect { subject }
588
                .to raise_error(GitlabCiYamlProcessor::ValidationError,
589
                                 /jobs:rspec:variables config should be a hash of key value pairs/)
590 591
            end
          end
592

593
          context 'when variables key defined but value not specified' do
594 595 596
            let(:variables) do
              nil
            end
597

598
            it 'returns empty array' do
599
              ##
600 601
              # When variables config is empty, we assume this is a valid
              # configuration, see issue #18775
602
              #
603 604
              expect(subject).to be_an_instance_of(Array)
              expect(subject).to be_empty
605
            end
606
          end
607
        end
Valery Sizov committed
608
      end
609 610

      context 'when job variables are not defined' do
611 612
        let(:config) do
          {
613 614
            before_script: ['pwd'],
            rspec: { script: 'rspec' }
615 616
          }
        end
617

618 619 620
        it 'returns empty array' do
          expect(subject).to be_an_instance_of(Array)
          expect(subject).to be_empty
621
        end
Valery Sizov committed
622
      end
623 624
    end

625 626 627 628 629 630 631
    describe "When" do
      %w(on_success on_failure always).each do |when_state|
        it "returns #{when_state} when defined" do
          config = YAML.dump({
                               rspec: { script: "rspec", when: when_state }
                             })

632
          config_processor = GitlabCiYamlProcessor.new(config, path)
633

634 635 636 637 638 639 640
          builds = config_processor.builds_for_stage_and_ref("test", "master")
          expect(builds.size).to eq(1)
          expect(builds.first[:when]).to eq(when_state)
        end
      end
    end

641 642 643 644 645 646 647 648 649
    describe 'cache' do
      context 'when cache definition has unknown keys' do
        it 'raises relevant validation error' do
          config = YAML.dump(
            { cache: { untracked: true, invalid: 'key' },
              rspec: { script: 'rspec' } })

          expect { GitlabCiYamlProcessor.new(config) }.to raise_error(
            GitlabCiYamlProcessor::ValidationError,
650
            'cache config contains unknown keys: invalid'
651 652 653 654
          )
        end
      end

655 656
      it "returns cache when defined globally" do
        config = YAML.dump({
657
                             cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
658 659 660 661 662 663 664 665 666 667 668
                             rspec: {
                               script: "rspec"
                             }
                           })

        config_processor = GitlabCiYamlProcessor.new(config)

        expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
        expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
          paths: ["logs/", "binaries/"],
          untracked: true,
669
          key: 'key',
670 671 672 673 674 675
        )
      end

      it "returns cache when defined in a job" do
        config = YAML.dump({
                             rspec: {
676
                               cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
677 678 679 680 681 682 683 684 685 686
                               script: "rspec"
                             }
                           })

        config_processor = GitlabCiYamlProcessor.new(config)

        expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
        expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
          paths: ["logs/", "binaries/"],
          untracked: true,
687
          key: 'key',
688 689 690 691 692
        )
      end

      it "overwrite cache when defined for a job and globally" do
        config = YAML.dump({
693
                             cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'global' },
694 695
                             rspec: {
                               script: "rspec",
696
                               cache: { paths: ["test/"], untracked: false, key: 'local' },
697 698 699 700 701 702 703 704 705
                             }
                           })

        config_processor = GitlabCiYamlProcessor.new(config)

        expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
        expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
          paths: ["test/"],
          untracked: false,
706
          key: 'local',
707 708 709 710
        )
      end
    end

711 712 713 714 715 716
    describe "Artifacts" do
      it "returns artifacts when defined" do
        config = YAML.dump({
                             image:         "ruby:2.1",
                             services:      ["mysql"],
                             before_script: ["pwd"],
717
                             rspec:         {
718 719 720 721 722 723
                               artifacts: {
                                 paths: ["logs/", "binaries/"],
                                 untracked: true,
                                 name: "custom_name",
                                 expire_in: "7d"
                               },
724 725
                               script: "rspec"
                             }
726 727 728 729 730 731 732 733
                           })

        config_processor = GitlabCiYamlProcessor.new(config)

        expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
        expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
          stage: "test",
          stage_idx: 1,
734
          name: "rspec",
735
          commands: "pwd\nrspec",
736
          coverage_regex: nil,
737 738 739 740
          tag_list: [],
          options: {
            image: "ruby:2.1",
            services: ["mysql"],
741
            artifacts: {
742
              name: "custom_name",
743
              paths: ["logs/", "binaries/"],
744 745
              untracked: true,
              expire_in: "7d"
746
            }
747 748
          },
          when: "on_success",
749 750
          allow_failure: false,
          environment: nil,
751
          yaml_variables: []
752 753
        })
      end
754

Kamil Trzcinski committed
755
      %w[on_success on_failure always].each do |when_state|
756 757 758 759 760 761 762 763 764
        it "returns artifacts for when #{when_state}  defined" do
          config = YAML.dump({
                               rspec: {
                                 script: "rspec",
                                 artifacts: { paths: ["logs/", "binaries/"], when: when_state }
                               }
                             })

          config_processor = GitlabCiYamlProcessor.new(config, path)
Kamil Trzcinski committed
765

766 767 768 769 770
          builds = config_processor.builds_for_stage_and_ref("test", "master")
          expect(builds.size).to eq(1)
          expect(builds.first[:options][:artifacts][:when]).to eq(when_state)
        end
      end
771 772
    end

773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
    describe '#environment' do
      let(:config) do
        {
          deploy_to_production: { stage: 'deploy', script: 'test', environment: environment }
        }
      end

      let(:processor) { GitlabCiYamlProcessor.new(YAML.dump(config)) }
      let(:builds) { processor.builds_for_stage_and_ref('deploy', 'master') }

      context 'when a production environment is specified' do
        let(:environment) { 'production' }

        it 'does return production' do
          expect(builds.size).to eq(1)
          expect(builds.first[:environment]).to eq(environment)
Kamil Trzcinski committed
789
          expect(builds.first[:options]).to include(environment: { name: environment, action: "start" })
790 791 792 793 794 795 796 797 798 799 800 801
        end
      end

      context 'when hash is specified' do
        let(:environment) do
          { name: 'production',
            url: 'http://production.gitlab.com' }
        end

        it 'does return production and URL' do
          expect(builds.size).to eq(1)
          expect(builds.first[:environment]).to eq(environment[:name])
802
          expect(builds.first[:options]).to include(environment: environment)
803
        end
804 805 806 807 808 809 810 811 812 813 814 815 816

        context 'the url has a port as variable' do
          let(:environment) do
            { name: 'production',
              url: 'http://production.gitlab.com:$PORT' }
          end

          it 'allows a variable for the port' do
            expect(builds.size).to eq(1)
            expect(builds.first[:environment]).to eq(environment[:name])
            expect(builds.first[:options]).to include(environment: environment)
          end
        end
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
      end

      context 'when no environment is specified' do
        let(:environment) { nil }

        it 'does return nil environment' do
          expect(builds.size).to eq(1)
          expect(builds.first[:environment]).to be_nil
        end
      end

      context 'is not a string' do
        let(:environment) { 1 }

        it 'raises error' do
832 833
          expect { builds }.to raise_error(
            'jobs:deploy_to_production:environment config should be a hash or a string')
834 835 836 837
        end
      end

      context 'is not a valid string' do
838
        let(:environment) { 'production:staging' }
839 840

        it 'raises error' do
841
          expect { builds }.to raise_error("jobs:deploy_to_production:environment name #{Gitlab::Regex.environment_name_regex_message}")
842
        end
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
      end

      context 'when on_stop is specified' do
        let(:review) { { stage: 'deploy', script: 'test', environment: { name: 'review', on_stop: 'close_review' } } }
        let(:config) { { review: review, close_review: close_review }.compact }

        context 'with matching job' do
          let(:close_review) { { stage: 'deploy', script: 'test', environment: { name: 'review', action: 'stop' } } }

          it 'does return a list of builds' do
            expect(builds.size).to eq(2)
            expect(builds.first[:environment]).to eq('review')
          end
        end

        context 'without matching job' do
          let(:close_review) { nil  }

          it 'raises error' do
            expect { builds }.to raise_error('review job: on_stop job close_review is not defined')
          end
        end

        context 'with close job without environment' do
          let(:close_review) { { stage: 'deploy', script: 'test' } }

          it 'raises error' do
            expect { builds }.to raise_error('review job: on_stop job close_review does not have environment defined')
          end
        end

        context 'with close job for different environment' do
          let(:close_review) { { stage: 'deploy', script: 'test', environment: 'production' } }

          it 'raises error' do
            expect { builds }.to raise_error('review job: on_stop job close_review have different environment name')
          end
        end

        context 'with close job without stop action' do
          let(:close_review) { { stage: 'deploy', script: 'test', environment: { name: 'review' } } }

          it 'raises error' do
            expect { builds }.to raise_error('review job: on_stop job close_review needs to have action stop defined')
          end
        end
889 890 891
      end
    end

892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
    describe "Dependencies" do
      let(:config) do
        {
          build1: { stage: 'build', script: 'test' },
          build2: { stage: 'build', script: 'test' },
          test1: { stage: 'test', script: 'test', dependencies: dependencies },
          test2: { stage: 'test', script: 'test' },
          deploy: { stage: 'test', script: 'test' }
        }
      end

      subject { GitlabCiYamlProcessor.new(YAML.dump(config)) }

      context 'no dependencies' do
        let(:dependencies) { }

908
        it { expect { subject }.not_to raise_error }
909 910 911
      end

      context 'dependencies to builds' do
912
        let(:dependencies) { ['build1', 'build2'] }
913

914
        it { expect { subject }.not_to raise_error }
915 916
      end

917 918 919
      context 'dependencies to builds defined as symbols' do
        let(:dependencies) { [:build1, :build2] }

920
        it { expect { subject }.not_to raise_error }
921 922
      end

923
      context 'undefined dependency' do
924
        let(:dependencies) { ['undefined'] }
925 926 927 928 929

        it { expect { subject }.to raise_error(GitlabCiYamlProcessor::ValidationError, 'test1 job: undefined dependency: undefined') }
      end

      context 'dependencies to deploy' do
930
        let(:dependencies) { ['deploy'] }
931 932 933 934 935

        it { expect { subject }.to raise_error(GitlabCiYamlProcessor::ValidationError, 'test1 job: dependency deploy is not defined in prior stages') }
      end
    end

936
    describe "Hidden jobs" do
937 938 939 940
      let(:config_processor) { GitlabCiYamlProcessor.new(config) }
      subject { config_processor.builds_for_stage_and_ref("test", "master") }

      shared_examples 'hidden_job_handling' do
Tomasz Maczukin committed
941
        it "doesn't create jobs that start with dot" do
942 943 944 945
          expect(subject.size).to eq(1)
          expect(subject.first).to eq({
            stage: "test",
            stage_idx: 1,
946
            name: "normal_job",
947
            commands: "test",
948
            coverage_regex: nil,
949 950 951
            tag_list: [],
            options: {},
            when: "on_success",
952 953
            allow_failure: false,
            environment: nil,
954
            yaml_variables: []
955 956
          })
        end
957 958
      end

Tomasz Maczukin committed
959
      context 'when hidden job have a script definition' do
960 961 962 963 964 965
        let(:config) do
          YAML.dump({
                      '.hidden_job' => { image: 'ruby:2.1', script: 'test' },
                      'normal_job' => { script: 'test' }
                    })
        end
966

967 968
        it_behaves_like 'hidden_job_handling'
      end
969

Tomasz Maczukin committed
970
      context "when hidden job doesn't have a script definition" do
971 972 973 974 975 976 977 978
        let(:config) do
          YAML.dump({
                      '.hidden_job' => { image: 'ruby:2.1' },
                      'normal_job' => { script: 'test' }
                    })
        end

        it_behaves_like 'hidden_job_handling'
979 980 981
      end
    end

982
    describe "YAML Alias/Anchor" do
983 984 985 986 987 988 989 990 991
      let(:config_processor) { GitlabCiYamlProcessor.new(config) }
      subject { config_processor.builds_for_stage_and_ref("build", "master") }

      shared_examples 'job_templates_handling' do
        it "is correctly supported for jobs" do
          expect(subject.size).to eq(2)
          expect(subject.first).to eq({
            stage: "build",
            stage_idx: 0,
992
            name: "job1",
993
            commands: "execute-script-for-job",
994
            coverage_regex: nil,
995 996 997
            tag_list: [],
            options: {},
            when: "on_success",
998 999
            allow_failure: false,
            environment: nil,
1000
            yaml_variables: []
1001 1002 1003 1004
          })
          expect(subject.second).to eq({
            stage: "build",
            stage_idx: 0,
1005
            name: "job2",
1006
            commands: "execute-script-for-job",
1007
            coverage_regex: nil,
1008 1009 1010
            tag_list: [],
            options: {},
            when: "on_success",
1011 1012
            allow_failure: false,
            environment: nil,
1013
            yaml_variables: []
1014 1015 1016 1017
          })
        end
      end

Tomasz Maczukin committed
1018
      context 'when template is a job' do
Tomasz Maczukin committed
1019
        let(:config) do
1020
          <<EOT
1021
job1: &JOBTMPL
1022
  stage: build
1023 1024 1025 1026
  script: execute-script-for-job

job2: *JOBTMPL
EOT
1027
        end
1028

1029 1030
        it_behaves_like 'job_templates_handling'
      end
1031

Tomasz Maczukin committed
1032
      context 'when template is a hidden job' do
Tomasz Maczukin committed
1033
        let(:config) do
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
          <<EOT
.template: &JOBTMPL
  stage: build
  script: execute-script-for-job

job1: *JOBTMPL

job2: *JOBTMPL
EOT
        end

        it_behaves_like 'job_templates_handling'
      end

Tomasz Maczukin committed
1048
      context 'when job adds its own keys to a template definition' do
Tomasz Maczukin committed
1049
        let(:config) do
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
          <<EOT
.template: &JOBTMPL
  stage: build

job1:
  <<: *JOBTMPL
  script: execute-script-for-job

job2:
  <<: *JOBTMPL
  script: execute-script-for-job
EOT
        end

        it_behaves_like 'job_templates_handling'
1065 1066 1067
      end
    end

Valery Sizov committed
1068
    describe "Error handling" do
1069 1070 1071 1072
      it "fails to parse YAML" do
        expect{GitlabCiYamlProcessor.new("invalid: yaml: test")}.to raise_error(Psych::SyntaxError)
      end

Valery Sizov committed
1073
      it "indicates that object is invalid" do
1074
        expect{GitlabCiYamlProcessor.new("invalid_yaml")}.to raise_error(GitlabCiYamlProcessor::ValidationError)
Valery Sizov committed
1075 1076 1077 1078 1079
      end

      it "returns errors if tags parameter is invalid" do
        config = YAML.dump({ rspec: { script: "test", tags: "mysql" } })
        expect do
1080
          GitlabCiYamlProcessor.new(config, path)
1081
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec tags should be an array of strings")
Valery Sizov committed
1082 1083 1084 1085 1086
      end

      it "returns errors if before_script parameter is invalid" do
        config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } })
        expect do
1087
          GitlabCiYamlProcessor.new(config, path)
1088
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script config should be an array of strings")
Valery Sizov committed
1089 1090
      end

1091 1092 1093 1094
      it "returns errors if job before_script parameter is not an array of strings" do
        config = YAML.dump({ rspec: { script: "test", before_script: [10, "test"] } })
        expect do
          GitlabCiYamlProcessor.new(config, path)
1095
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:before_script config should be an array of strings")
1096 1097
      end

1098 1099
      it "returns errors if after_script parameter is invalid" do
        config = YAML.dump({ after_script: "bundle update", rspec: { script: "test" } })
1100 1101
        expect do
          GitlabCiYamlProcessor.new(config, path)
1102
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "after_script config should be an array of strings")
1103 1104
      end

1105 1106
      it "returns errors if job after_script parameter is not an array of strings" do
        config = YAML.dump({ rspec: { script: "test", after_script: [10, "test"] } })
1107 1108
        expect do
          GitlabCiYamlProcessor.new(config, path)
1109
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:after_script config should be an array of strings")
1110 1111
      end

Valery Sizov committed
1112 1113 1114
      it "returns errors if image parameter is invalid" do
        config = YAML.dump({ image: ["test"], rspec: { script: "test" } })
        expect do
1115
          GitlabCiYamlProcessor.new(config, path)
1116
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image config should be a string")
Valery Sizov committed
1117 1118
      end

1119 1120 1121
      it "returns errors if job name is blank" do
        config = YAML.dump({ '' => { script: "test" } })
        expect do
1122
          GitlabCiYamlProcessor.new(config, path)
1123
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:job name can't be blank")
1124 1125 1126 1127 1128
      end

      it "returns errors if job name is non-string" do
        config = YAML.dump({ 10 => { script: "test" } })
        expect do
1129
          GitlabCiYamlProcessor.new(config, path)
1130
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:10 name should be a symbol")
1131 1132
      end

Valery Sizov committed
1133 1134 1135
      it "returns errors if job image parameter is invalid" do
        config = YAML.dump({ rspec: { script: "test", image: ["test"] } })
        expect do
1136
          GitlabCiYamlProcessor.new(config, path)
1137
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:image config should be a string")
Valery Sizov committed
1138 1139 1140 1141 1142
      end

      it "returns errors if services parameter is not an array" do
        config = YAML.dump({ services: "test", rspec: { script: "test" } })
        expect do
1143
          GitlabCiYamlProcessor.new(config, path)
1144
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be an array of strings")
Valery Sizov committed
1145 1146 1147 1148 1149
      end

      it "returns errors if services parameter is not an array of strings" do
        config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } })
        expect do
1150
          GitlabCiYamlProcessor.new(config, path)
1151
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be an array of strings")
Valery Sizov committed
1152 1153 1154 1155 1156
      end

      it "returns errors if job services parameter is not an array" do
        config = YAML.dump({ rspec: { script: "test", services: "test" } })
        expect do
1157
          GitlabCiYamlProcessor.new(config, path)
1158
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be an array of strings")
Valery Sizov committed
1159 1160 1161 1162 1163
      end

      it "returns errors if job services parameter is not an array of strings" do
        config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } })
        expect do
1164
          GitlabCiYamlProcessor.new(config, path)
1165
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be an array of strings")
Valery Sizov committed
1166 1167
      end

1168
      it "returns error if job configuration is invalid" do
Valery Sizov committed
1169 1170
        config = YAML.dump({ extra: "bundle update" })
        expect do
1171
          GitlabCiYamlProcessor.new(config, path)
1172
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra config should be a hash")
Valery Sizov committed
1173 1174
      end

1175 1176
      it "returns errors if services configuration is not correct" do
        config = YAML.dump({ extra: { script: 'rspec', services: "test" } })
Valery Sizov committed
1177
        expect do
1178
          GitlabCiYamlProcessor.new(config, path)
1179
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra:services config should be an array of strings")
Valery Sizov committed
1180 1181
      end

1182
      it "returns errors if there are no jobs defined" do
Valery Sizov committed
1183 1184
        config = YAML.dump({ before_script: ["bundle update"] })
        expect do
1185
          GitlabCiYamlProcessor.new(config, path)
1186 1187 1188 1189
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs config should contain at least one visible job")
      end

      it "returns errors if there are no visible jobs defined" do
1190
        config = YAML.dump({ before_script: ["bundle update"], '.hidden'.to_sym => { script: 'ls' } })
1191 1192 1193
        expect do
          GitlabCiYamlProcessor.new(config, path)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs config should contain at least one visible job")
Valery Sizov committed
1194 1195 1196 1197 1198
      end

      it "returns errors if job allow_failure parameter is not an boolean" do
        config = YAML.dump({ rspec: { script: "test", allow_failure: "string" } })
        expect do
1199
          GitlabCiYamlProcessor.new(config, path)
1200
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec allow failure should be a boolean value")
Valery Sizov committed
1201 1202 1203
      end

      it "returns errors if job stage is not a string" do
1204
        config = YAML.dump({ rspec: { script: "test", type: 1 } })
Valery Sizov committed
1205
        expect do
1206
          GitlabCiYamlProcessor.new(config, path)
1207
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:type config should be a string")
Valery Sizov committed
1208 1209 1210
      end

      it "returns errors if job stage is not a pre-defined stage" do
1211
        config = YAML.dump({ rspec: { script: "test", type: "acceptance" } })
Valery Sizov committed
1212
        expect do
1213
          GitlabCiYamlProcessor.new(config, path)
1214
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy")
Valery Sizov committed
1215 1216 1217
      end

      it "returns errors if job stage is not a defined stage" do
1218
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance" } })
Valery Sizov committed
1219
        expect do
1220
          GitlabCiYamlProcessor.new(config, path)
1221
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test")
Valery Sizov committed
1222 1223 1224
      end

      it "returns errors if stages is not an array" do
1225
        config = YAML.dump({ stages: "test", rspec: { script: "test" } })
Valery Sizov committed
1226
        expect do
1227
          GitlabCiYamlProcessor.new(config, path)
1228
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages config should be an array of strings")
Valery Sizov committed
1229 1230 1231
      end

      it "returns errors if stages is not an array of strings" do
1232
        config = YAML.dump({ stages: [true, "test"], rspec: { script: "test" } })
Valery Sizov committed
1233
        expect do
1234
          GitlabCiYamlProcessor.new(config, path)
1235
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages config should be an array of strings")
Valery Sizov committed
1236 1237 1238 1239 1240
      end

      it "returns errors if variables is not a map" do
        config = YAML.dump({ variables: "test", rspec: { script: "test" } })
        expect do
1241
          GitlabCiYamlProcessor.new(config, path)
1242
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables config should be a hash of key value pairs")
Valery Sizov committed
1243 1244
      end

1245
      it "returns errors if variables is not a map of key-value strings" do
Valery Sizov committed
1246 1247
        config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } })
        expect do
1248
          GitlabCiYamlProcessor.new(config, path)
1249
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables config should be a hash of key value pairs")
Valery Sizov committed
1250
      end
1251 1252

      it "returns errors if job when is not on_success, on_failure or always" do
Kamil Trzcinski committed
1253
        config = YAML.dump({ rspec: { script: "test", when: 1 } })
1254
        expect do
1255
          GitlabCiYamlProcessor.new(config, path)
1256
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec when should be on_success, on_failure, always or manual")
1257
      end
1258

1259 1260 1261 1262
      it "returns errors if job artifacts:name is not an a string" do
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { name: 1 } } })
        expect do
          GitlabCiYamlProcessor.new(config)
1263
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts name should be a string")
1264 1265
      end

1266 1267 1268 1269
      it "returns errors if job artifacts:when is not an a predefined value" do
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { when: 1 } } })
        expect do
          GitlabCiYamlProcessor.new(config)
1270
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts when should be on_success, on_failure or always")
1271 1272
      end

1273 1274 1275 1276
      it "returns errors if job artifacts:expire_in is not an a string" do
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { expire_in: 1 } } })
        expect do
          GitlabCiYamlProcessor.new(config)
1277
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts expire in should be a duration")
1278 1279 1280 1281 1282 1283
      end

      it "returns errors if job artifacts:expire_in is not an a valid duration" do
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { expire_in: "7 elephants" } } })
        expect do
          GitlabCiYamlProcessor.new(config)
1284
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts expire in should be a duration")
1285 1286
      end

1287 1288
      it "returns errors if job artifacts:untracked is not an array of strings" do
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { untracked: "string" } } })
1289 1290
        expect do
          GitlabCiYamlProcessor.new(config)
1291
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts untracked should be a boolean value")
1292 1293 1294 1295 1296 1297
      end

      it "returns errors if job artifacts:paths is not an array of strings" do
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { paths: "string" } } })
        expect do
          GitlabCiYamlProcessor.new(config)
1298
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts paths should be an array of strings")
1299
      end
1300 1301 1302 1303 1304

      it "returns errors if cache:untracked is not an array of strings" do
        config = YAML.dump({ cache: { untracked: "string" }, rspec: { script: "test" } })
        expect do
          GitlabCiYamlProcessor.new(config)
1305
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:untracked config should be a boolean value")
1306 1307 1308 1309 1310 1311
      end

      it "returns errors if cache:paths is not an array of strings" do
        config = YAML.dump({ cache: { paths: "string" }, rspec: { script: "test" } })
        expect do
          GitlabCiYamlProcessor.new(config)
1312
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:paths config should be an array of strings")
1313 1314
      end

1315 1316 1317 1318
      it "returns errors if cache:key is not a string" do
        config = YAML.dump({ cache: { key: 1 }, rspec: { script: "test" } })
        expect do
          GitlabCiYamlProcessor.new(config)
1319
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:key config should be a string or symbol")
1320 1321 1322 1323 1324 1325
      end

      it "returns errors if job cache:key is not an a string" do
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { key: 1 } } })
        expect do
          GitlabCiYamlProcessor.new(config)
1326
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:key config should be a string or symbol")
1327 1328
      end

1329 1330 1331 1332
      it "returns errors if job cache:untracked is not an array of strings" do
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { untracked: "string" } } })
        expect do
          GitlabCiYamlProcessor.new(config)
1333
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:untracked config should be a boolean value")
1334 1335 1336 1337 1338 1339
      end

      it "returns errors if job cache:paths is not an array of strings" do
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { paths: "string" } } })
        expect do
          GitlabCiYamlProcessor.new(config)
1340
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:paths config should be an array of strings")
1341
      end
1342 1343 1344 1345 1346

      it "returns errors if job dependencies is not an array of strings" do
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", dependencies: "string" } })
        expect do
          GitlabCiYamlProcessor.new(config)
1347
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec dependencies should be an array of strings")
1348
      end
1349
    end
1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361

    describe "Validate configuration templates" do
      templates = Dir.glob("#{Rails.root.join('vendor/gitlab-ci-yml')}/**/*.gitlab-ci.yml")

      templates.each do |file|
        it "does not return errors for #{file}" do
          file = File.read(file)

          expect { GitlabCiYamlProcessor.new(file) }.not_to raise_error
        end
      end
    end
1362

Katarzyna Kobierska committed
1363
    describe "#validation_message" do
Katarzyna Kobierska committed
1364 1365
      context "when the YAML could not be parsed" do
        it "returns an error about invalid configutaion" do
1366
          content = YAML.dump("invalid: yaml: test")
Katarzyna Kobierska committed
1367

Katarzyna Kobierska committed
1368 1369
          expect(GitlabCiYamlProcessor.validation_message(content))
            .to eq "Invalid configuration format"
1370
        end
Katarzyna Kobierska committed
1371
      end
1372

Katarzyna Kobierska committed
1373 1374
      context "when the tags parameter is invalid" do
        it "returns an error about invalid tags" do
1375
          content = YAML.dump({ rspec: { script: "test", tags: "mysql" } })
Katarzyna Kobierska committed
1376

Katarzyna Kobierska committed
1377 1378
          expect(GitlabCiYamlProcessor.validation_message(content))
            .to eq "jobs:rspec tags should be an array of strings"
Katarzyna Kobierska committed
1379 1380 1381
        end
      end

1382
      context "when YAML content is empty" do
Katarzyna Kobierska committed
1383
        it "returns an error about missing content" do
Katarzyna Kobierska committed
1384 1385
          expect(GitlabCiYamlProcessor.validation_message(''))
            .to eq "Please provide content of .gitlab-ci.yml"
1386
        end
Katarzyna Kobierska committed
1387
      end
1388

Katarzyna Kobierska committed
1389 1390
      context "when the YAML is valid" do
        it "does not return any errors" do
1391
          content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
Katarzyna Kobierska committed
1392 1393

          expect(GitlabCiYamlProcessor.validation_message(content)).to be_nil
1394 1395 1396
        end
      end
    end
1397 1398
  end
end