github.com/grafana/pyroscope@v1.18.0/pkg/frontend/vcs/github_test.go (about)

     1  package vcs
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/url"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/require"
    13  	"golang.org/x/oauth2"
    14  )
    15  
    16  func Test_githubAuthToken_toOAuthToken(t *testing.T) {
    17  	gat := githubAuthToken{
    18  		AccessToken:           "my_access_token",
    19  		ExpiresIn:             60 * time.Second, // 1 minute
    20  		RefreshToken:          "my_refresh_token",
    21  		RefreshTokenExpiresIn: 120 * time.Second, // 2 minutes
    22  		Scope:                 "refresh_token",
    23  		TokenType:             "bearer",
    24  	}
    25  
    26  	want := &oauth2.Token{
    27  		AccessToken:  "my_access_token",
    28  		TokenType:    "bearer",
    29  		RefreshToken: "my_refresh_token",
    30  		Expiry:       time.Now().Add(gat.ExpiresIn),
    31  	}
    32  
    33  	got := gat.toOAuthToken()
    34  
    35  	require.GreaterOrEqual(t, got.Expiry.UnixMilli(), want.Expiry.UnixMilli())
    36  	got.Expiry = want.Expiry
    37  
    38  	require.Equal(t, want, got)
    39  }
    40  
    41  func Test_refreshGithubToken(t *testing.T) {
    42  	want := githubAuthToken{
    43  		AccessToken:           "my_access_token",
    44  		ExpiresIn:             60 * time.Second, // 1 minute
    45  		RefreshToken:          "my_refresh_token",
    46  		RefreshTokenExpiresIn: 120 * time.Second, // 2 minutes
    47  		Scope:                 "refresh_token",
    48  		TokenType:             "bearer",
    49  	}
    50  	fakeGithubAPI := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    51  		w.Header().Set("Content-Type", "application/json")
    52  
    53  		query := (&url.URL{}).Query()
    54  		query.Add("access_token", want.AccessToken)
    55  		query.Add("expires_in", fmt.Sprintf("%d", int(want.ExpiresIn.Seconds())))
    56  		query.Add("refresh_token", want.RefreshToken)
    57  		query.Add("refresh_token_expires_in", fmt.Sprintf("%d", int(want.RefreshTokenExpiresIn.Seconds())))
    58  		query.Add("scope", want.Scope)
    59  		query.Add("token_type", want.TokenType)
    60  
    61  		_, err := w.Write([]byte(query.Encode()))
    62  		require.NoError(t, err)
    63  	}))
    64  	defer fakeGithubAPI.Close()
    65  
    66  	req, err := http.NewRequest("POST", fakeGithubAPI.URL, nil)
    67  	require.NoError(t, err)
    68  
    69  	got, err := refreshGithubToken(req, http.DefaultClient)
    70  	require.NoError(t, err)
    71  	require.Equal(t, want, *got)
    72  }
    73  
    74  func Test_buildGithubRefreshRequest(t *testing.T) {
    75  	oldToken := &oauth2.Token{
    76  		AccessToken:  "my_access_token",
    77  		TokenType:    "my_token_type",
    78  		RefreshToken: "my_refresh_token",
    79  		Expiry:       time.Unix(1713298947, 0), // 2024-04-16T20:22:27.346Z
    80  	}
    81  
    82  	// Override env vars.
    83  	githubAppClientID = "my_github_client_id"
    84  	githubAppClientSecret = "my_github_client_secret"
    85  
    86  	got, err := buildGithubRefreshRequest(context.Background(), oldToken)
    87  	require.NoError(t, err)
    88  
    89  	wantQuery := url.Values{
    90  		"client_id":     {githubAppClientID},
    91  		"client_secret": {githubAppClientSecret},
    92  		"grant_type":    {"refresh_token"},
    93  		"refresh_token": {oldToken.RefreshToken},
    94  	}
    95  	require.Equal(t, nil, got.Body)
    96  	require.Equal(t, wantQuery, got.URL.Query())
    97  
    98  	wantURL, err := url.Parse(githubRefreshURL)
    99  	require.NoError(t, err)
   100  	require.Equal(t, wantURL.Host, got.URL.Host)
   101  	require.Equal(t, wantURL.Path, got.URL.Path)
   102  }
   103  
   104  func Test_githubAuthTokenFromFormURLEncoded(t *testing.T) {
   105  	tests := []struct {
   106  		Name       string
   107  		Query      url.Values
   108  		Want       *githubAuthToken
   109  		WantErrMsg string
   110  	}{
   111  		{
   112  			Name: "valid query",
   113  			Query: url.Values{
   114  				"access_token":             {"my_access_token"},
   115  				"expires_in":               {"60"},
   116  				"refresh_token":            {"my_refresh_token"},
   117  				"refresh_token_expires_in": {"120"},
   118  				"scope":                    {"refresh_token"},
   119  				"token_type":               {"bearer"},
   120  			},
   121  			Want: &githubAuthToken{
   122  				AccessToken:           "my_access_token",
   123  				ExpiresIn:             60 * time.Second,
   124  				RefreshToken:          "my_refresh_token",
   125  				RefreshTokenExpiresIn: 120 * time.Second,
   126  				Scope:                 "refresh_token",
   127  				TokenType:             "bearer",
   128  			},
   129  		},
   130  		{
   131  			Name: "missing access_token",
   132  			Query: url.Values{
   133  				"expires_in":               {"60"},
   134  				"refresh_token":            {"my_refresh_token"},
   135  				"refresh_token_expires_in": {"120"},
   136  				"scope":                    {"refresh_token"},
   137  				"token_type":               {"bearer"},
   138  			},
   139  			WantErrMsg: "missing key: access_token",
   140  		},
   141  		{
   142  			Name: "missing expires_in",
   143  			Query: url.Values{
   144  				"access_token":             {"my_access_token"},
   145  				"refresh_token":            {"my_refresh_token"},
   146  				"refresh_token_expires_in": {"120"},
   147  				"scope":                    {"refresh_token"},
   148  				"token_type":               {"bearer"},
   149  			},
   150  			WantErrMsg: "missing key: expires_in",
   151  		},
   152  		{
   153  			Name: "missing refresh_token",
   154  			Query: url.Values{
   155  				"access_token":             {"my_access_token"},
   156  				"expires_in":               {"60"},
   157  				"refresh_token_expires_in": {"120"},
   158  				"scope":                    {"refresh_token"},
   159  				"token_type":               {"bearer"},
   160  			},
   161  			WantErrMsg: "missing key: refresh_token",
   162  		},
   163  		{
   164  			Name: "missing refresh_token_expires_in",
   165  			Query: url.Values{
   166  				"access_token":  {"my_access_token"},
   167  				"expires_in":    {"60"},
   168  				"refresh_token": {"my_refresh_token"},
   169  				"scope":         {"refresh_token"},
   170  				"token_type":    {"bearer"},
   171  			},
   172  			WantErrMsg: "missing key: refresh_token_expires_in",
   173  		},
   174  		{
   175  			Name: "missing scope",
   176  			Query: url.Values{
   177  				"access_token":             {"my_access_token"},
   178  				"expires_in":               {"60"},
   179  				"refresh_token":            {"my_refresh_token"},
   180  				"refresh_token_expires_in": {"120"},
   181  				"token_type":               {"bearer"},
   182  			},
   183  			WantErrMsg: "missing key: scope",
   184  		},
   185  		{
   186  			Name: "missing token_type",
   187  			Query: url.Values{
   188  				"access_token":             {"my_access_token"},
   189  				"expires_in":               {"60"},
   190  				"refresh_token":            {"my_refresh_token"},
   191  				"refresh_token_expires_in": {"120"},
   192  				"scope":                    {"refresh_token"},
   193  			},
   194  			WantErrMsg: "missing key: token_type",
   195  		},
   196  		{
   197  			Name: "invalid expires_in",
   198  			Query: url.Values{
   199  				"access_token":             {"my_access_token"},
   200  				"expires_in":               {"not_a_number"},
   201  				"refresh_token":            {"my_refresh_token"},
   202  				"refresh_token_expires_in": {"120"},
   203  				"scope":                    {"refresh_token"},
   204  				"token_type":               {"bearer"},
   205  			},
   206  			WantErrMsg: "failed to parse expires_in: strconv.Atoi: parsing \"not_a_number\": invalid syntax",
   207  		},
   208  		{
   209  			Name: "invalid refresh_token_expires_in",
   210  			Query: url.Values{
   211  				"access_token":             {"my_access_token"},
   212  				"expires_in":               {"60"},
   213  				"refresh_token":            {"my_refresh_token"},
   214  				"refresh_token_expires_in": {"not_a_number"},
   215  				"scope":                    {"refresh_token"},
   216  				"token_type":               {"bearer"},
   217  			},
   218  			WantErrMsg: "failed to parse refresh_token_expires_in: strconv.Atoi: parsing \"not_a_number\": invalid syntax",
   219  		},
   220  	}
   221  
   222  	for _, tt := range tests {
   223  		t.Run(tt.Name, func(t *testing.T) {
   224  			gat, err := githubAuthTokenFromFormURLEncoded(tt.Query)
   225  			if tt.WantErrMsg != "" {
   226  				require.Error(t, err)
   227  				require.EqualError(t, err, tt.WantErrMsg)
   228  			} else {
   229  				require.NoError(t, err)
   230  				require.Equal(t, tt.Want, gat)
   231  			}
   232  		})
   233  	}
   234  }