github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/cmd/artifactPrepareVersion_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package cmd 5 6 import ( 7 "fmt" 8 "net/http" 9 "testing" 10 "time" 11 12 "github.com/SAP/jenkins-library/pkg/mock" 13 "github.com/SAP/jenkins-library/pkg/orchestrator" 14 "github.com/SAP/jenkins-library/pkg/telemetry" 15 "github.com/SAP/jenkins-library/pkg/versioning" 16 17 "github.com/ghodss/yaml" 18 "github.com/stretchr/testify/assert" 19 "helm.sh/helm/v3/pkg/chart" 20 21 "github.com/go-git/go-git/v5" 22 gitConfig "github.com/go-git/go-git/v5/config" 23 "github.com/go-git/go-git/v5/plumbing" 24 "github.com/go-git/go-git/v5/plumbing/object" 25 gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" 26 "github.com/go-git/go-git/v5/plumbing/transport/ssh" 27 ) 28 29 type artifactVersioningMock struct { 30 originalVersion string 31 newVersion string 32 getVersionError string 33 setVersionError string 34 versioningScheme string 35 coordinates versioning.Coordinates 36 coordinatesError error 37 } 38 39 func (a *artifactVersioningMock) VersioningScheme() string { 40 return a.versioningScheme 41 } 42 43 func (a *artifactVersioningMock) GetVersion() (string, error) { 44 if len(a.getVersionError) > 0 { 45 return "", fmt.Errorf(a.getVersionError) 46 } 47 return a.originalVersion, nil 48 } 49 50 func (a *artifactVersioningMock) SetVersion(version string) error { 51 if len(a.setVersionError) > 0 { 52 return fmt.Errorf(a.setVersionError) 53 } 54 a.newVersion = version 55 return nil 56 } 57 58 func (a *artifactVersioningMock) GetCoordinates() (versioning.Coordinates, error) { 59 if a.coordinatesError != nil { 60 return versioning.Coordinates{}, a.coordinatesError 61 } 62 return a.coordinates, nil 63 } 64 65 type gitRepositoryMock struct { 66 createRemoteConfigs []*gitConfig.RemoteConfig 67 createRemoteCalls int 68 createRemoteError []string 69 deleteRemoteNames []string 70 deleteRemoteCalls int 71 deleteRemoteError []string 72 pushCalled bool 73 pushOptions *git.PushOptions 74 pushError string 75 remote *git.Remote 76 remoteError string 77 revision string 78 revisionHash plumbing.Hash 79 revisionError string 80 tag string 81 tagHash plumbing.Hash 82 tagError string 83 worktree *git.Worktree 84 worktreeError string 85 commitObjectHash string 86 } 87 88 func (r *gitRepositoryMock) CommitObject(hash plumbing.Hash) (*object.Commit, error) { 89 r.commitObjectHash = hash.String() 90 return &object.Commit{Hash: hash, Message: "Test commit message"}, nil 91 } 92 93 func (r *gitRepositoryMock) CreateTag(name string, hash plumbing.Hash, opts *git.CreateTagOptions) (*plumbing.Reference, error) { 94 if len(r.tagError) > 0 { 95 return nil, fmt.Errorf(r.tagError) 96 } 97 r.tag = name 98 r.tagHash = hash 99 return nil, nil 100 } 101 102 func (r *gitRepositoryMock) CreateRemote(config *gitConfig.RemoteConfig) (*git.Remote, error) { 103 r.createRemoteCalls++ 104 if len(r.createRemoteError) >= r.createRemoteCalls && len(r.createRemoteError[r.createRemoteCalls-1]) > 0 { 105 return nil, fmt.Errorf(r.createRemoteError[r.createRemoteCalls-1]) 106 } 107 r.createRemoteConfigs = append(r.createRemoteConfigs, config) 108 return nil, nil 109 } 110 111 func (r *gitRepositoryMock) DeleteRemote(name string) error { 112 r.deleteRemoteCalls++ 113 if len(r.deleteRemoteError) >= r.deleteRemoteCalls && len(r.deleteRemoteError[r.deleteRemoteCalls-1]) > 0 { 114 return fmt.Errorf(r.deleteRemoteError[r.deleteRemoteCalls-1]) 115 } 116 r.deleteRemoteNames = append(r.deleteRemoteNames, name) 117 return nil 118 } 119 120 func (r *gitRepositoryMock) Push(o *git.PushOptions) error { 121 if len(r.pushError) > 0 { 122 return fmt.Errorf(r.pushError) 123 } 124 r.pushCalled = true 125 r.pushOptions = o 126 return nil 127 } 128 129 func (r *gitRepositoryMock) Remote(name string) (*git.Remote, error) { 130 if len(r.remoteError) > 0 { 131 return &git.Remote{}, fmt.Errorf(r.remoteError) 132 } 133 return r.remote, nil 134 } 135 136 func (r *gitRepositoryMock) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, error) { 137 if len(r.revisionError) > 0 { 138 return nil, fmt.Errorf(r.revisionError) 139 } 140 r.revision = rev.String() 141 return &r.revisionHash, nil 142 } 143 144 func (r *gitRepositoryMock) Worktree() (*git.Worktree, error) { 145 if len(r.worktreeError) > 0 { 146 return nil, fmt.Errorf(r.worktreeError) 147 } 148 return r.worktree, nil 149 } 150 151 type gitWorktreeMock struct { 152 checkoutError string 153 checkoutOpts *git.CheckoutOptions 154 commitHash plumbing.Hash 155 commitMsg string 156 commitOpts *git.CommitOptions 157 commitError string 158 } 159 160 func (w *gitWorktreeMock) Checkout(opts *git.CheckoutOptions) error { 161 if len(w.checkoutError) > 0 { 162 return fmt.Errorf(w.checkoutError) 163 } 164 w.checkoutOpts = opts 165 return nil 166 } 167 func (w *gitWorktreeMock) Commit(msg string, opts *git.CommitOptions) (plumbing.Hash, error) { 168 if len(w.commitError) > 0 { 169 return plumbing.Hash{}, fmt.Errorf(w.commitError) 170 } 171 w.commitMsg = msg 172 w.commitOpts = opts 173 return w.commitHash, nil 174 } 175 176 type artifactPrepareVersionMockUtils struct { 177 *mock.ExecMockRunner 178 *mock.FilesMock 179 *mock.HttpClientMock 180 } 181 182 func newArtifactPrepareVersionMockUtils() *artifactPrepareVersionMockUtils { 183 utils := artifactPrepareVersionMockUtils{ 184 ExecMockRunner: &mock.ExecMockRunner{}, 185 FilesMock: &mock.FilesMock{}, 186 } 187 return &utils 188 } 189 190 func (a *artifactPrepareVersionMockUtils) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error { 191 // so far no dedicated logic required for testing 192 return nil 193 } 194 195 func (a *artifactPrepareVersionMockUtils) NewOrchestratorSpecificConfigProvider() (orchestrator.OrchestratorSpecificConfigProviding, error) { 196 return &orchestrator.UnknownOrchestratorConfigProvider{}, nil 197 } 198 199 func TestRunArtifactPrepareVersion(t *testing.T) { 200 201 t.Run("success case - cloud", func(t *testing.T) { 202 203 config := artifactPrepareVersionOptions{ 204 BuildTool: "maven", 205 IncludeCommitID: true, 206 Password: "****", 207 TagPrefix: "v", 208 Username: "testUser", 209 VersioningType: "cloud", 210 } 211 telemetryData := telemetry.CustomData{} 212 213 cpe := artifactPrepareVersionCommonPipelineEnvironment{} 214 215 versioningMock := artifactVersioningMock{ 216 originalVersion: "1.2.3", 217 versioningScheme: "maven", 218 } 219 220 utils := newArtifactPrepareVersionMockUtils() 221 222 worktree := gitWorktreeMock{ 223 commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{2, 3, 4}), 224 } 225 226 conf := gitConfig.RemoteConfig{Name: "origin", URLs: []string{"https://my.test.server"}} 227 228 repo := gitRepositoryMock{ 229 revisionHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3}), 230 remote: git.NewRemote(nil, &conf), 231 } 232 233 err := runArtifactPrepareVersion(&config, &telemetryData, &cpe, &versioningMock, utils, &repo, func(r gitRepository) (gitWorktree, error) { return &worktree, nil }) 234 235 assert.NoError(t, err) 236 237 assert.Contains(t, versioningMock.newVersion, "1.2.3") 238 assert.Contains(t, versioningMock.newVersion, fmt.Sprintf("_%v", repo.revisionHash.String())) 239 240 assert.Equal(t, "HEAD", repo.revision) 241 assert.Contains(t, repo.tag, "v1.2.3") 242 assert.Equal(t, &git.CheckoutOptions{Hash: repo.revisionHash, Keep: true}, worktree.checkoutOpts) 243 assert.True(t, repo.pushCalled) 244 245 assert.Contains(t, cpe.artifactVersion, "1.2.3") 246 assert.Contains(t, cpe.originalArtifactVersion, "1.2.3") 247 assert.Equal(t, worktree.commitHash.String(), cpe.git.commitID) 248 assert.Equal(t, "Test commit message", cpe.git.commitMessage) 249 250 assert.Equal(t, telemetry.CustomData{Custom1Label: "buildTool", Custom1: "maven", Custom2Label: "filePath", Custom2: ""}, telemetryData) 251 }) 252 253 t.Run("success case - cloud_noTag", func(t *testing.T) { 254 255 config := artifactPrepareVersionOptions{ 256 BuildTool: "maven", 257 IncludeCommitID: true, 258 Password: "****", 259 TagPrefix: "v", 260 Username: "testUser", 261 VersioningType: "cloud_noTag", 262 } 263 telemetryData := telemetry.CustomData{} 264 265 cpe := artifactPrepareVersionCommonPipelineEnvironment{} 266 267 versioningMock := artifactVersioningMock{ 268 originalVersion: "1.2.3", 269 versioningScheme: "maven", 270 } 271 272 utils := newArtifactPrepareVersionMockUtils() 273 274 worktree := gitWorktreeMock{ 275 commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{2, 3, 4}), 276 } 277 278 conf := gitConfig.RemoteConfig{Name: "origin", URLs: []string{"https://my.test.server"}} 279 280 repo := gitRepositoryMock{ 281 revisionHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3}), 282 remote: git.NewRemote(nil, &conf), 283 } 284 285 err := runArtifactPrepareVersion(&config, &telemetryData, &cpe, &versioningMock, utils, &repo, func(r gitRepository) (gitWorktree, error) { return &worktree, nil }) 286 287 assert.NoError(t, err) 288 289 assert.False(t, repo.pushCalled) 290 assert.Contains(t, cpe.artifactVersion, "1.2.3") 291 assert.Contains(t, cpe.originalArtifactVersion, "1.2.3") 292 assert.Equal(t, repo.revisionHash.String(), cpe.git.commitID) 293 }) 294 295 t.Run("success case - compatibility", func(t *testing.T) { 296 config := artifactPrepareVersionOptions{ 297 BuildTool: "maven", 298 VersioningType: "cloud", 299 VersioningTemplate: "${version}", 300 } 301 302 cpe := artifactPrepareVersionCommonPipelineEnvironment{} 303 304 versioningMock := artifactVersioningMock{ 305 originalVersion: "1.2.3", 306 versioningScheme: "maven", 307 } 308 309 worktree := gitWorktreeMock{} 310 repo := gitRepositoryMock{} 311 312 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, &cpe, &versioningMock, nil, &repo, func(r gitRepository) (gitWorktree, error) { return &worktree, nil }) 313 314 assert.NoError(t, err) 315 assert.Equal(t, "1.2.3", cpe.artifactVersion) 316 }) 317 318 t.Run("success case - library", func(t *testing.T) { 319 config := artifactPrepareVersionOptions{ 320 BuildTool: "maven", 321 VersioningType: "library", 322 } 323 324 cpe := artifactPrepareVersionCommonPipelineEnvironment{} 325 326 versioningMock := artifactVersioningMock{ 327 originalVersion: "1.2.3", 328 versioningScheme: "maven", 329 } 330 331 worktree := gitWorktreeMock{ 332 commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{2, 3, 4}), 333 } 334 repo := gitRepositoryMock{ 335 revisionHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3}), 336 } 337 338 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, &cpe, &versioningMock, nil, &repo, func(r gitRepository) (gitWorktree, error) { return &worktree, nil }) 339 340 assert.NoError(t, err) 341 assert.Equal(t, "1.2.3", cpe.artifactVersion) 342 assert.Equal(t, repo.revisionHash.String(), cpe.git.commitID) 343 }) 344 345 t.Run("success case - coordinates", func(t *testing.T) { 346 config := artifactPrepareVersionOptions{ 347 BuildTool: "maven", 348 VersioningType: "library", 349 FetchCoordinates: true, 350 } 351 352 cpe := artifactPrepareVersionCommonPipelineEnvironment{} 353 354 versioningMock := artifactVersioningMock{ 355 originalVersion: "1.2.3", 356 versioningScheme: "maven", 357 coordinates: versioning.Coordinates{GroupID: "my.testGroup", ArtifactID: "testArtifact", Packaging: "testPackaging"}, 358 } 359 360 worktree := gitWorktreeMock{ 361 commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{2, 3, 4}), 362 } 363 repo := gitRepositoryMock{ 364 revisionHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3}), 365 } 366 367 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, &cpe, &versioningMock, nil, &repo, func(r gitRepository) (gitWorktree, error) { return &worktree, nil }) 368 369 assert.NoError(t, err) 370 assert.Equal(t, "testArtifact", cpe.artifactID) 371 assert.Equal(t, "my.testGroup", cpe.groupID) 372 assert.Equal(t, "testPackaging", cpe.packaging) 373 }) 374 375 t.Run("error - failed to retrieve version", func(t *testing.T) { 376 config := artifactPrepareVersionOptions{} 377 378 versioningMock := artifactVersioningMock{ 379 getVersionError: "getVersion error", 380 } 381 382 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, nil, &versioningMock, nil, nil, nil) 383 assert.EqualError(t, err, "failed to retrieve version: getVersion error") 384 385 }) 386 387 t.Run("error - failed to retrieve git commit ID", func(t *testing.T) { 388 config := artifactPrepareVersionOptions{} 389 390 versioningMock := artifactVersioningMock{ 391 originalVersion: "1.2.3", 392 versioningScheme: "maven", 393 } 394 395 repo := gitRepositoryMock{revisionError: "revision error"} 396 397 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, nil, &versioningMock, nil, &repo, nil) 398 assert.EqualError(t, err, "failed to retrieve git commit ID: revision error") 399 }) 400 401 t.Run("error - versioning template", func(t *testing.T) { 402 config := artifactPrepareVersionOptions{ 403 VersioningType: "cloud", 404 } 405 406 versioningMock := artifactVersioningMock{ 407 originalVersion: "1.2.3", 408 versioningScheme: "notSupported", 409 } 410 411 utils := newArtifactPrepareVersionMockUtils() 412 413 repo := gitRepositoryMock{} 414 415 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, &artifactPrepareVersionCommonPipelineEnvironment{}, &versioningMock, utils, &repo, nil) 416 assert.Contains(t, fmt.Sprint(err), "failed to get versioning template for scheme 'notSupported'") 417 }) 418 419 t.Run("error - failed to retrieve git worktree", func(t *testing.T) { 420 config := artifactPrepareVersionOptions{ 421 VersioningType: "cloud", 422 } 423 424 versioningMock := artifactVersioningMock{ 425 originalVersion: "1.2.3", 426 versioningScheme: "maven", 427 } 428 429 utils := newArtifactPrepareVersionMockUtils() 430 431 repo := gitRepositoryMock{} 432 433 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, &artifactPrepareVersionCommonPipelineEnvironment{}, &versioningMock, utils, &repo, func(r gitRepository) (gitWorktree, error) { return nil, fmt.Errorf("worktree error") }) 434 assert.EqualError(t, err, "failed to retrieve git worktree: worktree error") 435 }) 436 437 t.Run("error - failed to initialize git worktree: ", func(t *testing.T) { 438 config := artifactPrepareVersionOptions{ 439 VersioningType: "cloud", 440 } 441 442 versioningMock := artifactVersioningMock{ 443 originalVersion: "1.2.3", 444 versioningScheme: "maven", 445 } 446 447 utils := newArtifactPrepareVersionMockUtils() 448 449 worktree := gitWorktreeMock{checkoutError: "checkout error"} 450 repo := gitRepositoryMock{} 451 452 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, &artifactPrepareVersionCommonPipelineEnvironment{}, &versioningMock, utils, &repo, func(r gitRepository) (gitWorktree, error) { return &worktree, nil }) 453 assert.EqualError(t, err, "failed to initialize worktree: checkout error") 454 }) 455 456 t.Run("error - failed to set version", func(t *testing.T) { 457 config := artifactPrepareVersionOptions{ 458 VersioningType: "cloud", 459 } 460 461 versioningMock := artifactVersioningMock{ 462 originalVersion: "1.2.3", 463 setVersionError: "setVersion error", 464 versioningScheme: "maven", 465 } 466 467 utils := newArtifactPrepareVersionMockUtils() 468 469 worktree := gitWorktreeMock{} 470 repo := gitRepositoryMock{} 471 472 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, &artifactPrepareVersionCommonPipelineEnvironment{}, &versioningMock, utils, &repo, func(r gitRepository) (gitWorktree, error) { return &worktree, nil }) 473 assert.EqualError(t, err, "failed to write version: setVersion error") 474 }) 475 476 t.Run("error - failed to push changes", func(t *testing.T) { 477 config := artifactPrepareVersionOptions{ 478 VersioningType: "cloud", 479 } 480 481 versioningMock := artifactVersioningMock{ 482 originalVersion: "1.2.3", 483 versioningScheme: "maven", 484 } 485 486 utils := newArtifactPrepareVersionMockUtils() 487 488 worktree := gitWorktreeMock{} 489 repo := gitRepositoryMock{} 490 491 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, &artifactPrepareVersionCommonPipelineEnvironment{}, &versioningMock, utils, &repo, func(r gitRepository) (gitWorktree, error) { return &worktree, nil }) 492 assert.Contains(t, fmt.Sprint(err), "failed to push changes for version '1.2.3") 493 }) 494 495 t.Run("error - failed to get coordinates", func(t *testing.T) { 496 config := artifactPrepareVersionOptions{ 497 BuildTool: "maven", 498 VersioningType: "library", 499 FetchCoordinates: true, 500 } 501 502 cpe := artifactPrepareVersionCommonPipelineEnvironment{} 503 504 versioningMock := artifactVersioningMock{ 505 originalVersion: "1.2.3", 506 versioningScheme: "maven", 507 coordinatesError: fmt.Errorf("coordinatesError"), 508 } 509 510 utils := newArtifactPrepareVersionMockUtils() 511 512 worktree := gitWorktreeMock{ 513 commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{2, 3, 4}), 514 } 515 repo := gitRepositoryMock{ 516 revisionHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3}), 517 } 518 519 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, &cpe, &versioningMock, utils, &repo, func(r gitRepository) (gitWorktree, error) { return &worktree, nil }) 520 521 assert.EqualError(t, err, "failed to get coordinates: coordinatesError") 522 }) 523 524 t.Run("warning - failed to get coordinates", func(t *testing.T) { 525 config := artifactPrepareVersionOptions{ 526 BuildTool: "maven", 527 VersioningType: "library", 528 FetchCoordinates: false, 529 } 530 531 cpe := artifactPrepareVersionCommonPipelineEnvironment{} 532 533 versioningMock := artifactVersioningMock{ 534 originalVersion: "1.2.3", 535 versioningScheme: "maven", 536 coordinatesError: fmt.Errorf("coordinatesError"), 537 } 538 539 utils := newArtifactPrepareVersionMockUtils() 540 541 worktree := gitWorktreeMock{ 542 commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{2, 3, 4}), 543 } 544 repo := gitRepositoryMock{ 545 revisionHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3}), 546 } 547 548 err := runArtifactPrepareVersion(&config, &telemetry.CustomData{}, &cpe, &versioningMock, utils, &repo, func(r gitRepository) (gitWorktree, error) { return &worktree, nil }) 549 550 assert.NoError(t, err) 551 }) 552 } 553 554 func TestVersioningTemplate(t *testing.T) { 555 tt := []struct { 556 scheme string 557 expected string 558 expectedErr string 559 }{ 560 {scheme: "maven", expected: "{{.Version}}{{if .Timestamp}}-{{.Timestamp}}{{if .CommitID}}_{{.CommitID}}{{end}}{{end}}"}, 561 {scheme: "semver2", expected: "{{.Version}}{{if .Timestamp}}-{{.Timestamp}}{{if .CommitID}}+{{.CommitID}}{{end}}{{end}}"}, 562 {scheme: "pep440", expected: "{{.Version}}{{if .Timestamp}}.{{.Timestamp}}{{if .CommitID}}+{{.CommitID}}{{end}}{{end}}"}, 563 {scheme: "notSupported", expected: "", expectedErr: "versioning scheme 'notSupported' not supported"}, 564 } 565 566 for _, test := range tt { 567 scheme, err := versioningTemplate(test.scheme) 568 assert.Equal(t, test.expected, scheme) 569 if len(test.expectedErr) == 0 { 570 assert.NoError(t, err) 571 } else { 572 assert.EqualError(t, err, test.expectedErr) 573 } 574 } 575 } 576 577 func TestCalculateNewVersion(t *testing.T) { 578 579 currentVersion := "1.2.3" 580 testTime := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) 581 582 commitID := plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3}).String() 583 584 tt := []struct { 585 versioningTemplate string 586 includeCommitID bool 587 shortCommitID bool 588 unixTimestamp bool 589 expected string 590 expectedErr string 591 }{ 592 {versioningTemplate: "", expectedErr: "failed calculate version, new version is ''"}, 593 {versioningTemplate: "{{.Version}}{{if .Timestamp}}-{{.Timestamp}}{{if .CommitID}}+{{.CommitID}}{{end}}{{end}}", expected: "1.2.3-20200101000000"}, 594 {versioningTemplate: "{{.Version}}{{if .Timestamp}}-{{.Timestamp}}{{if .CommitID}}+{{.CommitID}}{{end}}{{end}}", includeCommitID: true, expected: "1.2.3-20200101000000+428ecf70bc22df0ba3dcf194b5ce53e769abab07"}, 595 {versioningTemplate: "{{.Version}}{{if .Timestamp}}-{{.Timestamp}}{{if .CommitID}}+{{.CommitID}}{{end}}{{end}}", includeCommitID: true, shortCommitID: true, expected: "1.2.3-20200101000000+428ecf7"}, 596 {versioningTemplate: "{{.Version}}{{if .Timestamp}}-{{.Timestamp}}{{if .CommitID}}+{{.CommitID}}{{end}}{{end}}", includeCommitID: true, unixTimestamp: true, expected: "1.2.3-1577836800+428ecf70bc22df0ba3dcf194b5ce53e769abab07"}, 597 } 598 599 for _, test := range tt { 600 version, err := calculateNewVersion(test.versioningTemplate, currentVersion, commitID, test.includeCommitID, test.shortCommitID, test.unixTimestamp, testTime) 601 assert.Equal(t, test.expected, version) 602 if len(test.expectedErr) == 0 { 603 assert.NoError(t, err) 604 } else { 605 assert.EqualError(t, err, test.expectedErr) 606 } 607 } 608 } 609 610 func TestPushChanges(t *testing.T) { 611 612 newVersion := "1.2.3" 613 testTime := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) 614 615 conf := gitConfig.RemoteConfig{Name: "origin", URLs: []string{"https://my.test.server"}} 616 remote := git.NewRemote(nil, &conf) 617 618 t.Run("success - username/password", func(t *testing.T) { 619 config := artifactPrepareVersionOptions{Username: "testUser", Password: "****", CommitUserName: "Project Piper"} 620 repo := gitRepositoryMock{remote: remote} 621 worktree := gitWorktreeMock{commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3})} 622 623 commitID, err := pushChanges(&config, newVersion, &repo, &worktree, testTime, nil) 624 assert.NoError(t, err) 625 assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", commitID) 626 assert.Equal(t, "update version 1.2.3", worktree.commitMsg) 627 assert.Equal(t, &git.CommitOptions{All: true, Author: &object.Signature{Name: "Project Piper", When: testTime}}, worktree.commitOpts) 628 assert.Equal(t, "1.2.3", repo.tag) 629 assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", repo.tagHash.String()) 630 assert.Equal(t, &git.PushOptions{RefSpecs: []gitConfig.RefSpec{"refs/tags/1.2.3:refs/tags/1.2.3"}, Auth: &gitHttp.BasicAuth{Username: config.Username, Password: config.Password}}, repo.pushOptions) 631 }) 632 633 t.Run("success - ssh fallback", func(t *testing.T) { 634 config := artifactPrepareVersionOptions{CommitUserName: "Project Piper"} 635 repo := gitRepositoryMock{remote: remote} 636 worktree := gitWorktreeMock{commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3})} 637 customCerts := []byte("custom certs") 638 639 originalSSHAgentAuth := sshAgentAuth 640 sshAgentAuth = func(u string) (*ssh.PublicKeysCallback, error) { return &ssh.PublicKeysCallback{}, nil } 641 commitID, err := pushChanges(&config, newVersion, &repo, &worktree, testTime, customCerts) 642 sshAgentAuth = originalSSHAgentAuth 643 644 assert.NoError(t, err) 645 assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", commitID) 646 assert.Equal(t, "update version 1.2.3", worktree.commitMsg) 647 assert.Equal(t, &git.CommitOptions{All: true, Author: &object.Signature{Name: "Project Piper", When: testTime}}, worktree.commitOpts) 648 assert.Equal(t, "1.2.3", repo.tag) 649 assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", repo.tagHash.String()) 650 assert.Equal(t, &git.PushOptions{RefSpecs: []gitConfig.RefSpec{"refs/tags/1.2.3:refs/tags/1.2.3"}, Auth: &ssh.PublicKeysCallback{}, CABundle: customCerts}, repo.pushOptions) 651 }) 652 653 t.Run("success - ssh", func(t *testing.T) { 654 confSSH := gitConfig.RemoteConfig{Name: "origin", URLs: []string{"git@my.test.server"}} 655 remoteSSH := git.NewRemote(nil, &confSSH) 656 657 config := artifactPrepareVersionOptions{} 658 repo := gitRepositoryMock{remote: remoteSSH} 659 worktree := gitWorktreeMock{commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3})} 660 661 originalSSHAgentAuth := sshAgentAuth 662 sshAgentAuth = func(u string) (*ssh.PublicKeysCallback, error) { return &ssh.PublicKeysCallback{}, nil } 663 commitID, err := pushChanges(&config, newVersion, &repo, &worktree, testTime, nil) 664 sshAgentAuth = originalSSHAgentAuth 665 666 assert.NoError(t, err) 667 assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", commitID) 668 assert.Equal(t, &git.PushOptions{RefSpecs: []gitConfig.RefSpec{"refs/tags/1.2.3:refs/tags/1.2.3"}, Auth: &ssh.PublicKeysCallback{}}, repo.pushOptions) 669 }) 670 671 t.Run("error - commit", func(t *testing.T) { 672 config := artifactPrepareVersionOptions{} 673 repo := gitRepositoryMock{} 674 worktree := gitWorktreeMock{commitError: "commit error", commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3})} 675 676 commitID, err := pushChanges(&config, newVersion, &repo, &worktree, testTime, nil) 677 assert.Equal(t, "0000000000000000000000000000000000000000", commitID) 678 assert.EqualError(t, err, "failed to commit new version: commit error") 679 }) 680 681 t.Run("error - create tag", func(t *testing.T) { 682 config := artifactPrepareVersionOptions{} 683 repo := gitRepositoryMock{tagError: "tag error"} 684 worktree := gitWorktreeMock{commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3})} 685 686 commitID, err := pushChanges(&config, newVersion, &repo, &worktree, testTime, nil) 687 assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", commitID) 688 assert.EqualError(t, err, "tag error") 689 }) 690 691 t.Run("error - no remote url", func(t *testing.T) { 692 config := artifactPrepareVersionOptions{} 693 repo := gitRepositoryMock{} 694 worktree := gitWorktreeMock{commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3})} 695 696 commitID, err := pushChanges(&config, newVersion, &repo, &worktree, testTime, nil) 697 assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", commitID) 698 assert.EqualError(t, err, "no remote url maintained") 699 }) 700 701 t.Run("error - ssh fallback", func(t *testing.T) { 702 703 config := artifactPrepareVersionOptions{} 704 worktree := gitWorktreeMock{commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3})} 705 706 sshSuccess := func(u string) (*ssh.PublicKeysCallback, error) { return nil, nil } 707 sshFailure := func(u string) (*ssh.PublicKeysCallback, error) { return nil, fmt.Errorf("ssh error") } 708 709 tt := []struct { 710 repo gitRepositoryMock 711 sshAgentAuth func(string) (*ssh.PublicKeysCallback, error) 712 expectedError string 713 }{ 714 {repo: gitRepositoryMock{remote: remote, deleteRemoteError: []string{"delete error"}}, sshAgentAuth: sshSuccess, expectedError: "failed to update remote origin - remove: delete error"}, 715 {repo: gitRepositoryMock{remote: remote, createRemoteError: []string{"update error"}}, sshAgentAuth: sshSuccess, expectedError: "failed to update remote origin - create: update error"}, 716 {repo: gitRepositoryMock{remote: remote}, sshAgentAuth: sshFailure, expectedError: "failed to retrieve ssh authentication: ssh error"}, 717 {repo: gitRepositoryMock{remote: remote, deleteRemoteError: []string{"", "delete error"}}, sshAgentAuth: sshSuccess, expectedError: "failed to restore remote origin - remove: delete error"}, 718 {repo: gitRepositoryMock{remote: remote, createRemoteError: []string{"", "update error"}}, sshAgentAuth: sshSuccess, expectedError: "failed to restore remote origin - create: update error"}, 719 } 720 721 originalSSHAgentAuth := sshAgentAuth 722 723 for _, test := range tt { 724 sshAgentAuth = test.sshAgentAuth 725 commitID, err := pushChanges(&config, newVersion, &test.repo, &worktree, testTime, nil) 726 sshAgentAuth = originalSSHAgentAuth 727 728 assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", commitID) 729 assert.EqualError(t, err, test.expectedError) 730 } 731 }) 732 733 t.Run("error - push", func(t *testing.T) { 734 config := artifactPrepareVersionOptions{Username: "testUser", Password: "****"} 735 repo := gitRepositoryMock{remote: remote, pushError: "push error"} 736 worktree := gitWorktreeMock{commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3})} 737 738 commitID, err := pushChanges(&config, newVersion, &repo, &worktree, testTime, nil) 739 assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", commitID) 740 assert.EqualError(t, err, "push error") 741 }) 742 } 743 744 func TestTemplateCompatibility(t *testing.T) { 745 tt := []struct { 746 groovy string 747 versioningType string 748 timestamp bool 749 commitID bool 750 }{ 751 {groovy: `${version}`, versioningType: "library", timestamp: false, commitID: false}, 752 {groovy: `${version}-${timestamp}`, versioningType: "cloud", timestamp: true, commitID: false}, 753 {groovy: `${version}-${timestamp}${commitId?"_"+commitId:""`, versioningType: "cloud", timestamp: true, commitID: true}, 754 } 755 756 for _, test := range tt { 757 versioningType, timestamp, commitID := templateCompatibility(test.groovy) 758 assert.Equal(t, test.versioningType, versioningType) 759 assert.Equal(t, test.timestamp, timestamp) 760 assert.Equal(t, test.commitID, commitID) 761 } 762 } 763 764 func TestConvertHTTPToSSHURL(t *testing.T) { 765 tt := []struct { 766 httpURL string 767 expected string 768 }{ 769 {httpURL: "https://my.test.server/owner/repo.git", expected: "git@my.test.server:owner/repo.git"}, 770 } 771 772 for _, test := range tt { 773 assert.Equal(t, test.expected, convertHTTPToSSHURL(test.httpURL)) 774 } 775 } 776 777 func TestPropagateVersion(t *testing.T) { 778 t.Parallel() 779 780 gitCommitID := "theGitCommitId" 781 testTime := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) //20200101000000 782 783 t.Run("success case", func(t *testing.T) { 784 config := artifactPrepareVersionOptions{ 785 VersioningType: "cloud", 786 AdditionalTargetTools: []string{"helm"}, 787 } 788 789 chartMetadata := chart.Metadata{Version: "1.2.3"} 790 content, err := yaml.Marshal(chartMetadata) 791 assert.NoError(t, err) 792 793 utils := newArtifactPrepareVersionMockUtils() 794 utils.AddFile("myChart/Chart.yaml", content) 795 artifactOpts := versioning.Options{} 796 797 err = propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime) 798 assert.NoError(t, err) 799 }) 800 801 t.Run("success case - dedicated build descriptors", func(t *testing.T) { 802 config := artifactPrepareVersionOptions{ 803 VersioningType: "cloud", 804 AdditionalTargetTools: []string{"helm"}, 805 AdditionalTargetDescriptors: []string{"myChart/Chart.yaml"}, 806 IncludeCommitID: true, 807 } 808 809 chartMetadata := chart.Metadata{Version: "1.2.3"} 810 content, err := yaml.Marshal(chartMetadata) 811 assert.NoError(t, err) 812 813 utils := newArtifactPrepareVersionMockUtils() 814 utils.AddFile("myChart/Chart.yaml", content) 815 artifactOpts := versioning.Options{} 816 817 err = propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime) 818 assert.NoError(t, err) 819 820 chartContent, err := utils.FileRead("myChart/Chart.yaml") 821 assert.NoError(t, err) 822 chartMeta := chart.Metadata{} 823 err = yaml.Unmarshal(chartContent, &chartMeta) 824 assert.NoError(t, err) 825 826 assert.Equal(t, "1.2.4-20200101000000_theGitCommitId", chartMeta.AppVersion) 827 assert.Equal(t, "1.2.4-20200101000000+theGitCommitId", chartMeta.Version) 828 }) 829 830 t.Run("success case - dedicated build descriptors / no cloud", func(t *testing.T) { 831 config := artifactPrepareVersionOptions{ 832 VersioningType: "library", 833 AdditionalTargetTools: []string{"helm"}, 834 AdditionalTargetDescriptors: []string{"myChart/Chart.yaml"}, 835 } 836 837 chartMetadata := chart.Metadata{Version: "1.2.3"} 838 content, err := yaml.Marshal(chartMetadata) 839 assert.NoError(t, err) 840 841 utils := newArtifactPrepareVersionMockUtils() 842 utils.AddFile("myChart/Chart.yaml", content) 843 artifactOpts := versioning.Options{} 844 845 err = propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime) 846 assert.NoError(t, err) 847 848 chartContent, err := utils.FileRead("myChart/Chart.yaml") 849 assert.NoError(t, err) 850 chartMeta := chart.Metadata{} 851 err = yaml.Unmarshal(chartContent, &chartMeta) 852 assert.NoError(t, err) 853 854 assert.Equal(t, "1.2.4", chartMeta.AppVersion) 855 assert.Equal(t, "1.2.4", chartMeta.Version) 856 }) 857 858 t.Run("success case - noop", func(t *testing.T) { 859 config := artifactPrepareVersionOptions{} 860 utils := newArtifactPrepareVersionMockUtils() 861 artifactOpts := versioning.Options{} 862 863 err := propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime) 864 assert.NoError(t, err) 865 }) 866 867 t.Run("error case - wrong config", func(t *testing.T) { 868 config := artifactPrepareVersionOptions{ 869 AdditionalTargetDescriptors: []string{"pom.xml"}, 870 AdditionalTargetTools: []string{"maven", "helm"}, 871 } 872 utils := newArtifactPrepareVersionMockUtils() 873 artifactOpts := versioning.Options{} 874 875 err := propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime) 876 assert.EqualError(t, err, "additionalTargetDescriptors cannot have a different number of entries than additionalTargetTools") 877 }) 878 879 t.Run("error case - wrong target tool", func(t *testing.T) { 880 config := artifactPrepareVersionOptions{ 881 AdditionalTargetTools: []string{"notKnown"}, 882 } 883 utils := newArtifactPrepareVersionMockUtils() 884 artifactOpts := versioning.Options{} 885 886 err := propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime) 887 assert.Contains(t, fmt.Sprint(err), "failed to retrieve artifact") 888 }) 889 }