BigW Consortium Gitlab

gitlab_ci_yaml_processor_spec.rb 49.6 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

Valery Sizov committed
7 8 9 10 11 12 13 14 15
    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" }
        })

16
        config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
17 18 19 20

        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",
21
          stage_idx: 1,
22
          name: "rspec",
23 24
          commands: "pwd\nrspec",
          tag_list: [],
Valery Sizov committed
25
          options: {},
Kamil Trzcinski committed
26
          allow_failure: false,
27 28
          when: "on_success",
          environment: nil,
29
          yaml_variables: []
Valery Sizov committed
30 31
        })
      end
32

33
      describe 'only' do
34 35 36 37 38
        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
39

40
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
41

42 43
          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
        end
Valery Sizov committed
44

45 46 47 48 49
        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
50

51
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
52

53 54
          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
        end
Valery Sizov committed
55

56 57 58 59 60
        it "returns builds if only has specified this branch" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", only: ["master"] }
                             })
Valery Sizov committed
61

62
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
63

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

67 68 69 70 71
        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
72

73
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
74

75 76
          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
        end
Valery Sizov committed
77

78 79 80 81 82
        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
83

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
          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

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
        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

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
        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
147
                               rspec: { script: "rspec", type: "test", only: ["master", "deploy"] },
148 149 150
                               staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] },
                               production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] },
                             })
Valery Sizov committed
151

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

154
          expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2)
Kamil Trzcinski committed
155 156
          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)
157
        end
158 159 160 161 162 163 164

        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
165
              expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError, 'jobs:rspec:only config should be an array of strings or regexps')
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
            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
187 188
      end

189
      describe 'except' do
190 191 192 193 194
        it "returns builds if except has another branch" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", except: ["deploy"] }
                             })
Valery Sizov committed
195

196
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
197

198 199 200 201 202 203 204 205 206 207 208 209 210
          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
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
        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

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
        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

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
        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
303 304 305
                               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"] },
306 307
                             })

Kamil Trzcinski committed
308
          config_processor = GitlabCiYamlProcessor.new(config, 'fork')
309

Kamil Trzcinski committed
310 311 312
          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)
313 314
        end

315 316 317 318 319 320
        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
321
              expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError, 'jobs:rspec:except config should be an array of strings or regexps')
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
            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
344
    end
345

346 347 348
    describe "Scripts handling" do
      let(:config_data) { YAML.dump(config) }
      let(:config_processor) { GitlabCiYamlProcessor.new(config_data, path) }
349

350
      subject { config_processor.builds_for_stage_and_ref("test", "master").first }
351

352 353
      describe "before_script" do
        context "in global context" do
Kamil Trzcinski committed
354
          let(:config) do
355 356 357 358
            {
              before_script: ["global script"],
              test: { script: ["script"] }
            }
Kamil Trzcinski committed
359
          end
360

361 362 363 364
          it "return commands with scripts concencaced" do
            expect(subject[:commands]).to eq("global script\nscript")
          end
        end
365

366
        context "overwritten in local context" do
Kamil Trzcinski committed
367
          let(:config) do
368 369 370 371
            {
              before_script: ["global script"],
              test: { before_script: ["local script"], script: ["script"] }
            }
Kamil Trzcinski committed
372
          end
373 374 375 376 377 378 379 380

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

      describe "script" do
Kamil Trzcinski committed
381
        let(:config) do
382 383 384
          {
            test: { script: ["script"] }
          }
Kamil Trzcinski committed
385
        end
386 387 388 389 390 391

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

392
      describe "after_script" do
393
        context "in global context" do
394
          let(:config) do
395
            {
396
              after_script: ["after_script"],
397 398
              test: { script: ["script"] }
            }
399
          end
400

401 402
          it "return after_script in options" do
            expect(subject[:options][:after_script]).to eq(["after_script"])
403 404
          end
        end
405 406

        context "overwritten in local context" do
Kamil Trzcinski committed
407
          let(:config) do
408
            {
409 410
              after_script: ["local after_script"],
              test: { after_script: ["local after_script"], script: ["script"] }
411
            }
Kamil Trzcinski committed
412
          end
413

414 415
          it "return after_script in options" do
            expect(subject[:options][:after_script]).to eq(["local after_script"])
416 417
          end
        end
418 419
      end
    end
420

Valery Sizov committed
421 422 423 424 425 426 427 428 429
    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" }
                           })

430
        config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
431 432 433 434

        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",
435
          stage_idx: 1,
436
          name: "rspec",
437 438
          commands: "pwd\nrspec",
          tag_list: [],
Valery Sizov committed
439 440 441 442
          options: {
            image: "ruby:2.1",
            services: ["mysql"]
          },
443
          allow_failure: false,
444 445
          when: "on_success",
          environment: nil,
446
          yaml_variables: []
Valery Sizov committed
447 448 449 450 451 452 453 454 455 456 457
        })
      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" }
                           })

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

        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",
463
          stage_idx: 1,
464
          name: "rspec",
465 466
          commands: "pwd\nrspec",
          tag_list: [],
Valery Sizov committed
467 468 469 470
          options: {
            image: "ruby:2.5",
            services: ["postgresql"]
          },
471
          allow_failure: false,
472 473
          when: "on_success",
          environment: nil,
474
          yaml_variables: []
Valery Sizov committed
475 476
        })
      end
477 478
    end

479
    describe 'Variables' do
480
      let(:config_processor) { GitlabCiYamlProcessor.new(YAML.dump(config), path) }
Valery Sizov committed
481

482 483 484 485 486 487 488 489
      subject { config_processor.builds.first[:yaml_variables] }

      context 'when global variables are defined' do
        let(:variables) do
          { VAR1: 'value1', VAR2: 'value2' }
        end
        let(:config) do
          {
490 491 492
            variables: variables,
            before_script: ['pwd'],
            rspec: { script: 'rspec' }
493 494
          }
        end
Valery Sizov committed
495

496 497 498 499 500 501 502
        it 'returns global variables' do
          expect(subject).to contain_exactly(
            { key: :VAR1, value: 'value1', public: true },
            { key: :VAR2, value: 'value2', public: true }
          )
        end
      end
503

504 505 506 507 508 509 510 511 512 513 514 515 516 517
      context 'when job and global variables are defined' do
        let(:global_variables) do
          { VAR1: 'global1', VAR3: 'global3' }
        end
        let(:job_variables) do
          { VAR1: 'value1', VAR2: 'value2' }
        end
        let(:config) do
          {
            before_script: ['pwd'],
            variables: global_variables,
            rspec: { script: 'rspec', variables: job_variables }
          }
        end
518

519 520 521 522 523 524
        it 'returns all unique variables' do
          expect(subject).to contain_exactly(
            { key: :VAR3, value: 'global3', public: true },
            { key: :VAR1, value: 'value1', public: true },
            { key: :VAR2, value: 'value2', public: true }
          )
525 526 527 528
        end
      end

      context 'when job variables are defined' do
529 530 531 532 533 534
        let(:config) do
          {
            before_script: ['pwd'],
            rspec: { script: 'rspec', variables: variables }
          }
        end
535

536 537 538 539
        context 'when syntax is correct' do
          let(:variables) do
            { VAR1: 'value1', VAR2: 'value2' }
          end
540

541 542 543 544 545
          it 'returns job variables' do
            expect(subject).to contain_exactly(
              { key: :VAR1, value: 'value1', public: true },
              { key: :VAR2, value: 'value2', public: true }
            )
546 547
          end
        end
548

549
        context 'when syntax is incorrect' do
550
          context 'when variables defined but invalid' do
551 552 553
            let(:variables) do
              [ :VAR1, 'value1', :VAR2, 'value2' ]
            end
554

555 556
            it 'raises error' do
              expect { subject }
557
                .to raise_error(GitlabCiYamlProcessor::ValidationError,
558
                                 /jobs:rspec:variables config should be a hash of key value pairs/)
559 560
            end
          end
561

562
          context 'when variables key defined but value not specified' do
563 564 565
            let(:variables) do
              nil
            end
566

567
            it 'returns empty array' do
568
              ##
569 570
              # When variables config is empty, we assume this is a valid
              # configuration, see issue #18775
571
              #
572 573
              expect(subject).to be_an_instance_of(Array)
              expect(subject).to be_empty
574
            end
575
          end
576
        end
Valery Sizov committed
577
      end
578 579

      context 'when job variables are not defined' do
580 581
        let(:config) do
          {
582 583
            before_script: ['pwd'],
            rspec: { script: 'rspec' }
584 585
          }
        end
586

587 588 589
        it 'returns empty array' do
          expect(subject).to be_an_instance_of(Array)
          expect(subject).to be_empty
590
        end
Valery Sizov committed
591
      end
592 593
    end

594 595 596 597 598 599 600
    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 }
                             })

601
          config_processor = GitlabCiYamlProcessor.new(config, path)
602

603 604 605 606 607 608 609
          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

610 611 612 613 614 615 616 617 618
    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,
619
            'cache config contains unknown keys: invalid'
620 621 622 623
          )
        end
      end

624 625
      it "returns cache when defined globally" do
        config = YAML.dump({
626
                             cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
627 628 629 630 631 632 633 634 635 636 637
                             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,
638
          key: 'key',
639 640 641 642 643 644
        )
      end

      it "returns cache when defined in a job" do
        config = YAML.dump({
                             rspec: {
645
                               cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
646 647 648 649 650 651 652 653 654 655
                               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,
656
          key: 'key',
657 658 659 660 661
        )
      end

      it "overwrite cache when defined for a job and globally" do
        config = YAML.dump({
662
                             cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'global' },
663 664
                             rspec: {
                               script: "rspec",
665
                               cache: { paths: ["test/"], untracked: false, key: 'local' },
666 667 668 669 670 671 672 673 674
                             }
                           })

        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,
675
          key: 'local',
676 677 678 679
        )
      end
    end

680 681 682 683 684 685
    describe "Artifacts" do
      it "returns artifacts when defined" do
        config = YAML.dump({
                             image:         "ruby:2.1",
                             services:      ["mysql"],
                             before_script: ["pwd"],
686
                             rspec:         {
687 688 689 690 691 692
                               artifacts: {
                                 paths: ["logs/", "binaries/"],
                                 untracked: true,
                                 name: "custom_name",
                                 expire_in: "7d"
                               },
693 694
                               script: "rspec"
                             }
695 696 697 698 699 700 701 702
                           })

        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,
703
          name: "rspec",
704 705 706 707 708
          commands: "pwd\nrspec",
          tag_list: [],
          options: {
            image: "ruby:2.1",
            services: ["mysql"],
709
            artifacts: {
710
              name: "custom_name",
711
              paths: ["logs/", "binaries/"],
712 713
              untracked: true,
              expire_in: "7d"
714
            }
715 716
          },
          when: "on_success",
717 718
          allow_failure: false,
          environment: nil,
719
          yaml_variables: []
720 721
        })
      end
722

Kamil Trzcinski committed
723
      %w[on_success on_failure always].each do |when_state|
724 725 726 727 728 729 730 731 732
        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
733

734 735 736 737 738
          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
739 740
    end

741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
    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
757
          expect(builds.first[:options]).to include(environment: { name: environment, action: "start" })
758 759 760 761 762 763 764 765 766 767 768 769
        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])
770
          expect(builds.first[:options]).to include(environment: environment)
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
        end
      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
787 788
          expect { builds }.to raise_error(
            'jobs:deploy_to_production:environment config should be a hash or a string')
789 790 791 792
        end
      end

      context 'is not a valid string' do
793
        let(:environment) { 'production:staging' }
794 795

        it 'raises error' do
796
          expect { builds }.to raise_error("jobs:deploy_to_production:environment name #{Gitlab::Regex.environment_name_regex_message}")
797
        end
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
      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
844 845 846
      end
    end

847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
    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) { }

863
        it { expect { subject }.not_to raise_error }
864 865 866
      end

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

869
        it { expect { subject }.not_to raise_error }
870 871
      end

872 873 874
      context 'dependencies to builds defined as symbols' do
        let(:dependencies) { [:build1, :build2] }

875
        it { expect { subject }.not_to raise_error }
876 877
      end

878
      context 'undefined dependency' do
879
        let(:dependencies) { ['undefined'] }
880 881 882 883 884

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

      context 'dependencies to deploy' do
885
        let(:dependencies) { ['deploy'] }
886 887 888 889 890

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

891
    describe "Hidden jobs" do
892 893 894 895
      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
896
        it "doesn't create jobs that start with dot" do
897 898 899 900
          expect(subject.size).to eq(1)
          expect(subject.first).to eq({
            stage: "test",
            stage_idx: 1,
901
            name: "normal_job",
902 903 904 905
            commands: "test",
            tag_list: [],
            options: {},
            when: "on_success",
906 907
            allow_failure: false,
            environment: nil,
908
            yaml_variables: []
909 910
          })
        end
911 912
      end

Tomasz Maczukin committed
913
      context 'when hidden job have a script definition' do
914 915 916 917 918 919
        let(:config) do
          YAML.dump({
                      '.hidden_job' => { image: 'ruby:2.1', script: 'test' },
                      'normal_job' => { script: 'test' }
                    })
        end
920

921 922
        it_behaves_like 'hidden_job_handling'
      end
923

Tomasz Maczukin committed
924
      context "when hidden job doesn't have a script definition" do
925 926 927 928 929 930 931 932
        let(:config) do
          YAML.dump({
                      '.hidden_job' => { image: 'ruby:2.1' },
                      'normal_job' => { script: 'test' }
                    })
        end

        it_behaves_like 'hidden_job_handling'
933 934 935
      end
    end

936
    describe "YAML Alias/Anchor" do
937 938 939 940 941 942 943 944 945
      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,
946
            name: "job1",
947 948 949 950
            commands: "execute-script-for-job",
            tag_list: [],
            options: {},
            when: "on_success",
951 952
            allow_failure: false,
            environment: nil,
953
            yaml_variables: []
954 955 956 957
          })
          expect(subject.second).to eq({
            stage: "build",
            stage_idx: 0,
958
            name: "job2",
959 960 961 962
            commands: "execute-script-for-job",
            tag_list: [],
            options: {},
            when: "on_success",
963 964
            allow_failure: false,
            environment: nil,
965
            yaml_variables: []
966 967 968 969
          })
        end
      end

Tomasz Maczukin committed
970
      context 'when template is a job' do
Tomasz Maczukin committed
971
        let(:config) do
972
          <<EOT
973
job1: &JOBTMPL
974
  stage: build
975 976 977 978
  script: execute-script-for-job

job2: *JOBTMPL
EOT
979
        end
980

981 982
        it_behaves_like 'job_templates_handling'
      end
983

Tomasz Maczukin committed
984
      context 'when template is a hidden job' do
Tomasz Maczukin committed
985
        let(:config) do
986 987 988 989 990 991 992 993 994 995 996 997 998 999
          <<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
1000
      context 'when job adds its own keys to a template definition' do
Tomasz Maczukin committed
1001
        let(:config) do
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
          <<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'
1017 1018 1019
      end
    end

Valery Sizov committed
1020
    describe "Error handling" do
1021 1022 1023 1024
      it "fails to parse YAML" do
        expect{GitlabCiYamlProcessor.new("invalid: yaml: test")}.to raise_error(Psych::SyntaxError)
      end

Valery Sizov committed
1025
      it "indicates that object is invalid" do
1026
        expect{GitlabCiYamlProcessor.new("invalid_yaml")}.to raise_error(GitlabCiYamlProcessor::ValidationError)
Valery Sizov committed
1027 1028 1029 1030 1031
      end

      it "returns errors if tags parameter is invalid" do
        config = YAML.dump({ rspec: { script: "test", tags: "mysql" } })
        expect do
1032
          GitlabCiYamlProcessor.new(config, path)
1033
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec tags should be an array of strings")
Valery Sizov committed
1034 1035 1036 1037 1038
      end

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

1043 1044 1045 1046
      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)
1047
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:before_script config should be an array of strings")
1048 1049
      end

1050 1051
      it "returns errors if after_script parameter is invalid" do
        config = YAML.dump({ after_script: "bundle update", rspec: { script: "test" } })
1052 1053
        expect do
          GitlabCiYamlProcessor.new(config, path)
1054
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "after_script config should be an array of strings")
1055 1056
      end

1057 1058
      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"] } })
1059 1060
        expect do
          GitlabCiYamlProcessor.new(config, path)
1061
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:after_script config should be an array of strings")
1062 1063
      end

Valery Sizov committed
1064 1065 1066
      it "returns errors if image parameter is invalid" do
        config = YAML.dump({ image: ["test"], rspec: { script: "test" } })
        expect do
1067
          GitlabCiYamlProcessor.new(config, path)
1068
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image config should be a string")
Valery Sizov committed
1069 1070
      end

1071 1072 1073
      it "returns errors if job name is blank" do
        config = YAML.dump({ '' => { script: "test" } })
        expect do
1074
          GitlabCiYamlProcessor.new(config, path)
1075
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:job name can't be blank")
1076 1077 1078 1079 1080
      end

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

Valery Sizov committed
1085 1086 1087
      it "returns errors if job image parameter is invalid" do
        config = YAML.dump({ rspec: { script: "test", image: ["test"] } })
        expect do
1088
          GitlabCiYamlProcessor.new(config, path)
1089
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:image config should be a string")
Valery Sizov committed
1090 1091 1092 1093 1094
      end

      it "returns errors if services parameter is not an array" do
        config = YAML.dump({ services: "test", rspec: { script: "test" } })
        expect do
1095
          GitlabCiYamlProcessor.new(config, path)
1096
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be an array of strings")
Valery Sizov committed
1097 1098 1099 1100 1101
      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
1102
          GitlabCiYamlProcessor.new(config, path)
1103
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be an array of strings")
Valery Sizov committed
1104 1105 1106 1107 1108
      end

      it "returns errors if job services parameter is not an array" do
        config = YAML.dump({ rspec: { script: "test", services: "test" } })
        expect do
1109
          GitlabCiYamlProcessor.new(config, path)
1110
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be an array of strings")
Valery Sizov committed
1111 1112 1113 1114 1115
      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
1116
          GitlabCiYamlProcessor.new(config, path)
1117
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be an array of strings")
Valery Sizov committed
1118 1119
      end

1120
      it "returns error if job configuration is invalid" do
Valery Sizov committed
1121 1122
        config = YAML.dump({ extra: "bundle update" })
        expect do
1123
          GitlabCiYamlProcessor.new(config, path)
1124
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra config should be a hash")
Valery Sizov committed
1125 1126 1127 1128 1129
      end

      it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do
        config = YAML.dump({ extra: { services: "test" } })
        expect do
1130
          GitlabCiYamlProcessor.new(config, path)
1131
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra:services config should be an array of strings")
Valery Sizov committed
1132 1133
      end

1134
      it "returns errors if there are no jobs defined" do
Valery Sizov committed
1135 1136
        config = YAML.dump({ before_script: ["bundle update"] })
        expect do
1137
          GitlabCiYamlProcessor.new(config, path)
1138 1139 1140 1141
        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
1142
        config = YAML.dump({ before_script: ["bundle update"], '.hidden'.to_sym => { script: 'ls' } })
1143 1144 1145
        expect do
          GitlabCiYamlProcessor.new(config, path)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs config should contain at least one visible job")
Valery Sizov committed
1146 1147 1148 1149 1150
      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
1151
          GitlabCiYamlProcessor.new(config, path)
1152
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec allow failure should be a boolean value")
Valery Sizov committed
1153 1154 1155
      end

      it "returns errors if job stage is not a string" do
1156
        config = YAML.dump({ rspec: { script: "test", type: 1 } })
Valery Sizov committed
1157
        expect do
1158
          GitlabCiYamlProcessor.new(config, path)
1159
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:type config should be a string")
Valery Sizov committed
1160 1161 1162
      end

      it "returns errors if job stage is not a pre-defined stage" do
1163
        config = YAML.dump({ rspec: { script: "test", type: "acceptance" } })
Valery Sizov committed
1164
        expect do
1165
          GitlabCiYamlProcessor.new(config, path)
1166
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy")
Valery Sizov committed
1167 1168 1169
      end

      it "returns errors if job stage is not a defined stage" do
1170
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance" } })
Valery Sizov committed
1171
        expect do
1172
          GitlabCiYamlProcessor.new(config, path)
1173
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test")
Valery Sizov committed
1174 1175 1176
      end

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

      it "returns errors if stages is not an array of strings" do
1184
        config = YAML.dump({ stages: [true, "test"], rspec: { script: "test" } })
Valery Sizov committed
1185
        expect do
1186
          GitlabCiYamlProcessor.new(config, path)
1187
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages config should be an array of strings")
Valery Sizov committed
1188 1189 1190 1191 1192
      end

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

1197
      it "returns errors if variables is not a map of key-value strings" do
Valery Sizov committed
1198 1199
        config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } })
        expect do
1200
          GitlabCiYamlProcessor.new(config, path)
1201
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables config should be a hash of key value pairs")
Valery Sizov committed
1202
      end
1203 1204

      it "returns errors if job when is not on_success, on_failure or always" do
Kamil Trzcinski committed
1205
        config = YAML.dump({ rspec: { script: "test", when: 1 } })
1206
        expect do
1207
          GitlabCiYamlProcessor.new(config, path)
1208
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec when should be on_success, on_failure, always or manual")
1209
      end
1210

1211 1212 1213 1214
      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)
1215
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts name should be a string")
1216 1217
      end

1218 1219 1220 1221
      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)
1222
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts when should be on_success, on_failure or always")
1223 1224
      end

1225 1226 1227 1228
      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)
1229
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts expire in should be a duration")
1230 1231 1232 1233 1234 1235
      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)
1236
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts expire in should be a duration")
1237 1238
      end

1239 1240
      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" } } })
1241 1242
        expect do
          GitlabCiYamlProcessor.new(config)
1243
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts untracked should be a boolean value")
1244 1245 1246 1247 1248 1249
      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)
1250
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts paths should be an array of strings")
1251
      end
1252 1253 1254 1255 1256

      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)
1257
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:untracked config should be a boolean value")
1258 1259 1260 1261 1262 1263
      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)
1264
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:paths config should be an array of strings")
1265 1266
      end

1267 1268 1269 1270
      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)
1271
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:key config should be a string or symbol")
1272 1273 1274 1275 1276 1277
      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)
1278
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:key config should be a string or symbol")
1279 1280
      end

1281 1282 1283 1284
      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)
1285
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:untracked config should be a boolean value")
1286 1287 1288 1289 1290 1291
      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)
1292
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:paths config should be an array of strings")
1293
      end
1294 1295 1296 1297 1298

      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)
1299
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec dependencies should be an array of strings")
1300
      end
1301
    end
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313

    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
1314

Katarzyna Kobierska committed
1315
    describe "#validation_message" do
Katarzyna Kobierska committed
1316 1317
      context "when the YAML could not be parsed" do
        it "returns an error about invalid configutaion" do
1318
          content = YAML.dump("invalid: yaml: test")
Katarzyna Kobierska committed
1319

Katarzyna Kobierska committed
1320 1321
          expect(GitlabCiYamlProcessor.validation_message(content))
            .to eq "Invalid configuration format"
1322
        end
Katarzyna Kobierska committed
1323
      end
1324

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

Katarzyna Kobierska committed
1329 1330
          expect(GitlabCiYamlProcessor.validation_message(content))
            .to eq "jobs:rspec tags should be an array of strings"
Katarzyna Kobierska committed
1331 1332 1333
        end
      end

1334
      context "when YAML content is empty" do
Katarzyna Kobierska committed
1335
        it "returns an error about missing content" do
Katarzyna Kobierska committed
1336 1337
          expect(GitlabCiYamlProcessor.validation_message(''))
            .to eq "Please provide content of .gitlab-ci.yml"
1338
        end
Katarzyna Kobierska committed
1339
      end
1340

Katarzyna Kobierska committed
1341 1342
      context "when the YAML is valid" do
        it "does not return any errors" do
1343
          content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
Katarzyna Kobierska committed
1344 1345

          expect(GitlabCiYamlProcessor.validation_message(content)).to be_nil
1346 1347 1348
        end
      end
    end
1349 1350
  end
end