github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/resty/retry_test.go (about) 1 // Copyright (c) 2015-2021 Jeevanandam M (jeeva@myjeeva.com), All rights reserved. 2 // resty source code and usage is governed by a MIT style 3 // license that can be found in the LICENSE file. 4 5 package resty 6 7 import ( 8 "context" 9 "encoding/json" 10 "errors" 11 "net/http" 12 "reflect" 13 "strconv" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/stretchr/testify/assert" 19 ) 20 21 func TestBackoffSuccess(t *testing.T) { 22 attempts := 3 23 externalCounter := 0 24 retryErr := Backoff(func() (*Response, error) { 25 externalCounter++ 26 if externalCounter < attempts { 27 return nil, errors.New("not yet got the number we're after") 28 } 29 30 return nil, nil 31 }) 32 33 assert.Nil(t, retryErr) 34 assert.Equal(t, externalCounter, attempts) 35 } 36 37 func TestBackoffNoWaitForLastRetry(t *testing.T) { 38 attempts := 1 39 externalCounter := 0 40 numRetries := 1 41 42 canceledCtx, cancel := context.WithCancel(context.Background()) 43 defer cancel() 44 45 resp := &Response{ 46 Request: &Request{ 47 ctx: canceledCtx, 48 client: &Client{ 49 RetryAfter: func(*Client, *Response) (time.Duration, error) { 50 return 6, nil 51 }, 52 }, 53 }, 54 } 55 56 retryErr := Backoff(func() (*Response, error) { 57 externalCounter++ 58 return resp, nil 59 }, RetryConditions([]RetryConditionFunc{func(response *Response, err error) bool { 60 if externalCounter == attempts+numRetries { 61 // Backoff returns context canceled if goes to sleep after last retry. 62 cancel() 63 } 64 return true 65 }}), Retries(numRetries)) 66 67 assert.Nil(t, retryErr) 68 } 69 70 func TestBackoffTenAttemptsSuccess(t *testing.T) { 71 attempts := 10 72 externalCounter := 0 73 retryErr := Backoff(func() (*Response, error) { 74 externalCounter++ 75 if externalCounter < attempts { 76 return nil, errors.New("not yet got the number we're after") 77 } 78 return nil, nil 79 }, Retries(attempts), WaitTime(5), MaxWaitTime(500)) 80 81 assert.Nil(t, retryErr) 82 assert.Equal(t, externalCounter, attempts) 83 } 84 85 // Check to make sure the conditional of the retry condition is being used 86 func TestConditionalBackoffCondition(t *testing.T) { 87 attempts := 3 88 counter := 0 89 check := RetryConditionFunc(func(*Response, error) bool { 90 return attempts != counter 91 }) 92 retryErr := Backoff(func() (*Response, error) { 93 counter++ 94 return nil, nil 95 }, RetryConditions([]RetryConditionFunc{check})) 96 97 assert.Nil(t, retryErr) 98 assert.Equal(t, counter, attempts) 99 } 100 101 // Check to make sure that if the conditional is false we don't retry 102 func TestConditionalBackoffConditionNonExecution(t *testing.T) { 103 attempts := 3 104 counter := 0 105 106 retryErr := Backoff(func() (*Response, error) { 107 counter++ 108 return nil, nil 109 }, RetryConditions([]RetryConditionFunc{filler})) 110 111 assert.Nil(t, retryErr) 112 assert.NotEqual(t, counter, attempts) 113 } 114 115 // Check to make sure that RetryHooks are executed 116 func TestOnRetryBackoff(t *testing.T) { 117 attempts := 3 118 counter := 0 119 120 hook := func(r *Response, err error) { 121 counter++ 122 } 123 124 retryErr := Backoff(func() (*Response, error) { 125 return nil, nil 126 }, RetryHooks([]OnRetryFunc{hook})) 127 128 assert.Nil(t, retryErr) 129 assert.NotEqual(t, counter, attempts) 130 } 131 132 // Check to make sure the functions added to add conditionals work 133 func TestConditionalGet(t *testing.T) { 134 ts := createGetServer(t) 135 defer ts.Close() 136 attemptCount := 1 137 externalCounter := 0 138 139 // This check should pass on first run, and let the response through 140 check := RetryConditionFunc(func(*Response, error) bool { 141 externalCounter++ 142 return attemptCount != externalCounter 143 }) 144 145 client := dc().AddRetryCondition(check).SetRetryCount(1) 146 resp, err := client.R(). 147 SetQueryParam("request_no", strconv.FormatInt(time.Now().Unix(), 10)). 148 Get(ts.URL + "/") 149 150 assert.Nil(t, err) 151 assert.Equal(t, http.StatusOK, resp.StatusCode()) 152 assert.Equal(t, "200 OK", resp.Status()) 153 assert.NotNil(t, resp.Body()) 154 assert.Equal(t, "TestGet: text response", resp.String()) 155 assert.Equal(t, externalCounter, attemptCount) 156 157 logResponse(t, resp) 158 } 159 160 // Check to make sure the package Function works. 161 func TestConditionalGetDefaultClient(t *testing.T) { 162 ts := createGetServer(t) 163 defer ts.Close() 164 attemptCount := 1 165 externalCounter := 0 166 167 // This check should pass on first run, and let the response through 168 check := RetryConditionFunc(func(*Response, error) bool { 169 externalCounter++ 170 return attemptCount != externalCounter 171 }) 172 173 // Clear the default client. 174 client := dc() 175 // Proceed to check. 176 client.AddRetryCondition(check).SetRetryCount(1) 177 resp, err := client.R(). 178 SetQueryParam("request_no", strconv.FormatInt(time.Now().Unix(), 10)). 179 Get(ts.URL + "/") 180 181 assert.Nil(t, err) 182 assert.Equal(t, http.StatusOK, resp.StatusCode()) 183 assert.Equal(t, "200 OK", resp.Status()) 184 assert.NotNil(t, resp.Body()) 185 assert.Equal(t, "TestGet: text response", resp.String()) 186 assert.Equal(t, externalCounter, attemptCount) 187 188 logResponse(t, resp) 189 } 190 191 func TestClientRetryGet(t *testing.T) { 192 ts := createGetServer(t) 193 defer ts.Close() 194 195 c := dc(). 196 SetTimeout(time.Second * 3). 197 SetRetryCount(3) 198 199 resp, err := c.R().Get(ts.URL + "/set-retrycount-test") 200 assert.Equal(t, "", resp.Status()) 201 assert.Equal(t, "", resp.Proto()) 202 assert.Equal(t, 0, resp.StatusCode()) 203 assert.Equal(t, 0, len(resp.Cookies())) 204 assert.NotNil(t, resp.Body()) 205 assert.Equal(t, 0, len(resp.Header())) 206 207 assert.Equal(t, true, strings.HasPrefix(err.Error(), "Get "+ts.URL+"/set-retrycount-test") || 208 strings.HasPrefix(err.Error(), "Get \""+ts.URL+"/set-retrycount-test\"")) 209 } 210 211 func TestClientRetryWait(t *testing.T) { 212 ts := createGetServer(t) 213 defer ts.Close() 214 215 attempt := 0 216 217 retryCount := 5 218 retryIntervals := make([]uint64, retryCount+1) 219 220 // Set retry wait times that do not intersect with default ones 221 retryWaitTime := time.Duration(3) * time.Second 222 retryMaxWaitTime := time.Duration(9) * time.Second 223 224 c := dc(). 225 SetRetryCount(retryCount). 226 SetRetryWaitTime(retryWaitTime). 227 SetRetryMaxWaitTime(retryMaxWaitTime). 228 AddRetryCondition( 229 func(r *Response, _ error) bool { 230 timeSlept, _ := strconv.ParseUint(string(r.Body()), 10, 64) 231 retryIntervals[attempt] = timeSlept 232 attempt++ 233 return true 234 }, 235 ) 236 _, _ = c.R().Get(ts.URL + "/set-retrywaittime-test") 237 238 // 6 attempts were made 239 assert.Equal(t, attempt, 6) 240 241 // Initial attempt has 0 time slept since last request 242 assert.Equal(t, retryIntervals[0], uint64(0)) 243 244 for i := 1; i < len(retryIntervals); i++ { 245 slept := time.Duration(retryIntervals[i]) 246 // Ensure that client has slept some duration between 247 // waitTime and maxWaitTime for consequent requests 248 if slept < retryWaitTime || slept > retryMaxWaitTime { 249 t.Errorf("Client has slept %f seconds before retry %d", slept.Seconds(), i) 250 } 251 } 252 } 253 254 func TestClientRetryWaitMaxInfinite(t *testing.T) { 255 ts := createGetServer(t) 256 defer ts.Close() 257 258 attempt := 0 259 260 retryCount := 5 261 retryIntervals := make([]uint64, retryCount+1) 262 263 // Set retry wait times that do not intersect with default ones 264 retryWaitTime := time.Duration(3) * time.Second 265 retryMaxWaitTime := time.Duration(-1.0) // negative value 266 267 c := dc(). 268 SetRetryCount(retryCount). 269 SetRetryWaitTime(retryWaitTime). 270 SetRetryMaxWaitTime(retryMaxWaitTime). 271 AddRetryCondition( 272 func(r *Response, _ error) bool { 273 timeSlept, _ := strconv.ParseUint(string(r.Body()), 10, 64) 274 retryIntervals[attempt] = timeSlept 275 attempt++ 276 return true 277 }, 278 ) 279 _, _ = c.R().Get(ts.URL + "/set-retrywaittime-test") 280 281 // 6 attempts were made 282 assert.Equal(t, attempt, 6) 283 284 // Initial attempt has 0 time slept since last request 285 assert.Equal(t, retryIntervals[0], uint64(0)) 286 287 for i := 1; i < len(retryIntervals); i++ { 288 slept := time.Duration(retryIntervals[i]) 289 // Ensure that client has slept some duration between 290 // waitTime and maxWaitTime for consequent requests 291 if slept < retryWaitTime { 292 t.Errorf("Client has slept %f seconds before retry %d", slept.Seconds(), i) 293 } 294 } 295 } 296 297 func TestClientRetryWaitCallbackError(t *testing.T) { 298 ts := createGetServer(t) 299 defer ts.Close() 300 301 attempt := 0 302 303 retryCount := 5 304 retryIntervals := make([]uint64, retryCount+1) 305 306 // Set retry wait times that do not intersect with default ones 307 retryWaitTime := 3 * time.Second 308 retryMaxWaitTime := 9 * time.Second 309 310 retryAfter := func(client *Client, resp *Response) (time.Duration, error) { 311 return 0, errors.New("quota exceeded") 312 } 313 314 c := dc(). 315 SetRetryCount(retryCount). 316 SetRetryWaitTime(retryWaitTime). 317 SetRetryMaxWaitTime(retryMaxWaitTime). 318 SetRetryAfter(retryAfter). 319 AddRetryCondition( 320 func(r *Response, _ error) bool { 321 timeSlept, _ := strconv.ParseUint(string(r.Body()), 10, 64) 322 retryIntervals[attempt] = timeSlept 323 attempt++ 324 return true 325 }, 326 ) 327 328 _, err := c.R().Get(ts.URL + "/set-retrywaittime-test") 329 330 // 1 attempts were made 331 assert.Equal(t, attempt, 1) 332 333 // non-nil error was returned 334 assert.NotEqual(t, nil, err) 335 } 336 337 func TestClientRetryWaitCallback(t *testing.T) { 338 ts := createGetServer(t) 339 defer ts.Close() 340 341 attempt := 0 342 343 retryCount := 5 344 retryIntervals := make([]uint64, retryCount+1) 345 346 // Set retry wait times that do not intersect with default ones 347 retryWaitTime := 3 * time.Second 348 retryMaxWaitTime := 9 * time.Second 349 350 retryAfter := func(client *Client, resp *Response) (time.Duration, error) { 351 return 5 * time.Second, nil 352 } 353 354 c := dc(). 355 SetRetryCount(retryCount). 356 SetRetryWaitTime(retryWaitTime). 357 SetRetryMaxWaitTime(retryMaxWaitTime). 358 SetRetryAfter(retryAfter). 359 AddRetryCondition( 360 func(r *Response, _ error) bool { 361 timeSlept, _ := strconv.ParseUint(string(r.Body()), 10, 64) 362 retryIntervals[attempt] = timeSlept 363 attempt++ 364 return true 365 }, 366 ) 367 _, _ = c.R().Get(ts.URL + "/set-retrywaittime-test") 368 369 // 6 attempts were made 370 assert.Equal(t, attempt, 6) 371 372 // Initial attempt has 0 time slept since last request 373 assert.Equal(t, retryIntervals[0], uint64(0)) 374 375 for i := 1; i < len(retryIntervals); i++ { 376 slept := time.Duration(retryIntervals[i]) 377 // Ensure that client has slept some duration between 378 // waitTime and maxWaitTime for consequent requests 379 if slept < 5*time.Second-5*time.Millisecond || 5*time.Second+5*time.Millisecond < slept { 380 t.Logf("Client has slept %f seconds before retry %d", slept.Seconds(), i) 381 } 382 } 383 } 384 385 func TestClientRetryWaitCallbackTooShort(t *testing.T) { 386 ts := createGetServer(t) 387 defer ts.Close() 388 389 attempt := 0 390 391 retryCount := 5 392 retryIntervals := make([]uint64, retryCount+1) 393 394 // Set retry wait times that do not intersect with default ones 395 retryWaitTime := 3 * time.Second 396 retryMaxWaitTime := 9 * time.Second 397 398 retryAfter := func(client *Client, resp *Response) (time.Duration, error) { 399 return 2 * time.Second, nil // too short duration 400 } 401 402 c := dc(). 403 SetRetryCount(retryCount). 404 SetRetryWaitTime(retryWaitTime). 405 SetRetryMaxWaitTime(retryMaxWaitTime). 406 SetRetryAfter(retryAfter). 407 AddRetryCondition( 408 func(r *Response, _ error) bool { 409 timeSlept, _ := strconv.ParseUint(string(r.Body()), 10, 64) 410 retryIntervals[attempt] = timeSlept 411 attempt++ 412 return true 413 }, 414 ) 415 _, _ = c.R().Get(ts.URL + "/set-retrywaittime-test") 416 417 // 6 attempts were made 418 assert.Equal(t, attempt, 6) 419 420 // Initial attempt has 0 time slept since last request 421 assert.Equal(t, retryIntervals[0], uint64(0)) 422 423 for i := 1; i < len(retryIntervals); i++ { 424 slept := time.Duration(retryIntervals[i]) 425 // Ensure that client has slept some duration between 426 // waitTime and maxWaitTime for consequent requests 427 if slept < retryWaitTime-5*time.Millisecond || retryWaitTime+5*time.Millisecond < slept { 428 t.Logf("Client has slept %f seconds before retry %d", slept.Seconds(), i) 429 } 430 } 431 } 432 433 func TestClientRetryWaitCallbackTooLong(t *testing.T) { 434 ts := createGetServer(t) 435 defer ts.Close() 436 437 attempt := 0 438 439 retryCount := 5 440 retryIntervals := make([]uint64, retryCount+1) 441 442 // Set retry wait times that do not intersect with default ones 443 retryWaitTime := 1 * time.Second 444 retryMaxWaitTime := 3 * time.Second 445 446 retryAfter := func(client *Client, resp *Response) (time.Duration, error) { 447 return 4 * time.Second, nil // too long duration 448 } 449 450 c := dc(). 451 SetRetryCount(retryCount). 452 SetRetryWaitTime(retryWaitTime). 453 SetRetryMaxWaitTime(retryMaxWaitTime). 454 SetRetryAfter(retryAfter). 455 AddRetryCondition( 456 func(r *Response, _ error) bool { 457 timeSlept, _ := strconv.ParseUint(string(r.Body()), 10, 64) 458 retryIntervals[attempt] = timeSlept 459 attempt++ 460 return true 461 }, 462 ) 463 _, _ = c.R().Get(ts.URL + "/set-retrywaittime-test") 464 465 // 6 attempts were made 466 assert.Equal(t, attempt, 6) 467 468 // Initial attempt has 0 time slept since last request 469 assert.Equal(t, retryIntervals[0], uint64(0)) 470 471 for i := 1; i < len(retryIntervals); i++ { 472 slept := time.Duration(retryIntervals[i]) 473 // Ensure that client has slept some duration between 474 // waitTime and maxWaitTime for consequent requests 475 if slept < retryMaxWaitTime-5*time.Millisecond || retryMaxWaitTime+5*time.Millisecond < slept { 476 t.Logf("Client has slept %f seconds before retry %d", slept.Seconds(), i) 477 } 478 } 479 } 480 481 func TestClientRetryWaitCallbackSwitchToDefault(t *testing.T) { 482 ts := createGetServer(t) 483 defer ts.Close() 484 485 attempt := 0 486 487 retryCount := 5 488 retryIntervals := make([]uint64, retryCount+1) 489 490 // Set retry wait times that do not intersect with default ones 491 retryWaitTime := 1 * time.Second 492 retryMaxWaitTime := 3 * time.Second 493 494 retryAfter := func(client *Client, resp *Response) (time.Duration, error) { 495 return 0, nil // use default algorithm to determine retry-after time 496 } 497 498 c := dc(). 499 SetTrace(true). 500 SetRetryCount(retryCount). 501 SetRetryWaitTime(retryWaitTime). 502 SetRetryMaxWaitTime(retryMaxWaitTime). 503 SetRetryAfter(retryAfter). 504 AddRetryCondition( 505 func(r *Response, _ error) bool { 506 timeSlept, _ := strconv.ParseUint(string(r.Body()), 10, 64) 507 retryIntervals[attempt] = timeSlept 508 attempt++ 509 return true 510 }, 511 ) 512 resp, _ := c.R().Get(ts.URL + "/set-retrywaittime-test") 513 514 // 6 attempts were made 515 assert.Equal(t, attempt, 6) 516 assert.Equal(t, resp.Request.Attempt, 6) 517 assert.Equal(t, resp.Request.TraceInfo().RequestAttempt, 6) 518 519 // Initial attempt has 0 time slept since last request 520 assert.Equal(t, retryIntervals[0], uint64(0)) 521 522 for i := 1; i < len(retryIntervals); i++ { 523 slept := time.Duration(retryIntervals[i]) 524 expected := (1 << (uint(i - 1))) * time.Second 525 if expected > retryMaxWaitTime { 526 expected = retryMaxWaitTime 527 } 528 529 // Ensure that client has slept some duration between 530 // waitTime and maxWaitTime for consequent requests 531 if slept < expected/2-5*time.Millisecond || expected+5*time.Millisecond < slept { 532 t.Errorf("Client has slept %f seconds before retry %d", slept.Seconds(), i) 533 } 534 } 535 } 536 537 func TestClientRetryCancel(t *testing.T) { 538 ts := createGetServer(t) 539 defer ts.Close() 540 541 attempt := 0 542 543 retryCount := 5 544 retryIntervals := make([]uint64, retryCount+1) 545 546 // Set retry wait times that do not intersect with default ones 547 retryWaitTime := time.Duration(10) * time.Second 548 retryMaxWaitTime := time.Duration(20) * time.Second 549 550 c := dc(). 551 SetRetryCount(retryCount). 552 SetRetryWaitTime(retryWaitTime). 553 SetRetryMaxWaitTime(retryMaxWaitTime). 554 AddRetryCondition( 555 func(r *Response, _ error) bool { 556 timeSlept, _ := strconv.ParseUint(string(r.Body()), 10, 64) 557 retryIntervals[attempt] = timeSlept 558 attempt++ 559 return true 560 }, 561 ) 562 563 timeout := 2 * time.Second 564 565 ctx, cancelFunc := context.WithTimeout(context.Background(), timeout) 566 _, _ = c.R().SetContext(ctx).Get(ts.URL + "/set-retrywaittime-test") 567 568 // 1 attempts were made 569 assert.Equal(t, attempt, 1) 570 571 // Initial attempt has 0 time slept since last request 572 assert.Equal(t, retryIntervals[0], uint64(0)) 573 574 // Second attempt should be interrupted on context timeout 575 if time.Duration(retryIntervals[1]) > timeout { 576 t.Errorf("Client didn't awake on context cancel") 577 } 578 cancelFunc() 579 } 580 581 func TestClientRetryPost(t *testing.T) { 582 ts := createPostServer(t) 583 defer ts.Close() 584 585 usersmap := map[string]interface{}{ 586 "user1": map[string]interface{}{"FirstName": "firstname1", "LastName": "lastname1", "ZipCode": "10001"}, 587 } 588 589 var users []map[string]interface{} 590 users = append(users, usersmap) 591 592 c := dc() 593 c.SetRetryCount(3) 594 c.AddRetryCondition(func(r *Response, _ error) bool { 595 return r.StatusCode() >= http.StatusInternalServerError 596 }) 597 598 resp, _ := c.R(). 599 SetBody(&users). 600 Post(ts.URL + "/usersmap?status=500") 601 602 if resp != nil { 603 if resp.StatusCode() == http.StatusInternalServerError { 604 t.Logf("Got response body: %s", string(resp.body)) 605 var usersResponse []map[string]interface{} 606 err := json.Unmarshal(resp.body, &usersResponse) 607 assert.Nil(t, err) 608 609 if !reflect.DeepEqual(users, usersResponse) { 610 t.Errorf("Expected request body to be echoed back as response body. Instead got: %s", string(resp.body)) 611 } 612 613 return 614 } 615 t.Errorf("Got unexpected response code: %d with body: %s", resp.StatusCode(), string(resp.body)) 616 } 617 } 618 619 func TestClientRetryErrorRecover(t *testing.T) { 620 ts := createGetServer(t) 621 defer ts.Close() 622 623 c := dc(). 624 SetRetryCount(2). 625 SetError(AuthError{}). 626 AddRetryCondition( 627 func(r *Response, _ error) bool { 628 err, ok := r.Error().(*AuthError) 629 retry := ok && r.StatusCode() == 429 && err.Message == "too many" 630 return retry 631 }, 632 ) 633 634 resp, err := c.R(). 635 SetHeader(hdrContentTypeKey, "application/json; charset=utf-8"). 636 SetJSONEscapeHTML(false). 637 SetResult(AuthSuccess{}). 638 Get(ts.URL + "/set-retry-error-recover") 639 640 assert.Nil(t, err) 641 642 authSuccess := resp.Result().(*AuthSuccess) 643 644 assert.Equal(t, http.StatusOK, resp.StatusCode()) 645 assert.Equal(t, "hello", authSuccess.Message) 646 647 assert.Nil(t, resp.Error()) 648 } 649 650 func TestClientRetryCount(t *testing.T) { 651 ts := createGetServer(t) 652 defer ts.Close() 653 654 attempt := 0 655 656 c := dc(). 657 SetTimeout(time.Second * 3). 658 SetRetryCount(1). 659 AddRetryCondition( 660 func(r *Response, _ error) bool { 661 attempt++ 662 return true 663 }, 664 ) 665 666 resp, err := c.R().Get(ts.URL + "/set-retrycount-test") 667 assert.Equal(t, "", resp.Status()) 668 assert.Equal(t, "", resp.Proto()) 669 assert.Equal(t, 0, resp.StatusCode()) 670 assert.Equal(t, 0, len(resp.Cookies())) 671 assert.NotNil(t, resp.Body()) 672 assert.Equal(t, 0, len(resp.Header())) 673 674 // 2 attempts were made 675 assert.Equal(t, attempt, 2) 676 677 assert.Equal(t, true, strings.HasPrefix(err.Error(), "Get "+ts.URL+"/set-retrycount-test") || 678 strings.HasPrefix(err.Error(), "Get \""+ts.URL+"/set-retrycount-test\"")) 679 } 680 681 func TestClientErrorRetry(t *testing.T) { 682 ts := createGetServer(t) 683 defer ts.Close() 684 685 c := dc(). 686 SetTimeout(time.Second * 3). 687 SetRetryCount(1). 688 AddRetryAfterErrorCondition() 689 690 resp, err := c.R(). 691 SetHeader(hdrContentTypeKey, "application/json; charset=utf-8"). 692 SetJSONEscapeHTML(false). 693 SetResult(AuthSuccess{}). 694 Get(ts.URL + "/set-retry-error-recover") 695 696 assert.Nil(t, err) 697 698 authSuccess := resp.Result().(*AuthSuccess) 699 700 assert.Equal(t, http.StatusOK, resp.StatusCode()) 701 assert.Equal(t, "hello", authSuccess.Message) 702 703 assert.Nil(t, resp.Error()) 704 } 705 706 func TestClientRetryHook(t *testing.T) { 707 ts := createGetServer(t) 708 defer ts.Close() 709 710 attempt := 0 711 712 c := dc(). 713 SetRetryCount(2). 714 SetTimeout(time.Second * 3). 715 AddRetryHook( 716 func(r *Response, _ error) { 717 attempt++ 718 }, 719 ) 720 721 resp, err := c.R().Get(ts.URL + "/set-retrycount-test") 722 assert.Equal(t, "", resp.Status()) 723 assert.Equal(t, "", resp.Proto()) 724 assert.Equal(t, 0, resp.StatusCode()) 725 assert.Equal(t, 0, len(resp.Cookies())) 726 assert.NotNil(t, resp.Body()) 727 assert.Equal(t, 0, len(resp.Header())) 728 729 assert.Equal(t, 3, attempt) 730 731 assert.Equal(t, true, strings.HasPrefix(err.Error(), "Get "+ts.URL+"/set-retrycount-test") || 732 strings.HasPrefix(err.Error(), "Get \""+ts.URL+"/set-retrycount-test\"")) 733 } 734 735 func filler(*Response, error) bool { 736 return false 737 }