github.com/SAP/jenkins-library@v1.362.0/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) GetConfigProvider() (orchestrator.ConfigProvider, 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{BuildTool: "maven", FilePath: ""}, 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  }