github.com/andrewhsu/cli/v2@v2.0.1-0.20210910131313-d4b4061f5b89/pkg/cmd/factory/http_test.go (about)

     1  package factory
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"os"
     8  	"regexp"
     9  	"testing"
    10  
    11  	"github.com/MakeNowJust/heredoc"
    12  	"github.com/andrewhsu/cli/v2/pkg/iostreams"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestNewHTTPClient(t *testing.T) {
    18  	type args struct {
    19  		config     configGetter
    20  		appVersion string
    21  		setAccept  bool
    22  	}
    23  	tests := []struct {
    24  		name       string
    25  		args       args
    26  		envDebug   string
    27  		host       string
    28  		wantHeader map[string]string
    29  		wantStderr string
    30  	}{
    31  		{
    32  			name: "github.com with Accept header",
    33  			args: args{
    34  				config:     tinyConfig{"github.com:oauth_token": "MYTOKEN"},
    35  				appVersion: "v1.2.3",
    36  				setAccept:  true,
    37  			},
    38  			host: "github.com",
    39  			wantHeader: map[string]string{
    40  				"authorization": "token MYTOKEN",
    41  				"user-agent":    "GitHub CLI v1.2.3",
    42  				"accept":        "application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview",
    43  			},
    44  			wantStderr: "",
    45  		},
    46  		{
    47  			name: "github.com no Accept header",
    48  			args: args{
    49  				config:     tinyConfig{"github.com:oauth_token": "MYTOKEN"},
    50  				appVersion: "v1.2.3",
    51  				setAccept:  false,
    52  			},
    53  			host: "github.com",
    54  			wantHeader: map[string]string{
    55  				"authorization": "token MYTOKEN",
    56  				"user-agent":    "GitHub CLI v1.2.3",
    57  				"accept":        "",
    58  			},
    59  			wantStderr: "",
    60  		},
    61  		{
    62  			name: "github.com no authentication token",
    63  			args: args{
    64  				config:     tinyConfig{"example.com:oauth_token": "MYTOKEN"},
    65  				appVersion: "v1.2.3",
    66  				setAccept:  true,
    67  			},
    68  			host: "github.com",
    69  			wantHeader: map[string]string{
    70  				"authorization": "",
    71  				"user-agent":    "GitHub CLI v1.2.3",
    72  				"accept":        "application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview",
    73  			},
    74  			wantStderr: "",
    75  		},
    76  		{
    77  			name: "github.com in verbose mode",
    78  			args: args{
    79  				config:     tinyConfig{"github.com:oauth_token": "MYTOKEN"},
    80  				appVersion: "v1.2.3",
    81  				setAccept:  true,
    82  			},
    83  			host:     "github.com",
    84  			envDebug: "api",
    85  			wantHeader: map[string]string{
    86  				"authorization": "token MYTOKEN",
    87  				"user-agent":    "GitHub CLI v1.2.3",
    88  				"accept":        "application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview",
    89  			},
    90  			wantStderr: heredoc.Doc(`
    91  				* Request at <time>
    92  				* Request to http://<host>:<port>
    93  				> GET / HTTP/1.1
    94  				> Host: github.com
    95  				> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
    96  				> Authorization: token ████████████████████
    97  				> User-Agent: GitHub CLI v1.2.3
    98  				
    99  				< HTTP/1.1 204 No Content
   100  				< Date: <time>
   101  				
   102  				* Request took <duration>
   103  			`),
   104  		},
   105  		{
   106  			name: "GHES Accept header",
   107  			args: args{
   108  				config:     tinyConfig{"example.com:oauth_token": "GHETOKEN"},
   109  				appVersion: "v1.2.3",
   110  				setAccept:  true,
   111  			},
   112  			host: "example.com",
   113  			wantHeader: map[string]string{
   114  				"authorization": "token GHETOKEN",
   115  				"user-agent":    "GitHub CLI v1.2.3",
   116  				"accept":        "application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview, application/vnd.github.antiope-preview, application/vnd.github.shadow-cat-preview",
   117  			},
   118  			wantStderr: "",
   119  		},
   120  	}
   121  
   122  	var gotReq *http.Request
   123  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   124  		gotReq = r
   125  		w.WriteHeader(http.StatusNoContent)
   126  	}))
   127  	defer ts.Close()
   128  
   129  	for _, tt := range tests {
   130  		t.Run(tt.name, func(t *testing.T) {
   131  			oldDebug := os.Getenv("DEBUG")
   132  			os.Setenv("DEBUG", tt.envDebug)
   133  			t.Cleanup(func() {
   134  				os.Setenv("DEBUG", oldDebug)
   135  			})
   136  
   137  			io, _, _, stderr := iostreams.Test()
   138  			client, err := NewHTTPClient(io, tt.args.config, tt.args.appVersion, tt.args.setAccept)
   139  			require.NoError(t, err)
   140  
   141  			req, err := http.NewRequest("GET", ts.URL, nil)
   142  			req.Host = tt.host
   143  			require.NoError(t, err)
   144  
   145  			res, err := client.Do(req)
   146  			require.NoError(t, err)
   147  
   148  			for name, value := range tt.wantHeader {
   149  				assert.Equal(t, value, gotReq.Header.Get(name), name)
   150  			}
   151  
   152  			assert.Equal(t, 204, res.StatusCode)
   153  			assert.Equal(t, tt.wantStderr, normalizeVerboseLog(stderr.String()))
   154  		})
   155  	}
   156  }
   157  
   158  type tinyConfig map[string]string
   159  
   160  func (c tinyConfig) Get(host, key string) (string, error) {
   161  	return c[fmt.Sprintf("%s:%s", host, key)], nil
   162  }
   163  
   164  var requestAtRE = regexp.MustCompile(`(?m)^\* Request at .+`)
   165  var dateRE = regexp.MustCompile(`(?m)^< Date: .+`)
   166  var hostWithPortRE = regexp.MustCompile(`127\.0\.0\.1:\d+`)
   167  var durationRE = regexp.MustCompile(`(?m)^\* Request took .+`)
   168  
   169  func normalizeVerboseLog(t string) string {
   170  	t = requestAtRE.ReplaceAllString(t, "* Request at <time>")
   171  	t = hostWithPortRE.ReplaceAllString(t, "<host>:<port>")
   172  	t = dateRE.ReplaceAllString(t, "< Date: <time>")
   173  	t = durationRE.ReplaceAllString(t, "* Request took <duration>")
   174  	return t
   175  }