bitbucket.org/ai69/amoy@v0.2.3/http_test.go (about) 1 package amoy 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/http" 9 "reflect" 10 "strings" 11 "testing" 12 ) 13 14 var ( 15 testHttpbinURL = "https://httpbin.org" 16 testHTTPRetryTimes = uint(5) 17 ) 18 19 func init() { 20 testHttpbinURL = GetEnvVar("TEST_HTTPBIN_URL", "https://httpbin.org") 21 fmt.Println("HTTPBin URL:", testHttpbinURL) 22 } 23 24 func TestHTTPClient_Get(t *testing.T) { 25 defaultUA := defaultHTTPClientOpts.UserAgent 26 tests := []struct { 27 name string 28 opts *HTTPClientOptions 29 urlSuffix string 30 queryArgs map[string]string 31 headers map[string]string 32 wantStr []string 33 wantErr bool 34 }{ 35 {"Nil Opts", nil, "/get", nil, nil, []string{defaultUA}, false}, 36 {"Empty Opts", &HTTPClientOptions{}, "/get", nil, nil, []string{defaultUA}, false}, 37 {"Custom UserAgent", &HTTPClientOptions{UserAgent: "amoy-test"}, "/get", nil, nil, []string{"amoy-test"}, false}, 38 {"Custom Timeout OK", &HTTPClientOptions{Timeout: Seconds(2)}, "/get", nil, nil, []string{defaultUA, "/get"}, false}, 39 {"Custom Timeout Error", &HTTPClientOptions{Timeout: Seconds(2)}, "/delay/5", nil, nil, []string{defaultUA, "/delay"}, true}, 40 {"Set Query Args", nil, "/get", map[string]string{"a": "1", "b": "2"}, nil, []string{defaultUA, "/get", "a=1", "b=2"}, false}, 41 {"Set Headers", nil, "/get", nil, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, []string{defaultUA, "/get", "Apple", "Bravo"}, false}, 42 {"Invalid JSON", nil, "/base64/YWxvaGEK", nil, nil, []string{"aloha"}, false}, 43 {"Status Code OK", nil, "/status/200", nil, nil, nil, false}, 44 {"Status Code Error", nil, "/status/300", nil, nil, nil, true}, 45 {"Status Code Error 2", nil, "/status/404", nil, nil, nil, true}, 46 } 47 for _, tt := range tests { 48 t.Run(tt.name, func(t *testing.T) { 49 c := NewHTTPClient(tt.opts) 50 url := testHttpbinURL + tt.urlSuffix 51 var ( 52 got []byte 53 err error 54 ) 55 _ = BackOffRetryIf(func() error { 56 got, err = c.Get(url, tt.queryArgs, tt.headers) 57 return err 58 }, func(err error) bool { 59 return !strings.Contains(err.Error(), `status code: 502`) 60 }, testHTTPRetryTimes, Milliseconds(100)) 61 // check resp 62 if (err != nil) != tt.wantErr { 63 t.Errorf("Get(%s) error = %v, wantErr %v", url, err, tt.wantErr) 64 return 65 } 66 if !tt.wantErr && len(tt.wantStr) > 0 { 67 gs := string(got) 68 for _, s := range tt.wantStr { 69 if !strings.Contains(gs, s) { 70 t.Errorf("Get(%s) = %s, should contain %q", url, got, s) 71 } 72 } 73 } 74 }) 75 } 76 } 77 78 func TestHTTPClient_GetJSON(t *testing.T) { 79 defaultUA := defaultHTTPClientOpts.UserAgent 80 type respSchema struct { 81 Args map[string]string `json:"args"` 82 Headers struct { 83 AcceptEncoding string `json:"Accept-Encoding"` 84 ContentType string `json:"Content-Type"` 85 Host string `json:"Host"` 86 UserAgent string `json:"User-Agent"` 87 } `json:"headers"` 88 Origin string `json:"origin"` 89 URL string `json:"url"` 90 } 91 tests := []struct { 92 name string 93 opts *HTTPClientOptions 94 urlSuffix string 95 queryArgs map[string]string 96 headers map[string]string 97 wantStr []string 98 wantErr bool 99 }{ 100 {"Nil Opts", nil, "/get", nil, nil, []string{defaultUA}, false}, 101 {"Empty Opts", &HTTPClientOptions{}, "/get", nil, nil, []string{defaultUA}, false}, 102 {"Custom UserAgent", &HTTPClientOptions{UserAgent: "amoy-test"}, "/get", nil, nil, []string{"amoy-test"}, false}, 103 {"Custom Timeout OK", &HTTPClientOptions{Timeout: Seconds(2)}, "/get", nil, nil, []string{defaultUA, "/get"}, false}, 104 {"Custom Timeout Error", &HTTPClientOptions{Timeout: Seconds(2)}, "/delay/5", nil, nil, []string{defaultUA, "/delay"}, true}, 105 {"Set Query Args", nil, "/get", map[string]string{"a": "1", "b": "2"}, nil, []string{defaultUA, "/get", "a=1", "b=2"}, false}, 106 {"Set Headers", nil, "/get", nil, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, []string{defaultUA, "/get", "Apple", "Bravo"}, false}, 107 {"Set Query Headers", nil, "/get", map[string]string{"a": "1", "b": "2"}, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, []string{defaultUA, "/get", "a=1", "b=2", "Apple", "Bravo"}, false}, 108 {"Invalid JSON", nil, "/base64/YWxvaGEK", nil, nil, nil, true}, 109 {"Status Code OK", nil, "/get", nil, nil, nil, false}, 110 {"Status Code Error", nil, "/status/300", nil, nil, nil, true}, 111 {"Status Code Error 2", nil, "/status/404", nil, nil, nil, true}, 112 } 113 for _, tt := range tests { 114 t.Run(tt.name, func(t *testing.T) { 115 c := NewHTTPClient(tt.opts) 116 url := testHttpbinURL + tt.urlSuffix 117 var ( 118 got []byte 119 err error 120 resp respSchema 121 ) 122 _ = BackOffRetryIf(func() error { 123 got, err = c.GetJSON(url, tt.queryArgs, tt.headers, &resp) 124 return err 125 }, func(err error) bool { 126 return !strings.Contains(err.Error(), `status code: 502`) 127 }, testHTTPRetryTimes, Milliseconds(100)) 128 // check resp 129 if (err != nil) != tt.wantErr { 130 t.Errorf("GetJSON(%s) error = %v, wantErr %v", url, err, tt.wantErr) 131 return 132 } 133 if !tt.wantErr && len(tt.wantStr) > 0 { 134 gs := string(got) 135 gotUA := tt.wantStr[0] 136 if resp.Headers.UserAgent != gotUA { 137 t.Errorf("GetJSON(%s)'s UA = %s, want %q", url, got, gotUA) 138 } 139 for _, s := range tt.wantStr { 140 if !strings.Contains(gs, s) { 141 t.Errorf("GetJSON(%s) = %s, should contain %q", url, got, s) 142 } 143 } 144 } 145 }) 146 } 147 } 148 149 func TestHTTPClient_Post(t *testing.T) { 150 defaultUA := defaultHTTPClientOpts.UserAgent 151 tests := []struct { 152 name string 153 opts *HTTPClientOptions 154 urlSuffix string 155 queryArgs map[string]string 156 headers map[string]string 157 body []byte 158 wantStr []string 159 wantErr bool 160 }{ 161 {"Nil Opts", nil, "/post", nil, nil, nil, []string{defaultUA, "/post"}, false}, 162 {"Empty Opts", &HTTPClientOptions{}, "/post", nil, nil, nil, []string{defaultUA, "/post"}, false}, 163 {"Custom UserAgent", &HTTPClientOptions{UserAgent: "amoy-test"}, "/post", nil, nil, nil, []string{"amoy-test", "/post"}, false}, 164 {"Custom Timeout OK", &HTTPClientOptions{Timeout: Seconds(2)}, "/post", nil, nil, nil, []string{defaultUA, "/post"}, false}, 165 {"Custom Timeout Error", &HTTPClientOptions{Timeout: Seconds(2)}, "/delay/5", nil, nil, nil, []string{defaultUA, "/delay"}, true}, 166 {"Set Query Args", nil, "/post", map[string]string{"a": "1", "b": "2"}, nil, nil, []string{defaultUA, "/post", "a=1", "b=2"}, false}, 167 {"Set Headers", nil, "/post", nil, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, nil, []string{defaultUA, "/post", "Apple", "Bravo"}, false}, 168 {"Set Query Headers", nil, "/post", map[string]string{"a": "1", "b": "2"}, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, nil, []string{defaultUA, "/post", "a=1", "b=2", "Apple", "Bravo"}, false}, 169 {"No Data", nil, "/post", nil, nil, nil, []string{defaultUA, "/post", `""`}, false}, 170 {"Empty Data", nil, "/post", nil, nil, []byte{}, []string{defaultUA, "/post", `""`}, false}, 171 {"Data Body", nil, "/post", nil, nil, []byte("hello"), []string{defaultUA, "/post", "hello"}, false}, 172 {"Status Code OK", nil, "/post", nil, nil, nil, nil, false}, 173 {"Status Code Error", nil, "/status/300", nil, nil, nil, nil, true}, 174 {"Status Code Error 2", nil, "/status/404", nil, nil, nil, nil, true}, 175 } 176 for _, tt := range tests { 177 t.Run(tt.name, func(t *testing.T) { 178 c := NewHTTPClient(tt.opts) 179 url := testHttpbinURL + tt.urlSuffix 180 var ( 181 got []byte 182 err error 183 ) 184 _ = BackOffRetryIf(func() error { 185 var rd io.Reader 186 if tt.body != nil { 187 rd = bytes.NewReader(tt.body) 188 } 189 got, err = c.Post(url, tt.queryArgs, tt.headers, rd) 190 return err 191 }, func(err error) bool { 192 return !strings.Contains(err.Error(), `status code: 502`) 193 }, testHTTPRetryTimes, Milliseconds(100)) 194 // check resp 195 if (err != nil) != tt.wantErr { 196 t.Errorf("PostData(%s) error = %v, wantErr %v", url, err, tt.wantErr) 197 return 198 } 199 if !tt.wantErr && len(tt.wantStr) > 0 { 200 gs := string(got) 201 ds := string(tt.body) 202 if !strings.Contains(gs, ds) { 203 t.Errorf("PostData(%s)'s Payload = %s, should contain %q", url, got, ds) 204 } 205 for _, s := range tt.wantStr { 206 if !strings.Contains(gs, s) { 207 t.Errorf("PostData(%s) = %s, should contain %q", url, got, s) 208 } 209 } 210 } 211 }) 212 } 213 } 214 215 func TestHTTPClient_PostData(t *testing.T) { 216 defaultUA := defaultHTTPClientOpts.UserAgent 217 tests := []struct { 218 name string 219 opts *HTTPClientOptions 220 urlSuffix string 221 queryArgs map[string]string 222 headers map[string]string 223 body []byte 224 wantStr []string 225 wantErr bool 226 }{ 227 {"Nil Opts", nil, "/post", nil, nil, nil, []string{defaultUA, "/post"}, false}, 228 {"Empty Opts", &HTTPClientOptions{}, "/post", nil, nil, nil, []string{defaultUA, "/post"}, false}, 229 {"Custom UserAgent", &HTTPClientOptions{UserAgent: "amoy-test"}, "/post", nil, nil, nil, []string{"amoy-test", "/post"}, false}, 230 {"Custom Timeout OK", &HTTPClientOptions{Timeout: Seconds(2)}, "/post", nil, nil, nil, []string{defaultUA, "/post"}, false}, 231 {"Custom Timeout Error", &HTTPClientOptions{Timeout: Seconds(2)}, "/delay/5", nil, nil, nil, []string{defaultUA, "/delay"}, true}, 232 {"Set Query Args", nil, "/post", map[string]string{"a": "1", "b": "2"}, nil, nil, []string{defaultUA, "/post", "a=1", "b=2"}, false}, 233 {"Set Headers", nil, "/post", nil, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, nil, []string{defaultUA, "/post", "Apple", "Bravo"}, false}, 234 {"Set Query Headers", nil, "/post", map[string]string{"a": "1", "b": "2"}, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, nil, []string{defaultUA, "/post", "a=1", "b=2", "Apple", "Bravo"}, false}, 235 {"No Data", nil, "/post", nil, nil, nil, []string{defaultUA, "/post", `""`}, false}, 236 {"Data Body", nil, "/post", nil, nil, []byte("hello"), []string{defaultUA, "/post", "hello"}, false}, 237 {"Status Code OK", nil, "/post", nil, nil, nil, nil, false}, 238 {"Status Code Error", nil, "/status/300", nil, nil, nil, nil, true}, 239 {"Status Code Error 2", nil, "/status/404", nil, nil, nil, nil, true}, 240 } 241 for _, tt := range tests { 242 t.Run(tt.name, func(t *testing.T) { 243 c := NewHTTPClient(tt.opts) 244 url := testHttpbinURL + tt.urlSuffix 245 var ( 246 got []byte 247 err error 248 ) 249 _ = BackOffRetryIf(func() error { 250 got, err = c.PostData(url, tt.queryArgs, tt.headers, tt.body) 251 return err 252 }, func(err error) bool { 253 return !strings.Contains(err.Error(), `status code: 502`) 254 }, testHTTPRetryTimes, Milliseconds(100)) 255 // check resp 256 if (err != nil) != tt.wantErr { 257 t.Errorf("PostData(%s) error = %v, wantErr %v", url, err, tt.wantErr) 258 return 259 } 260 if !tt.wantErr && len(tt.wantStr) > 0 { 261 gs := string(got) 262 ds := string(tt.body) 263 if !strings.Contains(gs, ds) { 264 t.Errorf("PostData(%s)'s Payload = %s, should contain %q", url, got, ds) 265 } 266 for _, s := range tt.wantStr { 267 if !strings.Contains(gs, s) { 268 t.Errorf("PostData(%s) = %s, should contain %q", url, got, s) 269 } 270 } 271 } 272 }) 273 } 274 } 275 276 func TestHTTPClient_PostJSON(t *testing.T) { 277 var nilMap map[string]interface{} 278 defaultUA := defaultHTTPClientOpts.UserAgent 279 type respSchema struct { 280 Data string `json:"data"` 281 Files map[string]interface{} `json:"files"` 282 Form map[string]interface{} `json:"form"` 283 JSON map[string]interface{} `json:"json"` 284 Args map[string]string `json:"args"` 285 Headers struct { 286 AcceptEncoding string `json:"Accept-Encoding"` 287 ContentType string `json:"Content-Type"` 288 Host string `json:"Host"` 289 UserAgent string `json:"User-Agent"` 290 } `json:"headers"` 291 Origin string `json:"origin"` 292 URL string `json:"url"` 293 } 294 tests := []struct { 295 name string 296 opts *HTTPClientOptions 297 urlSuffix string 298 queryArgs map[string]string 299 headers map[string]string 300 body map[string]interface{} 301 wantStr []string 302 wantErr bool 303 }{ 304 {"Nil Opts", nil, "/post", nil, nil, nil, []string{defaultUA, "/post"}, false}, 305 {"Empty Opts", &HTTPClientOptions{}, "/post", nil, nil, nil, []string{defaultUA, "/post"}, false}, 306 {"Custom UserAgent", &HTTPClientOptions{UserAgent: "amoy-test"}, "/post", nil, nil, nil, []string{"amoy-test", "/post"}, false}, 307 {"Custom Timeout OK", &HTTPClientOptions{Timeout: Seconds(2)}, "/post", nil, nil, nil, []string{defaultUA, "/post"}, false}, 308 {"Custom Timeout Error", &HTTPClientOptions{Timeout: Seconds(2)}, "/delay/5", nil, nil, nil, []string{defaultUA, "/delay"}, true}, 309 {"Set Query Args", nil, "/post", map[string]string{"a": "1", "b": "2"}, nil, nil, []string{defaultUA, "/post", "a=1", "b=2"}, false}, 310 {"Set Headers", nil, "/post", nil, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, nil, []string{defaultUA, "/post", "Apple", "Bravo"}, false}, 311 {"Set Query Headers", nil, "/post", map[string]string{"a": "1", "b": "2"}, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, nil, []string{defaultUA, "/post", "a=1", "b=2", "Apple", "Bravo"}, false}, 312 {"Nil Data", nil, "/post", nil, nil, nil, []string{defaultUA, "/post", `""`, `{}`}, false}, 313 {"Nil Data 2", nil, "/post", nil, nil, nilMap, []string{defaultUA, "/post", `""`, `{}`}, false}, 314 {"Empty JSON", nil, "/post", nil, nil, map[string]interface{}{}, []string{defaultUA, "/post", `{}`}, false}, 315 {"JSON Data", nil, "/post", nil, nil, map[string]interface{}{"task": "hello", "job": "world", "success": true}, []string{defaultUA, "/post", "hello", "world"}, false}, 316 {"JSON Marshal Error", nil, "/post", nil, nil, map[string]interface{}{"task": "error", "func": strings.Repeat}, nil, true}, 317 {"Status Code OK", nil, "/post", nil, nil, nil, nil, false}, 318 {"Status Code Error", nil, "/status/300", nil, nil, nil, nil, true}, 319 {"Status Code Error 2", nil, "/status/404", nil, nil, nil, nil, true}, 320 } 321 for _, tt := range tests { 322 t.Run(tt.name, func(t *testing.T) { 323 c := NewHTTPClient(tt.opts) 324 url := testHttpbinURL + tt.urlSuffix 325 var ( 326 got []byte 327 err error 328 resp respSchema 329 ) 330 _ = BackOffRetryIf(func() error { 331 got, err = c.PostJSON(url, tt.queryArgs, tt.headers, tt.body, &resp) 332 return err 333 }, func(err error) bool { 334 return !strings.Contains(err.Error(), `status code: 502`) 335 }, testHTTPRetryTimes, Milliseconds(100)) 336 // check resp 337 if (err != nil) != tt.wantErr { 338 t.Errorf("PostJSON(%s) error = %v, wantErr %v", url, err, tt.wantErr) 339 return 340 } 341 if !tt.wantErr && tt.body != nil { 342 if !reflect.DeepEqual(resp.JSON, tt.body) { 343 t.Errorf("PostJSON(%s)'s JSON = %v, want %v", url, resp.JSON, tt.body) 344 } 345 } 346 if !tt.wantErr && len(tt.wantStr) > 0 { 347 gs := string(got) 348 gotUA := tt.wantStr[0] 349 if resp.Headers.UserAgent != gotUA { 350 t.Errorf("PostJSON(%s)'s UA = %s, want %q", url, got, gotUA) 351 } 352 for _, s := range tt.wantStr { 353 if !strings.Contains(gs, s) { 354 t.Errorf("PostJSON(%s) = %s, should contain %q", url, got, s) 355 } 356 } 357 } 358 }) 359 } 360 } 361 362 func TestHTTPClient_Custom_Head(t *testing.T) { 363 tests := []struct { 364 name string 365 opts *HTTPClientOptions 366 urlSuffix string 367 queryArgs map[string]string 368 headers map[string]string 369 body []byte 370 wantErr bool 371 }{ 372 {"Nil Opts", nil, "/get", nil, nil, nil, false}, 373 {"Empty Opts", &HTTPClientOptions{}, "/get", nil, nil, nil, false}, 374 {"Custom Timeout OK", &HTTPClientOptions{Timeout: Seconds(2)}, "/get", nil, nil, nil, false}, 375 {"Custom Timeout Error", &HTTPClientOptions{Timeout: Seconds(2)}, "/delay/5", nil, nil, nil, true}, 376 {"Set Query Args", nil, "/get", map[string]string{"a": "1", "b": "2"}, nil, nil, false}, 377 {"Set Headers", nil, "/get", nil, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, nil, false}, 378 {"Body Data", nil, "/base64/YWxvaGEK", nil, nil, []byte("aloha"), false}, 379 {"Status Code OK", nil, "/status/200", nil, nil, nil, false}, 380 {"Status Code Error", nil, "/status/300", nil, nil, nil, true}, 381 {"Status Code Error 2", nil, "/status/404", nil, nil, nil, true}, 382 } 383 for _, tt := range tests { 384 t.Run(tt.name, func(t *testing.T) { 385 c := NewHTTPClient(tt.opts) 386 url := testHttpbinURL + tt.urlSuffix 387 var ( 388 got []byte 389 err error 390 ) 391 _ = BackOffRetryIf(func() error { 392 var rd io.Reader 393 if tt.body != nil { 394 rd = bytes.NewReader(tt.body) 395 } 396 got, err = c.Custom(http.MethodHead, url, tt.queryArgs, tt.headers, rd) 397 return err 398 }, func(err error) bool { 399 return !strings.Contains(err.Error(), `status code: 502`) 400 }, testHTTPRetryTimes, Milliseconds(100)) 401 // check resp 402 if (err != nil) != tt.wantErr { 403 t.Errorf("Custom(Head, %s) error = %v, wantErr %v", url, err, tt.wantErr) 404 return 405 } 406 if !tt.wantErr && len(got) > 0 { 407 t.Errorf("Custom(Head, %s) = %s, want empty", url, got) 408 } 409 }) 410 } 411 } 412 413 func TestHTTPClient_Custom_Patch(t *testing.T) { 414 defaultUA := defaultHTTPClientOpts.UserAgent 415 tests := []struct { 416 name string 417 opts *HTTPClientOptions 418 urlSuffix string 419 queryArgs map[string]string 420 headers map[string]string 421 body []byte 422 wantStr []string 423 wantErr bool 424 }{ 425 {"Nil Opts", nil, "/patch", nil, nil, nil, []string{defaultUA}, false}, 426 {"Empty Opts", &HTTPClientOptions{}, "/patch", nil, nil, nil, []string{defaultUA}, false}, 427 {"Custom UserAgent", &HTTPClientOptions{UserAgent: "amoy-test"}, "/patch", nil, nil, nil, []string{"amoy-test"}, false}, 428 {"Custom Timeout OK", &HTTPClientOptions{Timeout: Seconds(2)}, "/patch", nil, nil, nil, []string{defaultUA, "/patch"}, false}, 429 {"Custom Timeout Error", &HTTPClientOptions{Timeout: Seconds(2)}, "/delay/5", nil, nil, nil, []string{defaultUA, "/delay"}, true}, 430 {"Set Query Args", nil, "/patch", map[string]string{"a": "1", "b": "2"}, nil, nil, []string{defaultUA, "/patch", "a=1", "b=2"}, false}, 431 {"Set Headers", nil, "/patch", nil, map[string]string{"Type-A": "Apple", "Type-B": "Bravo"}, nil, []string{defaultUA, "/patch", "Apple", "Bravo"}, false}, 432 {"Body Data", nil, "/patch", nil, nil, []byte("aloha"), []string{"aloha"}, false}, 433 {"Status Code OK", nil, "/status/200", nil, nil, nil, nil, false}, 434 {"Status Code Error", nil, "/status/300", nil, nil, nil, nil, true}, 435 {"Status Code Error 2", nil, "/status/404", nil, nil, nil, nil, true}, 436 } 437 for _, tt := range tests { 438 t.Run(tt.name, func(t *testing.T) { 439 c := NewHTTPClient(tt.opts) 440 url := testHttpbinURL + tt.urlSuffix 441 var ( 442 got []byte 443 err error 444 ) 445 _ = BackOffRetryIf(func() error { 446 var rd io.Reader 447 if tt.body != nil { 448 rd = bytes.NewReader(tt.body) 449 } 450 got, err = c.Custom(http.MethodPatch, url, tt.queryArgs, tt.headers, rd) 451 return err 452 }, func(err error) bool { 453 return !strings.Contains(err.Error(), `status code: 502`) 454 }, testHTTPRetryTimes, Milliseconds(100)) 455 // check resp 456 if (err != nil) != tt.wantErr { 457 t.Errorf("Custom(Patch, %s) error = %v, wantErr %v", url, err, tt.wantErr) 458 return 459 } 460 if !tt.wantErr && len(tt.wantStr) > 0 { 461 gs := string(got) 462 for _, s := range tt.wantStr { 463 if !strings.Contains(gs, s) { 464 t.Errorf("Custom(Patch, %s) = %s, should contain %q", url, got, s) 465 } 466 } 467 } 468 }) 469 } 470 } 471 472 func TestHTTPClient_EmbeddingClient(t *testing.T) { 473 defaultUA := "Go-http-client/" 474 tests := []struct { 475 name string 476 opts *HTTPClientOptions 477 urlSuffix string 478 queryArgs map[string]string 479 headers map[string]string 480 wantStr []string 481 wantErr bool 482 }{ 483 {"Custom Timeout OK", &HTTPClientOptions{Timeout: Seconds(2)}, "/get", nil, nil, []string{defaultUA, "/get"}, false}, 484 {"Custom Timeout Error", &HTTPClientOptions{Timeout: Seconds(2)}, "/delay/5", nil, nil, nil, true}, 485 {"Missed Auth", &HTTPClientOptions{Username: "joe", Password: "secret"}, "/basic-auth/joe/secret", nil, nil, nil, true}, 486 } 487 for _, tt := range tests { 488 t.Run(tt.name, func(t *testing.T) { 489 c := NewHTTPClient(tt.opts) 490 url := testHttpbinURL + tt.urlSuffix 491 var ( 492 got []byte 493 err error 494 ) 495 err = BackOffRetryIf(func() error { 496 resp, e := c.Client.Get(url) 497 if e != nil { 498 return e 499 } 500 if resp.StatusCode != http.StatusOK { 501 return fmt.Errorf("status code: %d", resp.StatusCode) 502 } 503 defer resp.Body.Close() 504 got, err = ioutil.ReadAll(resp.Body) 505 return err 506 }, func(err error) bool { 507 return !strings.Contains(err.Error(), `status code: 502`) 508 }, testHTTPRetryTimes, Milliseconds(100)) 509 // check resp 510 if (err != nil) != tt.wantErr { 511 t.Errorf("EmbeddingClient Get(%s) error = %v, wantErr %v", url, err, tt.wantErr) 512 return 513 } 514 if !tt.wantErr && len(tt.wantStr) > 0 { 515 gs := string(got) 516 for _, s := range tt.wantStr { 517 if !strings.Contains(gs, s) { 518 t.Errorf("EmbeddingClient Get(%s) = %s, should contain %q", url, got, s) 519 } 520 } 521 } 522 }) 523 } 524 } 525 526 func TestHTTPClient_Auth_Basic(t *testing.T) { 527 tests := []struct { 528 name string 529 opts *HTTPClientOptions 530 urlSuffix string 531 queryArgs map[string]string 532 headers map[string]string 533 wantStr []string 534 wantErr bool 535 }{ 536 {"Nil Opts", nil, "/basic-auth/joe/secret", nil, nil, nil, true}, 537 {"Empty Opts", &HTTPClientOptions{}, "/basic-auth/joe/secret", nil, nil, nil, true}, 538 {"Incorrect User", &HTTPClientOptions{Username: "john", Password: "secret"}, "/basic-auth/joe/secret", nil, nil, nil, true}, 539 {"Incorrect Password", &HTTPClientOptions{Username: "joe", Password: "nopass"}, "/basic-auth/joe/secret", nil, nil, nil, true}, 540 {"Token Override", &HTTPClientOptions{Username: "joe", Password: "secret", BearerToken: "abc12345"}, "/basic-auth/joe/secret", nil, nil, nil, true}, 541 {"Correct", &HTTPClientOptions{Username: "joe", Password: "secret"}, "/basic-auth/joe/secret", nil, nil, []string{"true", "joe"}, false}, 542 {"Correct 2", &HTTPClientOptions{Username: "john", Password: "imagine"}, "/basic-auth/john/imagine", nil, nil, []string{"true", "john"}, false}, 543 } 544 for _, tt := range tests { 545 t.Run(tt.name, func(t *testing.T) { 546 c := NewHTTPClient(tt.opts) 547 url := testHttpbinURL + tt.urlSuffix 548 var ( 549 got []byte 550 err error 551 ) 552 _ = BackOffRetryIf(func() error { 553 got, err = c.Get(url, tt.queryArgs, tt.headers) 554 return err 555 }, func(err error) bool { 556 return !strings.Contains(err.Error(), `status code: 502`) 557 }, testHTTPRetryTimes, Milliseconds(100)) 558 // check resp 559 if (err != nil) != tt.wantErr { 560 t.Errorf("Auth Basic(%s) error = %v, wantErr %v", url, err, tt.wantErr) 561 return 562 } 563 if !tt.wantErr && len(tt.wantStr) > 0 { 564 gs := string(got) 565 for _, s := range tt.wantStr { 566 if !strings.Contains(gs, s) { 567 t.Errorf("Auth Basic(%s) = %s, should contain %q", url, got, s) 568 } 569 } 570 } 571 }) 572 } 573 } 574 575 func TestHTTPClient_Auth_Bearer(t *testing.T) { 576 tests := []struct { 577 name string 578 opts *HTTPClientOptions 579 urlSuffix string 580 queryArgs map[string]string 581 headers map[string]string 582 wantStr []string 583 wantErr bool 584 }{ 585 {"Nil Opts", nil, "/bearer", nil, nil, nil, true}, 586 {"Empty Opts", &HTTPClientOptions{}, "/bearer", nil, nil, nil, true}, 587 {"No Token", &HTTPClientOptions{BearerToken: ""}, "/bearer", nil, nil, nil, true}, 588 {"Correct Token", &HTTPClientOptions{BearerToken: "abc12345"}, "/bearer", nil, nil, []string{"abc12345"}, false}, 589 {"Duplicate User", &HTTPClientOptions{Username: "joe", Password: "secret", BearerToken: "abc12345"}, "/bearer", nil, nil, []string{"abc12345"}, false}, 590 } 591 for _, tt := range tests { 592 t.Run(tt.name, func(t *testing.T) { 593 c := NewHTTPClient(tt.opts) 594 url := testHttpbinURL + tt.urlSuffix 595 var ( 596 got []byte 597 err error 598 ) 599 _ = BackOffRetryIf(func() error { 600 got, err = c.Get(url, tt.queryArgs, tt.headers) 601 return err 602 }, func(err error) bool { 603 return !strings.Contains(err.Error(), `status code: 502`) 604 }, testHTTPRetryTimes, Milliseconds(100)) 605 // check resp 606 if (err != nil) != tt.wantErr { 607 t.Errorf("Auth Bearer(%s) error = %v, wantErr %v", url, err, tt.wantErr) 608 return 609 } 610 if !tt.wantErr && len(tt.wantStr) > 0 { 611 gs := string(got) 612 for _, s := range tt.wantStr { 613 if !strings.Contains(gs, s) { 614 t.Errorf("Auth Bearer(%s) = %s, should contain %q", url, got, s) 615 } 616 } 617 } 618 }) 619 } 620 } 621 622 func TestHTTPClient_Redirect(t *testing.T) { 623 defaultUA := defaultHTTPClientOpts.UserAgent 624 tests := []struct { 625 name string 626 opts *HTTPClientOptions 627 urlSuffix string 628 queryArgs map[string]string 629 headers map[string]string 630 wantStr []string 631 wantErr bool 632 }{ 633 {"Nil Opts", nil, "/redirect/1", nil, nil, []string{defaultUA, "/get"}, false}, 634 {"Empty Opts", &HTTPClientOptions{}, "/redirect/1", nil, nil, []string{defaultUA, "/get"}, false}, 635 {"Enable Redirect", &HTTPClientOptions{DisableRedirect: false}, "/redirect/1", nil, nil, []string{defaultUA, "/get"}, false}, 636 {"Disable Redirect", &HTTPClientOptions{DisableRedirect: true}, "/redirect/1", nil, nil, nil, true}, 637 {"Redirect Too Many", &HTTPClientOptions{DisableRedirect: false}, "/redirect/10", nil, nil, nil, true}, 638 } 639 for _, tt := range tests { 640 t.Run(tt.name, func(t *testing.T) { 641 c := NewHTTPClient(tt.opts) 642 url := testHttpbinURL + tt.urlSuffix 643 var ( 644 got []byte 645 err error 646 ) 647 _ = BackOffRetryIf(func() error { 648 got, err = c.Get(url, tt.queryArgs, tt.headers) 649 return err 650 }, func(err error) bool { 651 return !strings.Contains(err.Error(), `status code: 502`) 652 }, testHTTPRetryTimes, Milliseconds(100)) 653 // check resp 654 if (err != nil) != tt.wantErr { 655 t.Errorf("Redirect(%s) error = %v, wantErr %v", url, err, tt.wantErr) 656 return 657 } 658 if !tt.wantErr && len(tt.wantStr) > 0 { 659 gs := string(got) 660 for _, s := range tt.wantStr { 661 if !strings.Contains(gs, s) { 662 t.Errorf("Redirect(%s) = %s, should contain %q", url, got, s) 663 } 664 } 665 } 666 }) 667 } 668 } 669 670 func TestHTTPClient_Error(t *testing.T) { 671 tests := []struct { 672 name string 673 opts *HTTPClientOptions 674 url string 675 queryArgs map[string]string 676 headers map[string]string 677 wantStr []string 678 wantErr bool 679 }{ 680 {"No Host", nil, "https://example.invalid", nil, nil, nil, true}, 681 {"Expired Cert", nil, "https://expired.badssl.com", nil, nil, nil, true}, 682 {"Expired Cert Ignored", &HTTPClientOptions{Insecure: true}, "https://expired.badssl.com", nil, nil, []string{"expired"}, false}, 683 {"Revoked Cert", nil, "https://revoked.badssl.com", nil, nil, nil, true}, 684 {"Revoked Cert Ignored", &HTTPClientOptions{Insecure: true}, "https://revoked.badssl.com", nil, nil, []string{"revoked"}, false}, 685 {"Wrong Host Cert", nil, "https://wrong.host.badssl.com", nil, nil, nil, true}, 686 {"Wrong Host Cert Ignored", &HTTPClientOptions{Insecure: true}, "https://wrong.host.badssl.com", nil, nil, []string{"wrong"}, false}, 687 {"Self Signed Cert", nil, "https://self-signed.badssl.com", nil, nil, nil, true}, 688 {"Self Signed Cert Ignored", &HTTPClientOptions{Insecure: true}, "https://self-signed.badssl.com", nil, nil, []string{"self-signed"}, false}, 689 } 690 for _, tt := range tests { 691 t.Run(tt.name, func(t *testing.T) { 692 c := NewHTTPClient(tt.opts) 693 var ( 694 got []byte 695 err error 696 ) 697 _ = BackOffRetryIf(func() error { 698 got, err = c.Get(tt.url, tt.queryArgs, tt.headers) 699 return err 700 }, func(err error) bool { 701 return !strings.Contains(err.Error(), `status code: 502`) 702 }, testHTTPRetryTimes, Milliseconds(100)) 703 // check resp 704 if (err != nil) != tt.wantErr { 705 t.Errorf("Error(%s) error = %v, data = %d, wantErr %v", tt.url, err, len(got), tt.wantErr) 706 return 707 } 708 if !tt.wantErr && len(tt.wantStr) > 0 { 709 gs := string(got) 710 for _, s := range tt.wantStr { 711 if !strings.Contains(gs, s) { 712 t.Errorf("Error(%s) = %s, should contain %q", tt.url, got, s) 713 } 714 } 715 } 716 }) 717 } 718 }