github.com/argoproj/argo-cd/v2@v2.10.9/applicationset/services/scm_provider/bitbucket_server_test.go (about) 1 package scm_provider 2 3 import ( 4 "context" 5 "io" 6 "net/http" 7 "net/http/httptest" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 ) 12 13 func defaultHandler(t *testing.T) func(http.ResponseWriter, *http.Request) { 14 return func(w http.ResponseWriter, r *http.Request) { 15 w.Header().Set("Content-Type", "application/json") 16 var err error 17 switch r.RequestURI { 18 case "/rest/api/1.0/projects/PROJECT/repos?limit=100": 19 _, err = io.WriteString(w, `{ 20 "size": 1, 21 "limit": 100, 22 "isLastPage": true, 23 "values": [ 24 { 25 "id": 1, 26 "name": "REPO", 27 "project": { 28 "key": "PROJECT" 29 }, 30 "links": { 31 "clone": [ 32 { 33 "href": "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 34 "name": "ssh" 35 }, 36 { 37 "href": "https://mycompany.bitbucket.org/scm/PROJECT/REPO.git", 38 "name": "http" 39 } 40 ] 41 } 42 } 43 ], 44 "start": 0 45 }`) 46 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches?limit=100": 47 _, err = io.WriteString(w, `{ 48 "size": 1, 49 "limit": 100, 50 "isLastPage": true, 51 "values": [ 52 { 53 "id": "refs/heads/main", 54 "displayId": "main", 55 "type": "BRANCH", 56 "latestCommit": "8d51122def5632836d1cb1026e879069e10a1e13", 57 "latestChangeset": "8d51122def5632836d1cb1026e879069e10a1e13", 58 "isDefault": true 59 } 60 ], 61 "start": 0 62 }`) 63 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": 64 _, err = io.WriteString(w, `{ 65 "id": "refs/heads/main", 66 "displayId": "main", 67 "type": "BRANCH", 68 "latestCommit": "8d51122def5632836d1cb1026e879069e10a1e13", 69 "latestChangeset": "8d51122def5632836d1cb1026e879069e10a1e13", 70 "isDefault": true 71 }`) 72 default: 73 t.Fail() 74 } 75 if err != nil { 76 t.Fail() 77 } 78 } 79 } 80 81 func verifyDefaultRepo(t *testing.T, err error, repos []*Repository) { 82 assert.NoError(t, err) 83 assert.Equal(t, 1, len(repos)) 84 assert.Equal(t, Repository{ 85 Organization: "PROJECT", 86 Repository: "REPO", 87 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 88 Branch: "main", 89 SHA: "8d51122def5632836d1cb1026e879069e10a1e13", 90 Labels: []string{}, 91 RepositoryId: 1, 92 }, *repos[0]) 93 } 94 95 func TestListReposNoAuth(t *testing.T) { 96 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 97 assert.Empty(t, r.Header.Get("Authorization")) 98 defaultHandler(t)(w, r) 99 })) 100 defer ts.Close() 101 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) 102 assert.NoError(t, err) 103 repos, err := provider.ListRepos(context.Background(), "ssh") 104 verifyDefaultRepo(t, err, repos) 105 } 106 107 func TestListReposPagination(t *testing.T) { 108 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 109 assert.Empty(t, r.Header.Get("Authorization")) 110 var err error 111 switch r.RequestURI { 112 case "/rest/api/1.0/projects/PROJECT/repos?limit=100": 113 _, err = io.WriteString(w, `{ 114 "size": 1, 115 "limit": 100, 116 "isLastPage": false, 117 "values": [ 118 { 119 "id": 100, 120 "name": "REPO", 121 "project": { 122 "key": "PROJECT" 123 }, 124 "links": { 125 "clone": [ 126 { 127 "href": "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 128 "name": "ssh" 129 }, 130 { 131 "href": "https://mycompany.bitbucket.org/scm/PROJECT/REPO.git", 132 "name": "http" 133 } 134 ] 135 } 136 } 137 ], 138 "start": 0, 139 "nextPageStart": 200 140 }`) 141 case "/rest/api/1.0/projects/PROJECT/repos?limit=100&start=200": 142 _, err = io.WriteString(w, `{ 143 "size": 1, 144 "limit": 100, 145 "isLastPage": true, 146 "values": [ 147 { 148 "id": 200, 149 "name": "REPO2", 150 "project": { 151 "key": "PROJECT" 152 }, 153 "links": { 154 "clone": [ 155 { 156 "href": "ssh://git@mycompany.bitbucket.org/PROJECT/REPO2.git", 157 "name": "ssh" 158 }, 159 { 160 "href": "https://mycompany.bitbucket.org/scm/PROJECT/REPO2.git", 161 "name": "http" 162 } 163 ] 164 } 165 } 166 ], 167 "start": 200 168 }`) 169 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": 170 _, err = io.WriteString(w, `{ 171 "id": "refs/heads/main", 172 "displayId": "main", 173 "type": "BRANCH", 174 "latestCommit": "8d51122def5632836d1cb1026e879069e10a1e13", 175 "isDefault": true 176 }`) 177 case "/rest/api/1.0/projects/PROJECT/repos/REPO2/branches/default": 178 _, err = io.WriteString(w, `{ 179 "id": "refs/heads/development", 180 "displayId": "development", 181 "type": "BRANCH", 182 "latestCommit": "2d51122def5632836d1cb1026e879069e10a1e13", 183 "isDefault": true 184 }`) 185 default: 186 t.Fail() 187 } 188 if err != nil { 189 t.Fail() 190 } 191 })) 192 defer ts.Close() 193 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) 194 assert.NoError(t, err) 195 repos, err := provider.ListRepos(context.Background(), "ssh") 196 assert.NoError(t, err) 197 assert.Equal(t, 2, len(repos)) 198 assert.Equal(t, Repository{ 199 Organization: "PROJECT", 200 Repository: "REPO", 201 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 202 Branch: "main", 203 SHA: "8d51122def5632836d1cb1026e879069e10a1e13", 204 Labels: []string{}, 205 RepositoryId: 100, 206 }, *repos[0]) 207 208 assert.Equal(t, Repository{ 209 Organization: "PROJECT", 210 Repository: "REPO2", 211 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO2.git", 212 Branch: "development", 213 SHA: "2d51122def5632836d1cb1026e879069e10a1e13", 214 Labels: []string{}, 215 RepositoryId: 200, 216 }, *repos[1]) 217 } 218 219 func TestGetBranchesBranchPagination(t *testing.T) { 220 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 221 assert.Empty(t, r.Header.Get("Authorization")) 222 switch r.RequestURI { 223 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches?limit=100": 224 _, err := io.WriteString(w, `{ 225 "size": 1, 226 "limit": 100, 227 "isLastPage": false, 228 "values": [ 229 { 230 "id": "refs/heads/main", 231 "displayId": "main", 232 "type": "BRANCH", 233 "latestCommit": "8d51122def5632836d1cb1026e879069e10a1e13", 234 "latestChangeset": "8d51122def5632836d1cb1026e879069e10a1e13", 235 "isDefault": true 236 } 237 ], 238 "start": 0, 239 "nextPageStart": 200 240 }`) 241 if err != nil { 242 t.Fail() 243 } 244 return 245 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches?limit=100&start=200": 246 _, err := io.WriteString(w, `{ 247 "size": 1, 248 "limit": 100, 249 "isLastPage": true, 250 "values": [ 251 { 252 "id": "refs/heads/feature", 253 "displayId": "feature", 254 "type": "BRANCH", 255 "latestCommit": "9d51122def5632836d1cb1026e879069e10a1e13", 256 "latestChangeset": "9d51122def5632836d1cb1026e879069e10a1e13", 257 "isDefault": true 258 } 259 ], 260 "start": 200 261 }`) 262 if err != nil { 263 t.Fail() 264 } 265 return 266 } 267 defaultHandler(t)(w, r) 268 })) 269 defer ts.Close() 270 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) 271 assert.NoError(t, err) 272 repos, err := provider.GetBranches(context.Background(), &Repository{ 273 Organization: "PROJECT", 274 Repository: "REPO", 275 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 276 Labels: []string{}, 277 RepositoryId: 1, 278 }) 279 assert.NoError(t, err) 280 assert.Equal(t, 2, len(repos)) 281 assert.Equal(t, Repository{ 282 Organization: "PROJECT", 283 Repository: "REPO", 284 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 285 Branch: "main", 286 SHA: "8d51122def5632836d1cb1026e879069e10a1e13", 287 Labels: []string{}, 288 RepositoryId: 1, 289 }, *repos[0]) 290 291 assert.Equal(t, Repository{ 292 Organization: "PROJECT", 293 Repository: "REPO", 294 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 295 Branch: "feature", 296 SHA: "9d51122def5632836d1cb1026e879069e10a1e13", 297 Labels: []string{}, 298 RepositoryId: 1, 299 }, *repos[1]) 300 } 301 302 func TestGetBranchesDefaultOnly(t *testing.T) { 303 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 304 assert.Empty(t, r.Header.Get("Authorization")) 305 switch r.RequestURI { 306 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": 307 _, err := io.WriteString(w, `{ 308 "id": "refs/heads/default", 309 "displayId": "default", 310 "type": "BRANCH", 311 "latestCommit": "ab51122def5632836d1cb1026e879069e10a1e13", 312 "latestChangeset": "ab51122def5632836d1cb1026e879069e10a1e13", 313 "isDefault": true 314 }`) 315 if err != nil { 316 t.Fail() 317 } 318 return 319 } 320 defaultHandler(t)(w, r) 321 })) 322 defer ts.Close() 323 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) 324 assert.NoError(t, err) 325 repos, err := provider.GetBranches(context.Background(), &Repository{ 326 Organization: "PROJECT", 327 Repository: "REPO", 328 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 329 Labels: []string{}, 330 RepositoryId: 1, 331 }) 332 assert.NoError(t, err) 333 assert.Equal(t, 1, len(repos)) 334 assert.Equal(t, Repository{ 335 Organization: "PROJECT", 336 Repository: "REPO", 337 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 338 Branch: "default", 339 SHA: "ab51122def5632836d1cb1026e879069e10a1e13", 340 Labels: []string{}, 341 RepositoryId: 1, 342 }, *repos[0]) 343 } 344 345 func TestGetBranchesMissingDefault(t *testing.T) { 346 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 347 assert.Empty(t, r.Header.Get("Authorization")) 348 switch r.RequestURI { 349 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": 350 http.Error(w, "Not found", http.StatusNotFound) 351 } 352 defaultHandler(t)(w, r) 353 })) 354 defer ts.Close() 355 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) 356 assert.NoError(t, err) 357 repos, err := provider.GetBranches(context.Background(), &Repository{ 358 Organization: "PROJECT", 359 Repository: "REPO", 360 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 361 Labels: []string{}, 362 RepositoryId: 1, 363 }) 364 assert.NoError(t, err) 365 assert.Empty(t, repos) 366 } 367 368 func TestGetBranchesEmptyRepo(t *testing.T) { 369 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 370 assert.Empty(t, r.Header.Get("Authorization")) 371 switch r.RequestURI { 372 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": 373 return 374 } 375 })) 376 defer ts.Close() 377 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) 378 assert.NoError(t, err) 379 repos, err := provider.GetBranches(context.Background(), &Repository{ 380 Organization: "PROJECT", 381 Repository: "REPO", 382 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 383 Labels: []string{}, 384 RepositoryId: 1, 385 }) 386 assert.Empty(t, repos) 387 assert.NoError(t, err) 388 } 389 390 func TestGetBranchesErrorDefaultBranch(t *testing.T) { 391 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 392 assert.Empty(t, r.Header.Get("Authorization")) 393 switch r.RequestURI { 394 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": 395 http.Error(w, "Internal server error", http.StatusInternalServerError) 396 } 397 defaultHandler(t)(w, r) 398 })) 399 defer ts.Close() 400 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) 401 assert.NoError(t, err) 402 _, err = provider.GetBranches(context.Background(), &Repository{ 403 Organization: "PROJECT", 404 Repository: "REPO", 405 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 406 Labels: []string{}, 407 RepositoryId: 1, 408 }) 409 assert.Error(t, err) 410 } 411 412 func TestListReposBasicAuth(t *testing.T) { 413 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 414 assert.Equal(t, "Basic dXNlcjpwYXNzd29yZA==", r.Header.Get("Authorization")) 415 assert.Equal(t, "no-check", r.Header.Get("X-Atlassian-Token")) 416 defaultHandler(t)(w, r) 417 })) 418 defer ts.Close() 419 provider, err := NewBitbucketServerProviderBasicAuth(context.Background(), "user", "password", ts.URL, "PROJECT", true) 420 assert.NoError(t, err) 421 repos, err := provider.ListRepos(context.Background(), "ssh") 422 verifyDefaultRepo(t, err, repos) 423 } 424 425 func TestListReposDefaultBranch(t *testing.T) { 426 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 427 assert.Empty(t, r.Header.Get("Authorization")) 428 switch r.RequestURI { 429 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": 430 _, err := io.WriteString(w, `{ 431 "id": "refs/heads/default", 432 "displayId": "default", 433 "type": "BRANCH", 434 "latestCommit": "1d51122def5632836d1cb1026e879069e10a1e13", 435 "latestChangeset": "1d51122def5632836d1cb1026e879069e10a1e13", 436 "isDefault": true 437 }`) 438 if err != nil { 439 t.Fail() 440 } 441 return 442 } 443 defaultHandler(t)(w, r) 444 })) 445 defer ts.Close() 446 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) 447 assert.NoError(t, err) 448 repos, err := provider.ListRepos(context.Background(), "ssh") 449 assert.NoError(t, err) 450 assert.Equal(t, 1, len(repos)) 451 assert.Equal(t, Repository{ 452 Organization: "PROJECT", 453 Repository: "REPO", 454 URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git", 455 Branch: "default", 456 SHA: "1d51122def5632836d1cb1026e879069e10a1e13", 457 Labels: []string{}, 458 RepositoryId: 1, 459 }, *repos[0]) 460 } 461 462 func TestListReposMissingDefaultBranch(t *testing.T) { 463 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 464 assert.Empty(t, r.Header.Get("Authorization")) 465 switch r.RequestURI { 466 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": 467 http.Error(w, "Not found", http.StatusNotFound) 468 } 469 defaultHandler(t)(w, r) 470 })) 471 defer ts.Close() 472 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) 473 assert.NoError(t, err) 474 repos, err := provider.ListRepos(context.Background(), "ssh") 475 assert.NoError(t, err) 476 assert.Empty(t, repos) 477 } 478 479 func TestListReposErrorDefaultBranch(t *testing.T) { 480 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 481 assert.Empty(t, r.Header.Get("Authorization")) 482 switch r.RequestURI { 483 case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default": 484 http.Error(w, "Internal server error", http.StatusInternalServerError) 485 } 486 defaultHandler(t)(w, r) 487 })) 488 defer ts.Close() 489 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false) 490 assert.NoError(t, err) 491 _, err = provider.ListRepos(context.Background(), "ssh") 492 assert.Error(t, err) 493 } 494 495 func TestListReposCloneProtocol(t *testing.T) { 496 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 497 assert.Empty(t, r.Header.Get("Authorization")) 498 defaultHandler(t)(w, r) 499 })) 500 defer ts.Close() 501 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) 502 assert.NoError(t, err) 503 repos, err := provider.ListRepos(context.Background(), "https") 504 assert.NoError(t, err) 505 assert.Equal(t, 1, len(repos)) 506 assert.Equal(t, Repository{ 507 Organization: "PROJECT", 508 Repository: "REPO", 509 URL: "https://mycompany.bitbucket.org/scm/PROJECT/REPO.git", 510 Branch: "main", 511 SHA: "8d51122def5632836d1cb1026e879069e10a1e13", 512 Labels: []string{}, 513 RepositoryId: 1, 514 }, *repos[0]) 515 } 516 517 func TestListReposUnknownProtocol(t *testing.T) { 518 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 519 assert.Empty(t, r.Header.Get("Authorization")) 520 defaultHandler(t)(w, r) 521 })) 522 defer ts.Close() 523 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) 524 assert.NoError(t, err) 525 _, errProtocol := provider.ListRepos(context.Background(), "http") 526 assert.NotNil(t, errProtocol) 527 } 528 529 func TestBitbucketServerHasPath(t *testing.T) { 530 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 531 var err error 532 switch r.RequestURI { 533 case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/pkg?at=main&limit=100&type=true": 534 _, err = io.WriteString(w, `{"type":"DIRECTORY"}`) 535 case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/pkg/?at=main&limit=100&type=true": 536 _, err = io.WriteString(w, `{"type":"DIRECTORY"}`) 537 case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/anotherpkg/file.txt?at=main&limit=100&type=true": 538 _, err = io.WriteString(w, `{"type":"FILE"}`) 539 540 case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/anotherpkg/missing.txt?at=main&limit=100&type=true": 541 http.Error(w, "The path \"anotherpkg/missing.txt\" does not exist at revision \"main\"", http.StatusNotFound) 542 case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/notathing?at=main&limit=100&type=true": 543 http.Error(w, "The path \"notathing\" does not exist at revision \"main\"", http.StatusNotFound) 544 545 case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/return-redirect?at=main&limit=100&type=true": 546 http.Redirect(w, r, "http://"+r.Host+"/rest/api/1.0/projects/PROJECT/repos/REPO/browse/redirected?at=main&limit=100&type=true", http.StatusMovedPermanently) 547 case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/redirected?at=main&limit=100&type=true": 548 _, err = io.WriteString(w, `{"type":"DIRECTORY"}`) 549 550 case "/rest/api/1.0/projects/PROJECT/repos/REPO/browse/unauthorized-response?at=main&limit=100&type=true": 551 http.Error(w, "Authentication failed", http.StatusUnauthorized) 552 553 default: 554 t.Fail() 555 } 556 if err != nil { 557 t.Fail() 558 } 559 })) 560 defer ts.Close() 561 provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", true) 562 assert.NoError(t, err) 563 repo := &Repository{ 564 Organization: "PROJECT", 565 Repository: "REPO", 566 Branch: "main", 567 } 568 ok, err := provider.RepoHasPath(context.Background(), repo, "pkg") 569 assert.NoError(t, err) 570 assert.True(t, ok) 571 572 ok, err = provider.RepoHasPath(context.Background(), repo, "pkg/") 573 assert.NoError(t, err) 574 assert.True(t, ok) 575 576 ok, err = provider.RepoHasPath(context.Background(), repo, "anotherpkg/file.txt") 577 assert.NoError(t, err) 578 assert.True(t, ok) 579 580 ok, err = provider.RepoHasPath(context.Background(), repo, "anotherpkg/missing.txt") 581 assert.NoError(t, err) 582 assert.False(t, ok) 583 584 ok, err = provider.RepoHasPath(context.Background(), repo, "notathing") 585 assert.NoError(t, err) 586 assert.False(t, ok) 587 588 ok, err = provider.RepoHasPath(context.Background(), repo, "return-redirect") 589 assert.NoError(t, err) 590 assert.True(t, ok) 591 592 _, err = provider.RepoHasPath(context.Background(), repo, "unauthorized-response") 593 assert.Error(t, err) 594 }