github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/cmd/cloudFoundryDeploy_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package cmd 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "testing" 11 "time" 12 13 "github.com/SAP/jenkins-library/pkg/cloudfoundry" 14 "github.com/SAP/jenkins-library/pkg/command" 15 "github.com/SAP/jenkins-library/pkg/mock" 16 "github.com/SAP/jenkins-library/pkg/piperutils" 17 "github.com/SAP/jenkins-library/pkg/yaml" 18 "github.com/stretchr/testify/assert" 19 ) 20 21 type manifestMock struct { 22 manifestFileName string 23 apps []map[string]interface{} 24 } 25 26 func (m manifestMock) GetAppName(index int) (string, error) { 27 val, err := m.GetApplicationProperty(index, "name") 28 if err != nil { 29 return "", err 30 } 31 if v, ok := val.(string); ok { 32 return v, nil 33 } 34 return "", fmt.Errorf("Cannot resolve application name") 35 } 36 func (m manifestMock) ApplicationHasProperty(index int, name string) (bool, error) { 37 _, exists := m.apps[index][name] 38 return exists, nil 39 } 40 func (m manifestMock) GetApplicationProperty(index int, name string) (interface{}, error) { 41 return m.apps[index][name], nil 42 } 43 func (m manifestMock) GetFileName() string { 44 return m.manifestFileName 45 } 46 func (m manifestMock) Transform() error { 47 return nil 48 } 49 func (m manifestMock) IsModified() bool { 50 return false 51 } 52 func (m manifestMock) GetApplications() ([]map[string]interface{}, error) { 53 return m.apps, nil 54 } 55 func (m manifestMock) WriteManifest() error { 56 return nil 57 } 58 59 func TestCfDeployment(t *testing.T) { 60 61 defer func() { 62 fileUtils = &piperutils.Files{} 63 _replaceVariables = yaml.Substitute 64 }() 65 66 filesMock := mock.FilesMock{} 67 filesMock.AddDir("/home/me") 68 err := filesMock.Chdir("/home/me") 69 assert.NoError(t, err) 70 fileUtils = &filesMock 71 72 // everything below in the config map annotated with '//default' is a default in the metadata 73 // since we don't get injected these values during the tests we set it here. 74 defaultConfig := cloudFoundryDeployOptions{ 75 Org: "myOrg", 76 Space: "mySpace", 77 Username: "me", 78 Password: "******", 79 APIEndpoint: "https://examples.sap.com/cf", 80 SmokeTestStatusCode: 200, // default 81 Manifest: "manifest.yml", //default 82 MtaDeployParameters: "-f", // default 83 DeployType: "standard", // default 84 } 85 86 config := defaultConfig 87 88 successfulLogin := cloudfoundry.LoginOptions{ 89 CfAPIEndpoint: "https://examples.sap.com/cf", 90 CfOrg: "myOrg", 91 CfSpace: "mySpace", 92 Username: "me", 93 Password: "******", 94 CfLoginOpts: []string{}, 95 } 96 97 var loginOpts cloudfoundry.LoginOptions 98 var logoutCalled bool 99 100 noopCfAPICalls := func(t *testing.T, s mock.ExecMockRunner) { 101 assert.Empty(t, s.Calls) // --> in case of an invalid deploy tool there must be no cf api calls 102 assert.Empty(t, loginOpts) // no login options: login has not been called 103 assert.False(t, logoutCalled) 104 } 105 106 prepareDefaultManifestMocking := func(manifestName string, appNames []string) func() { 107 108 filesMock.AddFile(manifestName, []byte("file content does not matter")) 109 110 apps := []map[string]interface{}{} 111 112 for _, appName := range appNames { 113 apps = append(apps, map[string]interface{}{"name": appName}) 114 } 115 116 _getManifest = func(name string) (cloudfoundry.Manifest, error) { 117 return manifestMock{ 118 manifestFileName: manifestName, 119 apps: apps, 120 }, nil 121 } 122 123 return func() { 124 _ = filesMock.FileRemove(manifestName) // slightly mis-use since that is intended to be used by code under test, not test code 125 _getManifest = getManifest 126 } 127 } 128 129 withLoginAndLogout := func(t *testing.T, asserts func(t *testing.T)) { 130 assert.Equal(t, successfulLogin, loginOpts) 131 asserts(t) 132 assert.True(t, logoutCalled) 133 } 134 135 cleanup := func() { 136 loginOpts = cloudfoundry.LoginOptions{} 137 logoutCalled = false 138 config = defaultConfig 139 } 140 141 defer func() { 142 _cfLogin = cfLogin 143 _cfLogout = cfLogout 144 }() 145 146 _cfLogin = func(c command.ExecRunner, opts cloudfoundry.LoginOptions) error { 147 loginOpts = opts 148 return nil 149 } 150 151 _cfLogout = func(c command.ExecRunner) error { 152 logoutCalled = true 153 return nil 154 } 155 156 _replaceVariables = func(manifest string, replacements map[string]interface{}, replacementsFiles []string) (bool, error) { 157 return false, nil 158 } 159 160 t.Run("Test invalid appname", func(t *testing.T) { 161 162 defer cleanup() 163 config.AppName = "a_z" 164 s := mock.ExecMockRunner{} 165 err := runCloudFoundryDeploy(&config, nil, nil, &s) 166 167 assert.EqualError(t, err, "Your application name 'a_z' contains a '_' (underscore) which is not allowed, only letters, dashes and numbers can be used. Please change the name to fit this requirement(s). For more details please visit https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#basic-settings.") 168 }) 169 170 t.Run("Manifest substitution", func(t *testing.T) { 171 172 defer func() { 173 cleanup() 174 _replaceVariables = func(manifest string, replacements map[string]interface{}, replacementsFiles []string) (bool, error) { 175 return false, nil 176 } 177 }() 178 179 s := mock.ExecMockRunner{} 180 181 var manifestForSubstitution string 182 var replacements map[string]interface{} 183 var replacementFiles []string 184 185 defer prepareDefaultManifestMocking("substitute-manifest.yml", []string{"testAppName"})() 186 config.DeployTool = "cf_native" 187 config.DeployType = "blue-green" 188 config.AppName = "myApp" 189 config.Manifest = "substitute-manifest.yml" 190 191 _replaceVariables = func(manifest string, _replacements map[string]interface{}, _replacementsFiles []string) (bool, error) { 192 manifestForSubstitution = manifest 193 replacements = _replacements 194 replacementFiles = _replacementsFiles 195 return false, nil 196 } 197 198 t.Run("straight forward", func(t *testing.T) { 199 200 defer func() { 201 config.ManifestVariables = []string{} 202 config.ManifestVariablesFiles = []string{} 203 }() 204 205 config.ManifestVariables = []string{"k1=v1"} 206 config.ManifestVariablesFiles = []string{"myVars.yml"} 207 208 err := runCloudFoundryDeploy(&config, nil, nil, &s) 209 210 if assert.NoError(t, err) { 211 assert.Equal(t, "substitute-manifest.yml", manifestForSubstitution) 212 assert.Equal(t, map[string]interface{}{"k1": "v1"}, replacements) 213 assert.Equal(t, []string{"myVars.yml"}, replacementFiles) 214 } 215 }) 216 217 t.Run("empty", func(t *testing.T) { 218 219 defer func() { 220 config.ManifestVariables = []string{} 221 config.ManifestVariablesFiles = []string{} 222 }() 223 224 config.ManifestVariables = []string{} 225 config.ManifestVariablesFiles = []string{} 226 227 err := runCloudFoundryDeploy(&config, nil, nil, &s) 228 229 if assert.NoError(t, err) { 230 assert.Equal(t, "substitute-manifest.yml", manifestForSubstitution) 231 assert.Equal(t, map[string]interface{}{}, replacements) 232 assert.Equal(t, []string{}, replacementFiles) 233 } 234 }) 235 }) 236 237 t.Run("Invalid deploytool", func(t *testing.T) { 238 239 defer cleanup() 240 241 s := mock.ExecMockRunner{} 242 243 config.DeployTool = "invalid" 244 245 err := runCloudFoundryDeploy(&config, nil, nil, &s) 246 247 if assert.NoError(t, err) { 248 noopCfAPICalls(t, s) 249 } 250 }) 251 252 t.Run("deploytool cf native", func(t *testing.T) { 253 254 defer cleanup() 255 256 defer prepareDefaultManifestMocking("manifest.yml", []string{"testAppName"})() 257 258 config.DeployTool = "cf_native" 259 config.CfHome = "/home/me1" 260 config.CfPluginHome = "/home/me2" 261 262 s := mock.ExecMockRunner{} 263 264 err := runCloudFoundryDeploy(&config, nil, nil, &s) 265 266 if assert.NoError(t, err) { 267 268 t.Run("check cf api calls", func(t *testing.T) { 269 270 withLoginAndLogout(t, func(t *testing.T) { 271 assert.Equal(t, []mock.ExecCall{ 272 {Exec: "cf", Params: []string{"version"}}, 273 {Exec: "cf", Params: []string{"plugins"}}, 274 {Exec: "cf", Params: []string{"push", "-f", "manifest.yml"}}, 275 }, s.Calls) 276 }) 277 }) 278 279 t.Run("check environment variables", func(t *testing.T) { 280 assert.Contains(t, s.Env, "CF_HOME=/home/me1") 281 assert.Contains(t, s.Env, "CF_PLUGIN_HOME=/home/me2") 282 assert.Contains(t, s.Env, "STATUS_CODE=200") 283 }) 284 } 285 }) 286 287 t.Run("influx reporting", func(t *testing.T) { 288 289 defer cleanup() 290 291 s := mock.ExecMockRunner{} 292 293 defer func() { 294 _now = time.Now 295 }() 296 297 _now = func() time.Time { 298 // There was the big eclipse in Karlsruhe 299 return time.Date(1999, time.August, 11, 12, 32, 0, 0, time.UTC) 300 } 301 302 defer prepareDefaultManifestMocking("manifest.yml", []string{"testAppName"})() 303 304 config.DeployTool = "cf_native" 305 config.ArtifactVersion = "0.1.2" 306 config.CommitHash = "123456" 307 308 influxData := cloudFoundryDeployInflux{} 309 310 err := runCloudFoundryDeploy(&config, nil, &influxData, &s) 311 312 if assert.NoError(t, err) { 313 314 expected := cloudFoundryDeployInflux{} 315 316 expected.deployment_data.fields.artifactURL = "n/a" 317 expected.deployment_data.fields.deployTime = "AUG 11 1999 12:32:00" 318 expected.deployment_data.fields.jobTrigger = "n/a" 319 expected.deployment_data.fields.commitHash = "123456" 320 321 expected.deployment_data.tags.artifactVersion = "0.1.2" 322 expected.deployment_data.tags.deployUser = "me" 323 expected.deployment_data.tags.deployResult = "SUCCESS" 324 expected.deployment_data.tags.cfAPIEndpoint = "https://examples.sap.com/cf" 325 expected.deployment_data.tags.cfOrg = "myOrg" 326 expected.deployment_data.tags.cfSpace = "mySpace" 327 328 assert.Equal(t, expected, influxData) 329 330 } 331 332 }) 333 334 t.Run("deploy cf native with docker image and docker username", func(t *testing.T) { 335 336 defer cleanup() 337 338 config.DeployTool = "cf_native" 339 config.DeployDockerImage = "repo/image:tag" 340 config.DockerUsername = "me" 341 config.AppName = "testAppName" 342 343 config.Manifest = "" 344 345 s := mock.ExecMockRunner{} 346 347 err := runCloudFoundryDeploy(&config, nil, nil, &s) 348 349 if assert.NoError(t, err) { 350 351 withLoginAndLogout(t, func(t *testing.T) { 352 assert.Equal(t, []mock.ExecCall{ 353 {Exec: "cf", Params: []string{"version"}}, 354 {Exec: "cf", Params: []string{"plugins"}}, 355 {Exec: "cf", Params: []string{"push", 356 "testAppName", 357 "--docker-image", 358 "repo/image:tag", 359 "--docker-username", 360 "me", 361 }}, 362 }, s.Calls) 363 }) 364 } 365 }) 366 367 t.Run("deploy_cf_native with manifest and docker credentials", func(t *testing.T) { 368 369 defer cleanup() 370 371 // Docker image can be done via manifest.yml. 372 // if a private Docker registry is used, --docker-username and DOCKER_PASSWORD 373 // must be set; this is checked by this test 374 375 config.DeployTool = "cf_native" 376 config.DeployDockerImage = "repo/image:tag" 377 config.DockerUsername = "test_cf_docker" 378 config.DockerPassword = "********" 379 config.AppName = "testAppName" 380 381 config.Manifest = "" 382 383 s := mock.ExecMockRunner{} 384 385 err := runCloudFoundryDeploy(&config, nil, nil, &s) 386 387 if assert.NoError(t, err) { 388 t.Run("check shell calls", func(t *testing.T) { 389 390 withLoginAndLogout(t, func(t *testing.T) { 391 392 assert.Equal(t, []mock.ExecCall{ 393 {Exec: "cf", Params: []string{"version"}}, 394 {Exec: "cf", Params: []string{"plugins"}}, 395 {Exec: "cf", Params: []string{"push", 396 "testAppName", 397 "--docker-image", 398 "repo/image:tag", 399 "--docker-username", 400 "test_cf_docker", 401 }}, 402 }, s.Calls) 403 }) 404 }) 405 406 t.Run("check environment variables", func(t *testing.T) { 407 //REVISIT: in the corresponding groovy test we checked for "${'********'}" 408 // I don't understand why, but we should discuss ... 409 assert.Contains(t, s.Env, "CF_DOCKER_PASSWORD=********") 410 }) 411 } 412 }) 413 414 t.Run("deploy cf native blue green with manifest and docker credentials", func(t *testing.T) { 415 416 defer cleanup() 417 418 // Blue Green Deploy cf cli plugin does not support --docker-username and --docker-image parameters 419 // docker username and docker image have to be set in the manifest file 420 // if a private docker repository is used the CF_DOCKER_PASSWORD env variable must be set 421 422 config.DeployTool = "cf_native" 423 config.DeployType = "blue-green" 424 config.DockerUsername = "test_cf_docker" 425 config.DockerPassword = "********" 426 config.AppName = "testAppName" 427 428 defer prepareDefaultManifestMocking("manifest.yml", []string{"testAppName"})() 429 430 s := mock.ExecMockRunner{} 431 432 err := runCloudFoundryDeploy(&config, nil, nil, &s) 433 434 if assert.NoError(t, err) { 435 436 t.Run("check shell calls", func(t *testing.T) { 437 438 withLoginAndLogout(t, func(t *testing.T) { 439 440 assert.Equal(t, []mock.ExecCall{ 441 {Exec: "cf", Params: []string{"version"}}, 442 {Exec: "cf", Params: []string{"plugins"}}, 443 {Exec: "cf", Params: []string{ 444 "blue-green-deploy", 445 "testAppName", 446 "--delete-old-apps", 447 "-f", 448 "manifest.yml", 449 }}, 450 }, s.Calls) 451 }) 452 }) 453 454 t.Run("check environment variables", func(t *testing.T) { 455 //REVISIT: in the corresponding groovy test we checked for "${'********'}" 456 // I don't understand why, but we should discuss ... 457 assert.Contains(t, s.Env, "CF_DOCKER_PASSWORD=********") 458 }) 459 } 460 }) 461 462 t.Run("deploy cf native app name from manifest", func(t *testing.T) { 463 464 defer cleanup() 465 466 config.DeployTool = "cf_native" 467 config.Manifest = "test-manifest.yml" 468 469 // app name is not asserted since it does not appear in the cf calls 470 // but it is checked that an app name is present, hence we need it here. 471 defer prepareDefaultManifestMocking("test-manifest.yml", []string{"dummyApp"})() 472 473 s := mock.ExecMockRunner{} 474 475 err := runCloudFoundryDeploy(&config, nil, nil, &s) 476 477 if assert.NoError(t, err) { 478 479 t.Run("check shell calls", func(t *testing.T) { 480 481 withLoginAndLogout(t, func(t *testing.T) { 482 483 assert.Equal(t, []mock.ExecCall{ 484 {Exec: "cf", Params: []string{"version"}}, 485 {Exec: "cf", Params: []string{"plugins"}}, 486 {Exec: "cf", Params: []string{ 487 "push", 488 "-f", 489 "test-manifest.yml", 490 }}, 491 }, s.Calls) 492 493 }) 494 }) 495 } 496 }) 497 498 t.Run("get app name from default manifest with cf native deployment", func(t *testing.T) { 499 500 defer cleanup() 501 502 config.DeployTool = "cf_native" 503 config.Manifest = "" 504 config.AppName = "" 505 506 //app name does not need to be set if it can be found in the manifest.yml 507 //manifest name does not need to be set- the default manifest.yml will be used if not set 508 defer prepareDefaultManifestMocking("manifest.yml", []string{"newAppName"})() 509 510 s := mock.ExecMockRunner{} 511 512 err := runCloudFoundryDeploy(&config, nil, nil, &s) 513 514 if assert.NoError(t, err) { 515 516 t.Run("check shell calls", func(t *testing.T) { 517 518 withLoginAndLogout(t, func(t *testing.T) { 519 520 assert.Equal(t, []mock.ExecCall{ 521 {Exec: "cf", Params: []string{"version"}}, 522 {Exec: "cf", Params: []string{"plugins"}}, 523 {Exec: "cf", Params: []string{ 524 "push", 525 }}, 526 }, s.Calls) 527 528 }) 529 }) 530 } 531 }) 532 533 t.Run("deploy cf native without app name", func(t *testing.T) { 534 535 defer cleanup() 536 537 config.DeployTool = "cf_native" 538 config.Manifest = "test-manifest.yml" 539 540 // Here we don't provide an application name from the mock. To make that 541 // more explicit we provide the empty string default explicitly. 542 defer prepareDefaultManifestMocking("test-manifest.yml", []string{""})() 543 544 s := mock.ExecMockRunner{} 545 546 err := runCloudFoundryDeploy(&config, nil, nil, &s) 547 548 if assert.EqualError(t, err, "appName from manifest 'test-manifest.yml' is empty") { 549 550 t.Run("check shell calls", func(t *testing.T) { 551 noopCfAPICalls(t, s) 552 }) 553 } 554 }) 555 556 // tests from groovy checking for keep old instances are already contained above. Search for '--delete-old-apps' 557 558 t.Run("deploy cf native blue green keep old instance", func(t *testing.T) { 559 560 defer cleanup() 561 562 config.DeployTool = "cf_native" 563 config.DeployType = "blue-green" 564 config.Manifest = "test-manifest.yml" 565 config.AppName = "myTestApp" 566 config.KeepOldInstance = true 567 568 s := mock.ExecMockRunner{} 569 570 err := runCloudFoundryDeploy(&config, nil, nil, &s) 571 572 if assert.NoError(t, err) { 573 574 t.Run("check shell calls", func(t *testing.T) { 575 576 withLoginAndLogout(t, func(t *testing.T) { 577 578 assert.Equal(t, []mock.ExecCall{ 579 {Exec: "cf", Params: []string{"version"}}, 580 {Exec: "cf", Params: []string{"plugins"}}, 581 {Exec: "cf", Params: []string{ 582 "blue-green-deploy", 583 "myTestApp", 584 "-f", 585 "test-manifest.yml", 586 }}, 587 {Exec: "cf", Params: []string{ 588 "stop", 589 "myTestApp-old", 590 // MIGRATE FFROM GROOVY: in contrast to groovy there is not redirect of everything &> to a file since we 591 // read the stream directly now. 592 }}, 593 }, s.Calls) 594 }) 595 }) 596 } 597 }) 598 599 t.Run("cf deploy blue green multiple applications", func(t *testing.T) { 600 601 defer cleanup() 602 603 config.DeployTool = "cf_native" 604 config.DeployType = "blue-green" 605 config.Manifest = "test-manifest.yml" 606 config.AppName = "myTestApp" 607 608 defer prepareDefaultManifestMocking("test-manifest.yml", []string{"app1", "app2"})() 609 610 s := mock.ExecMockRunner{} 611 612 err := runCloudFoundryDeploy(&config, nil, nil, &s) 613 614 if assert.EqualError(t, err, "Your manifest contains more than one application. For blue green deployments your manifest file may contain only one application") { 615 t.Run("check shell calls", func(t *testing.T) { 616 noopCfAPICalls(t, s) 617 }) 618 } 619 }) 620 621 t.Run("cf native deploy blue green with no route", func(t *testing.T) { 622 623 defer cleanup() 624 625 config.DeployTool = "cf_native" 626 config.DeployType = "blue-green" 627 config.Manifest = "test-manifest.yml" 628 config.AppName = "myTestApp" 629 630 defer func() { 631 _ = filesMock.FileRemove("test-manifest.yml") 632 _getManifest = getManifest 633 }() 634 635 filesMock.AddFile("test-manifest.yml", []byte("Content does not matter")) 636 637 _getManifest = func(name string) (cloudfoundry.Manifest, error) { 638 return manifestMock{ 639 manifestFileName: "test-manifest.yml", 640 apps: []map[string]interface{}{ 641 { 642 "name": "app1", 643 "no-route": true, 644 }, 645 }, 646 }, 647 nil 648 } 649 650 s := mock.ExecMockRunner{} 651 652 err := runCloudFoundryDeploy(&config, nil, nil, &s) 653 654 if assert.NoError(t, err) { 655 656 t.Run("check shell calls", func(t *testing.T) { 657 658 withLoginAndLogout(t, func(t *testing.T) { 659 660 assert.Equal(t, []mock.ExecCall{ 661 {Exec: "cf", Params: []string{"version"}}, 662 {Exec: "cf", Params: []string{"plugins"}}, 663 {Exec: "cf", Params: []string{ 664 "push", 665 "myTestApp", 666 "-f", 667 "test-manifest.yml", 668 }}, 669 }, s.Calls) 670 }) 671 }) 672 } 673 }) 674 675 t.Run("cf native deployment failure", func(t *testing.T) { 676 677 defer cleanup() 678 679 config.DeployTool = "cf_native" 680 config.DeployType = "blue-green" 681 config.Manifest = "test-manifest.yml" 682 config.AppName = "myTestApp" 683 684 defer prepareDefaultManifestMocking("test-manifest.yml", []string{"app"})() 685 686 s := mock.ExecMockRunner{} 687 688 s.ShouldFailOnCommand = map[string]error{"cf.*deploy.*": fmt.Errorf("cf deploy failed")} 689 err := runCloudFoundryDeploy(&config, nil, nil, &s) 690 691 if assert.EqualError(t, err, "cf deploy failed") { 692 t.Run("check shell calls", func(t *testing.T) { 693 694 // we should try to logout in this case 695 assert.True(t, logoutCalled) 696 }) 697 } 698 }) 699 700 t.Run("cf native deployment failure when logging in", func(t *testing.T) { 701 702 defer cleanup() 703 704 config.DeployTool = "cf_native" 705 config.DeployType = "blue-green" 706 config.Manifest = "test-manifest.yml" 707 config.AppName = "myTestApp" 708 709 defer func() { 710 711 _cfLogin = func(c command.ExecRunner, opts cloudfoundry.LoginOptions) error { 712 loginOpts = opts 713 return nil 714 } 715 }() 716 717 _cfLogin = func(c command.ExecRunner, opts cloudfoundry.LoginOptions) error { 718 loginOpts = opts 719 return fmt.Errorf("Unable to login") 720 } 721 722 defer prepareDefaultManifestMocking("test-manifest.yml", []string{"app1"})() 723 724 s := mock.ExecMockRunner{} 725 726 err := runCloudFoundryDeploy(&config, nil, nil, &s) 727 728 if assert.EqualError(t, err, "Unable to login") { 729 t.Run("check shell calls", func(t *testing.T) { 730 731 // no calls to the cf client in this case 732 assert.Equal(t, 733 []mock.ExecCall{ 734 {Exec: "cf", Params: []string{"version"}}, 735 }, s.Calls) 736 // no logout 737 assert.False(t, logoutCalled) 738 }) 739 } 740 }) 741 742 // TODO testCfNativeBlueGreenKeepOldInstanceShouldThrowErrorOnStopError 743 744 t.Run("cf native deploy standard should not stop instance", func(t *testing.T) { 745 746 defer cleanup() 747 748 config.DeployTool = "cf_native" 749 config.DeployType = "standard" 750 config.Manifest = "test-manifest.yml" 751 config.AppName = "myTestApp" 752 config.KeepOldInstance = true 753 754 defer prepareDefaultManifestMocking("test-manifest.yml", []string{"app"})() 755 756 s := mock.ExecMockRunner{} 757 758 err := runCloudFoundryDeploy(&config, nil, nil, &s) 759 760 if assert.NoError(t, err) { 761 762 t.Run("check shell calls", func(t *testing.T) { 763 764 withLoginAndLogout(t, func(t *testing.T) { 765 766 assert.Equal(t, []mock.ExecCall{ 767 {Exec: "cf", Params: []string{"version"}}, 768 {Exec: "cf", Params: []string{"plugins"}}, 769 {Exec: "cf", Params: []string{ 770 "push", 771 "myTestApp", 772 "-f", 773 "test-manifest.yml", 774 }}, 775 776 // 777 // There is no cf stop 778 // 779 780 }, s.Calls) 781 }) 782 }) 783 } 784 }) 785 786 t.Run("testCfNativeWithoutAppNameBlueGreen", func(t *testing.T) { 787 788 defer cleanup() 789 790 config.DeployTool = "cf_native" 791 config.DeployType = "blue-green" 792 config.Manifest = "test-manifest.yml" 793 794 defer func() { 795 _ = filesMock.FileRemove("test-manifest.yml") 796 _getManifest = getManifest 797 }() 798 799 filesMock.AddFile("test-manifest.yml", []byte("The content does not matter")) 800 801 _getManifest = func(name string) (cloudfoundry.Manifest, error) { 802 return manifestMock{ 803 manifestFileName: "test-manifest.yml", 804 apps: []map[string]interface{}{ 805 { 806 "there-is": "no-app-name", 807 }, 808 }, 809 }, 810 nil 811 } 812 813 s := mock.ExecMockRunner{} 814 815 err := runCloudFoundryDeploy(&config, nil, nil, &s) 816 817 if assert.EqualError(t, err, "Blue-green plugin requires app name to be passed (see https://github.com/bluemixgaragelondon/cf-blue-green-deploy/issues/27)") { 818 819 t.Run("check shell calls", func(t *testing.T) { 820 noopCfAPICalls(t, s) 821 }) 822 } 823 }) 824 825 // TODO add test for testCfNativeFailureInShellCall 826 827 t.Run("deploytool mtaDeployPlugin blue green", func(t *testing.T) { 828 829 defer cleanup() 830 831 config.DeployTool = "mtaDeployPlugin" 832 config.DeployType = "blue-green" 833 config.MtaPath = "target/test.mtar" 834 835 defer func() { 836 _ = filesMock.FileRemove("target/test.mtar") 837 }() 838 839 filesMock.AddFile("target/test.mtar", []byte("content does not matter")) 840 841 s := mock.ExecMockRunner{} 842 843 err := runCloudFoundryDeploy(&config, nil, nil, &s) 844 845 if assert.NoError(t, err) { 846 847 t.Run("check shell calls", func(t *testing.T) { 848 849 withLoginAndLogout(t, func(t *testing.T) { 850 851 assert.Equal(t, []mock.ExecCall{ 852 {Exec: "cf", Params: []string{"version"}}, 853 {Exec: "cf", Params: []string{"plugins"}}, 854 {Exec: "cf", Params: []string{ 855 "bg-deploy", 856 "target/test.mtar", 857 "-f", 858 "--no-confirm", 859 }}, 860 861 // 862 // There is no cf stop 863 // 864 865 }, s.Calls) 866 }) 867 }) 868 } 869 }) 870 871 // TODO: add test for influx reporting (influx reporting is missing at the moment) 872 873 t.Run("cf push with variables from file and as list", func(t *testing.T) { 874 875 defer cleanup() 876 877 config.DeployTool = "cf_native" 878 config.Manifest = "test-manifest.yml" 879 config.ManifestVariablesFiles = []string{"vars.yaml"} 880 config.ManifestVariables = []string{"appName=testApplicationFromVarsList"} 881 config.AppName = "testAppName" 882 883 defer func() { 884 _getManifest = getManifest 885 _getVarsOptions = cloudfoundry.GetVarsOptions 886 _getVarsFileOptions = cloudfoundry.GetVarsFileOptions 887 }() 888 889 _getVarsOptions = func(vars []string) ([]string, error) { 890 return []string{"--var", "appName=testApplicationFromVarsList"}, nil 891 } 892 _getVarsFileOptions = func(varFiles []string) ([]string, error) { 893 return []string{"--vars-file", "vars.yaml"}, nil 894 } 895 896 filesMock.AddFile("test-manifest.yml", []byte("content does not matter")) 897 898 _getManifest = func(name string) (cloudfoundry.Manifest, error) { 899 return manifestMock{ 900 manifestFileName: "test-manifest.yml", 901 apps: []map[string]interface{}{ 902 { 903 "name": "myApp", 904 }, 905 }, 906 }, 907 nil 908 } 909 910 s := mock.ExecMockRunner{} 911 912 err := runCloudFoundryDeploy(&config, nil, nil, &s) 913 914 if assert.NoError(t, err) { 915 916 t.Run("check shell calls", func(t *testing.T) { 917 918 withLoginAndLogout(t, func(t *testing.T) { 919 920 // Revisit: we don't verify a log message in case of a non existing vars file 921 922 assert.Equal(t, []mock.ExecCall{ 923 {Exec: "cf", Params: []string{"version"}}, 924 {Exec: "cf", Params: []string{"plugins"}}, 925 {Exec: "cf", Params: []string{ 926 "push", 927 "testAppName", 928 "--var", 929 "appName=testApplicationFromVarsList", 930 "--vars-file", 931 "vars.yaml", 932 "-f", 933 "test-manifest.yml", 934 }}, 935 }, s.Calls) 936 }) 937 }) 938 } 939 }) 940 941 t.Run("cf push with variables from file which does not exist", func(t *testing.T) { 942 943 defer cleanup() 944 945 config.DeployTool = "cf_native" 946 config.Manifest = "test-manifest.yml" 947 config.ManifestVariablesFiles = []string{"vars.yaml", "vars-does-not-exist.yaml"} 948 config.AppName = "testAppName" 949 950 defer func() { 951 _ = filesMock.FileRemove("test-manifest.yml") 952 _ = filesMock.FileRemove("vars.yaml") 953 _getManifest = getManifest 954 _getVarsOptions = cloudfoundry.GetVarsOptions 955 _getVarsFileOptions = cloudfoundry.GetVarsFileOptions 956 }() 957 958 filesMock.AddFile("test-manifest.yml", []byte("content does not matter")) 959 960 _getManifest = func(name string) (cloudfoundry.Manifest, error) { 961 return manifestMock{ 962 manifestFileName: "test-manifest.yml", 963 apps: []map[string]interface{}{ 964 { 965 "name": "myApp", 966 }, 967 }, 968 }, 969 nil 970 } 971 972 s := mock.ExecMockRunner{} 973 974 var receivedVarOptions []string 975 var receivedVarsFileOptions []string 976 977 _getVarsOptions = func(vars []string) ([]string, error) { 978 receivedVarOptions = vars 979 return []string{}, nil 980 } 981 _getVarsFileOptions = func(varFiles []string) ([]string, error) { 982 receivedVarsFileOptions = varFiles 983 return []string{"--vars-file", "vars.yaml"}, nil 984 } 985 986 err := runCloudFoundryDeploy(&config, nil, nil, &s) 987 988 if assert.NoError(t, err) { 989 990 t.Run("check received vars options", func(t *testing.T) { 991 assert.Empty(t, receivedVarOptions) 992 }) 993 994 t.Run("check received vars file options", func(t *testing.T) { 995 assert.Equal(t, []string{"vars.yaml", "vars-does-not-exist.yaml"}, receivedVarsFileOptions) 996 }) 997 998 t.Run("check shell calls", func(t *testing.T) { 999 1000 withLoginAndLogout(t, func(t *testing.T) { 1001 // Revisit: we don't verify a log message in case of a non existing vars file 1002 1003 assert.Equal(t, []mock.ExecCall{ 1004 {Exec: "cf", Params: []string{"version"}}, 1005 {Exec: "cf", Params: []string{"plugins"}}, 1006 {Exec: "cf", Params: []string{ 1007 "push", 1008 "testAppName", 1009 "--vars-file", 1010 "vars.yaml", 1011 "-f", 1012 "test-manifest.yml", 1013 }}, 1014 }, s.Calls) 1015 }) 1016 }) 1017 } 1018 }) 1019 1020 // TODO: testCfPushDeploymentWithoutVariableSubstitution is already handled above (?) 1021 1022 // TODO: testCfBlueGreenDeploymentWithVariableSubstitution variable substitution is not handled at the moment (pr pending). 1023 // but anyway we should not test the full cycle here, but only that the variables substitution tool is called in the appropriate way. 1024 // variable substitution should be tested at the variables substitution tool itself (yaml util) 1025 1026 t.Run("deploytool mtaDeployPlugin", func(t *testing.T) { 1027 1028 defer cleanup() 1029 1030 config.DeployTool = "mtaDeployPlugin" 1031 config.MtaDeployParameters = "-f" 1032 1033 t.Run("mta config file from project sources", func(t *testing.T) { 1034 1035 defer func() { _ = filesMock.FileRemove("xyz.mtar") }() 1036 1037 // The mock is inaccurat here. 1038 // AddFile() adds the file absolute, prefix with the current working directory 1039 // Glob() returns the absolute path - but without leading slash - , whereas 1040 // the real Glob returns the path relative to the current workdir. 1041 // In order to mimic the behavior in the free wild we add the mtar at the root dir. 1042 filesMock.AddDir("/") 1043 assert.NoError(t, filesMock.Chdir("/")) 1044 filesMock.AddFile("xyz.mtar", []byte("content does not matter")) 1045 // restor the expected working dir. 1046 assert.NoError(t, filesMock.Chdir("/home/me")) 1047 s := mock.ExecMockRunner{} 1048 err := runCloudFoundryDeploy(&config, nil, nil, &s) 1049 1050 if assert.NoError(t, err) { 1051 1052 withLoginAndLogout(t, func(t *testing.T) { 1053 1054 assert.Equal(t, s.Calls, []mock.ExecCall{ 1055 {Exec: "cf", Params: []string{"version"}}, 1056 {Exec: "cf", Params: []string{"plugins"}}, 1057 {Exec: "cf", Params: []string{"deploy", "xyz.mtar", "-f"}}}) 1058 1059 }) 1060 } 1061 }) 1062 1063 t.Run("mta config file from project config does not exist", func(t *testing.T) { 1064 defer func() { config.MtaPath = "" }() 1065 config.MtaPath = "my.mtar" 1066 s := mock.ExecMockRunner{} 1067 err := runCloudFoundryDeploy(&config, nil, nil, &s) 1068 assert.EqualError(t, err, "mtar file 'my.mtar' retrieved from configuration does not exist") 1069 }) 1070 1071 // TODO: add test for mtar file from project config which does exist in project sources 1072 }) 1073 } 1074 1075 func TestValidateDeployTool(t *testing.T) { 1076 testCases := []struct { 1077 runName string 1078 deployToolGiven string 1079 buildTool string 1080 deployToolExpected string 1081 }{ 1082 {"no params", "", "", ""}, 1083 {"build tool MTA", "", "mta", "mtaDeployPlugin"}, 1084 {"build tool other", "", "other", "cf_native"}, 1085 {"deploy and build tool given", "given", "unknown", "given"}, 1086 {"only deploy tool given", "given", "", "given"}, 1087 } 1088 1089 t.Parallel() 1090 1091 for _, test := range testCases { 1092 t.Run(test.runName, func(t *testing.T) { 1093 config := cloudFoundryDeployOptions{BuildTool: test.buildTool, DeployTool: test.deployToolGiven} 1094 validateDeployTool(&config) 1095 assert.Equal(t, test.deployToolExpected, config.DeployTool, 1096 "expected different deployTool result") 1097 }) 1098 } 1099 } 1100 1101 func TestMtarLookup(t *testing.T) { 1102 1103 defer func() { 1104 fileUtils = piperutils.Files{} 1105 }() 1106 1107 filesMock := mock.FilesMock{} 1108 fileUtils = &filesMock 1109 1110 t.Run("One MTAR", func(t *testing.T) { 1111 1112 defer func() { _ = filesMock.FileRemove("x.mtar") }() 1113 filesMock.AddFile("x.mtar", []byte("content does not matter")) 1114 1115 path, err := findMtar() 1116 1117 if assert.NoError(t, err) { 1118 assert.Equal(t, "x.mtar", path) 1119 } 1120 }) 1121 1122 t.Run("No MTAR", func(t *testing.T) { 1123 1124 // nothing needs to be configures. There is simply no 1125 // mtar in the file system mock, so no mtar will be found. 1126 1127 _, err := findMtar() 1128 1129 assert.EqualError(t, err, "No mtar file matching pattern '**/*.mtar' found") 1130 }) 1131 1132 t.Run("Several MTARs", func(t *testing.T) { 1133 1134 defer func() { 1135 _ = filesMock.FileRemove("x.mtar") 1136 _ = filesMock.FileRemove("y.mtar") 1137 }() 1138 1139 filesMock.AddFile("x.mtar", []byte("content does not matter")) 1140 filesMock.AddFile("y.mtar", []byte("content does not matter")) 1141 1142 _, err := findMtar() 1143 assert.EqualError(t, err, "Found multiple mtar files matching pattern '**/*.mtar' (x.mtar,y.mtar), please specify file via parameter 'mtarPath'") 1144 }) 1145 } 1146 1147 func TestSmokeTestScriptHandling(t *testing.T) { 1148 1149 filesMock := mock.FilesMock{} 1150 filesMock.AddDir("/home/me") 1151 err := filesMock.Chdir("/home/me") 1152 assert.NoError(t, err) 1153 filesMock.AddFileWithMode("mySmokeTestScript.sh", []byte("Content does not matter"), 0644) 1154 fileUtils = &filesMock 1155 1156 var canExec os.FileMode = 0755 1157 1158 t.Run("non default existing smoke test file", func(t *testing.T) { 1159 1160 parts, err := handleSmokeTestScript("mySmokeTestScript.sh") 1161 if assert.NoError(t, err) { 1162 // when the none-default file name is provided the file must already exist 1163 // in the project sources. 1164 assert.False(t, filesMock.HasWrittenFile("mySmokeTestScript.sh")) 1165 info, e := filesMock.Stat("mySmokeTestScript.sh") 1166 if assert.NoError(t, e) { 1167 assert.Equal(t, canExec, info.Mode()) 1168 } 1169 1170 assert.Equal(t, []string{ 1171 "--smoke-test", 1172 filepath.FromSlash("/home/me/mySmokeTestScript.sh"), 1173 }, parts) 1174 } 1175 }) 1176 1177 t.Run("non default not existing smoke test file", func(t *testing.T) { 1178 1179 parts, err := handleSmokeTestScript("notExistingSmokeTestScript.sh") 1180 if assert.EqualError(t, err, "failed to make smoke-test script executable: chmod: notExistingSmokeTestScript.sh: No such file or directory") { 1181 assert.False(t, filesMock.HasWrittenFile("notExistingSmokeTestScript.sh")) 1182 assert.Equal(t, []string{}, parts) 1183 } 1184 }) 1185 1186 t.Run("default smoke test file", func(t *testing.T) { 1187 1188 parts, err := handleSmokeTestScript("blueGreenCheckScript.sh") 1189 1190 if assert.NoError(t, err) { 1191 1192 info, e := filesMock.Stat("blueGreenCheckScript.sh") 1193 if assert.NoError(t, e) { 1194 assert.Equal(t, canExec, info.Mode()) 1195 } 1196 1197 // in this case we provide the file. We overwrite in case there is already such a file ... 1198 assert.True(t, filesMock.HasWrittenFile("blueGreenCheckScript.sh")) 1199 1200 content, e := filesMock.FileRead("blueGreenCheckScript.sh") 1201 1202 if assert.NoError(t, e) { 1203 assert.Equal(t, "#!/usr/bin/env bash\n# this is simply testing if the application root returns HTTP STATUS_CODE\ncurl -so /dev/null -w '%{response_code}' https://$1 | grep $STATUS_CODE", string(content)) 1204 } 1205 1206 assert.Equal(t, []string{ 1207 "--smoke-test", 1208 filepath.FromSlash("/home/me/blueGreenCheckScript.sh"), 1209 }, parts) 1210 } 1211 }) 1212 } 1213 1214 func TestDefaultManifestVariableFilesHandling(t *testing.T) { 1215 1216 filesMock := mock.FilesMock{} 1217 filesMock.AddDir("/home/me") 1218 err := filesMock.Chdir("/home/me") 1219 assert.NoError(t, err) 1220 fileUtils = &filesMock 1221 1222 t.Run("default manifest variable file is the only one and exists", func(t *testing.T) { 1223 defer func() { 1224 _ = filesMock.FileRemove("manifest-variables.yml") 1225 }() 1226 filesMock.AddFile("manifest-variables.yml", []byte("Content does not matter")) 1227 1228 manifestFiles, err := validateManifestVariablesFiles( 1229 []string{ 1230 "manifest-variables.yml", 1231 }, 1232 ) 1233 1234 if assert.NoError(t, err) { 1235 assert.Equal(t, 1236 []string{ 1237 "manifest-variables.yml", 1238 }, manifestFiles) 1239 } 1240 }) 1241 1242 t.Run("default manifest variable file is the only one and does not exist", func(t *testing.T) { 1243 1244 manifestFiles, err := validateManifestVariablesFiles( 1245 []string{ 1246 "manifest-variables.yml", 1247 }, 1248 ) 1249 1250 if assert.NoError(t, err) { 1251 assert.Equal(t, []string{}, manifestFiles) 1252 } 1253 }) 1254 1255 t.Run("default manifest variable file among others remains if it does not exist", func(t *testing.T) { 1256 1257 // in this case we might fail later. 1258 1259 manifestFiles, err := validateManifestVariablesFiles( 1260 []string{ 1261 "manifest-variables.yml", 1262 "a-second-file.yml", 1263 }, 1264 ) 1265 1266 if assert.NoError(t, err) { 1267 // the order in which the files are returned is significant. 1268 assert.Equal(t, []string{ 1269 "manifest-variables.yml", 1270 "a-second-file.yml", 1271 }, manifestFiles) 1272 } 1273 }) 1274 } 1275 1276 func TestExtensionDescriptorsWithMinusE(t *testing.T) { 1277 1278 t.Run("ExtensionDescriptorsWithMinusE", func(t *testing.T) { 1279 extDesc, _ := handleMtaExtensionDescriptors("-e 1.yaml -e 2.yaml") 1280 assert.Equal(t, []string{ 1281 "-e", 1282 "1.yaml,2.yaml", 1283 }, extDesc) 1284 }) 1285 1286 t.Run("ExtensionDescriptorsFirstOneWithoutMinusE", func(t *testing.T) { 1287 extDesc, _ := handleMtaExtensionDescriptors("1.yaml -e 2.yaml") 1288 assert.Equal(t, []string{ 1289 "-e", 1290 "1.yaml,2.yaml", 1291 }, extDesc) 1292 }) 1293 1294 t.Run("NoExtensionDescriptors", func(t *testing.T) { 1295 extDesc, _ := handleMtaExtensionDescriptors("") 1296 assert.Equal(t, []string{}, extDesc) 1297 }) 1298 } 1299 1300 func TestAppNameChecks(t *testing.T) { 1301 1302 t.Run("appName with alpha-numeric chars should work", func(t *testing.T) { 1303 err := validateAppName("myValidAppName123") 1304 assert.NoError(t, err) 1305 }) 1306 1307 t.Run("appName with alpha-numeric chars and dash should work", func(t *testing.T) { 1308 err := validateAppName("my-Valid-AppName123") 1309 assert.NoError(t, err) 1310 }) 1311 1312 t.Run("empty appName should work", func(t *testing.T) { 1313 // we consider the empty string as valid appname since we only check app names handed over from outside 1314 // in case there is no (real) app name provided from outside we might still find an appname in the metadata 1315 // That app name in turn is not checked. 1316 err := validateAppName("") 1317 assert.NoError(t, err) 1318 }) 1319 1320 t.Run("single char appName should work", func(t *testing.T) { 1321 err := validateAppName("a") 1322 assert.NoError(t, err) 1323 }) 1324 1325 t.Run("appName with alpha-numeric chars and trailing dash should throw an error", func(t *testing.T) { 1326 err := validateAppName("my-Invalid-AppName123-") 1327 assert.EqualError(t, err, "Your application name 'my-Invalid-AppName123-' starts or ends with a '-' (dash) which is not allowed, only letters and numbers can be used. Please change the name to fit this requirement(s). For more details please visit https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#basic-settings.") 1328 }) 1329 1330 t.Run("appName with underscores should throw an error", func(t *testing.T) { 1331 err := validateAppName("my_invalid_app_name") 1332 assert.EqualError(t, err, "Your application name 'my_invalid_app_name' contains a '_' (underscore) which is not allowed, only letters, dashes and numbers can be used. Please change the name to fit this requirement(s). For more details please visit https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#basic-settings.") 1333 }) 1334 1335 } 1336 1337 func TestMtaExtensionCredentials(t *testing.T) { 1338 1339 filesMock := mock.FilesMock{} 1340 filesMock.AddDir("/home/me") 1341 err := filesMock.Chdir("/home/me") 1342 assert.NoError(t, err) 1343 fileUtils = &filesMock 1344 1345 _environ = func() []string { 1346 return []string{ 1347 "MY_CRED_ENV_VAR1=**$0****", 1348 "MY_CRED_ENV_VAR2=++$1++++", 1349 } 1350 } 1351 1352 defer func() { 1353 fileUtils = piperutils.Files{} 1354 _environ = os.Environ 1355 }() 1356 1357 t.Run("extension file does not exist", func(t *testing.T) { 1358 _, _, err := handleMtaExtensionCredentials("mtaextDoesNotExist.mtaext", map[string]interface{}{}) 1359 assert.EqualError(t, err, "Cannot handle credentials for mta extension file 'mtaextDoesNotExist.mtaext': could not read 'mtaextDoesNotExist.mtaext'") 1360 }) 1361 1362 t.Run("credential cannot be retrieved", func(t *testing.T) { 1363 1364 filesMock.AddFile("mtaext.mtaext", []byte( 1365 `'_schema-version: '3.1' 1366 ID: test.ext 1367 extends: test 1368 parameters 1369 test-credentials1: "<%= testCred1 %>" 1370 test-credentials2: "<%=testCred2%>"`)) 1371 _, _, err := handleMtaExtensionCredentials( 1372 "mtaext.mtaext", 1373 map[string]interface{}{ 1374 "testCred1": "myCredEnvVar1NotDefined", 1375 "testCred2": "myCredEnvVar2NotDefined", 1376 }, 1377 ) 1378 assert.EqualError(t, err, "cannot handle mta extension credentials: No credentials found for '[myCredEnvVar1NotDefined myCredEnvVar2NotDefined]'/'[MY_CRED_ENV_VAR1_NOT_DEFINED MY_CRED_ENV_VAR2_NOT_DEFINED]'. Are these credentials maintained?") 1379 }) 1380 1381 t.Run("irrelevant credentials do not cause failures", func(t *testing.T) { 1382 1383 filesMock.AddFile("mtaext.mtaext", []byte( 1384 `'_schema-version: '3.1' 1385 ID: test.ext 1386 extends: test 1387 parameters 1388 test-credentials1: "<%= testCred1 %>" 1389 test-credentials2: "<%=testCred2%>`)) 1390 _, _, err := handleMtaExtensionCredentials( 1391 "mtaext.mtaext", 1392 map[string]interface{}{ 1393 "testCred1": "myCredEnvVar1", 1394 "testCred2": "myCredEnvVar2", 1395 "testCredNotUsed": "myCredEnvVarWhichDoesNotExist", //<-- This here is not used. 1396 }, 1397 ) 1398 assert.NoError(t, err) 1399 }) 1400 1401 t.Run("invalid chars in credential key name", func(t *testing.T) { 1402 filesMock.AddFile("mtaext.mtaext", []byte( 1403 `'_schema-version: '3.1' 1404 ID: test.ext 1405 extends: test 1406 parameters 1407 test-credentials1: "<%= testCred1 %>" 1408 test-credentials2: "<%=testCred2%>`)) 1409 _, _, err := handleMtaExtensionCredentials("mtaext.mtaext", 1410 map[string]interface{}{ 1411 "test.*Cred1": "myCredEnvVar1", 1412 }, 1413 ) 1414 assert.EqualError(t, err, "credential key name 'test.*Cred1' contains unsupported character. Must contain only ^[-_A-Za-z0-9]+$") 1415 }) 1416 1417 t.Run("unresolved placeholders does not cause an error", func(t *testing.T) { 1418 // we emit a log message, but it does not fail 1419 filesMock.AddFile("mtaext-unresolved.mtaext", []byte("<%= unresolved %>")) 1420 updated, containsUnresolved, err := handleMtaExtensionCredentials("mtaext-unresolved.mtaext", map[string]interface{}{}) 1421 assert.True(t, containsUnresolved) 1422 assert.False(t, updated) 1423 assert.NoError(t, err) 1424 }) 1425 1426 t.Run("replace straight forward", func(t *testing.T) { 1427 mtaFileName := "mtaext.mtaext" 1428 filesMock.AddFile(mtaFileName, []byte( 1429 `'_schema-version: '3.1' 1430 ID: test.ext 1431 extends: test 1432 parameters 1433 test-credentials1: "<%= testCred1 %>" 1434 test-credentials2: "<%=testCred2%>" 1435 test-credentials3: "<%= testCred2%>" 1436 test-credentials4: "<%=testCred2 %>" 1437 test-credentials5: "<%= testCred2 %>"`)) 1438 updated, containsUnresolved, err := handleMtaExtensionCredentials( 1439 mtaFileName, 1440 map[string]interface{}{ 1441 "testCred1": "myCredEnvVar1", 1442 "testCred2": "myCredEnvVar2", 1443 }, 1444 ) 1445 if assert.NoError(t, err) { 1446 b, e := fileUtils.FileRead(mtaFileName) 1447 if e != nil { 1448 assert.Fail(t, "Cannot read mta extension file: %v", e) 1449 } 1450 content := string(b) 1451 assert.Contains(t, content, "test-credentials1: \"**$0****\"") 1452 assert.Contains(t, content, "test-credentials2: \"++$1++++\"") 1453 assert.Contains(t, content, "test-credentials3: \"++$1++++\"") 1454 assert.Contains(t, content, "test-credentials4: \"++$1++++\"") 1455 assert.Contains(t, content, "test-credentials5: \"++$1++++\"") 1456 1457 assert.True(t, updated) 1458 assert.False(t, containsUnresolved) 1459 } 1460 }) 1461 } 1462 1463 func TestEnvVarKeyModification(t *testing.T) { 1464 envVarCompatibleKey := toEnvVarKey("Mta.EXtensionCredential~Credential_Id1Abc") 1465 assert.Equal(t, "MTA_EXTENSION_CREDENTIAL_CREDENTIAL_ID1_ABC", envVarCompatibleKey) 1466 }