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

     1  package pull_request
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    14  )
    15  
    16  func defaultHandlerCloud(t *testing.T) func(http.ResponseWriter, *http.Request) {
    17  	t.Helper()
    18  	return func(w http.ResponseWriter, r *http.Request) {
    19  		w.Header().Set("Content-Type", "application/json")
    20  		var err error
    21  		switch r.RequestURI {
    22  		case "/repositories/OWNER/REPO/pullrequests/":
    23  			_, err = io.WriteString(w, `{
    24  					"size": 1,
    25  					"pagelen": 10,
    26  					"page": 1,
    27  					"values": [
    28  						{
    29  							"id": 101,
    30  							"title": "feat(foo-bar)",
    31  							"source": {
    32  								"branch": {
    33  									"name": "feature/foo-bar"
    34  								},
    35  								"commit": {
    36  									"type": "commit",
    37  									"hash": "1a8dd249c04a"
    38  								}
    39  							},
    40  							"author": {
    41  								"nickname": "testName"
    42  							}
    43  						}
    44  					]
    45  				}`)
    46  		default:
    47  			t.Fail()
    48  		}
    49  		if err != nil {
    50  			t.Fail()
    51  		}
    52  	}
    53  }
    54  
    55  func TestParseUrlEmptyUrl(t *testing.T) {
    56  	url, err := parseURL("")
    57  	bitbucketURL, _ := url.Parse("https://api.bitbucket.org/2.0")
    58  
    59  	require.NoError(t, err)
    60  	assert.Equal(t, bitbucketURL, url)
    61  }
    62  
    63  func TestInvalidBaseUrlBasicAuthCloud(t *testing.T) {
    64  	_, err := NewBitbucketCloudServiceBasicAuth("http:// example.org", "user", "password", "OWNER", "REPO")
    65  
    66  	require.Error(t, err)
    67  }
    68  
    69  func TestInvalidBaseUrlBearerTokenCloud(t *testing.T) {
    70  	_, err := NewBitbucketCloudServiceBearerToken("http:// example.org", "TOKEN", "OWNER", "REPO")
    71  
    72  	require.Error(t, err)
    73  }
    74  
    75  func TestInvalidBaseUrlNoAuthCloud(t *testing.T) {
    76  	_, err := NewBitbucketCloudServiceNoAuth("http:// example.org", "OWNER", "REPO")
    77  
    78  	require.Error(t, err)
    79  }
    80  
    81  func TestListPullRequestBearerTokenCloud(t *testing.T) {
    82  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    83  		assert.Equal(t, "Bearer TOKEN", r.Header.Get("Authorization"))
    84  		defaultHandlerCloud(t)(w, r)
    85  	}))
    86  	defer ts.Close()
    87  	svc, err := NewBitbucketCloudServiceBearerToken(ts.URL, "TOKEN", "OWNER", "REPO")
    88  	require.NoError(t, err)
    89  	pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
    90  	require.NoError(t, err)
    91  	assert.Len(t, pullRequests, 1)
    92  	assert.Equal(t, 101, pullRequests[0].Number)
    93  	assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
    94  	assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
    95  	assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
    96  	assert.Equal(t, "testName", pullRequests[0].Author)
    97  }
    98  
    99  func TestListPullRequestNoAuthCloud(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  		defaultHandlerCloud(t)(w, r)
   103  	}))
   104  	defer ts.Close()
   105  	svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
   106  	require.NoError(t, err)
   107  	pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
   108  	require.NoError(t, err)
   109  	assert.Len(t, pullRequests, 1)
   110  	assert.Equal(t, 101, pullRequests[0].Number)
   111  	assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
   112  	assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
   113  	assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
   114  	assert.Equal(t, "testName", pullRequests[0].Author)
   115  }
   116  
   117  func TestListPullRequestBasicAuthCloud(t *testing.T) {
   118  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   119  		assert.Equal(t, "Basic dXNlcjpwYXNzd29yZA==", r.Header.Get("Authorization"))
   120  		defaultHandlerCloud(t)(w, r)
   121  	}))
   122  	defer ts.Close()
   123  	svc, err := NewBitbucketCloudServiceBasicAuth(ts.URL, "user", "password", "OWNER", "REPO")
   124  	require.NoError(t, err)
   125  	pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
   126  	require.NoError(t, err)
   127  	assert.Len(t, pullRequests, 1)
   128  	assert.Equal(t, 101, pullRequests[0].Number)
   129  	assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
   130  	assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
   131  	assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
   132  	assert.Equal(t, "testName", pullRequests[0].Author)
   133  }
   134  
   135  func TestListPullRequestPaginationCloud(t *testing.T) {
   136  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   137  		w.Header().Set("Content-Type", "application/json")
   138  		var err error
   139  		switch r.RequestURI {
   140  		case "/repositories/OWNER/REPO/pullrequests/":
   141  			_, err = fmt.Fprintf(w, `{
   142  				"size": 2,
   143  				"pagelen": 1,
   144  				"page": 1,
   145  				"next": "http://%s/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2",
   146  				"values": [
   147  					{
   148  						"id": 101,
   149  						"title": "feat(101)",
   150  						"source": {
   151  							"branch": {
   152  								"name": "feature-101"
   153  							},
   154  							"commit": {
   155  								"type": "commit",
   156  								"hash": "1a8dd249c04a"
   157  							}
   158  						},
   159  						"author": {
   160  							"nickname": "testName"
   161  						}
   162  					},
   163  					{
   164  						"id": 102,
   165  						"title": "feat(102)",
   166  						"source": {
   167  							"branch": {
   168  								"name": "feature-102"
   169  							},
   170  							"commit": {
   171  								"type": "commit",
   172  								"hash": "4cf807e67a6d"
   173  							}
   174  						},
   175  						"author": {
   176  							"nickname": "testName"
   177  						}
   178  					}
   179  				]
   180  			}`, r.Host)
   181  		case "/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2":
   182  			_, err = fmt.Fprintf(w, `{
   183  				"size": 2,
   184  				"pagelen": 1,
   185  				"page": 2,
   186  				"previous": "http://%s/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=1",
   187  				"values": [
   188  					{
   189  						"id": 103,
   190  						"title": "feat(103)",
   191  						"source": {
   192  							"branch": {
   193  								"name": "feature-103"
   194  							},
   195  							"commit": {
   196  								"type": "commit",
   197  								"hash": "6344d9623e3b"
   198  							}
   199  						},
   200  						"author": {
   201  							"nickname": "testName"
   202  						}
   203  					}
   204  				]
   205  			}`, r.Host)
   206  		default:
   207  			t.Fail()
   208  		}
   209  		if err != nil {
   210  			t.Fail()
   211  		}
   212  	}))
   213  	defer ts.Close()
   214  	svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
   215  	require.NoError(t, err)
   216  	pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
   217  	require.NoError(t, err)
   218  	assert.Len(t, pullRequests, 3)
   219  	assert.Equal(t, PullRequest{
   220  		Number:  101,
   221  		Title:   "feat(101)",
   222  		Branch:  "feature-101",
   223  		HeadSHA: "1a8dd249c04a",
   224  		Author:  "testName",
   225  	}, *pullRequests[0])
   226  	assert.Equal(t, PullRequest{
   227  		Number:  102,
   228  		Title:   "feat(102)",
   229  		Branch:  "feature-102",
   230  		HeadSHA: "4cf807e67a6d",
   231  		Author:  "testName",
   232  	}, *pullRequests[1])
   233  	assert.Equal(t, PullRequest{
   234  		Number:  103,
   235  		Title:   "feat(103)",
   236  		Branch:  "feature-103",
   237  		HeadSHA: "6344d9623e3b",
   238  		Author:  "testName",
   239  	}, *pullRequests[2])
   240  }
   241  
   242  func TestListResponseErrorCloud(t *testing.T) {
   243  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
   244  		w.WriteHeader(http.StatusInternalServerError)
   245  	}))
   246  	defer ts.Close()
   247  	svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
   248  	_, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
   249  	require.Error(t, err)
   250  }
   251  
   252  func TestListResponseMalformedCloud(t *testing.T) {
   253  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   254  		w.Header().Set("Content-Type", "application/json")
   255  		switch r.RequestURI {
   256  		case "/repositories/OWNER/REPO/pullrequests/":
   257  			_, err := io.WriteString(w, `[{
   258  				"size": 1,
   259  				"pagelen": 10,
   260  				"page": 1,
   261  				"values": [{ "id": 101 }]
   262  			}]`)
   263  			if err != nil {
   264  				t.Fail()
   265  			}
   266  		default:
   267  			t.Fail()
   268  		}
   269  	}))
   270  	defer ts.Close()
   271  	svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
   272  	_, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
   273  	require.Error(t, err)
   274  }
   275  
   276  func TestListResponseMalformedValuesCloud(t *testing.T) {
   277  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   278  		w.Header().Set("Content-Type", "application/json")
   279  		switch r.RequestURI {
   280  		case "/repositories/OWNER/REPO/pullrequests/":
   281  			_, err := io.WriteString(w, `{
   282  				"size": 1,
   283  				"pagelen": 10,
   284  				"page": 1,
   285  				"values": { "id": 101 }
   286  			}`)
   287  			if err != nil {
   288  				t.Fail()
   289  			}
   290  		default:
   291  			t.Fail()
   292  		}
   293  	}))
   294  	defer ts.Close()
   295  	svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
   296  	_, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
   297  	require.Error(t, err)
   298  }
   299  
   300  func TestListResponseEmptyCloud(t *testing.T) {
   301  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   302  		w.Header().Set("Content-Type", "application/json")
   303  		switch r.RequestURI {
   304  		case "/repositories/OWNER/REPO/pullrequests/":
   305  			_, err := io.WriteString(w, `{
   306  				"size": 1,
   307  				"pagelen": 10,
   308  				"page": 1,
   309  				"values": []
   310  			}`)
   311  			if err != nil {
   312  				t.Fail()
   313  			}
   314  		default:
   315  			t.Fail()
   316  		}
   317  	}))
   318  	defer ts.Close()
   319  	svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
   320  	require.NoError(t, err)
   321  	pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
   322  	require.NoError(t, err)
   323  	assert.Empty(t, pullRequests)
   324  }
   325  
   326  func TestListPullRequestBranchMatchCloud(t *testing.T) {
   327  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   328  		w.Header().Set("Content-Type", "application/json")
   329  		var err error
   330  		switch r.RequestURI {
   331  		case "/repositories/OWNER/REPO/pullrequests/":
   332  			_, err = fmt.Fprintf(w, `{
   333  				"size": 2,
   334  				"pagelen": 1,
   335  				"page": 1,
   336  				"next": "http://%s/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2",
   337  				"values": [
   338  					{
   339  						"id": 101,
   340  						"title": "feat(101)",
   341  						"source": {
   342  							"branch": {
   343  								"name": "feature-101"
   344  							},
   345  							"commit": {
   346  								"type": "commit",
   347  								"hash": "1a8dd249c04a"
   348  							}
   349  						},
   350  						"author": {
   351  							"nickname": "testName"
   352  						},
   353  						"destination": {
   354  							"branch": {
   355  								"name": "master"
   356  							}
   357  						}
   358  					},
   359  					{
   360  						"id": 200,
   361  						"title": "feat(200)",
   362  						"source": {
   363  							"branch": {
   364  								"name": "feature-200"
   365  							},
   366  							"commit": {
   367  								"type": "commit",
   368  								"hash": "4cf807e67a6d"
   369  							}
   370  						},
   371  						"author": {
   372  							"nickname": "testName"
   373  						},
   374  						"destination": {
   375  							"branch": {
   376  								"name": "branch-200"
   377  							}
   378  						}
   379  					}
   380  				]
   381  			}`, r.Host)
   382  		case "/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2":
   383  			_, err = fmt.Fprintf(w, `{
   384  				"size": 2,
   385  				"pagelen": 1,
   386  				"page": 2,
   387  				"previous": "http://%s/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=1",
   388  				"values": [
   389  					{
   390  						"id": 102,
   391  						"title": "feat(102)",
   392  						"source": {
   393  							"branch": {
   394  								"name": "feature-102"
   395  							},
   396  							"commit": {
   397  								"type": "commit",
   398  								"hash": "6344d9623e3b"
   399  							}
   400  						},
   401  						"author": {
   402  							"nickname": "testName"
   403  						},
   404  						"destination": {
   405  							"branch": {
   406  								"name": "master"
   407  							}
   408  						}
   409  					}
   410  				]
   411  			}`, r.Host)
   412  		default:
   413  			t.Fail()
   414  		}
   415  		if err != nil {
   416  			t.Fail()
   417  		}
   418  	}))
   419  	defer ts.Close()
   420  	regexp := `feature-1[\d]{2}`
   421  	svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
   422  	require.NoError(t, err)
   423  	pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{
   424  		{
   425  			BranchMatch: &regexp,
   426  		},
   427  	})
   428  	require.NoError(t, err)
   429  	assert.Len(t, pullRequests, 2)
   430  	assert.Equal(t, PullRequest{
   431  		Number:       101,
   432  		Title:        "feat(101)",
   433  		Branch:       "feature-101",
   434  		HeadSHA:      "1a8dd249c04a",
   435  		Author:       "testName",
   436  		TargetBranch: "master",
   437  	}, *pullRequests[0])
   438  	assert.Equal(t, PullRequest{
   439  		Number:       102,
   440  		Title:        "feat(102)",
   441  		Branch:       "feature-102",
   442  		HeadSHA:      "6344d9623e3b",
   443  		Author:       "testName",
   444  		TargetBranch: "master",
   445  	}, *pullRequests[1])
   446  
   447  	regexp = `.*2$`
   448  	svc, err = NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
   449  	require.NoError(t, err)
   450  	pullRequests, err = ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{
   451  		{
   452  			BranchMatch: &regexp,
   453  		},
   454  	})
   455  	require.NoError(t, err)
   456  	assert.Len(t, pullRequests, 1)
   457  	assert.Equal(t, PullRequest{
   458  		Number:       102,
   459  		Title:        "feat(102)",
   460  		Branch:       "feature-102",
   461  		HeadSHA:      "6344d9623e3b",
   462  		Author:       "testName",
   463  		TargetBranch: "master",
   464  	}, *pullRequests[0])
   465  
   466  	regexp = `[\d{2}`
   467  	svc, err = NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
   468  	require.NoError(t, err)
   469  	_, err = ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{
   470  		{
   471  			BranchMatch: &regexp,
   472  		},
   473  	})
   474  	require.Error(t, err)
   475  
   476  	regexp = `feature-2[\d]{2}`
   477  	targetRegexp := `branch.*`
   478  	pullRequests, err = ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{
   479  		{
   480  			BranchMatch:       &regexp,
   481  			TargetBranchMatch: &targetRegexp,
   482  		},
   483  	})
   484  	require.NoError(t, err)
   485  	assert.Len(t, pullRequests, 1)
   486  	assert.Equal(t, PullRequest{
   487  		Number:       200,
   488  		Title:        "feat(200)",
   489  		Branch:       "feature-200",
   490  		HeadSHA:      "4cf807e67a6d",
   491  		Author:       "testName",
   492  		TargetBranch: "branch-200",
   493  	}, *pullRequests[0])
   494  }
   495  
   496  func TestBitbucketCloudListReturnsRepositoryNotFoundError(t *testing.T) {
   497  	mux := http.NewServeMux()
   498  	server := httptest.NewServer(mux)
   499  	defer server.Close()
   500  
   501  	path := "/repositories/nonexistent/nonexistent/pullrequests/"
   502  
   503  	mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
   504  		// Return 404 status to simulate repository not found
   505  		w.WriteHeader(http.StatusNotFound)
   506  		_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
   507  	})
   508  
   509  	svc, err := NewBitbucketCloudServiceNoAuth(server.URL, "nonexistent", "nonexistent")
   510  	require.NoError(t, err)
   511  
   512  	prs, err := svc.List(t.Context())
   513  
   514  	// Should return empty pull requests list
   515  	assert.Empty(t, prs)
   516  
   517  	// Should return RepositoryNotFoundError
   518  	require.Error(t, err)
   519  	assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
   520  }