github.com/argoproj/argo-cd/v3@v3.2.1/applicationset/services/scm_provider/bitbucket_server_test.go (about)

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