github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/config/evaluation_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package config 5 6 import ( 7 "errors" 8 "fmt" 9 "io" 10 "os" 11 "path/filepath" 12 "strings" 13 "testing" 14 15 "github.com/SAP/jenkins-library/pkg/mock" 16 "github.com/stretchr/testify/assert" 17 ) 18 19 func evaluateConditionsGlobMock(pattern string) ([]string, error) { 20 matches := []string{} 21 switch pattern { 22 case "**/conf.js": 23 matches = append(matches, "conf.js") 24 case "**/package.json": 25 matches = append(matches, "package.json", "package/node_modules/lib/package.json", "node_modules/package.json", "test/package.json") 26 27 } 28 return matches, nil 29 } 30 31 func evaluateConditionsOpenFileMock(name string, _ map[string]string) (io.ReadCloser, error) { 32 var fileContent io.ReadCloser 33 switch name { 34 case "package.json": 35 fileContent = io.NopCloser(strings.NewReader(` 36 { 37 "scripts": { 38 "npmScript": "echo test", 39 "npmScript2": "echo test" 40 } 41 } 42 `)) 43 case "_package.json": 44 fileContent = io.NopCloser(strings.NewReader("wrong json format")) 45 case "test/package.json": 46 fileContent = io.NopCloser(strings.NewReader("{}")) 47 } 48 return fileContent, nil 49 } 50 51 func TestRunConfigV1EvaluateConditionsV1(t *testing.T) { 52 config := Config{Stages: map[string]map[string]interface{}{ 53 "Test Stage 1": { 54 "step1": true, // explicit activate 55 "step5": true, // explicit activate 56 "step2": false, // explicit deactivate 57 "testKey": "testVal", // some condition 1 58 "testKey2": "testVal2", // some condition 2 59 }, 60 }} 61 filesMock := mock.FilesMock{} 62 envRootPath := ".pipeline" 63 64 tests := []struct { 65 name string 66 pipelineConfig PipelineDefinitionV1 67 wantRunSteps map[string]map[string]bool 68 wantRunStages map[string]bool 69 }{ 70 { 71 name: "all steps in stage are inactive", 72 pipelineConfig: PipelineDefinitionV1{Spec: Spec{Stages: []Stage{{DisplayName: "Test Stage 1", 73 Steps: []Step{{ 74 Name: "step1", 75 NotActiveConditions: []StepCondition{{ConfigKey: "testKey"}}, 76 }, { 77 Name: "step2", 78 }, { 79 Name: "step3", 80 NotActiveConditions: []StepCondition{{ConfigKey: "testKey"}}, 81 }}, 82 }, 83 }}}, 84 wantRunSteps: map[string]map[string]bool{ 85 "Test Stage 1": { 86 "step1": false, 87 "step2": false, 88 "step3": false, 89 }}, 90 wantRunStages: map[string]bool{"Test Stage 1": false}, 91 }, 92 { 93 name: "simple stepActive conditions", 94 pipelineConfig: PipelineDefinitionV1{Spec: Spec{Stages: []Stage{{DisplayName: "Test Stage 1", 95 Steps: []Step{{ 96 Name: "step3", 97 Conditions: []StepCondition{{ConfigKey: "testKey"}}, 98 }, { 99 Name: "step4", 100 Conditions: []StepCondition{{ConfigKey: "notExistentKey"}}, 101 }}, 102 }, 103 }}}, 104 wantRunSteps: map[string]map[string]bool{ 105 "Test Stage 1": { 106 "step3": true, 107 "step4": false, 108 }}, 109 wantRunStages: map[string]bool{"Test Stage 1": true}, 110 }, 111 { 112 name: "explicit active/deactivate over stepActiveCondition", 113 pipelineConfig: PipelineDefinitionV1{Spec: Spec{Stages: []Stage{{DisplayName: "Test Stage 1", 114 Steps: []Step{{ 115 Name: "step1", 116 Conditions: []StepCondition{{ConfigKey: "notExistentKey"}}, 117 }, { 118 Name: "step2", 119 Conditions: []StepCondition{{ConfigKey: "testKey"}}, 120 }}, 121 }, 122 }}}, 123 wantRunSteps: map[string]map[string]bool{ 124 "Test Stage 1": { 125 "step1": true, 126 "step2": false, 127 }}, 128 wantRunStages: map[string]bool{"Test Stage 1": true}, 129 }, 130 { 131 name: "stepNotActiveCondition over stepActiveCondition", 132 pipelineConfig: PipelineDefinitionV1{Spec: Spec{Stages: []Stage{{DisplayName: "Test Stage 1", 133 Steps: []Step{{ 134 Name: "step3", 135 Conditions: []StepCondition{{ConfigKey: "testKey"}}, 136 NotActiveConditions: []StepCondition{{ConfigKey: "testKey2"}}, 137 }, { 138 // false notActive condition 139 Name: "step4", 140 Conditions: []StepCondition{{ConfigKey: "testKey"}}, 141 NotActiveConditions: []StepCondition{{ConfigKey: "notExistentKey"}}, 142 }}, 143 }, 144 }}}, 145 wantRunSteps: map[string]map[string]bool{ 146 "Test Stage 1": { 147 "step3": false, 148 "step4": true, 149 }}, 150 wantRunStages: map[string]bool{"Test Stage 1": true}, 151 }, 152 { 153 name: "stepNotActiveCondition over explicitly activated step", 154 pipelineConfig: PipelineDefinitionV1{Spec: Spec{Stages: []Stage{{DisplayName: "Test Stage 1", 155 Steps: []Step{{ 156 Name: "step1", 157 NotActiveConditions: []StepCondition{{ConfigKey: "testKey"}}, 158 }, { 159 Name: "step5", 160 NotActiveConditions: []StepCondition{{ConfigKey: "notExistentKey"}}, 161 }}, 162 }, 163 }}}, 164 wantRunSteps: map[string]map[string]bool{ 165 "Test Stage 1": { 166 "step1": false, 167 "step5": true, 168 }}, 169 wantRunStages: map[string]bool{"Test Stage 1": true}, 170 }, 171 { 172 name: "deactivate if only active step in stage", 173 pipelineConfig: PipelineDefinitionV1{Spec: Spec{Stages: []Stage{{DisplayName: "Test Stage 1", 174 Steps: []Step{{ 175 Name: "step1", 176 NotActiveConditions: []StepCondition{{ConfigKey: "testKey"}}, 177 }, { 178 Name: "step2", 179 }, { 180 Name: "step3", 181 NotActiveConditions: []StepCondition{{OnlyActiveStepInStage: true}}, 182 }, { 183 Name: "step4", 184 Conditions: []StepCondition{{ConfigKey: "keyNotExist"}}, 185 }}, 186 }, 187 }}}, 188 wantRunSteps: map[string]map[string]bool{ 189 "Test Stage 1": { 190 "step1": false, 191 "step2": false, 192 "step3": false, 193 "step4": false, 194 }}, 195 wantRunStages: map[string]bool{"Test Stage 1": false}, 196 }, 197 { 198 name: "OnlyActiveStepInStage: one of the next steps is active", 199 pipelineConfig: PipelineDefinitionV1{Spec: Spec{Stages: []Stage{{DisplayName: "Test Stage 1", 200 Steps: []Step{{ 201 Name: "step1", 202 NotActiveConditions: []StepCondition{{ConfigKey: "testKey"}}, 203 }, { 204 Name: "step2", 205 }, { 206 Name: "step3", 207 Conditions: []StepCondition{{ConfigKey: "testKey"}}, 208 NotActiveConditions: []StepCondition{{OnlyActiveStepInStage: true}}, 209 }, { 210 Name: "step4", 211 Conditions: []StepCondition{{ConfigKey: "testKey2"}}, 212 }}, 213 }, 214 }}}, 215 wantRunSteps: map[string]map[string]bool{ 216 "Test Stage 1": { 217 "step1": false, 218 "step2": false, 219 "step3": true, 220 "step4": true, 221 }}, 222 wantRunStages: map[string]bool{"Test Stage 1": true}, 223 }, 224 } 225 for _, tt := range tests { 226 t.Run(tt.name, func(t *testing.T) { 227 r := &RunConfigV1{PipelineConfig: tt.pipelineConfig} 228 assert.NoError(t, r.evaluateConditionsV1(&config, &filesMock, envRootPath), 229 fmt.Sprintf("evaluateConditionsV1() err, pipelineConfig = %v", tt.pipelineConfig), 230 ) 231 232 assert.Equal(t, tt.wantRunSteps, r.RunSteps, "RunSteps mismatch") 233 assert.Equal(t, tt.wantRunStages, r.RunStages, "RunStages mismatch") 234 }) 235 } 236 } 237 238 func TestEvaluateV1(t *testing.T) { 239 tt := []struct { 240 name string 241 config StepConfig 242 stepCondition StepCondition 243 runSteps map[string]bool 244 expected bool 245 expectedError error 246 }{ 247 { 248 name: "Config condition - true", 249 config: StepConfig{Config: map[string]interface{}{ 250 "deployTool": "helm3", 251 }}, 252 stepCondition: StepCondition{Config: map[string][]interface{}{"deployTool": {"helm", "helm3", "kubectl"}}}, 253 expected: true, 254 }, 255 { 256 name: "Config condition - false", 257 config: StepConfig{Config: map[string]interface{}{ 258 "deployTool": "notsupported", 259 }}, 260 stepCondition: StepCondition{Config: map[string][]interface{}{"deployTool": {"helm", "helm3", "kubectl"}}}, 261 expected: false, 262 }, 263 { 264 name: "Config condition - integer - true", 265 config: StepConfig{Config: map[string]interface{}{ 266 "executors": 1, 267 }}, 268 stepCondition: StepCondition{Config: map[string][]interface{}{"executors": {1}}}, 269 expected: true, 270 }, 271 { 272 name: "Config condition - wrong condition definition", 273 config: StepConfig{Config: map[string]interface{}{ 274 "deployTool": "helm3", 275 }}, 276 stepCondition: StepCondition{Config: map[string][]interface{}{"deployTool": {"helm", "helm3", "kubectl"}, "deployTool2": {"myTool"}}}, 277 expectedError: fmt.Errorf("only one config key allowed per condition but 2 provided"), 278 }, 279 { 280 name: "ConfigKey condition - true", 281 config: StepConfig{Config: map[string]interface{}{ 282 "dockerRegistryUrl": "https://my.docker.registry.url", 283 }}, 284 stepCondition: StepCondition{ConfigKey: "dockerRegistryUrl"}, 285 expected: true, 286 }, 287 { 288 name: "ConfigKey condition - false", 289 config: StepConfig{Config: map[string]interface{}{}}, 290 stepCondition: StepCondition{ConfigKey: "dockerRegistryUrl"}, 291 expected: false, 292 }, 293 { 294 name: "nested ConfigKey condition - true", 295 config: StepConfig{Config: map[string]interface{}{ 296 "cloudFoundry": map[string]interface{}{"space": "dev"}, 297 }}, 298 stepCondition: StepCondition{ConfigKey: "cloudFoundry/space"}, 299 expected: true, 300 }, 301 { 302 name: "nested ConfigKey condition - false", 303 config: StepConfig{Config: map[string]interface{}{ 304 "cloudFoundry": map[string]interface{}{"noSpace": "dev"}, 305 }}, 306 stepCondition: StepCondition{ConfigKey: "cloudFoundry/space"}, 307 expected: false, 308 }, 309 { 310 name: "FilePattern condition - true", 311 config: StepConfig{Config: map[string]interface{}{}}, 312 stepCondition: StepCondition{FilePattern: "**/conf.js"}, 313 expected: true, 314 }, 315 { 316 name: "FilePattern condition - false", 317 config: StepConfig{Config: map[string]interface{}{}}, 318 stepCondition: StepCondition{FilePattern: "**/confx.js"}, 319 expected: false, 320 }, 321 { 322 name: "FilePatternFromConfig condition - true", 323 config: StepConfig{Config: map[string]interface{}{ 324 "newmanCollection": "**/*.postman_collection.json", 325 }}, 326 stepCondition: StepCondition{FilePatternFromConfig: "newmanCollection"}, 327 expected: true, 328 }, 329 { 330 name: "FilePatternFromConfig condition - false", 331 config: StepConfig{Config: map[string]interface{}{ 332 "newmanCollection": "**/*.postmanx_collection.json", 333 }}, 334 stepCondition: StepCondition{FilePatternFromConfig: "newmanCollection"}, 335 expected: false, 336 }, 337 { 338 name: "FilePatternFromConfig condition - false, empty value", 339 config: StepConfig{Config: map[string]interface{}{ 340 "newmanCollection": "", 341 }}, 342 stepCondition: StepCondition{FilePatternFromConfig: "newmanCollection"}, 343 expected: false, 344 }, 345 { 346 name: "NpmScript condition - true", 347 config: StepConfig{Config: map[string]interface{}{}}, 348 stepCondition: StepCondition{NpmScript: "testScript"}, 349 expected: true, 350 }, 351 { 352 name: "NpmScript condition - true", 353 config: StepConfig{Config: map[string]interface{}{}}, 354 stepCondition: StepCondition{NpmScript: "missingScript"}, 355 expected: false, 356 }, 357 { 358 name: "Inactive condition - false", 359 config: StepConfig{Config: map[string]interface{}{}}, 360 stepCondition: StepCondition{Inactive: true}, 361 expected: false, 362 }, 363 { 364 name: "Inactive condition - true", 365 config: StepConfig{Config: map[string]interface{}{}}, 366 stepCondition: StepCondition{Inactive: false}, 367 expected: true, 368 }, 369 { 370 name: "CommonPipelineEnvironment - true", 371 config: StepConfig{Config: map[string]interface{}{}}, 372 stepCondition: StepCondition{CommonPipelineEnvironment: map[string]interface{}{"myCpeTrueFile": "myTrueValue"}}, 373 expected: true, 374 }, 375 { 376 name: "CommonPipelineEnvironment - false", 377 config: StepConfig{Config: map[string]interface{}{}}, 378 stepCondition: StepCondition{CommonPipelineEnvironment: map[string]interface{}{"myCpeTrueFile": "notMyTrueValue"}}, 379 expected: false, 380 }, 381 { 382 name: "CommonPipelineEnvironmentVariableExists - true", 383 config: StepConfig{Config: map[string]interface{}{}}, 384 stepCondition: StepCondition{PipelineEnvironmentFilled: "custom/myCpeTrueFile"}, 385 expected: true, 386 }, 387 { 388 name: "CommonPipelineEnvironmentVariableExists - false", 389 config: StepConfig{Config: map[string]interface{}{}}, 390 stepCondition: StepCondition{PipelineEnvironmentFilled: "custom/notMyCpeTrueFile"}, 391 expected: false, 392 }, 393 { 394 name: "NotActiveCondition: all previous steps are inactive", 395 config: StepConfig{Config: map[string]interface{}{}}, 396 stepCondition: StepCondition{OnlyActiveStepInStage: true}, 397 runSteps: map[string]bool{"step1": false, "step2": false}, 398 expected: true, 399 }, 400 { 401 name: "NotActiveCondition: one of the previous steps is active", 402 config: StepConfig{Config: map[string]interface{}{}}, 403 stepCondition: StepCondition{OnlyActiveStepInStage: true}, 404 runSteps: map[string]bool{"step1": false, "step2": false, "step3": true}, 405 expected: false, 406 }, 407 { 408 name: "No condition - true", 409 config: StepConfig{Config: map[string]interface{}{}}, 410 expected: true, 411 }, 412 } 413 414 packageJson := `{ 415 "scripts": { 416 "testScript": "whatever" 417 } 418 }` 419 420 filesMock := mock.FilesMock{} 421 filesMock.AddFile("conf.js", []byte("//test")) 422 filesMock.AddFile("my.postman_collection.json", []byte("{}")) 423 filesMock.AddFile("package.json", []byte(packageJson)) 424 425 dir := t.TempDir() 426 427 cpeDir := filepath.Join(dir, "commonPipelineEnvironment") 428 err := os.MkdirAll(filepath.Join(cpeDir, "custom"), 0700) 429 if err != nil { 430 t.Fatal("Failed to create sub directories") 431 } 432 os.WriteFile(filepath.Join(cpeDir, "myCpeTrueFile"), []byte("myTrueValue"), 0700) 433 os.WriteFile(filepath.Join(cpeDir, "custom", "myCpeTrueFile"), []byte("myTrueValue"), 0700) 434 435 for _, test := range tt { 436 t.Run(test.name, func(t *testing.T) { 437 active, err := test.stepCondition.evaluateV1(test.config, &filesMock, "dummy", dir, test.runSteps) 438 if test.expectedError == nil { 439 assert.NoError(t, err) 440 } else { 441 assert.EqualError(t, err, fmt.Sprint(test.expectedError)) 442 } 443 assert.Equal(t, test.expected, active) 444 }) 445 } 446 } 447 448 func TestEvaluateConditions(t *testing.T) { 449 tests := []struct { 450 name string 451 customConfig *Config 452 stageConfig io.ReadCloser 453 runStepsExpected map[string]map[string]bool 454 globFunc func(pattern string) ([]string, error) 455 wantErr bool 456 }{ 457 { 458 name: "test config condition - success", 459 customConfig: &Config{ 460 General: map[string]interface{}{ 461 "testGeneral": "myVal1", 462 }, 463 Stages: map[string]map[string]interface{}{ 464 "testStage2": { 465 "testStage": "myVal2", 466 }, 467 }, 468 Steps: map[string]map[string]interface{}{ 469 "thirdStep": { 470 "testStep1": "myVal3", 471 }, 472 }, 473 }, 474 stageConfig: io.NopCloser(strings.NewReader(` 475 stages: 476 testStage1: 477 stepConditions: 478 firstStep: 479 config: testGeneral 480 testStage2: 481 stepConditions: 482 secondStep: 483 config: testStage 484 testStage3: 485 stepConditions: 486 thirdStep: 487 config: testStep1 488 forthStep: 489 config: testStep2 490 `)), 491 runStepsExpected: map[string]map[string]bool{ 492 "testStage1": {"firstStep": true}, 493 "testStage2": {"secondStep": true}, 494 "testStage3": { 495 "thirdStep": true, 496 "forthStep": false, 497 }, 498 }, 499 wantErr: false, 500 }, 501 { 502 name: "test config condition - wrong usage with list", 503 customConfig: &Config{ 504 General: map[string]interface{}{}, 505 Stages: map[string]map[string]interface{}{}, 506 Steps: map[string]map[string]interface{}{}, 507 }, 508 stageConfig: io.NopCloser(strings.NewReader(` 509 stages: 510 testStage1: 511 stepConditions: 512 firstStep: 513 config: 514 - testGeneral 515 `)), 516 runStepsExpected: map[string]map[string]bool{}, 517 wantErr: true, 518 }, 519 { 520 name: "test config value condition - success", 521 customConfig: &Config{ 522 General: map[string]interface{}{ 523 "testGeneral": "myVal1", 524 }, 525 Stages: map[string]map[string]interface{}{}, 526 Steps: map[string]map[string]interface{}{ 527 "thirdStep": { 528 "testStep": "myVal3", 529 }, 530 }, 531 }, 532 stageConfig: io.NopCloser(strings.NewReader(` 533 stages: 534 testStage1: 535 stepConditions: 536 firstStep: 537 config: 538 testGeneral: 539 - myValx 540 - myVal1 541 testStage2: 542 stepConditions: 543 secondStep: 544 config: 545 testStage: 546 - maValXyz 547 testStage3: 548 stepConditions: 549 thirdStep: 550 config: 551 testStep: 552 - myVal3 553 `)), 554 runStepsExpected: map[string]map[string]bool{ 555 "testStage1": {"firstStep": true}, 556 "testStage2": {"secondStep": false}, 557 "testStage3": {"thirdStep": true}, 558 }, 559 wantErr: false, 560 }, 561 { 562 name: "test configKey condition - success", 563 customConfig: &Config{ 564 General: map[string]interface{}{ 565 "myKey1_1": "myVal1_1", 566 }, 567 Stages: map[string]map[string]interface{}{}, 568 Steps: map[string]map[string]interface{}{ 569 "thirdStep": { 570 "myKey3_1": "myVal3_1", 571 }, 572 }, 573 }, 574 stageConfig: io.NopCloser(strings.NewReader(` 575 stages: 576 testStage1: 577 stepConditions: 578 firstStep: 579 configKeys: 580 - myKey1_1 581 - myKey1_2 582 testStage2: 583 stepConditions: 584 secondStep: 585 configKeys: 586 - myKey2_1 587 testStage3: 588 stepConditions: 589 thirdStep: 590 configKeys: 591 - myKey3_1 592 `)), 593 runStepsExpected: map[string]map[string]bool{ 594 "testStage1": {"firstStep": true}, 595 "testStage2": {"secondStep": false}, 596 "testStage3": {"thirdStep": true}, 597 }, 598 wantErr: false, 599 }, 600 { 601 name: "test configKey condition - not list", 602 customConfig: &Config{ 603 General: map[string]interface{}{ 604 "myKey1_1": "myVal1_1", 605 }, 606 Stages: map[string]map[string]interface{}{}, 607 Steps: map[string]map[string]interface{}{}, 608 }, 609 stageConfig: io.NopCloser(strings.NewReader(` 610 stages: 611 testStage1: 612 stepConditions: 613 firstStep: 614 configKeys: myKey1_1 615 `)), 616 wantErr: true, 617 }, 618 { 619 name: "test configKey condition - success", 620 customConfig: &Config{ 621 General: map[string]interface{}{ 622 "myKey1_1": "myVal1_1", 623 }, 624 Stages: map[string]map[string]interface{}{}, 625 Steps: map[string]map[string]interface{}{ 626 "thirdStep": { 627 "myKey3_1": "myVal3_1", 628 }, 629 }, 630 }, 631 stageConfig: io.NopCloser(strings.NewReader(` 632 stages: 633 testStage1: 634 stepConditions: 635 firstStep: 636 configKeys: 637 - myKey1_1 638 - myKey1_2 639 testStage2: 640 stepConditions: 641 secondStep: 642 configKeys: 643 - myKey2_1 644 testStage3: 645 stepConditions: 646 thirdStep: 647 configKeys: 648 - myKey3_1 649 `)), 650 runStepsExpected: map[string]map[string]bool{ 651 "testStage1": {"firstStep": true}, 652 "testStage2": {"secondStep": false}, 653 "testStage3": {"thirdStep": true}, 654 }, 655 wantErr: false, 656 }, 657 { 658 name: "test filePattern condition - success", 659 globFunc: evaluateConditionsGlobMock, 660 customConfig: &Config{ 661 General: map[string]interface{}{}, 662 Stages: map[string]map[string]interface{}{}, 663 Steps: map[string]map[string]interface{}{}, 664 }, 665 stageConfig: io.NopCloser(strings.NewReader(` 666 stages: 667 testStage1: 668 stepConditions: 669 firstStep: 670 filePattern: '**/conf.js' 671 secondStep: 672 filePattern: '**/conf.jsx' 673 `)), 674 runStepsExpected: map[string]map[string]bool{ 675 "testStage1": { 676 "firstStep": true, 677 "secondStep": false, 678 }, 679 }, 680 wantErr: false, 681 }, 682 { 683 name: "test filePattern condition - error while searching files by pattern", 684 customConfig: &Config{ 685 General: map[string]interface{}{}, 686 Stages: map[string]map[string]interface{}{}, 687 Steps: map[string]map[string]interface{}{}, 688 }, 689 stageConfig: io.NopCloser(strings.NewReader(` 690 stages: 691 testStage1: 692 stepConditions: 693 firstStep: 694 filePattern: '**/conf.js' 695 `)), 696 runStepsExpected: map[string]map[string]bool{}, 697 globFunc: func(pattern string) ([]string, error) { 698 return nil, errors.New("failed to check if file exists") 699 }, 700 wantErr: true, 701 }, 702 { 703 name: "test filePattern condition with list - success", 704 globFunc: evaluateConditionsGlobMock, 705 customConfig: &Config{ 706 General: map[string]interface{}{}, 707 Stages: map[string]map[string]interface{}{}, 708 Steps: map[string]map[string]interface{}{}, 709 }, 710 stageConfig: io.NopCloser(strings.NewReader(` 711 stages: 712 testStage1: 713 stepConditions: 714 firstStep: 715 filePattern: 716 - '**/conf.js' 717 - 'myCollection.json' 718 secondStep: 719 filePattern: 720 - '**/conf.jsx' 721 `)), 722 runStepsExpected: map[string]map[string]bool{ 723 "testStage1": { 724 "firstStep": true, 725 "secondStep": false, 726 }, 727 }, 728 wantErr: false, 729 }, 730 { 731 name: "test filePattern condition with list - error while searching files by pattern", 732 customConfig: &Config{ 733 General: map[string]interface{}{}, 734 Stages: map[string]map[string]interface{}{}, 735 Steps: map[string]map[string]interface{}{}, 736 }, 737 stageConfig: io.NopCloser(strings.NewReader(` 738 stages: 739 testStage1: 740 stepConditions: 741 firstStep: 742 filePattern: 743 - '**/conf.js' 744 - 'myCollection.json' 745 `)), 746 runStepsExpected: map[string]map[string]bool{}, 747 globFunc: func(pattern string) ([]string, error) { 748 return nil, errors.New("failed to check if file exists") 749 }, 750 wantErr: true, 751 }, 752 { 753 name: "test filePatternFromConfig condition - success", 754 globFunc: evaluateConditionsGlobMock, 755 customConfig: &Config{ 756 General: map[string]interface{}{}, 757 Stages: map[string]map[string]interface{}{}, 758 Steps: map[string]map[string]interface{}{ 759 "firstStep": { 760 "myVal1": "**/conf.js", 761 }, 762 "thirdStep": { 763 "myVal3": "**/conf.jsx", 764 }, 765 }, 766 }, 767 stageConfig: io.NopCloser(strings.NewReader(` 768 stages: 769 testStage1: 770 stepConditions: 771 firstStep: 772 filePatternFromConfig: myVal1 773 secondStep: 774 filePatternFromConfig: myVal2 775 thirdStep: 776 filePatternFromConfig: myVal3 777 `)), 778 runStepsExpected: map[string]map[string]bool{ 779 "testStage1": { 780 "firstStep": true, 781 "secondStep": false, 782 "thirdStep": false, 783 }, 784 }, 785 wantErr: false, 786 }, 787 { 788 name: "test filePatternFromConfig condition - error while searching files by pattern", 789 customConfig: &Config{ 790 General: map[string]interface{}{}, 791 Stages: map[string]map[string]interface{}{}, 792 Steps: map[string]map[string]interface{}{ 793 "firstStep": { 794 "myVal1": "**/conf.js", 795 }, 796 }, 797 }, 798 stageConfig: io.NopCloser(strings.NewReader(` 799 stages: 800 testStage1: 801 stepConditions: 802 firstStep: 803 filePatternFromConfig: myVal1 804 `)), 805 runStepsExpected: map[string]map[string]bool{}, 806 globFunc: func(pattern string) ([]string, error) { 807 return nil, errors.New("failed to check if file exists") 808 }, 809 wantErr: true, 810 }, 811 { 812 name: "test npmScripts condition - success", 813 globFunc: evaluateConditionsGlobMock, 814 customConfig: &Config{ 815 General: map[string]interface{}{}, 816 Stages: map[string]map[string]interface{}{}, 817 Steps: map[string]map[string]interface{}{}, 818 }, 819 stageConfig: io.NopCloser(strings.NewReader(` 820 stages: 821 testStage1: 822 stepConditions: 823 firstStep: 824 npmScripts: 'npmScript' 825 secondStep: 826 npmScripts: 'npmScript1' 827 `)), 828 runStepsExpected: map[string]map[string]bool{ 829 "testStage1": { 830 "firstStep": true, 831 "secondStep": false, 832 }, 833 }, 834 wantErr: false, 835 }, 836 { 837 name: "test npmScripts condition with list - success", 838 globFunc: evaluateConditionsGlobMock, 839 customConfig: &Config{ 840 General: map[string]interface{}{}, 841 Stages: map[string]map[string]interface{}{}, 842 Steps: map[string]map[string]interface{}{}, 843 }, 844 stageConfig: io.NopCloser(strings.NewReader(` 845 stages: 846 testStage1: 847 stepConditions: 848 firstStep: 849 npmScripts: 850 - 'npmScript' 851 - 'npmScript2' 852 secondStep: 853 npmScripts: 854 - 'npmScript3' 855 - 'npmScript4' 856 `)), 857 runStepsExpected: map[string]map[string]bool{ 858 "testStage1": { 859 "firstStep": true, 860 "secondStep": false, 861 }, 862 }, 863 wantErr: false, 864 }, 865 { 866 name: "test npmScripts condition - json with wrong format", 867 customConfig: &Config{ 868 General: map[string]interface{}{}, 869 Stages: map[string]map[string]interface{}{}, 870 Steps: map[string]map[string]interface{}{}, 871 }, 872 stageConfig: io.NopCloser(strings.NewReader(` 873 stages: 874 testStage1: 875 stepConditions: 876 firstStep: 877 npmScripts: 878 - 'npmScript3' 879 `)), 880 runStepsExpected: map[string]map[string]bool{}, 881 globFunc: func(pattern string) ([]string, error) { 882 return []string{"_package.json"}, nil 883 }, 884 wantErr: true, 885 }, 886 { 887 name: "test npmScripts condition - error while searching package.json", 888 customConfig: &Config{ 889 General: map[string]interface{}{}, 890 Stages: map[string]map[string]interface{}{}, 891 Steps: map[string]map[string]interface{}{}, 892 }, 893 stageConfig: io.NopCloser(strings.NewReader(` 894 stages: 895 testStage1: 896 stepConditions: 897 firstStep: 898 npmScripts: 899 - 'npmScript3' 900 `)), 901 runStepsExpected: map[string]map[string]bool{}, 902 globFunc: func(pattern string) ([]string, error) { 903 return nil, errors.New("failed to check if file exists") 904 }, 905 wantErr: true, 906 }, 907 { 908 name: "test explicit activation / de-activation of step", 909 customConfig: &Config{ 910 Stages: map[string]map[string]interface{}{ 911 "testStage1": { 912 "firstStep": true, 913 "fisecondStep": false, 914 }, 915 }, 916 }, 917 stageConfig: io.NopCloser(strings.NewReader(` 918 stages: 919 testStage1: 920 stepConditions: 921 firstStep: 922 config: testGeneral 923 secondStep: 924 config: testStage 925 `)), 926 runStepsExpected: map[string]map[string]bool{ 927 "testStage1": { 928 "firstStep": true, 929 "secondStep": false, 930 }, 931 }, 932 }, 933 } 934 for _, tt := range tests { 935 t.Run(tt.name, func(t *testing.T) { 936 runConfig := RunConfig{ 937 StageConfigFile: tt.stageConfig, 938 RunSteps: map[string]map[string]bool{}, 939 OpenFile: evaluateConditionsOpenFileMock, 940 } 941 err := runConfig.loadConditions() 942 assert.NoError(t, err) 943 err = runConfig.evaluateConditions(tt.customConfig, nil, nil, nil, nil, tt.globFunc) 944 if tt.wantErr { 945 assert.Error(t, err) 946 } else { 947 assert.NoError(t, err) 948 assert.Equal(t, tt.runStepsExpected, runConfig.RunSteps) 949 } 950 }) 951 } 952 } 953 954 func TestAnyOtherStepIsActive(t *testing.T) { 955 targetStep := "step3" 956 957 tests := []struct { 958 name string 959 runSteps map[string]bool 960 want bool 961 }{ 962 { 963 name: "all steps are inactive (target active)", 964 runSteps: map[string]bool{ 965 "step1": false, 966 "step2": false, 967 "step3": true, 968 "step4": false, 969 }, 970 want: false, 971 }, 972 { 973 name: "all steps are inactive (target inactive)", 974 runSteps: map[string]bool{ 975 "step1": false, 976 "step2": false, 977 "step3": false, 978 "step4": false, 979 }, 980 want: false, 981 }, 982 { 983 name: "some previous step is active", 984 runSteps: map[string]bool{ 985 "step1": false, 986 "step2": true, 987 "step3": false, 988 "step4": false, 989 }, 990 want: true, 991 }, 992 { 993 name: "some next step is active", 994 runSteps: map[string]bool{ 995 "step1": false, 996 "step2": false, 997 "step3": true, 998 "step4": true, 999 }, 1000 want: true, 1001 }, 1002 } 1003 for _, tt := range tests { 1004 t.Run(tt.name, func(t *testing.T) { 1005 assert.Equalf(t, tt.want, anyOtherStepIsActive(targetStep, tt.runSteps), "anyOtherStepIsActive(%v, %v)", targetStep, tt.runSteps) 1006 }) 1007 } 1008 }