github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/api/http_client_test.go (about) 1 package api 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/ungtb10d/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 tokenGetter 20 appVersion string 21 setAccept bool 22 } 23 tests := []struct { 24 name string 25 args args 26 envDebug string 27 setGhDebug bool 28 envGhDebug string 29 host string 30 wantHeader map[string]string 31 wantStderr string 32 }{ 33 { 34 name: "github.com with Accept header", 35 args: args{ 36 config: tinyConfig{"github.com:oauth_token": "MYTOKEN"}, 37 appVersion: "v1.2.3", 38 setAccept: true, 39 }, 40 host: "github.com", 41 wantHeader: map[string]string{ 42 "authorization": "token MYTOKEN", 43 "user-agent": "GitHub CLI v1.2.3", 44 "accept": "application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview", 45 }, 46 wantStderr: "", 47 }, 48 { 49 name: "github.com no Accept header", 50 args: args{ 51 config: tinyConfig{"github.com:oauth_token": "MYTOKEN"}, 52 appVersion: "v1.2.3", 53 setAccept: false, 54 }, 55 host: "github.com", 56 wantHeader: map[string]string{ 57 "authorization": "token MYTOKEN", 58 "user-agent": "GitHub CLI v1.2.3", 59 "accept": "", 60 }, 61 wantStderr: "", 62 }, 63 { 64 name: "github.com no authentication token", 65 args: args{ 66 config: tinyConfig{"example.com:oauth_token": "MYTOKEN"}, 67 appVersion: "v1.2.3", 68 setAccept: true, 69 }, 70 host: "github.com", 71 wantHeader: map[string]string{ 72 "authorization": "", 73 "user-agent": "GitHub CLI v1.2.3", 74 "accept": "application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview", 75 }, 76 wantStderr: "", 77 }, 78 { 79 name: "github.com in verbose mode", 80 args: args{ 81 config: tinyConfig{"github.com:oauth_token": "MYTOKEN"}, 82 appVersion: "v1.2.3", 83 setAccept: true, 84 }, 85 host: "github.com", 86 envDebug: "api", 87 setGhDebug: false, 88 wantHeader: map[string]string{ 89 "authorization": "token MYTOKEN", 90 "user-agent": "GitHub CLI v1.2.3", 91 "accept": "application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview", 92 }, 93 wantStderr: heredoc.Doc(` 94 * Request at <time> 95 * Request to http://<host>:<port> 96 > GET / HTTP/1.1 97 > Host: github.com 98 > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview 99 > Authorization: token ████████████████████ 100 > Content-Type: application/json; charset=utf-8 101 > Time-Zone: <timezone> 102 > User-Agent: GitHub CLI v1.2.3 103 104 < HTTP/1.1 204 No Content 105 < Date: <time> 106 107 * Request took <duration> 108 `), 109 }, 110 { 111 name: "github.com in verbose mode", 112 args: args{ 113 config: tinyConfig{"github.com:oauth_token": "MYTOKEN"}, 114 appVersion: "v1.2.3", 115 setAccept: true, 116 }, 117 host: "github.com", 118 envGhDebug: "api", 119 setGhDebug: true, 120 wantHeader: map[string]string{ 121 "authorization": "token MYTOKEN", 122 "user-agent": "GitHub CLI v1.2.3", 123 "accept": "application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview", 124 }, 125 wantStderr: heredoc.Doc(` 126 * Request at <time> 127 * Request to http://<host>:<port> 128 > GET / HTTP/1.1 129 > Host: github.com 130 > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview 131 > Authorization: token ████████████████████ 132 > Content-Type: application/json; charset=utf-8 133 > Time-Zone: <timezone> 134 > User-Agent: GitHub CLI v1.2.3 135 136 < HTTP/1.1 204 No Content 137 < Date: <time> 138 139 * Request took <duration> 140 `), 141 }, 142 { 143 name: "GHES Accept header", 144 args: args{ 145 config: tinyConfig{"example.com:oauth_token": "GHETOKEN"}, 146 appVersion: "v1.2.3", 147 setAccept: true, 148 }, 149 host: "example.com", 150 wantHeader: map[string]string{ 151 "authorization": "token GHETOKEN", 152 "user-agent": "GitHub CLI v1.2.3", 153 "accept": "application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview", 154 }, 155 wantStderr: "", 156 }, 157 } 158 159 var gotReq *http.Request 160 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 161 gotReq = r 162 w.WriteHeader(http.StatusNoContent) 163 })) 164 defer ts.Close() 165 166 for _, tt := range tests { 167 t.Run(tt.name, func(t *testing.T) { 168 t.Setenv("DEBUG", tt.envDebug) 169 if tt.setGhDebug { 170 t.Setenv("GH_DEBUG", tt.envGhDebug) 171 } else { 172 os.Unsetenv("GH_DEBUG") 173 } 174 175 ios, _, _, stderr := iostreams.Test() 176 client, err := NewHTTPClient(HTTPClientOptions{ 177 AppVersion: tt.args.appVersion, 178 Config: tt.args.config, 179 Log: ios.ErrOut, 180 SkipAcceptHeaders: !tt.args.setAccept, 181 }) 182 require.NoError(t, err) 183 184 req, err := http.NewRequest("GET", ts.URL, nil) 185 req.Header.Set("time-zone", "Europe/Amsterdam") 186 req.Host = tt.host 187 require.NoError(t, err) 188 189 res, err := client.Do(req) 190 191 require.NoError(t, err) 192 193 for name, value := range tt.wantHeader { 194 assert.Equal(t, value, gotReq.Header.Get(name), name) 195 } 196 197 assert.Equal(t, 204, res.StatusCode) 198 assert.Equal(t, tt.wantStderr, normalizeVerboseLog(stderr.String())) 199 }) 200 } 201 } 202 203 type tinyConfig map[string]string 204 205 func (c tinyConfig) AuthToken(host string) (string, string) { 206 return c[fmt.Sprintf("%s:%s", host, "oauth_token")], "oauth_token" 207 } 208 209 var requestAtRE = regexp.MustCompile(`(?m)^\* Request at .+`) 210 var dateRE = regexp.MustCompile(`(?m)^< Date: .+`) 211 var hostWithPortRE = regexp.MustCompile(`127\.0\.0\.1:\d+`) 212 var durationRE = regexp.MustCompile(`(?m)^\* Request took .+`) 213 var timezoneRE = regexp.MustCompile(`(?m)^> Time-Zone: .+`) 214 215 func normalizeVerboseLog(t string) string { 216 t = requestAtRE.ReplaceAllString(t, "* Request at <time>") 217 t = hostWithPortRE.ReplaceAllString(t, "<host>:<port>") 218 t = dateRE.ReplaceAllString(t, "< Date: <time>") 219 t = durationRE.ReplaceAllString(t, "* Request took <duration>") 220 t = timezoneRE.ReplaceAllString(t, "> Time-Zone: <timezone>") 221 return t 222 }