github.com/jaylevin/jenkins-library@v1.230.4/cmd/artifactPrepareVersion_test.go (about)

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