github.com/xgoffin/jenkins-library@v1.154.0/cmd/golangBuild_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    13  	"github.com/SAP/jenkins-library/pkg/mock"
    14  	"github.com/SAP/jenkins-library/pkg/multiarch"
    15  	"github.com/SAP/jenkins-library/pkg/telemetry"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  
    19  	"golang.org/x/mod/modfile"
    20  )
    21  
    22  type golangBuildMockUtils struct {
    23  	*mock.ExecMockRunner
    24  	*mock.FilesMock
    25  
    26  	returnFileUploadStatus int   // expected to be set upfront
    27  	returnFileUploadError  error // expected to be set upfront
    28  
    29  	clientOptions []piperhttp.ClientOptions // set by mock
    30  	fileUploads   map[string]string         // set by mock
    31  }
    32  
    33  func (g *golangBuildMockUtils) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error {
    34  	return fmt.Errorf("not implemented")
    35  }
    36  
    37  func (g *golangBuildMockUtils) GetRepositoryURL(module string) (string, error) {
    38  	return fmt.Sprintf("https://%s.git", module), nil
    39  }
    40  
    41  func (g *golangBuildMockUtils) SendRequest(method string, url string, r io.Reader, header http.Header, cookies []*http.Cookie) (*http.Response, error) {
    42  	return nil, fmt.Errorf("not implemented")
    43  }
    44  
    45  func (g *golangBuildMockUtils) SetOptions(options piperhttp.ClientOptions) {
    46  	g.clientOptions = append(g.clientOptions, options)
    47  }
    48  
    49  func (g *golangBuildMockUtils) UploadRequest(method, url, file, fieldName string, header http.Header, cookies []*http.Cookie, uploadType string) (*http.Response, error) {
    50  	g.fileUploads[file] = url
    51  
    52  	response := http.Response{
    53  		StatusCode: g.returnFileUploadStatus,
    54  	}
    55  
    56  	return &response, g.returnFileUploadError
    57  }
    58  
    59  func (g *golangBuildMockUtils) UploadFile(url, file, fieldName string, header http.Header, cookies []*http.Cookie, uploadType string) (*http.Response, error) {
    60  	return g.UploadRequest(http.MethodPut, url, file, fieldName, header, cookies, uploadType)
    61  }
    62  
    63  func (g *golangBuildMockUtils) Upload(data piperhttp.UploadRequestData) (*http.Response, error) {
    64  	return nil, fmt.Errorf("not implemented")
    65  }
    66  
    67  func (g *golangBuildMockUtils) getDockerImageValue(stepName string) (string, error) {
    68  	return "golang:latest", nil
    69  }
    70  
    71  func newGolangBuildTestsUtils() *golangBuildMockUtils {
    72  	utils := golangBuildMockUtils{
    73  		ExecMockRunner: &mock.ExecMockRunner{},
    74  		FilesMock:      &mock.FilesMock{},
    75  		//clientOptions:  []piperhttp.ClientOptions{},
    76  		fileUploads: map[string]string{},
    77  	}
    78  	return &utils
    79  }
    80  
    81  func TestRunGolangBuild(t *testing.T) {
    82  	cpe := golangBuildCommonPipelineEnvironment{}
    83  
    84  	t.Run("success - no tests", func(t *testing.T) {
    85  		config := golangBuildOptions{
    86  			TargetArchitectures: []string{"linux,amd64"},
    87  		}
    88  		utils := newGolangBuildTestsUtils()
    89  		telemetryData := telemetry.CustomData{}
    90  
    91  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
    92  		assert.NoError(t, err)
    93  		assert.Equal(t, "go", utils.ExecMockRunner.Calls[0].Exec)
    94  		assert.Equal(t, []string{"build", "-trimpath"}, utils.ExecMockRunner.Calls[0].Params)
    95  	})
    96  
    97  	t.Run("success - tests & ldflags", func(t *testing.T) {
    98  		config := golangBuildOptions{
    99  			RunTests:            true,
   100  			LdflagsTemplate:     "test",
   101  			Packages:            []string{"package/foo"},
   102  			TargetArchitectures: []string{"linux,amd64"},
   103  		}
   104  		utils := newGolangBuildTestsUtils()
   105  		telemetryData := telemetry.CustomData{}
   106  
   107  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   108  		assert.NoError(t, err)
   109  		assert.Equal(t, "go", utils.ExecMockRunner.Calls[0].Exec)
   110  		assert.Equal(t, []string{"install", "gotest.tools/gotestsum@latest"}, utils.ExecMockRunner.Calls[0].Params)
   111  		assert.Equal(t, "gotestsum", utils.ExecMockRunner.Calls[1].Exec)
   112  		assert.Equal(t, []string{"--junitfile", "TEST-go.xml", "--", fmt.Sprintf("-coverprofile=%v", coverageFile), "./..."}, utils.ExecMockRunner.Calls[1].Params)
   113  		assert.Equal(t, "go", utils.ExecMockRunner.Calls[2].Exec)
   114  		assert.Equal(t, []string{"build", "-trimpath", "-ldflags", "test", "package/foo"}, utils.ExecMockRunner.Calls[2].Params)
   115  	})
   116  
   117  	t.Run("success - tests with coverage", func(t *testing.T) {
   118  		config := golangBuildOptions{
   119  			RunTests:            true,
   120  			ReportCoverage:      true,
   121  			TargetArchitectures: []string{"linux,amd64"},
   122  		}
   123  		utils := newGolangBuildTestsUtils()
   124  		telemetryData := telemetry.CustomData{}
   125  
   126  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   127  		assert.NoError(t, err)
   128  		assert.Equal(t, "go", utils.ExecMockRunner.Calls[2].Exec)
   129  		assert.Equal(t, []string{"tool", "cover", "-html", coverageFile, "-o", "coverage.html"}, utils.ExecMockRunner.Calls[2].Params)
   130  	})
   131  
   132  	t.Run("success - integration tests", func(t *testing.T) {
   133  		config := golangBuildOptions{
   134  			RunIntegrationTests: true,
   135  			TargetArchitectures: []string{"linux,amd64"},
   136  		}
   137  		utils := newGolangBuildTestsUtils()
   138  		telemetryData := telemetry.CustomData{}
   139  
   140  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   141  		assert.NoError(t, err)
   142  		assert.Equal(t, "go", utils.ExecMockRunner.Calls[0].Exec)
   143  		assert.Equal(t, []string{"install", "gotest.tools/gotestsum@latest"}, utils.ExecMockRunner.Calls[0].Params)
   144  		assert.Equal(t, "gotestsum", utils.ExecMockRunner.Calls[1].Exec)
   145  		assert.Equal(t, []string{"--junitfile", "TEST-integration.xml", "--", "-tags=integration", "./..."}, utils.ExecMockRunner.Calls[1].Params)
   146  		assert.Equal(t, "go", utils.ExecMockRunner.Calls[2].Exec)
   147  		assert.Equal(t, []string{"build", "-trimpath"}, utils.ExecMockRunner.Calls[2].Params)
   148  	})
   149  
   150  	t.Run("success - publishes binaries", func(t *testing.T) {
   151  		config := golangBuildOptions{
   152  			TargetArchitectures:      []string{"linux,amd64"},
   153  			Output:                   "testBin",
   154  			Publish:                  true,
   155  			TargetRepositoryURL:      "https://my.target.repository.local",
   156  			TargetRepositoryUser:     "user",
   157  			TargetRepositoryPassword: "password",
   158  			ArtifactVersion:          "1.0.0",
   159  		}
   160  		utils := newGolangBuildTestsUtils()
   161  		utils.returnFileUploadStatus = 201
   162  		utils.FilesMock.AddFile("go.mod", []byte("module example.com/my/module"))
   163  		telemetryData := telemetry.CustomData{}
   164  
   165  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   166  		if assert.NoError(t, err) {
   167  			assert.Equal(t, "go", utils.ExecMockRunner.Calls[0].Exec)
   168  			assert.Equal(t, []string{"build", "-trimpath", "-o", "testBin-linux.amd64"}, utils.ExecMockRunner.Calls[0].Params)
   169  
   170  			assert.Equal(t, 1, len(utils.fileUploads))
   171  			assert.Equal(t, "https://my.target.repository.local/go/example.com/my/module/1.0.0/testBin-linux.amd64", utils.fileUploads["testBin-linux.amd64"])
   172  		}
   173  	})
   174  
   175  	t.Run("success - publishes binaries (when TargetRepositoryURL ends with slash)", func(t *testing.T) {
   176  		config := golangBuildOptions{
   177  			TargetArchitectures:      []string{"linux,amd64"},
   178  			Output:                   "testBin",
   179  			Publish:                  true,
   180  			TargetRepositoryURL:      "https://my.target.repository.local/",
   181  			TargetRepositoryUser:     "user",
   182  			TargetRepositoryPassword: "password",
   183  			ArtifactVersion:          "1.0.0",
   184  		}
   185  		utils := newGolangBuildTestsUtils()
   186  		utils.returnFileUploadStatus = 200
   187  		utils.FilesMock.AddFile("go.mod", []byte("module example.com/my/module"))
   188  		telemetryData := telemetry.CustomData{}
   189  
   190  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   191  		if assert.NoError(t, err) {
   192  			assert.Equal(t, "go", utils.ExecMockRunner.Calls[0].Exec)
   193  			assert.Equal(t, []string{"build", "-trimpath", "-o", "testBin-linux.amd64"}, utils.ExecMockRunner.Calls[0].Params)
   194  
   195  			assert.Equal(t, 1, len(utils.fileUploads))
   196  			assert.Equal(t, "https://my.target.repository.local/go/example.com/my/module/1.0.0/testBin-linux.amd64", utils.fileUploads["testBin-linux.amd64"])
   197  		}
   198  	})
   199  
   200  	t.Run("success - create BOM", func(t *testing.T) {
   201  		config := golangBuildOptions{
   202  			CreateBOM:           true,
   203  			TargetArchitectures: []string{"linux,amd64"},
   204  		}
   205  		utils := newGolangBuildTestsUtils()
   206  		telemetryData := telemetry.CustomData{}
   207  
   208  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   209  		assert.NoError(t, err)
   210  		assert.Equal(t, 3, len(utils.ExecMockRunner.Calls))
   211  		assert.Equal(t, "go", utils.ExecMockRunner.Calls[0].Exec)
   212  		assert.Equal(t, []string{"install", "github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest"}, utils.ExecMockRunner.Calls[0].Params)
   213  		assert.Equal(t, "cyclonedx-gomod", utils.ExecMockRunner.Calls[1].Exec)
   214  		assert.Equal(t, []string{"mod", "-licenses", "-test", "-output", "bom.xml"}, utils.ExecMockRunner.Calls[1].Params)
   215  		assert.Equal(t, "go", utils.ExecMockRunner.Calls[2].Exec)
   216  		assert.Equal(t, []string{"build", "-trimpath"}, utils.ExecMockRunner.Calls[2].Params)
   217  	})
   218  
   219  	t.Run("failure - install pre-requisites for testing", func(t *testing.T) {
   220  		config := golangBuildOptions{
   221  			RunTests: true,
   222  		}
   223  		utils := newGolangBuildTestsUtils()
   224  		utils.ShouldFailOnCommand = map[string]error{"go install gotest.tools/gotestsum": fmt.Errorf("install failure")}
   225  		telemetryData := telemetry.CustomData{}
   226  
   227  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   228  		assert.EqualError(t, err, "failed to install pre-requisite: install failure")
   229  	})
   230  
   231  	t.Run("failure - install pre-requisites for BOM creation", func(t *testing.T) {
   232  		config := golangBuildOptions{
   233  			CreateBOM: true,
   234  		}
   235  		utils := newGolangBuildTestsUtils()
   236  		utils.ShouldFailOnCommand = map[string]error{"go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest": fmt.Errorf("install failure")}
   237  		telemetryData := telemetry.CustomData{}
   238  
   239  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   240  		assert.EqualError(t, err, "failed to install pre-requisite: install failure")
   241  	})
   242  
   243  	t.Run("failure - test run failure", func(t *testing.T) {
   244  		config := golangBuildOptions{
   245  			RunTests: true,
   246  		}
   247  		utils := newGolangBuildTestsUtils()
   248  		utils.ShouldFailOnCommand = map[string]error{"gotestsum --junitfile": fmt.Errorf("test failure")}
   249  		telemetryData := telemetry.CustomData{}
   250  
   251  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   252  		assert.EqualError(t, err, "running tests failed - junit result missing: test failure")
   253  	})
   254  
   255  	t.Run("failure - test failure", func(t *testing.T) {
   256  		config := golangBuildOptions{
   257  			RunTests: true,
   258  		}
   259  		utils := newGolangBuildTestsUtils()
   260  		utils.ShouldFailOnCommand = map[string]error{"gotestsum --junitfile": fmt.Errorf("test failure")}
   261  		utils.AddFile("TEST-go.xml", []byte("some content"))
   262  		utils.AddFile(coverageFile, []byte("some content"))
   263  		telemetryData := telemetry.CustomData{}
   264  
   265  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   266  		assert.EqualError(t, err, "some tests failed")
   267  	})
   268  
   269  	t.Run("failure - prepareLdflags", func(t *testing.T) {
   270  		config := golangBuildOptions{
   271  			RunTests:            true,
   272  			LdflagsTemplate:     "{{.CPE.test",
   273  			TargetArchitectures: []string{"linux,amd64"},
   274  		}
   275  		utils := newGolangBuildTestsUtils()
   276  		telemetryData := telemetry.CustomData{}
   277  
   278  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   279  		assert.Contains(t, fmt.Sprint(err), "failed to parse ldflagsTemplate")
   280  	})
   281  
   282  	t.Run("failure - build failure", func(t *testing.T) {
   283  		config := golangBuildOptions{
   284  			RunIntegrationTests: true,
   285  			TargetArchitectures: []string{"linux,amd64"},
   286  		}
   287  		utils := newGolangBuildTestsUtils()
   288  		utils.ShouldFailOnCommand = map[string]error{"go build": fmt.Errorf("build failure")}
   289  		telemetryData := telemetry.CustomData{}
   290  
   291  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   292  		assert.EqualError(t, err, "failed to run build for linux.amd64: build failure")
   293  	})
   294  
   295  	t.Run("failure - publish - no target repository defined", func(t *testing.T) {
   296  		config := golangBuildOptions{
   297  			TargetArchitectures: []string{"linux,amd64"},
   298  			Output:              "testBin",
   299  			Publish:             true,
   300  		}
   301  		utils := newGolangBuildTestsUtils()
   302  		telemetryData := telemetry.CustomData{}
   303  
   304  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   305  		assert.EqualError(t, err, "there's no target repository for binary publishing configured")
   306  	})
   307  
   308  	t.Run("failure - publish - no go.mod file found", func(t *testing.T) {
   309  		config := golangBuildOptions{
   310  			TargetArchitectures:      []string{"linux,amd64"},
   311  			Output:                   "testBin",
   312  			Publish:                  true,
   313  			TargetRepositoryURL:      "https://my.target.repository.local",
   314  			TargetRepositoryUser:     "user",
   315  			TargetRepositoryPassword: "password",
   316  			ArtifactVersion:          "1.0.0",
   317  		}
   318  		utils := newGolangBuildTestsUtils()
   319  		telemetryData := telemetry.CustomData{}
   320  
   321  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   322  		assert.EqualError(t, err, "go.mod file not found")
   323  	})
   324  
   325  	t.Run("failure - publish - go.mod file without module path", func(t *testing.T) {
   326  		config := golangBuildOptions{
   327  			TargetArchitectures:      []string{"linux,amd64"},
   328  			Output:                   "testBin",
   329  			Publish:                  true,
   330  			TargetRepositoryURL:      "https://my.target.repository.local",
   331  			TargetRepositoryUser:     "user",
   332  			TargetRepositoryPassword: "password",
   333  			ArtifactVersion:          "1.0.0",
   334  		}
   335  		utils := newGolangBuildTestsUtils()
   336  		utils.FilesMock.AddFile("go.mod", []byte(""))
   337  		telemetryData := telemetry.CustomData{}
   338  
   339  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   340  		assert.EqualError(t, err, "go.mod doesn't declare a module path")
   341  	})
   342  
   343  	t.Run("failure - publish - no artifactVersion set", func(t *testing.T) {
   344  		config := golangBuildOptions{
   345  			TargetArchitectures:      []string{"linux,amd64"},
   346  			Output:                   "testBin",
   347  			Publish:                  true,
   348  			TargetRepositoryURL:      "https://my.target.repository.local",
   349  			TargetRepositoryUser:     "user",
   350  			TargetRepositoryPassword: "password",
   351  		}
   352  		utils := newGolangBuildTestsUtils()
   353  		utils.FilesMock.AddFile("go.mod", []byte("module example.com/my/module"))
   354  		telemetryData := telemetry.CustomData{}
   355  
   356  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   357  		assert.EqualError(t, err, "no build descriptor available, supported: [VERSION version.txt go.mod]")
   358  	})
   359  
   360  	t.Run("failure - publish - received unexpected status code", func(t *testing.T) {
   361  		config := golangBuildOptions{
   362  			TargetArchitectures:      []string{"linux,amd64"},
   363  			Output:                   "testBin",
   364  			Publish:                  true,
   365  			TargetRepositoryURL:      "https://my.target.repository.local",
   366  			TargetRepositoryUser:     "user",
   367  			TargetRepositoryPassword: "password",
   368  			ArtifactVersion:          "1.0.0",
   369  		}
   370  		utils := newGolangBuildTestsUtils()
   371  		utils.returnFileUploadStatus = 500
   372  		utils.FilesMock.AddFile("go.mod", []byte("module example.com/my/module"))
   373  		telemetryData := telemetry.CustomData{}
   374  
   375  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   376  		assert.EqualError(t, err, "couldn't upload artifact, received status code 500")
   377  	})
   378  
   379  	t.Run("failure - create BOM", func(t *testing.T) {
   380  		config := golangBuildOptions{
   381  			CreateBOM:           true,
   382  			TargetArchitectures: []string{"linux,amd64"},
   383  		}
   384  		utils := newGolangBuildTestsUtils()
   385  		utils.ShouldFailOnCommand = map[string]error{"cyclonedx-gomod mod -licenses -test -output bom.xml": fmt.Errorf("BOM creation failure")}
   386  		telemetryData := telemetry.CustomData{}
   387  
   388  		err := runGolangBuild(&config, &telemetryData, utils, &cpe)
   389  		assert.EqualError(t, err, "BOM creation failed: BOM creation failure")
   390  	})
   391  }
   392  
   393  func TestRunGolangTests(t *testing.T) {
   394  	t.Parallel()
   395  
   396  	t.Run("success", func(t *testing.T) {
   397  		t.Parallel()
   398  		config := golangBuildOptions{}
   399  		utils := newGolangBuildTestsUtils()
   400  		utils.AddFile("TEST-go.xml", []byte("some content"))
   401  		utils.AddFile(coverageFile, []byte("some content"))
   402  
   403  		success, err := runGolangTests(&config, utils)
   404  		assert.NoError(t, err)
   405  		assert.True(t, success)
   406  		assert.Equal(t, "gotestsum", utils.ExecMockRunner.Calls[0].Exec)
   407  		assert.Equal(t, []string{"--junitfile", "TEST-go.xml", "--", fmt.Sprintf("-coverprofile=%v", coverageFile), "./..."}, utils.ExecMockRunner.Calls[0].Params)
   408  	})
   409  
   410  	t.Run("success - failed tests", func(t *testing.T) {
   411  		t.Parallel()
   412  		config := golangBuildOptions{}
   413  		utils := newGolangBuildTestsUtils()
   414  		utils.AddFile("TEST-go.xml", []byte("some content"))
   415  		utils.AddFile(coverageFile, []byte("some content"))
   416  		utils.ExecMockRunner.ShouldFailOnCommand = map[string]error{"gotestsum": fmt.Errorf("execution error")}
   417  
   418  		success, err := runGolangTests(&config, utils)
   419  		assert.NoError(t, err)
   420  		assert.False(t, success)
   421  	})
   422  
   423  	t.Run("error - run failed, no junit", func(t *testing.T) {
   424  		t.Parallel()
   425  		config := golangBuildOptions{}
   426  		utils := newGolangBuildTestsUtils()
   427  		utils.ExecMockRunner.ShouldFailOnCommand = map[string]error{"gotestsum": fmt.Errorf("execution error")}
   428  
   429  		_, err := runGolangTests(&config, utils)
   430  		assert.EqualError(t, err, "running tests failed - junit result missing: execution error")
   431  	})
   432  
   433  	t.Run("error - run failed, no coverage", func(t *testing.T) {
   434  		t.Parallel()
   435  		config := golangBuildOptions{}
   436  		utils := newGolangBuildTestsUtils()
   437  		utils.ExecMockRunner.ShouldFailOnCommand = map[string]error{"gotestsum": fmt.Errorf("execution error")}
   438  		utils.AddFile("TEST-go.xml", []byte("some content"))
   439  
   440  		_, err := runGolangTests(&config, utils)
   441  		assert.EqualError(t, err, "running tests failed - coverage output missing: execution error")
   442  	})
   443  }
   444  
   445  func TestRunGolangIntegrationTests(t *testing.T) {
   446  	t.Parallel()
   447  
   448  	t.Run("success", func(t *testing.T) {
   449  		t.Parallel()
   450  		config := golangBuildOptions{}
   451  		utils := newGolangBuildTestsUtils()
   452  		utils.AddFile("TEST-integration.xml", []byte("some content"))
   453  
   454  		success, err := runGolangIntegrationTests(&config, utils)
   455  		assert.NoError(t, err)
   456  		assert.True(t, success)
   457  		assert.Equal(t, "gotestsum", utils.ExecMockRunner.Calls[0].Exec)
   458  		assert.Equal(t, []string{"--junitfile", "TEST-integration.xml", "--", "-tags=integration", "./..."}, utils.ExecMockRunner.Calls[0].Params)
   459  	})
   460  
   461  	t.Run("success - failed tests", func(t *testing.T) {
   462  		t.Parallel()
   463  		config := golangBuildOptions{}
   464  		utils := newGolangBuildTestsUtils()
   465  		utils.AddFile("TEST-integration.xml", []byte("some content"))
   466  		utils.ExecMockRunner.ShouldFailOnCommand = map[string]error{"gotestsum": fmt.Errorf("execution error")}
   467  
   468  		success, err := runGolangIntegrationTests(&config, utils)
   469  		assert.NoError(t, err)
   470  		assert.False(t, success)
   471  	})
   472  
   473  	t.Run("error - run failed", func(t *testing.T) {
   474  		t.Parallel()
   475  		config := golangBuildOptions{}
   476  		utils := newGolangBuildTestsUtils()
   477  		utils.ExecMockRunner.ShouldFailOnCommand = map[string]error{"gotestsum": fmt.Errorf("execution error")}
   478  
   479  		_, err := runGolangIntegrationTests(&config, utils)
   480  		assert.EqualError(t, err, "running tests failed: execution error")
   481  	})
   482  }
   483  
   484  func TestReportGolangTestCoverage(t *testing.T) {
   485  	t.Parallel()
   486  
   487  	t.Run("success - cobertura", func(t *testing.T) {
   488  		t.Parallel()
   489  		config := golangBuildOptions{CoverageFormat: "cobertura"}
   490  		utils := newGolangBuildTestsUtils()
   491  		utils.AddFile(coverageFile, []byte("some content"))
   492  
   493  		err := reportGolangTestCoverage(&config, utils)
   494  		assert.NoError(t, err)
   495  		assert.Equal(t, "go", utils.ExecMockRunner.Calls[0].Exec)
   496  		assert.Equal(t, []string{"install", "github.com/boumenot/gocover-cobertura@latest"}, utils.ExecMockRunner.Calls[0].Params)
   497  		assert.Equal(t, "gocover-cobertura", utils.ExecMockRunner.Calls[1].Exec)
   498  		exists, err := utils.FileExists("cobertura-coverage.xml")
   499  		assert.NoError(t, err)
   500  		assert.True(t, exists)
   501  	})
   502  
   503  	t.Run("success - cobertura exclude generated", func(t *testing.T) {
   504  		t.Parallel()
   505  		config := golangBuildOptions{CoverageFormat: "cobertura", ExcludeGeneratedFromCoverage: true}
   506  		utils := newGolangBuildTestsUtils()
   507  		utils.AddFile(coverageFile, []byte("some content"))
   508  
   509  		err := reportGolangTestCoverage(&config, utils)
   510  		assert.NoError(t, err)
   511  		assert.Equal(t, "gocover-cobertura", utils.ExecMockRunner.Calls[1].Exec)
   512  		assert.Equal(t, []string{"-ignore-gen-files"}, utils.ExecMockRunner.Calls[1].Params)
   513  	})
   514  
   515  	t.Run("error - cobertura installation", func(t *testing.T) {
   516  		t.Parallel()
   517  		config := golangBuildOptions{CoverageFormat: "cobertura", ExcludeGeneratedFromCoverage: true}
   518  		utils := newGolangBuildTestsUtils()
   519  		utils.ExecMockRunner.ShouldFailOnCommand = map[string]error{"go install github.com/boumenot/gocover-cobertura": fmt.Errorf("install error")}
   520  
   521  		err := reportGolangTestCoverage(&config, utils)
   522  		assert.EqualError(t, err, "failed to install pre-requisite: install error")
   523  	})
   524  
   525  	t.Run("error - cobertura missing coverage file", func(t *testing.T) {
   526  		t.Parallel()
   527  		config := golangBuildOptions{CoverageFormat: "cobertura", ExcludeGeneratedFromCoverage: true}
   528  		utils := newGolangBuildTestsUtils()
   529  
   530  		err := reportGolangTestCoverage(&config, utils)
   531  		assert.Contains(t, fmt.Sprint(err), "failed to read coverage file")
   532  	})
   533  
   534  	t.Run("error - cobertura coversion", func(t *testing.T) {
   535  		t.Parallel()
   536  		config := golangBuildOptions{CoverageFormat: "cobertura", ExcludeGeneratedFromCoverage: true}
   537  		utils := newGolangBuildTestsUtils()
   538  		utils.AddFile(coverageFile, []byte("some content"))
   539  		utils.ExecMockRunner.ShouldFailOnCommand = map[string]error{"gocover-cobertura -ignore-gen-files": fmt.Errorf("execution error")}
   540  
   541  		err := reportGolangTestCoverage(&config, utils)
   542  		assert.EqualError(t, err, "failed to convert coverage data to cobertura format: execution error")
   543  	})
   544  
   545  	t.Run("error - writing cobertura file", func(t *testing.T) {
   546  		t.Parallel()
   547  		config := golangBuildOptions{CoverageFormat: "cobertura", ExcludeGeneratedFromCoverage: true}
   548  		utils := newGolangBuildTestsUtils()
   549  		utils.AddFile(coverageFile, []byte("some content"))
   550  		utils.FileWriteError = fmt.Errorf("write failure")
   551  
   552  		err := reportGolangTestCoverage(&config, utils)
   553  		assert.EqualError(t, err, "failed to create cobertura coverage file: write failure")
   554  	})
   555  
   556  	t.Run("success - html", func(t *testing.T) {
   557  		t.Parallel()
   558  		config := golangBuildOptions{}
   559  		utils := newGolangBuildTestsUtils()
   560  
   561  		err := reportGolangTestCoverage(&config, utils)
   562  		assert.NoError(t, err)
   563  		assert.Equal(t, "go", utils.ExecMockRunner.Calls[0].Exec)
   564  		assert.Equal(t, []string{"tool", "cover", "-html", coverageFile, "-o", "coverage.html"}, utils.ExecMockRunner.Calls[0].Params)
   565  	})
   566  
   567  	t.Run("error - html", func(t *testing.T) {
   568  		t.Parallel()
   569  		config := golangBuildOptions{}
   570  		utils := newGolangBuildTestsUtils()
   571  		utils.ExecMockRunner.ShouldFailOnCommand = map[string]error{"go tool cover -html cover.out -o coverage.html": fmt.Errorf("execution error")}
   572  		utils.AddFile(coverageFile, []byte("some content"))
   573  
   574  		err := reportGolangTestCoverage(&config, utils)
   575  		assert.EqualError(t, err, "failed to create html coverage file: execution error")
   576  	})
   577  }
   578  
   579  func TestPrepareLdflags(t *testing.T) {
   580  	t.Parallel()
   581  	dir, err := ioutil.TempDir("", "")
   582  	defer os.RemoveAll(dir) // clean up
   583  	assert.NoError(t, err, "Error when creating temp dir")
   584  
   585  	err = os.Mkdir(filepath.Join(dir, "commonPipelineEnvironment"), 0777)
   586  	assert.NoError(t, err, "Error when creating folder structure")
   587  
   588  	err = ioutil.WriteFile(filepath.Join(dir, "commonPipelineEnvironment", "artifactVersion"), []byte("1.2.3"), 0666)
   589  	assert.NoError(t, err, "Error when creating cpe file")
   590  
   591  	t.Run("success - default", func(t *testing.T) {
   592  		config := golangBuildOptions{LdflagsTemplate: "-X version={{ .CPE.artifactVersion }}"}
   593  		utils := newGolangBuildTestsUtils()
   594  		result, err := prepareLdflags(&config, utils, dir)
   595  		assert.NoError(t, err)
   596  		assert.Equal(t, "-X version=1.2.3", result)
   597  	})
   598  
   599  	t.Run("error - template parsing", func(t *testing.T) {
   600  		config := golangBuildOptions{LdflagsTemplate: "-X version={{ .CPE.artifactVersion "}
   601  		utils := newGolangBuildTestsUtils()
   602  		_, err := prepareLdflags(&config, utils, dir)
   603  		assert.Contains(t, fmt.Sprint(err), "failed to parse ldflagsTemplate")
   604  	})
   605  }
   606  
   607  func TestRunGolangBuildPerArchitecture(t *testing.T) {
   608  	t.Parallel()
   609  
   610  	t.Run("success - default", func(t *testing.T) {
   611  		t.Parallel()
   612  		config := golangBuildOptions{}
   613  		utils := newGolangBuildTestsUtils()
   614  		ldflags := ""
   615  		architecture, _ := multiarch.ParsePlatformString("linux,amd64")
   616  
   617  		binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
   618  		assert.NoError(t, err)
   619  		assert.Greater(t, len(utils.Env), 3)
   620  		assert.Contains(t, utils.Env, "CGO_ENABLED=0")
   621  		assert.Contains(t, utils.Env, "GOOS=linux")
   622  		assert.Contains(t, utils.Env, "GOARCH=amd64")
   623  		assert.Equal(t, utils.Calls[0].Exec, "go")
   624  		assert.Equal(t, utils.Calls[0].Params[0], "build")
   625  		assert.Empty(t, binaryName)
   626  	})
   627  
   628  	t.Run("success - custom params", func(t *testing.T) {
   629  		t.Parallel()
   630  		config := golangBuildOptions{BuildFlags: []string{"--flag1", "val1", "--flag2", "val2"}, Output: "testBin", Packages: []string{"./test/.."}}
   631  		utils := newGolangBuildTestsUtils()
   632  		ldflags := "-X test=test"
   633  		architecture, _ := multiarch.ParsePlatformString("linux,amd64")
   634  
   635  		binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
   636  		assert.NoError(t, err)
   637  		assert.Contains(t, utils.Calls[0].Params, "-o")
   638  		assert.Contains(t, utils.Calls[0].Params, "testBin-linux.amd64")
   639  		assert.Contains(t, utils.Calls[0].Params, "./test/..")
   640  		assert.Contains(t, utils.Calls[0].Params, "-ldflags")
   641  		assert.Contains(t, utils.Calls[0].Params, "-X test=test")
   642  		assert.Equal(t, "testBin-linux.amd64", binaryName)
   643  	})
   644  
   645  	t.Run("success - windows", func(t *testing.T) {
   646  		t.Parallel()
   647  		config := golangBuildOptions{Output: "testBin"}
   648  		utils := newGolangBuildTestsUtils()
   649  		ldflags := ""
   650  		architecture, _ := multiarch.ParsePlatformString("windows,amd64")
   651  
   652  		binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
   653  		assert.NoError(t, err)
   654  		assert.Contains(t, utils.Calls[0].Params, "-o")
   655  		assert.Contains(t, utils.Calls[0].Params, "testBin-windows.amd64.exe")
   656  		assert.Equal(t, "testBin-windows.amd64.exe", binaryName)
   657  	})
   658  
   659  	t.Run("execution error", func(t *testing.T) {
   660  		t.Parallel()
   661  		config := golangBuildOptions{}
   662  		utils := newGolangBuildTestsUtils()
   663  		utils.ShouldFailOnCommand = map[string]error{"go build": fmt.Errorf("execution error")}
   664  		ldflags := ""
   665  		architecture, _ := multiarch.ParsePlatformString("linux,amd64")
   666  
   667  		_, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
   668  		assert.EqualError(t, err, "failed to run build for linux.amd64: execution error")
   669  	})
   670  
   671  }
   672  
   673  func TestPrepareGolangEnvironment(t *testing.T) {
   674  	modTestFile := `
   675  module private.example.com/m
   676  
   677  require (
   678          example.com/public/module v1.0.0
   679          private1.example.com/private/repo v0.1.0
   680          private2.example.com/another/repo v0.2.0
   681  )
   682  
   683  go 1.17`
   684  
   685  	type expectations struct {
   686  		envVars          []string
   687  		commandsExecuted [][]string
   688  	}
   689  	tests := []struct {
   690  		name           string
   691  		modFileContent string
   692  		globPattern    string
   693  		gitToken       string
   694  		expect         expectations
   695  	}{
   696  		{
   697  			name:           "success - does nothing if privateModules is not set",
   698  			modFileContent: modTestFile,
   699  			globPattern:    "",
   700  			gitToken:       "secret",
   701  			expect:         expectations{},
   702  		},
   703  		{
   704  			name:           "success - goprivate is set and authentication properly configured",
   705  			modFileContent: modTestFile,
   706  			globPattern:    "*.example.com",
   707  			gitToken:       "secret",
   708  			expect: expectations{
   709  				envVars: []string{"GOPRIVATE=*.example.com"},
   710  				commandsExecuted: [][]string{
   711  					[]string{"git", "config", "--global", "url.https://secret@private1.example.com/private/repo.git.insteadOf", "https://private1.example.com/private/repo.git"},
   712  					[]string{"git", "config", "--global", "url.https://secret@private2.example.com/another/repo.git.insteadOf", "https://private2.example.com/another/repo.git"},
   713  				},
   714  			},
   715  		},
   716  	}
   717  
   718  	for _, tt := range tests {
   719  		t.Run(tt.name, func(t *testing.T) {
   720  			utils := newGolangBuildTestsUtils()
   721  
   722  			goModFile, _ := modfile.Parse("go.mod", []byte(tt.modFileContent), nil)
   723  
   724  			config := golangBuildOptions{}
   725  			config.PrivateModules = tt.globPattern
   726  			config.PrivateModulesGitToken = tt.gitToken
   727  
   728  			err := prepareGolangEnvironment(&config, goModFile, utils)
   729  
   730  			if assert.NoError(t, err) {
   731  				assert.Subset(t, os.Environ(), tt.expect.envVars)
   732  				assert.Equal(t, len(tt.expect.commandsExecuted), len(utils.Calls))
   733  
   734  				for i, expectedCommand := range tt.expect.commandsExecuted {
   735  					assert.Equal(t, expectedCommand[0], utils.Calls[i].Exec)
   736  					assert.Equal(t, expectedCommand[1:], utils.Calls[i].Params)
   737  				}
   738  			}
   739  		})
   740  	}
   741  }
   742  
   743  func TestLookupGolangPrivateModulesRepositories(t *testing.T) {
   744  	t.Parallel()
   745  
   746  	modTestFile := `
   747  module private.example.com/m
   748  
   749  require (
   750  	example.com/public/module v1.0.0
   751  	private1.example.com/private/repo v0.1.0
   752  	private2.example.com/another/repo v0.2.0
   753  )
   754  
   755  go 1.17`
   756  
   757  	type expectations struct {
   758  		repos        []string
   759  		errorMessage string
   760  	}
   761  	tests := []struct {
   762  		name           string
   763  		modFileContent string
   764  		globPattern    string
   765  		expect         expectations
   766  	}{
   767  		{
   768  			name:           "Does nothing if glob pattern is empty",
   769  			modFileContent: modTestFile,
   770  			expect: expectations{
   771  				repos: []string{},
   772  			},
   773  		},
   774  		{
   775  			name:           "Does nothing if there is no go.mod file",
   776  			globPattern:    "private.example.com",
   777  			modFileContent: "",
   778  			expect: expectations{
   779  				repos: []string{},
   780  			},
   781  		},
   782  		{
   783  			name:           "Detects all private repos using a glob pattern",
   784  			modFileContent: modTestFile,
   785  			globPattern:    "*.example.com",
   786  			expect: expectations{
   787  				repos: []string{"https://private1.example.com/private/repo.git", "https://private2.example.com/another/repo.git"},
   788  			},
   789  		},
   790  		{
   791  			name:           "Detects all private repos",
   792  			modFileContent: modTestFile,
   793  			globPattern:    "private1.example.com,private2.example.com",
   794  			expect: expectations{
   795  				repos: []string{"https://private1.example.com/private/repo.git", "https://private2.example.com/another/repo.git"},
   796  			},
   797  		},
   798  		{
   799  			name:           "Detects a dedicated repo",
   800  			modFileContent: modTestFile,
   801  			globPattern:    "private2.example.com",
   802  			expect: expectations{
   803  				repos: []string{"https://private2.example.com/another/repo.git"},
   804  			},
   805  		},
   806  	}
   807  
   808  	for _, tt := range tests {
   809  		t.Run(tt.name, func(t *testing.T) {
   810  			utils := newGolangBuildTestsUtils()
   811  
   812  			goModFile, _ := modfile.Parse("", []byte(tt.modFileContent), nil)
   813  
   814  			repos, err := lookupGolangPrivateModulesRepositories(goModFile, tt.globPattern, utils)
   815  
   816  			if tt.expect.errorMessage == "" {
   817  				assert.NoError(t, err)
   818  				assert.Equal(t, tt.expect.repos, repos)
   819  			} else {
   820  				assert.EqualError(t, err, tt.expect.errorMessage)
   821  			}
   822  		})
   823  	}
   824  }