github.com/triarius/goreleaser@v1.12.5/internal/client/gitlab_test.go (about) 1 package client 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net/http" 8 "net/http/httptest" 9 "os" 10 "strconv" 11 "strings" 12 "testing" 13 "text/template" 14 15 "github.com/triarius/goreleaser/internal/artifact" 16 "github.com/triarius/goreleaser/pkg/config" 17 "github.com/triarius/goreleaser/pkg/context" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func TestGitLabReleaseURLTemplate(t *testing.T) { 22 repo := config.Repo{ 23 Owner: "owner", 24 Name: "name", 25 } 26 tests := []struct { 27 name string 28 repo config.Repo 29 downloadURL string 30 wantDownloadURL string 31 wantErr bool 32 }{ 33 { 34 name: "default_download_url", 35 downloadURL: DefaultGitLabDownloadURL, 36 repo: repo, 37 wantDownloadURL: "https://gitlab.com/owner/name/-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}", 38 }, 39 { 40 name: "default_download_url_no_owner", 41 downloadURL: DefaultGitLabDownloadURL, 42 repo: config.Repo{Name: "name"}, 43 wantDownloadURL: "https://gitlab.com/name/-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}", 44 }, 45 { 46 name: "download_url_template", 47 repo: repo, 48 downloadURL: "{{ .Env.GORELEASER_TEST_GITLAB_URLS_DOWNLOAD }}", 49 wantDownloadURL: "https://gitlab.mycompany.com/owner/name/-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}", 50 }, 51 { 52 name: "download_url_template_invalid_value", 53 downloadURL: "{{ .Eenv.GORELEASER_NOT_EXISTS }}", 54 wantErr: true, 55 }, 56 { 57 name: "download_url_template_invalid", 58 downloadURL: "{{.dddddddddd", 59 wantErr: true, 60 }, 61 { 62 name: "download_url_string", 63 downloadURL: "https://gitlab.mycompany.com", 64 wantDownloadURL: "https://gitlab.mycompany.com/", 65 }, 66 } 67 68 for _, tt := range tests { 69 ctx := context.New(config.Project{ 70 Env: []string{ 71 "GORELEASER_TEST_GITLAB_URLS_DOWNLOAD=https://gitlab.mycompany.com", 72 }, 73 GitLabURLs: config.GitLabURLs{ 74 Download: tt.downloadURL, 75 }, 76 Release: config.Release{ 77 GitLab: tt.repo, 78 }, 79 }) 80 client, err := NewGitLab(ctx, ctx.Token) 81 require.NoError(t, err) 82 83 urlTpl, err := client.ReleaseURLTemplate(ctx) 84 if tt.wantErr { 85 require.Error(t, err) 86 return 87 } 88 89 require.NoError(t, err) 90 require.Equal(t, tt.wantDownloadURL, urlTpl) 91 } 92 } 93 94 func TestGitLabURLsAPITemplate(t *testing.T) { 95 tests := []struct { 96 name string 97 apiURL string 98 wantHost string 99 }{ 100 { 101 name: "default_values", 102 wantHost: "gitlab.com", 103 }, 104 { 105 name: "speicifed_api_env_key", 106 apiURL: "https://gitlab.mycompany.com", 107 wantHost: "gitlab.mycompany.com", 108 }, 109 } 110 111 for _, tt := range tests { 112 t.Run(tt.name, func(t *testing.T) { 113 envs := []string{} 114 gitlabURLs := config.GitLabURLs{} 115 116 if tt.apiURL != "" { 117 envs = append(envs, fmt.Sprintf("GORELEASER_TEST_GITLAB_URLS_API=%s", tt.apiURL)) 118 gitlabURLs.API = "{{ .Env.GORELEASER_TEST_GITLAB_URLS_API }}" 119 } 120 121 ctx := context.New(config.Project{ 122 Env: envs, 123 GitLabURLs: gitlabURLs, 124 }) 125 126 client, err := NewGitLab(ctx, ctx.Token) 127 require.NoError(t, err) 128 129 gitlabClient, ok := client.(*gitlabClient) 130 require.True(t, ok) 131 132 require.Equal(t, tt.wantHost, gitlabClient.client.BaseURL().Host) 133 }) 134 } 135 136 t.Run("no_env_specified", func(t *testing.T) { 137 ctx := context.New(config.Project{ 138 GitLabURLs: config.GitLabURLs{ 139 API: "{{ .Env.GORELEASER_NOT_EXISTS }}", 140 }, 141 }) 142 143 _, err := NewGitLab(ctx, ctx.Token) 144 require.ErrorAs(t, err, &template.ExecError{}) 145 }) 146 147 t.Run("invalid_template", func(t *testing.T) { 148 ctx := context.New(config.Project{ 149 GitLabURLs: config.GitLabURLs{ 150 API: "{{.dddddddddd", 151 }, 152 }) 153 154 _, err := NewGitLab(ctx, ctx.Token) 155 require.Error(t, err) 156 }) 157 } 158 159 func TestGitLabURLsDownloadTemplate(t *testing.T) { 160 tests := []struct { 161 name string 162 usePackageRegistry bool 163 downloadURL string 164 wantURL string 165 wantErr bool 166 }{ 167 { 168 name: "empty_download_url", 169 wantURL: "/", 170 }, 171 { 172 name: "download_url_template", 173 downloadURL: "{{ .Env.GORELEASER_TEST_GITLAB_URLS_DOWNLOAD }}", 174 wantURL: "https://gitlab.mycompany.com/", 175 }, 176 { 177 name: "download_url_template_invalid_value", 178 downloadURL: "{{ .Eenv.GORELEASER_NOT_EXISTS }}", 179 wantErr: true, 180 }, 181 { 182 name: "download_url_template_invalid", 183 downloadURL: "{{.dddddddddd", 184 wantErr: true, 185 }, 186 { 187 name: "download_url_string", 188 downloadURL: "https://gitlab.mycompany.com", 189 wantURL: "https://gitlab.mycompany.com/", 190 }, 191 { 192 name: "url_registry", 193 wantURL: "/api/v4/projects/test%2Ftest/packages/generic/projectname/v1%2E0%2E0/test", 194 usePackageRegistry: true, 195 }, 196 } 197 198 for _, tt := range tests { 199 t.Run(tt.name, func(t *testing.T) { 200 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 201 defer fmt.Fprint(w, "{}") 202 defer w.WriteHeader(http.StatusOK) 203 defer r.Body.Close() 204 205 if !strings.Contains(r.URL.Path, "assets/links") { 206 _, _ = io.Copy(io.Discard, r.Body) 207 return 208 } 209 210 b, err := io.ReadAll(r.Body) 211 require.NoError(t, err) 212 213 reqBody := map[string]interface{}{} 214 err = json.Unmarshal(b, &reqBody) 215 require.NoError(t, err) 216 217 url := reqBody["url"].(string) 218 require.Truef(t, strings.HasSuffix(url, tt.wantURL), "expected %q to end with %q", url, tt.wantURL) 219 })) 220 defer srv.Close() 221 222 ctx := context.New(config.Project{ 223 ProjectName: "projectname", 224 Env: []string{ 225 "GORELEASER_TEST_GITLAB_URLS_DOWNLOAD=https://gitlab.mycompany.com", 226 }, 227 Release: config.Release{ 228 GitLab: config.Repo{ 229 Owner: "test", 230 Name: "test", 231 }, 232 }, 233 GitLabURLs: config.GitLabURLs{ 234 API: srv.URL, 235 Download: tt.downloadURL, 236 UsePackageRegistry: tt.usePackageRegistry, 237 }, 238 }) 239 240 ctx.Version = "v1.0.0" 241 242 tmpFile, err := os.CreateTemp(t.TempDir(), "") 243 require.NoError(t, err) 244 245 client, err := NewGitLab(ctx, ctx.Token) 246 require.NoError(t, err) 247 248 err = client.Upload(ctx, "1234", &artifact.Artifact{Name: "test", Path: "some-path"}, tmpFile) 249 if tt.wantErr { 250 require.Error(t, err) 251 return 252 } 253 require.NoError(t, err) 254 }) 255 } 256 } 257 258 func TestGitLabCreateReleaseUknownHost(t *testing.T) { 259 ctx := context.New(config.Project{ 260 Release: config.Release{ 261 GitLab: config.Repo{ 262 Owner: "owner", 263 Name: "name", 264 }, 265 }, 266 GitLabURLs: config.GitLabURLs{ 267 API: "http://goreleaser-notexists", 268 }, 269 }) 270 client, err := NewGitLab(ctx, "test-token") 271 require.NoError(t, err) 272 273 _, err = client.CreateRelease(ctx, "body") 274 require.Error(t, err) 275 } 276 277 func TestGitLabCreateReleaseReleaseNotExists(t *testing.T) { 278 notExistsStatusCodes := []int{http.StatusNotFound, http.StatusForbidden} 279 280 for _, tt := range notExistsStatusCodes { 281 t.Run(strconv.Itoa(tt), func(t *testing.T) { 282 totalRequests := 0 283 createdRelease := false 284 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 285 defer r.Body.Close() 286 totalRequests++ 287 288 if !strings.Contains(r.URL.Path, "releases") { 289 w.WriteHeader(http.StatusOK) 290 fmt.Fprint(w, "{}") 291 return 292 } 293 294 // Check if release exists 295 if r.Method == http.MethodGet { 296 w.WriteHeader(tt) 297 fmt.Fprint(w, "{}") 298 return 299 } 300 301 // Create release if it doens't exists 302 if r.Method == http.MethodPost { 303 createdRelease = true 304 w.WriteHeader(http.StatusOK) 305 fmt.Fprint(w, "{}") 306 return 307 } 308 309 require.FailNow(t, "should not reach here") 310 })) 311 defer srv.Close() 312 313 ctx := context.New(config.Project{ 314 GitLabURLs: config.GitLabURLs{ 315 API: srv.URL, 316 }, 317 }) 318 client, err := NewGitLab(ctx, "test-token") 319 require.NoError(t, err) 320 321 _, err = client.CreateRelease(ctx, "body") 322 require.NoError(t, err) 323 require.True(t, createdRelease) 324 require.Equal(t, 3, totalRequests) 325 }) 326 } 327 } 328 329 func TestGitLabCreateReleaseUnkownHTTPError(t *testing.T) { 330 totalRequests := 0 331 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 332 totalRequests++ 333 defer r.Body.Close() 334 335 w.WriteHeader(http.StatusUnprocessableEntity) 336 fmt.Fprint(w, "{}") 337 })) 338 defer srv.Close() 339 340 ctx := context.New(config.Project{ 341 GitLabURLs: config.GitLabURLs{ 342 API: srv.URL, 343 }, 344 }) 345 client, err := NewGitLab(ctx, "test-token") 346 require.NoError(t, err) 347 348 _, err = client.CreateRelease(ctx, "body") 349 require.Error(t, err) 350 require.Equal(t, 2, totalRequests) 351 } 352 353 func TestGitlabGetDefaultBranch(t *testing.T) { 354 totalRequests := 0 355 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 356 totalRequests++ 357 defer r.Body.Close() 358 359 // Assume the request to create a branch was good 360 w.WriteHeader(http.StatusOK) 361 fmt.Fprint(w, "{}") 362 })) 363 defer srv.Close() 364 365 ctx := context.New(config.Project{ 366 GitLabURLs: config.GitLabURLs{ 367 API: srv.URL, 368 }, 369 }) 370 client, err := NewGitLab(ctx, "test-token") 371 require.NoError(t, err) 372 repo := Repo{ 373 Owner: "someone", 374 Name: "something", 375 Branch: "somebranch", 376 } 377 378 _, err = client.GetDefaultBranch(ctx, repo) 379 require.NoError(t, err) 380 require.Equal(t, 2, totalRequests) 381 } 382 383 func TestGitlabGetDefaultBranchErr(t *testing.T) { 384 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 385 defer r.Body.Close() 386 387 // Assume the request to create a branch was good 388 w.WriteHeader(http.StatusNotImplemented) 389 fmt.Fprint(w, "{}") 390 })) 391 defer srv.Close() 392 393 ctx := context.New(config.Project{ 394 GitLabURLs: config.GitLabURLs{ 395 API: srv.URL, 396 }, 397 }) 398 client, err := NewGitLab(ctx, "test-token") 399 require.NoError(t, err) 400 repo := Repo{ 401 Owner: "someone", 402 Name: "something", 403 Branch: "somebranch", 404 } 405 406 _, err = client.GetDefaultBranch(ctx, repo) 407 require.Error(t, err) 408 } 409 410 func TestGitlabChangelog(t *testing.T) { 411 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 412 if strings.HasSuffix(r.URL.Path, "projects/someone/something/repository/compare") { 413 r, err := os.Open("testdata/gitlab/compare.json") 414 require.NoError(t, err) 415 _, err = io.Copy(w, r) 416 require.NoError(t, err) 417 return 418 } 419 defer r.Body.Close() 420 })) 421 defer srv.Close() 422 423 ctx := context.New(config.Project{ 424 GitLabURLs: config.GitLabURLs{ 425 API: srv.URL, 426 }, 427 }) 428 client, err := NewGitLab(ctx, "test-token") 429 require.NoError(t, err) 430 repo := Repo{ 431 Owner: "someone", 432 Name: "something", 433 Branch: "somebranch", 434 } 435 436 log, err := client.Changelog(ctx, repo, "v1.0.0", "v1.1.0") 437 require.NoError(t, err) 438 require.Equal(t, "6dcb09b5: Fix all the bugs (Joey User <joey@user.edu>)", log) 439 } 440 441 func TestGitlabCreateFile(t *testing.T) { 442 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 443 // Handle the test where we know the branch 444 if strings.HasSuffix(r.URL.Path, "projects/someone/something/repository/files/newfile.txt") { 445 _, err := io.Copy(w, strings.NewReader(`{ "file_path": "newfile.txt", "branch": "somebranch" }`)) 446 require.NoError(t, err) 447 return 448 } 449 // Handle the test where we detect the branch 450 if strings.HasSuffix(r.URL.Path, "projects/someone/something/repository/files/newfile-in-default.txt") { 451 _, err := io.Copy(w, strings.NewReader(`{ "file_path": "newfile.txt", "branch": "main" }`)) 452 require.NoError(t, err) 453 return 454 } 455 // File of doooom...gets created, but 404s when getting fetched 456 if strings.HasSuffix(r.URL.Path, "projects/someone/something/repository/files/doomed-file-404.txt") { 457 if r.Method == "PUT" { 458 _, err := io.Copy(w, strings.NewReader(`{ "file_path": "doomed-file-404.txt", "branch": "main" }`)) 459 require.NoError(t, err) 460 } else { 461 w.WriteHeader(http.StatusNotFound) 462 } 463 return 464 } 465 466 defer r.Body.Close() 467 })) 468 defer srv.Close() 469 470 ctx := context.New(config.Project{ 471 GitLabURLs: config.GitLabURLs{ 472 API: srv.URL, 473 }, 474 }) 475 476 client, err := NewGitLab(ctx, "test-token") 477 require.NoError(t, err) 478 479 // Test using an arbitrary branch 480 repo := Repo{ 481 Owner: "someone", 482 Name: "something", 483 Branch: "somebranch", 484 } 485 486 err = client.CreateFile(ctx, config.CommitAuthor{Name: repo.Owner}, repo, []byte("Hello there"), "newfile.txt", "test: test commit") 487 require.NoError(t, err) 488 489 // Test detecting the default branch 490 repo = Repo{ 491 Owner: "someone", 492 Name: "something", 493 // Note there is no branch here, gonna try and guess it! 494 } 495 496 err = client.CreateFile(ctx, config.CommitAuthor{Name: repo.Owner}, repo, []byte("Hello there"), "newfile-in-default.txt", "test: test commit") 497 require.NoError(t, err) 498 499 // Test a doomed file. This is a file that is 'successfully' created, but returns a 404 when trying to fetch 500 repo = Repo{ 501 Owner: "someone", 502 Name: "something", 503 Branch: "doomed", 504 } 505 506 err = client.CreateFile(ctx, config.CommitAuthor{Name: repo.Owner}, repo, []byte("Hello there"), "doomed-file-404.txt", "test: test commit") 507 require.Error(t, err) 508 } 509 510 func TestCloseMileston(t *testing.T) { 511 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 512 if strings.HasSuffix(r.URL.Path, "projects/someone/something/milestones") { 513 r, err := os.Open("testdata/gitlab/milestones.json") 514 require.NoError(t, err) 515 _, err = io.Copy(w, r) 516 require.NoError(t, err) 517 return 518 } else if strings.HasSuffix(r.URL.Path, "projects/someone/something/milestones/12") { 519 r, err := os.Open("testdata/gitlab/milestone.json") 520 require.NoError(t, err) 521 _, err = io.Copy(w, r) 522 require.NoError(t, err) 523 return 524 } 525 defer r.Body.Close() 526 })) 527 defer srv.Close() 528 529 ctx := context.New(config.Project{ 530 GitLabURLs: config.GitLabURLs{ 531 API: srv.URL, 532 }, 533 }) 534 client, err := NewGitLab(ctx, "test-token") 535 require.NoError(t, err) 536 537 repo := Repo{ 538 Owner: "someone", 539 Name: "something", 540 } 541 542 err = client.CloseMilestone(ctx, repo, "10.0") 543 require.NoError(t, err) 544 545 // Be sure to error on missing milestones 546 err = client.CloseMilestone(ctx, repo, "never-will-exist") 547 require.Error(t, err) 548 } 549 550 func TestCheckUseJobToken(t *testing.T) { 551 tests := []struct { 552 useJobToken bool 553 token string 554 ciToken string 555 want bool 556 desc string 557 name string 558 }{ 559 { 560 useJobToken: true, 561 token: "real-ci-token", 562 ciToken: "real-ci-token", 563 desc: "token and CI_JOB_TOKEN match so should return true", 564 want: true, 565 name: "UseJobToken-tokens-equal", 566 }, 567 { 568 useJobToken: true, 569 token: "some-random-token", 570 ciToken: "real-ci-token", 571 desc: "token and CI_JOB_TOKEN do NOT match so should return false", 572 want: false, 573 name: "UseJobToken-tokens-diff", 574 }, 575 { 576 useJobToken: false, 577 token: "real-ci-token", 578 ciToken: "real-ci-token", 579 desc: "token and CI_JOB_TOKEN match, however UseJobToken is set to false, so return false", 580 want: false, 581 name: "NoUseJobToken-tokens-equal", 582 }, 583 { 584 useJobToken: false, 585 token: "real-ci-token", 586 ciToken: "real-ci-token", 587 desc: "token and CI_JOB_TOKEN do not match, and UseJobToken is set to false, should return false", 588 want: false, 589 name: "NoUseJobToken-tokens-diff", 590 }, 591 } 592 for _, tt := range tests { 593 t.Run(tt.name, func(t *testing.T) { 594 t.Setenv("CI_JOB_TOKEN", tt.ciToken) 595 ctx := context.New(config.Project{ 596 GitLabURLs: config.GitLabURLs{ 597 UseJobToken: tt.useJobToken, 598 }, 599 }) 600 got := checkUseJobToken(*ctx, tt.token) 601 require.Equal(t, tt.want, got, tt.desc) 602 }) 603 } 604 }