github.com/jaylevin/jenkins-library@v1.230.4/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: [go.mod VERSION version.txt]") 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 binaryNames, 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.Len(t, binaryNames, 1) 643 assert.Contains(t, binaryNames, "testBin-linux.amd64") 644 }) 645 646 t.Run("success - windows", func(t *testing.T) { 647 t.Parallel() 648 config := golangBuildOptions{Output: "testBin"} 649 utils := newGolangBuildTestsUtils() 650 ldflags := "" 651 architecture, _ := multiarch.ParsePlatformString("windows,amd64") 652 653 binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) 654 assert.NoError(t, err) 655 assert.Contains(t, utils.Calls[0].Params, "-o") 656 assert.Contains(t, utils.Calls[0].Params, "testBin-windows.amd64.exe") 657 assert.Len(t, binaryNames, 1) 658 assert.Contains(t, binaryNames, "testBin-windows.amd64.exe") 659 }) 660 661 t.Run("success - multiple main packages (linux)", func(t *testing.T) { 662 t.Parallel() 663 config := golangBuildOptions{Output: "test/", Packages: []string{"package/foo", "package/bar"}} 664 utils := newGolangBuildTestsUtils() 665 utils.StdoutReturn = map[string]string{ 666 "go list -f {{ .Name }} package/foo": "main", 667 "go list -f {{ .Name }} package/bar": "main", 668 } 669 ldflags := "" 670 architecture, _ := multiarch.ParsePlatformString("linux,amd64") 671 672 binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) 673 assert.NoError(t, err) 674 assert.Contains(t, utils.Calls[0].Params, "list") 675 assert.Contains(t, utils.Calls[0].Params, "package/foo") 676 assert.Contains(t, utils.Calls[1].Params, "list") 677 assert.Contains(t, utils.Calls[1].Params, "package/bar") 678 679 assert.Len(t, binaryNames, 2) 680 assert.Contains(t, binaryNames, "test-linux-amd64/foo") 681 assert.Contains(t, binaryNames, "test-linux-amd64/bar") 682 }) 683 684 t.Run("success - multiple main packages (windows)", func(t *testing.T) { 685 t.Parallel() 686 config := golangBuildOptions{Output: "test/", Packages: []string{"package/foo", "package/bar"}} 687 utils := newGolangBuildTestsUtils() 688 utils.StdoutReturn = map[string]string{ 689 "go list -f {{ .Name }} package/foo": "main", 690 "go list -f {{ .Name }} package/bar": "main", 691 } 692 ldflags := "" 693 architecture, _ := multiarch.ParsePlatformString("windows,amd64") 694 695 binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) 696 assert.NoError(t, err) 697 assert.Contains(t, utils.Calls[0].Params, "list") 698 assert.Contains(t, utils.Calls[0].Params, "package/foo") 699 assert.Contains(t, utils.Calls[1].Params, "list") 700 assert.Contains(t, utils.Calls[1].Params, "package/bar") 701 702 assert.Len(t, binaryNames, 2) 703 assert.Contains(t, binaryNames, "test-windows-amd64/foo.exe") 704 assert.Contains(t, binaryNames, "test-windows-amd64/bar.exe") 705 }) 706 707 t.Run("success - multiple mixed packages", func(t *testing.T) { 708 t.Parallel() 709 config := golangBuildOptions{Output: "test/", Packages: []string{"package/foo", "package/bar"}} 710 utils := newGolangBuildTestsUtils() 711 utils.StdoutReturn = map[string]string{ 712 "go list -f {{ .Name }} package/foo": "main", 713 "go list -f {{ .Name }} package/bar": "bar", 714 } 715 ldflags := "" 716 architecture, _ := multiarch.ParsePlatformString("linux,amd64") 717 718 binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) 719 assert.NoError(t, err) 720 assert.Contains(t, utils.Calls[0].Params, "list") 721 assert.Contains(t, utils.Calls[0].Params, "package/foo") 722 assert.Contains(t, utils.Calls[1].Params, "list") 723 assert.Contains(t, utils.Calls[1].Params, "package/bar") 724 725 assert.Len(t, binaryNames, 1) 726 assert.Contains(t, binaryNames, "test-linux-amd64/foo") 727 }) 728 729 t.Run("execution error", func(t *testing.T) { 730 t.Parallel() 731 config := golangBuildOptions{} 732 utils := newGolangBuildTestsUtils() 733 utils.ShouldFailOnCommand = map[string]error{"go build": fmt.Errorf("execution error")} 734 ldflags := "" 735 architecture, _ := multiarch.ParsePlatformString("linux,amd64") 736 737 _, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) 738 assert.EqualError(t, err, "failed to run build for linux.amd64: execution error") 739 }) 740 741 } 742 743 func TestPrepareGolangEnvironment(t *testing.T) { 744 modTestFile := ` 745 module private.example.com/m 746 747 require ( 748 example.com/public/module v1.0.0 749 private1.example.com/private/repo v0.1.0 750 private2.example.com/another/repo v0.2.0 751 ) 752 753 go 1.17` 754 755 type expectations struct { 756 envVars []string 757 commandsExecuted [][]string 758 } 759 tests := []struct { 760 name string 761 modFileContent string 762 globPattern string 763 gitToken string 764 expect expectations 765 }{ 766 { 767 name: "success - does nothing if privateModules is not set", 768 modFileContent: modTestFile, 769 globPattern: "", 770 gitToken: "secret", 771 expect: expectations{}, 772 }, 773 { 774 name: "success - goprivate is set and authentication properly configured", 775 modFileContent: modTestFile, 776 globPattern: "*.example.com", 777 gitToken: "secret", 778 expect: expectations{ 779 envVars: []string{"GOPRIVATE=*.example.com"}, 780 commandsExecuted: [][]string{ 781 []string{"git", "config", "--global", "url.https://secret@private1.example.com/private/repo.git.insteadOf", "https://private1.example.com/private/repo.git"}, 782 []string{"git", "config", "--global", "url.https://secret@private2.example.com/another/repo.git.insteadOf", "https://private2.example.com/another/repo.git"}, 783 }, 784 }, 785 }, 786 } 787 788 for _, tt := range tests { 789 t.Run(tt.name, func(t *testing.T) { 790 utils := newGolangBuildTestsUtils() 791 792 goModFile, _ := modfile.Parse("go.mod", []byte(tt.modFileContent), nil) 793 794 config := golangBuildOptions{} 795 config.PrivateModules = tt.globPattern 796 config.PrivateModulesGitToken = tt.gitToken 797 798 err := prepareGolangEnvironment(&config, goModFile, utils) 799 800 if assert.NoError(t, err) { 801 assert.Subset(t, os.Environ(), tt.expect.envVars) 802 assert.Equal(t, len(tt.expect.commandsExecuted), len(utils.Calls)) 803 804 for i, expectedCommand := range tt.expect.commandsExecuted { 805 assert.Equal(t, expectedCommand[0], utils.Calls[i].Exec) 806 assert.Equal(t, expectedCommand[1:], utils.Calls[i].Params) 807 } 808 } 809 }) 810 } 811 } 812 813 func TestLookupGolangPrivateModulesRepositories(t *testing.T) { 814 t.Parallel() 815 816 modTestFile := ` 817 module private.example.com/m 818 819 require ( 820 example.com/public/module v1.0.0 821 private1.example.com/private/repo v0.1.0 822 private2.example.com/another/repo v0.2.0 823 ) 824 825 go 1.17` 826 827 type expectations struct { 828 repos []string 829 errorMessage string 830 } 831 tests := []struct { 832 name string 833 modFileContent string 834 globPattern string 835 expect expectations 836 }{ 837 { 838 name: "Does nothing if glob pattern is empty", 839 modFileContent: modTestFile, 840 expect: expectations{ 841 repos: []string{}, 842 }, 843 }, 844 { 845 name: "Does nothing if there is no go.mod file", 846 globPattern: "private.example.com", 847 modFileContent: "", 848 expect: expectations{ 849 repos: []string{}, 850 }, 851 }, 852 { 853 name: "Detects all private repos using a glob pattern", 854 modFileContent: modTestFile, 855 globPattern: "*.example.com", 856 expect: expectations{ 857 repos: []string{"https://private1.example.com/private/repo.git", "https://private2.example.com/another/repo.git"}, 858 }, 859 }, 860 { 861 name: "Detects all private repos", 862 modFileContent: modTestFile, 863 globPattern: "private1.example.com,private2.example.com", 864 expect: expectations{ 865 repos: []string{"https://private1.example.com/private/repo.git", "https://private2.example.com/another/repo.git"}, 866 }, 867 }, 868 { 869 name: "Detects a dedicated repo", 870 modFileContent: modTestFile, 871 globPattern: "private2.example.com", 872 expect: expectations{ 873 repos: []string{"https://private2.example.com/another/repo.git"}, 874 }, 875 }, 876 } 877 878 for _, tt := range tests { 879 t.Run(tt.name, func(t *testing.T) { 880 utils := newGolangBuildTestsUtils() 881 882 goModFile, _ := modfile.Parse("", []byte(tt.modFileContent), nil) 883 884 repos, err := lookupGolangPrivateModulesRepositories(goModFile, tt.globPattern, utils) 885 886 if tt.expect.errorMessage == "" { 887 assert.NoError(t, err) 888 assert.Equal(t, tt.expect.repos, repos) 889 } else { 890 assert.EqualError(t, err, tt.expect.errorMessage) 891 } 892 }) 893 } 894 }