BigW Consortium Gitlab

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

33 34 35 36 37 38
      describe :only do
        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 147
        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
148
                               rspec: { script: "rspec", type: "test", only: ["master", "deploy"] },
149 150 151
                               staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] },
                               production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] },
                             })
Valery Sizov committed
152

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

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

161 162 163 164 165 166
      describe :except do
        it "returns builds if except has another branch" do
          config = YAML.dump({
                               before_script: ["pwd"],
                               rspec: { script: "rspec", except: ["deploy"] }
                             })
Valery Sizov committed
167

168
          config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
169

170 171 172 173 174 175 176 177 178 179 180 181 182
          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
183

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
        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

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
        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

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
        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
275 276 277
                               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"] },
278 279
                             })

Kamil Trzcinski committed
280
          config_processor = GitlabCiYamlProcessor.new(config, 'fork')
281

Kamil Trzcinski committed
282 283 284
          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)
285
        end
Valery Sizov committed
286
      end
287

288
    end
289 290 291 292 293 294 295
    
    describe "Scripts handling" do
      let(:config_data) { YAML.dump(config) }
      let(:config_processor) { GitlabCiYamlProcessor.new(config_data, path) }
      
      subject { config_processor.builds_for_stage_and_ref("test", "master").first }
      
296 297
      describe "before_script" do
        context "in global context" do
Kamil Trzcinski committed
298
          let(:config) do
299 300 301 302
            {
              before_script: ["global script"],
              test: { script: ["script"] }
            }
Kamil Trzcinski committed
303
          end
304 305 306 307 308 309 310
          
          it "return commands with scripts concencaced" do
            expect(subject[:commands]).to eq("global script\nscript")
          end
        end
 
        context "overwritten in local context" do
Kamil Trzcinski committed
311
          let(:config) do
312 313 314 315
            {
              before_script: ["global script"],
              test: { before_script: ["local script"], script: ["script"] }
            }
Kamil Trzcinski committed
316
          end
317 318 319 320 321 322 323 324

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

      describe "script" do
Kamil Trzcinski committed
325
        let(:config) do
326 327 328
          {
            test: { script: ["script"] }
          }
Kamil Trzcinski committed
329
        end
330 331 332 333 334 335

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

336
      describe "after_script" do
337
        context "in global context" do
338
          let(:config) do
339
            {
340
              after_script: ["after_script"],
341 342
              test: { script: ["script"] }
            }
343
          end
344

345 346
          it "return after_script in options" do
            expect(subject[:options][:after_script]).to eq(["after_script"])
347 348
          end
        end
349 350

        context "overwritten in local context" do
Kamil Trzcinski committed
351
          let(:config) do
352
            {
353 354
              after_script: ["local after_script"],
              test: { after_script: ["local after_script"], script: ["script"] }
355
            }
Kamil Trzcinski committed
356
          end
357

358 359
          it "return after_script in options" do
            expect(subject[:options][:after_script]).to eq(["local after_script"])
360 361
          end
        end
362 363
      end
    end
364

Valery Sizov committed
365 366 367 368 369 370 371 372 373
    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" }
                           })

374
        config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
375 376 377 378 379

        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({
          except: nil,
          stage: "test",
380
          stage_idx: 1,
Valery Sizov committed
381 382
          name: :rspec,
          only: nil,
383 384
          commands: "pwd\nrspec",
          tag_list: [],
Valery Sizov committed
385 386 387 388
          options: {
            image: "ruby:2.1",
            services: ["mysql"]
          },
389 390
          allow_failure: false,
          when: "on_success"
Valery Sizov committed
391 392 393 394 395 396 397 398 399 400 401
        })
      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" }
                           })

402
        config_processor = GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
403 404 405 406 407

        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({
          except: nil,
          stage: "test",
408
          stage_idx: 1,
Valery Sizov committed
409 410
          name: :rspec,
          only: nil,
411 412
          commands: "pwd\nrspec",
          tag_list: [],
Valery Sizov committed
413 414 415 416
          options: {
            image: "ruby:2.5",
            services: ["postgresql"]
          },
417 418
          allow_failure: false,
          when: "on_success"
Valery Sizov committed
419 420
        })
      end
421 422
    end

423
    describe 'Variables' do
424
      context 'when global variables are defined' do
425
        it 'returns global variables' do
426
          variables = {
427 428
            VAR1: 'value1',
            VAR2: 'value2',
429
          }
Valery Sizov committed
430

431
          config = YAML.dump({
432 433 434 435
            variables: variables,
            before_script: ['pwd'],
            rspec: { script: 'rspec' }
          })
Valery Sizov committed
436

437
          config_processor = GitlabCiYamlProcessor.new(config, path)
438

439
          expect(config_processor.global_variables).to eq(variables)
440 441 442 443
        end
      end

      context 'when job variables are defined' do
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
        context 'when syntax is correct' do
          it 'returns job variables' do
            variables =  {
              KEY1: 'value1',
              SOME_KEY_2: 'value2'
            }

            config =  YAML.dump(
              { before_script: ['pwd'],
                rspec: {
                  variables: variables,
                  script: 'rspec' }
              })

            config_processor = GitlabCiYamlProcessor.new(config, path)

            expect(config_processor.job_variables(:rspec)).to eq variables
          end
        end
463

464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
        context 'when syntax is incorrect' do
          it 'raises error' do
            variables = [:KEY1, 'value1', :KEY2, 'value2']

            config =  YAML.dump(
              { before_script: ['pwd'],
                rspec: {
                  variables: variables,
                  script: 'rspec' }
              })

            expect { GitlabCiYamlProcessor.new(config, path) }
              .to raise_error(GitlabCiYamlProcessor::ValidationError,
                               /job: variables should be a map/)
          end
479
        end
Valery Sizov committed
480
      end
481 482 483 484

      context 'when job variables are not defined' do
        it 'returns empty array' do
          config = YAML.dump({
485 486 487
            before_script: ['pwd'],
            rspec: { script: 'rspec' }
          })
488 489

          config_processor = GitlabCiYamlProcessor.new(config, path)
490

491 492
          expect(config_processor.job_variables(:rspec)).to eq []
        end
Valery Sizov committed
493
      end
494 495
    end

496 497 498 499 500 501 502
    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 }
                             })

503
          config_processor = GitlabCiYamlProcessor.new(config, path)
504 505 506 507 508 509 510
          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

511 512 513
    describe "Caches" do
      it "returns cache when defined globally" do
        config = YAML.dump({
514
                             cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
515 516 517 518 519 520 521 522 523 524 525
                             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,
526
          key: 'key',
527 528 529 530 531 532
        )
      end

      it "returns cache when defined in a job" do
        config = YAML.dump({
                             rspec: {
533
                               cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
534 535 536 537 538 539 540 541 542 543
                               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,
544
          key: 'key',
545 546 547 548 549
        )
      end

      it "overwrite cache when defined for a job and globally" do
        config = YAML.dump({
550
                             cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'global' },
551 552
                             rspec: {
                               script: "rspec",
553
                               cache: { paths: ["test/"], untracked: false, key: 'local' },
554 555 556 557 558 559 560 561 562
                             }
                           })

        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,
563
          key: 'local',
564 565 566 567
        )
      end
    end

568 569 570 571 572 573
    describe "Artifacts" do
      it "returns artifacts when defined" do
        config = YAML.dump({
                             image:         "ruby:2.1",
                             services:      ["mysql"],
                             before_script: ["pwd"],
574
                             rspec:         {
575
                               artifacts: { paths: ["logs/", "binaries/"], untracked: true, name: "custom_name" },
576 577
                               script: "rspec"
                             }
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
                           })

        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({
          except: nil,
          stage: "test",
          stage_idx: 1,
          name: :rspec,
          only: nil,
          commands: "pwd\nrspec",
          tag_list: [],
          options: {
            image: "ruby:2.1",
            services: ["mysql"],
594
            artifacts: {
595
              name: "custom_name",
596 597 598
              paths: ["logs/", "binaries/"],
              untracked: true
            }
599 600 601 602 603 604 605
          },
          when: "on_success",
          allow_failure: false
        })
      end
    end

606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
    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) { }

        it { expect { subject }.to_not raise_error }
      end

      context 'dependencies to builds' do
626
        let(:dependencies) { ['build1', 'build2'] }
627 628 629 630

        it { expect { subject }.to_not raise_error }
      end

631 632 633 634 635 636
      context 'dependencies to builds defined as symbols' do
        let(:dependencies) { [:build1, :build2] }

        it { expect { subject }.to_not raise_error }
      end

637
      context 'undefined dependency' do
638
        let(:dependencies) { ['undefined'] }
639 640 641 642 643

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

      context 'dependencies to deploy' do
644
        let(:dependencies) { ['deploy'] }
645 646 647 648 649

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

650
    describe "Hidden jobs" do
651 652 653 654
      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
655
        it "doesn't create jobs that start with dot" do
656 657 658 659 660 661 662 663 664 665 666 667 668 669
          expect(subject.size).to eq(1)
          expect(subject.first).to eq({
            except: nil,
            stage: "test",
            stage_idx: 1,
            name: :normal_job,
            only: nil,
            commands: "test",
            tag_list: [],
            options: {},
            when: "on_success",
            allow_failure: false
          })
        end
670 671
      end

Tomasz Maczukin committed
672
      context 'when hidden job have a script definition' do
673 674 675 676 677 678
        let(:config) do
          YAML.dump({
                      '.hidden_job' => { image: 'ruby:2.1', script: 'test' },
                      'normal_job' => { script: 'test' }
                    })
        end
679

680 681
        it_behaves_like 'hidden_job_handling'
      end
682

Tomasz Maczukin committed
683
      context "when hidden job doesn't have a script definition" do
684 685 686 687 688 689 690 691
        let(:config) do
          YAML.dump({
                      '.hidden_job' => { image: 'ruby:2.1' },
                      'normal_job' => { script: 'test' }
                    })
        end

        it_behaves_like 'hidden_job_handling'
692 693 694
      end
    end

695
    describe "YAML Alias/Anchor" do
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
      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({
            except: nil,
            stage: "build",
            stage_idx: 0,
            name: :job1,
            only: nil,
            commands: "execute-script-for-job",
            tag_list: [],
            options: {},
            when: "on_success",
            allow_failure: false
          })
          expect(subject.second).to eq({
            except: nil,
            stage: "build",
            stage_idx: 0,
            name: :job2,
            only: nil,
            commands: "execute-script-for-job",
            tag_list: [],
            options: {},
            when: "on_success",
            allow_failure: false
          })
        end
      end

Tomasz Maczukin committed
729
      context 'when template is a job' do
Tomasz Maczukin committed
730
        let(:config) do
731
          <<EOT
732
job1: &JOBTMPL
733
  stage: build
734 735 736 737
  script: execute-script-for-job

job2: *JOBTMPL
EOT
738
        end
739

740 741
        it_behaves_like 'job_templates_handling'
      end
742

Tomasz Maczukin committed
743
      context 'when template is a hidden job' do
Tomasz Maczukin committed
744
        let(:config) do
745 746 747 748 749 750 751 752 753 754 755 756 757 758
          <<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
759
      context 'when job adds its own keys to a template definition' do
Tomasz Maczukin committed
760
        let(:config) do
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
          <<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'
776 777 778
      end
    end

Valery Sizov committed
779
    describe "Error handling" do
780 781 782 783
      it "fails to parse YAML" do
        expect{GitlabCiYamlProcessor.new("invalid: yaml: test")}.to raise_error(Psych::SyntaxError)
      end

Valery Sizov committed
784
      it "indicates that object is invalid" do
785
        expect{GitlabCiYamlProcessor.new("invalid_yaml")}.to raise_error(GitlabCiYamlProcessor::ValidationError)
Valery Sizov committed
786 787 788 789 790
      end

      it "returns errors if tags parameter is invalid" do
        config = YAML.dump({ rspec: { script: "test", tags: "mysql" } })
        expect do
791
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
792 793 794 795 796 797
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings")
      end

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

802 803 804 805 806 807 808
      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)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: before_script should be an array of strings")
      end

809 810
      it "returns errors if after_script parameter is invalid" do
        config = YAML.dump({ after_script: "bundle update", rspec: { script: "test" } })
811 812
        expect do
          GitlabCiYamlProcessor.new(config, path)
813
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "after_script should be an array of strings")
814 815
      end

816 817
      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"] } })
818 819
        expect do
          GitlabCiYamlProcessor.new(config, path)
820
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: after_script should be an array of strings")
821 822
      end

Valery Sizov committed
823 824 825
      it "returns errors if image parameter is invalid" do
        config = YAML.dump({ image: ["test"], rspec: { script: "test" } })
        expect do
826
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
827 828 829
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string")
      end

830 831 832
      it "returns errors if job name is blank" do
        config = YAML.dump({ '' => { script: "test" } })
        expect do
833
          GitlabCiYamlProcessor.new(config, path)
834 835 836 837 838 839
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string")
      end

      it "returns errors if job name is non-string" do
        config = YAML.dump({ 10 => { script: "test" } })
        expect do
840
          GitlabCiYamlProcessor.new(config, path)
841 842 843
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string")
      end

Valery Sizov committed
844 845 846
      it "returns errors if job image parameter is invalid" do
        config = YAML.dump({ rspec: { script: "test", image: ["test"] } })
        expect do
847
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
848 849 850 851 852 853
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string")
      end

      it "returns errors if services parameter is not an array" do
        config = YAML.dump({ services: "test", rspec: { script: "test" } })
        expect do
854
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
855 856 857 858 859 860
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings")
      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
861
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
862 863 864 865 866 867
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings")
      end

      it "returns errors if job services parameter is not an array" do
        config = YAML.dump({ rspec: { script: "test", services: "test" } })
        expect do
868
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
869 870 871 872 873 874
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings")
      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
875
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
876 877 878 879 880 881
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings")
      end

      it "returns errors if there are unknown parameters" do
        config = YAML.dump({ extra: "bundle update" })
        expect do
882
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
883 884 885 886 887 888
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra")
      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
889
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
890 891 892
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra")
      end

893
      it "returns errors if there are no jobs defined" do
Valery Sizov committed
894 895
        config = YAML.dump({ before_script: ["bundle update"] })
        expect do
896
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
897 898 899 900 901 902
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job")
      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
903
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
904 905 906 907
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean")
      end

      it "returns errors if job stage is not a string" do
908
        config = YAML.dump({ rspec: { script: "test", type: 1 } })
Valery Sizov committed
909
        expect do
910
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
911 912 913 914
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy")
      end

      it "returns errors if job stage is not a pre-defined stage" do
915
        config = YAML.dump({ rspec: { script: "test", type: "acceptance" } })
Valery Sizov committed
916
        expect do
917
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
918 919 920 921
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy")
      end

      it "returns errors if job stage is not a defined stage" do
922
        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance" } })
Valery Sizov committed
923
        expect do
924
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
925 926 927 928 929 930
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test")
      end

      it "returns errors if stages is not an array" do
        config = YAML.dump({ types: "test", rspec: { script: "test" } })
        expect do
931
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
932 933 934 935 936 937
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings")
      end

      it "returns errors if stages is not an array of strings" do
        config = YAML.dump({ types: [true, "test"], rspec: { script: "test" } })
        expect do
938
          GitlabCiYamlProcessor.new(config, path)
Valery Sizov committed
939 940 941 942 943 944
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings")
      end

      it "returns errors if variables is not a map" do
        config = YAML.dump({ variables: "test", rspec: { script: "test" } })
        expect do
945
          GitlabCiYamlProcessor.new(config, path)
946
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-value strings")
Valery Sizov committed
947 948
      end

949
      it "returns errors if variables is not a map of key-value strings" do
Valery Sizov committed
950 951
        config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } })
        expect do
952
          GitlabCiYamlProcessor.new(config, path)
953
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-value strings")
Valery Sizov committed
954
      end
955 956

      it "returns errors if job when is not on_success, on_failure or always" do
Kamil Trzcinski committed
957
        config = YAML.dump({ rspec: { script: "test", when: 1 } })
958
        expect do
959
          GitlabCiYamlProcessor.new(config, path)
960 961
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure or always")
      end
962

963 964 965 966 967 968 969
      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)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:name parameter should be a string")
      end

970 971
      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" } } })
972 973
        expect do
          GitlabCiYamlProcessor.new(config)
974 975 976 977 978 979 980 981
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:untracked parameter should be an boolean")
      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)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:paths parameter should be an array of strings")
982
      end
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997

      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)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:untracked parameter should be an boolean")
      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)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:paths parameter should be an array of strings")
      end

998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
      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)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:key parameter should be a string")
      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)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:key parameter should be a string")
      end

1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
      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)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:untracked parameter should be an boolean")
      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)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:paths parameter should be an array of strings")
      end
1025 1026 1027 1028 1029 1030 1031

      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)
        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: dependencies parameter should be an array of strings")
      end
1032 1033 1034
    end
  end
end