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 }