MCPcopy Create free account
hub / github.com/cli/cli / TestWithoutFollowingRedirects

Function TestWithoutFollowingRedirects

api/client_test.go:366–412  ·  view source on GitHub ↗

TestWithoutFollowingRedirects verifies that WithoutFollowingRedirects stops the request at the redirect and surfaces an HTTPError rather than following through. It also proves the redirect is not followed by recording every method the transport sees: without the option the transport would receive a

(t *testing.T)

Source from the content-addressed store, hash-verified

364// the correct behaviour for repo delete: the caller learns the operation failed rather than
365// receiving a misleading success.
366func TestWithoutFollowingRedirects(t *testing.T) {
367 newCountingTransport := func(methods *[]string) http.RoundTripper {
368 return roundTripFunc(func(req *http.Request) (*http.Response, error) {
369 *methods = append(*methods, req.Method)
370 resp := &http.Response{
371 StatusCode: http.StatusMovedPermanently,
372 Header: http.Header{"Location": []string{"https://api.github.com/repos/OWNER/RENAMED"}},
373 Body: io.NopCloser(bytes.NewBufferString("")),
374 Request: req,
375 }
376 return resp, nil
377 })
378 }
379
380 t.Run("without option: redirect is followed, DELETE becomes GET", func(t *testing.T) {
381 var methods []string
382 transport := newCountingTransport(&methods)
383 // Second request (the followed redirect) must also be handled by the transport.
384 redirectTransport := redirectFollowingTransport{
385 inner: transport,
386 methods: &methods,
387 }
388 client := NewClientFromHTTP(&http.Client{Transport: redirectTransport})
389
390 _, _ = client.Request("github.com", http.MethodDelete, "repos/OWNER/REPO", nil)
391
392 // The transport sees DELETE then GET because Go's default policy demotes DELETE to GET.
393 require.GreaterOrEqual(t, len(methods), 2)
394 assert.Equal(t, http.MethodDelete, methods[0])
395 assert.Equal(t, http.MethodGet, methods[1])
396 })
397
398 t.Run("with option: redirect is not followed, HTTPError carries redirect status", func(t *testing.T) {
399 var methods []string
400 client := NewClientFromHTTP(&http.Client{Transport: newCountingTransport(&methods)})
401
402 _, err := client.Request("github.com", http.MethodDelete, "repos/OWNER/REPO", nil, WithoutFollowingRedirects())
403 require.Error(t, err)
404
405 var httpErr HTTPError
406 require.ErrorAs(t, err, &httpErr)
407 assert.Equal(t, http.StatusMovedPermanently, httpErr.StatusCode)
408
409 // Transport is only asked for the original DELETE; the redirect is never followed.
410 assert.Equal(t, []string{http.MethodDelete}, methods)
411 })
412}
413
414// roundTripFunc lets a plain function satisfy http.RoundTripper.
415type roundTripFunc func(*http.Request) (*http.Response, error)

Callers

nothing calls this directly

Calls 7

roundTripFuncFuncType · 0.85
NewClientFromHTTPFunction · 0.85
RequestMethod · 0.80
EqualMethod · 0.80
RunMethod · 0.65
ErrorMethod · 0.45

Tested by

no test coverage detected