1package autorest 2 3// Copyright 2017 Microsoft Corporation 4// 5// Licensed under the Apache License, Version 2.0 (the "License"); 6// you may not use this file except in compliance with the License. 7// You may obtain a copy of the License at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, 13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// See the License for the specific language governing permissions and 15// limitations under the License. 16 17import ( 18 "bytes" 19 "context" 20 "fmt" 21 "log" 22 "net/http" 23 "net/http/httptest" 24 "os" 25 "reflect" 26 "sync" 27 "testing" 28 "time" 29 30 "github.com/Azure/go-autorest/autorest/mocks" 31) 32 33func ExampleSendWithSender() { 34 r := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) 35 mocks.SetAcceptedHeaders(r) 36 37 client := mocks.NewSender() 38 client.AppendAndRepeatResponse(r, 10) 39 40 logger := log.New(os.Stdout, "autorest: ", 0) 41 na := NullAuthorizer{} 42 43 req, _ := Prepare(&http.Request{}, 44 AsGet(), 45 WithBaseURL("https://microsoft.com/a/b/c/"), 46 na.WithAuthorization()) 47 48 r, _ = SendWithSender(client, req, 49 WithLogging(logger), 50 DoErrorIfStatusCode(http.StatusAccepted), 51 DoCloseIfError(), 52 DoRetryForAttempts(5, time.Duration(0))) 53 54 Respond(r, 55 ByDiscardingBody(), 56 ByClosing()) 57 58 // Output: 59 // autorest: Sending GET https://microsoft.com/a/b/c/ 60 // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted 61 // autorest: Sending GET https://microsoft.com/a/b/c/ 62 // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted 63 // autorest: Sending GET https://microsoft.com/a/b/c/ 64 // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted 65 // autorest: Sending GET https://microsoft.com/a/b/c/ 66 // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted 67 // autorest: Sending GET https://microsoft.com/a/b/c/ 68 // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted 69} 70 71func ExampleDoRetryForAttempts() { 72 client := mocks.NewSender() 73 client.SetAndRepeatError(fmt.Errorf("Faux Error"), 10) 74 75 // Retry with backoff -- ensure returned Bodies are closed 76 r, _ := SendWithSender(client, mocks.NewRequest(), 77 DoCloseIfError(), 78 DoRetryForAttempts(5, time.Duration(0))) 79 80 Respond(r, 81 ByDiscardingBody(), 82 ByClosing()) 83 84 fmt.Printf("Retry stopped after %d attempts", client.Attempts()) 85 // Output: Retry stopped after 5 attempts 86} 87 88func ExampleDoErrorIfStatusCode() { 89 client := mocks.NewSender() 90 client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("204 NoContent", http.StatusNoContent), 10) 91 92 // Chain decorators to retry the request, up to five times, if the status code is 204 93 r, _ := SendWithSender(client, mocks.NewRequest(), 94 DoErrorIfStatusCode(http.StatusNoContent), 95 DoCloseIfError(), 96 DoRetryForAttempts(5, time.Duration(0))) 97 98 Respond(r, 99 ByDiscardingBody(), 100 ByClosing()) 101 102 fmt.Printf("Retry stopped after %d attempts with code %s", client.Attempts(), r.Status) 103 // Output: Retry stopped after 5 attempts with code 204 NoContent 104} 105 106func TestSendWithSenderRunsDecoratorsInOrder(t *testing.T) { 107 client := mocks.NewSender() 108 s := "" 109 110 r, err := SendWithSender(client, mocks.NewRequest(), 111 withMessage(&s, "a"), 112 withMessage(&s, "b"), 113 withMessage(&s, "c")) 114 if err != nil { 115 t.Fatalf("autorest: SendWithSender returned an error (%v)", err) 116 } 117 118 Respond(r, 119 ByDiscardingBody(), 120 ByClosing()) 121 122 if s != "abc" { 123 t.Fatalf("autorest: SendWithSender invoke decorators out of order; expected 'abc', received '%s'", s) 124 } 125} 126 127func TestCreateSender(t *testing.T) { 128 f := false 129 130 s := CreateSender( 131 (func() SendDecorator { 132 return func(s Sender) Sender { 133 return SenderFunc(func(r *http.Request) (*http.Response, error) { 134 f = true 135 return nil, nil 136 }) 137 } 138 })()) 139 s.Do(&http.Request{}) 140 141 if !f { 142 t.Fatal("autorest: CreateSender failed to apply supplied decorator") 143 } 144} 145 146func TestSend(t *testing.T) { 147 f := false 148 149 Send(&http.Request{}, 150 (func() SendDecorator { 151 return func(s Sender) Sender { 152 return SenderFunc(func(r *http.Request) (*http.Response, error) { 153 f = true 154 return nil, nil 155 }) 156 } 157 })()) 158 159 if !f { 160 t.Fatal("autorest: Send failed to apply supplied decorator") 161 } 162} 163 164func TestAfterDelayWaits(t *testing.T) { 165 client := mocks.NewSender() 166 167 d := 2 * time.Second 168 169 tt := time.Now() 170 r, _ := SendWithSender(client, mocks.NewRequest(), 171 AfterDelay(d)) 172 s := time.Since(tt) 173 if s < d { 174 t.Fatal("autorest: AfterDelay failed to wait for at least the specified duration") 175 } 176 177 Respond(r, 178 ByDiscardingBody(), 179 ByClosing()) 180} 181 182func TestAfterDelay_Cancels(t *testing.T) { 183 client := mocks.NewSender() 184 ctx, cancel := context.WithCancel(context.Background()) 185 delay := 5 * time.Second 186 187 var wg sync.WaitGroup 188 wg.Add(1) 189 start := time.Now() 190 end := time.Now() 191 var err error 192 go func() { 193 req := mocks.NewRequest() 194 req = req.WithContext(ctx) 195 _, err = SendWithSender(client, req, 196 AfterDelay(delay)) 197 end = time.Now() 198 wg.Done() 199 }() 200 cancel() 201 wg.Wait() 202 time.Sleep(5 * time.Millisecond) 203 if end.Sub(start) >= delay { 204 t.Fatal("autorest: AfterDelay elapsed") 205 } 206 if err == nil { 207 t.Fatal("autorest: AfterDelay didn't cancel") 208 } 209} 210 211func TestAfterDelayDoesNotWaitTooLong(t *testing.T) { 212 client := mocks.NewSender() 213 214 d := 5 * time.Millisecond 215 start := time.Now() 216 r, _ := SendWithSender(client, mocks.NewRequest(), 217 AfterDelay(d)) 218 219 if time.Since(start) > (5 * d) { 220 t.Fatal("autorest: AfterDelay waited too long (exceeded 5 times specified duration)") 221 } 222 223 Respond(r, 224 ByDiscardingBody(), 225 ByClosing()) 226} 227 228func TestAsIs(t *testing.T) { 229 client := mocks.NewSender() 230 231 r1 := mocks.NewResponse() 232 client.AppendResponse(r1) 233 234 r2, err := SendWithSender(client, mocks.NewRequest(), 235 AsIs()) 236 if err != nil { 237 t.Fatalf("autorest: AsIs returned an unexpected error (%v)", err) 238 } else if !reflect.DeepEqual(r1, r2) { 239 t.Fatalf("autorest: AsIs modified the response -- received %v, expected %v", r2, r1) 240 } 241 242 Respond(r1, 243 ByDiscardingBody(), 244 ByClosing()) 245 Respond(r2, 246 ByDiscardingBody(), 247 ByClosing()) 248} 249 250func TestDoCloseIfError(t *testing.T) { 251 client := mocks.NewSender() 252 client.AppendResponse(mocks.NewResponseWithStatus("400 BadRequest", http.StatusBadRequest)) 253 254 r, _ := SendWithSender(client, mocks.NewRequest(), 255 DoErrorIfStatusCode(http.StatusBadRequest), 256 DoCloseIfError()) 257 258 if r.Body.(*mocks.Body).IsOpen() { 259 t.Fatal("autorest: Expected DoCloseIfError to close response body -- it was left open") 260 } 261 262 Respond(r, 263 ByDiscardingBody(), 264 ByClosing()) 265} 266 267func TestDoCloseIfErrorAcceptsNilResponse(t *testing.T) { 268 client := mocks.NewSender() 269 270 SendWithSender(client, mocks.NewRequest(), 271 (func() SendDecorator { 272 return func(s Sender) Sender { 273 return SenderFunc(func(r *http.Request) (*http.Response, error) { 274 resp, err := s.Do(r) 275 if err != nil { 276 resp.Body.Close() 277 } 278 return nil, fmt.Errorf("Faux Error") 279 }) 280 } 281 })(), 282 DoCloseIfError()) 283} 284 285func TestDoCloseIfErrorAcceptsNilBody(t *testing.T) { 286 client := mocks.NewSender() 287 288 SendWithSender(client, mocks.NewRequest(), 289 (func() SendDecorator { 290 return func(s Sender) Sender { 291 return SenderFunc(func(r *http.Request) (*http.Response, error) { 292 resp, err := s.Do(r) 293 if err != nil { 294 resp.Body.Close() 295 } 296 resp.Body = nil 297 return resp, fmt.Errorf("Faux Error") 298 }) 299 } 300 })(), 301 DoCloseIfError()) 302} 303 304func TestDoErrorIfStatusCode(t *testing.T) { 305 client := mocks.NewSender() 306 client.AppendResponse(mocks.NewResponseWithStatus("400 BadRequest", http.StatusBadRequest)) 307 308 r, err := SendWithSender(client, mocks.NewRequest(), 309 DoErrorIfStatusCode(http.StatusBadRequest), 310 DoCloseIfError()) 311 if err == nil { 312 t.Fatal("autorest: DoErrorIfStatusCode failed to emit an error for passed code") 313 } 314 315 Respond(r, 316 ByDiscardingBody(), 317 ByClosing()) 318} 319 320func TestDoErrorIfStatusCodeIgnoresStatusCodes(t *testing.T) { 321 client := mocks.NewSender() 322 client.AppendResponse(newAcceptedResponse()) 323 324 r, err := SendWithSender(client, mocks.NewRequest(), 325 DoErrorIfStatusCode(http.StatusBadRequest), 326 DoCloseIfError()) 327 if err != nil { 328 t.Fatal("autorest: DoErrorIfStatusCode failed to ignore a status code") 329 } 330 331 Respond(r, 332 ByDiscardingBody(), 333 ByClosing()) 334} 335 336func TestDoErrorUnlessStatusCode(t *testing.T) { 337 client := mocks.NewSender() 338 client.AppendResponse(mocks.NewResponseWithStatus("400 BadRequest", http.StatusBadRequest)) 339 340 r, err := SendWithSender(client, mocks.NewRequest(), 341 DoErrorUnlessStatusCode(http.StatusAccepted), 342 DoCloseIfError()) 343 if err == nil { 344 t.Fatal("autorest: DoErrorUnlessStatusCode failed to emit an error for an unknown status code") 345 } 346 347 Respond(r, 348 ByDiscardingBody(), 349 ByClosing()) 350} 351 352func TestDoErrorUnlessStatusCodeIgnoresStatusCodes(t *testing.T) { 353 client := mocks.NewSender() 354 client.AppendResponse(newAcceptedResponse()) 355 356 r, err := SendWithSender(client, mocks.NewRequest(), 357 DoErrorUnlessStatusCode(http.StatusAccepted), 358 DoCloseIfError()) 359 if err != nil { 360 t.Fatal("autorest: DoErrorUnlessStatusCode emitted an error for a knonwn status code") 361 } 362 363 Respond(r, 364 ByDiscardingBody(), 365 ByClosing()) 366} 367 368func TestDoRetryForAttemptsStopsAfterSuccess(t *testing.T) { 369 client := mocks.NewSender() 370 371 r, err := SendWithSender(client, mocks.NewRequest(), 372 DoRetryForAttempts(5, time.Duration(0))) 373 if client.Attempts() != 1 { 374 t.Fatalf("autorest: DoRetryForAttempts failed to stop after success -- expected attempts %v, actual %v", 375 1, client.Attempts()) 376 } 377 if err != nil { 378 t.Fatalf("autorest: DoRetryForAttempts returned an unexpected error (%v)", err) 379 } 380 381 Respond(r, 382 ByDiscardingBody(), 383 ByClosing()) 384} 385 386func TestDoRetryForAttemptsStopsAfterAttempts(t *testing.T) { 387 client := mocks.NewSender() 388 client.SetAndRepeatError(fmt.Errorf("Faux Error"), 10) 389 390 r, err := SendWithSender(client, mocks.NewRequest(), 391 DoRetryForAttempts(5, time.Duration(0)), 392 DoCloseIfError()) 393 if err == nil { 394 t.Fatal("autorest: Mock client failed to emit errors") 395 } 396 397 Respond(r, 398 ByDiscardingBody(), 399 ByClosing()) 400 401 if client.Attempts() != 5 { 402 t.Fatal("autorest: DoRetryForAttempts failed to stop after specified number of attempts") 403 } 404} 405 406func TestDoRetryForAttemptsReturnsResponse(t *testing.T) { 407 client := mocks.NewSender() 408 client.SetError(fmt.Errorf("Faux Error")) 409 410 r, err := SendWithSender(client, mocks.NewRequest(), 411 DoRetryForAttempts(1, time.Duration(0))) 412 if err == nil { 413 t.Fatal("autorest: Mock client failed to emit errors") 414 } 415 416 if r == nil { 417 t.Fatal("autorest: DoRetryForAttempts failed to return the underlying response") 418 } 419 420 Respond(r, 421 ByDiscardingBody(), 422 ByClosing()) 423} 424 425func TestDoRetryForDurationStopsAfterSuccess(t *testing.T) { 426 client := mocks.NewSender() 427 428 r, err := SendWithSender(client, mocks.NewRequest(), 429 DoRetryForDuration(10*time.Millisecond, time.Duration(0))) 430 if client.Attempts() != 1 { 431 t.Fatalf("autorest: DoRetryForDuration failed to stop after success -- expected attempts %v, actual %v", 432 1, client.Attempts()) 433 } 434 if err != nil { 435 t.Fatalf("autorest: DoRetryForDuration returned an unexpected error (%v)", err) 436 } 437 438 Respond(r, 439 ByDiscardingBody(), 440 ByClosing()) 441} 442 443func TestDoRetryForDurationStopsAfterDuration(t *testing.T) { 444 client := mocks.NewSender() 445 client.SetAndRepeatError(fmt.Errorf("Faux Error"), -1) 446 447 d := 5 * time.Millisecond 448 start := time.Now() 449 r, err := SendWithSender(client, mocks.NewRequest(), 450 DoRetryForDuration(d, time.Duration(0)), 451 DoCloseIfError()) 452 if err == nil { 453 t.Fatal("autorest: Mock client failed to emit errors") 454 } 455 456 if time.Since(start) < d { 457 t.Fatal("autorest: DoRetryForDuration failed stopped too soon") 458 } 459 460 Respond(r, 461 ByDiscardingBody(), 462 ByClosing()) 463} 464 465func TestDoRetryForDurationStopsWithinReason(t *testing.T) { 466 client := mocks.NewSender() 467 client.SetAndRepeatError(fmt.Errorf("Faux Error"), -1) 468 469 d := 5 * time.Second 470 start := time.Now() 471 r, err := SendWithSender(client, mocks.NewRequest(), 472 DoRetryForDuration(d, time.Duration(0)), 473 DoCloseIfError()) 474 if err == nil { 475 t.Fatal("autorest: Mock client failed to emit errors") 476 } 477 478 if time.Since(start) > (5 * d) { 479 t.Fatal("autorest: DoRetryForDuration failed stopped soon enough (exceeded 5 times specified duration)") 480 } 481 482 Respond(r, 483 ByDiscardingBody(), 484 ByClosing()) 485} 486 487func TestDoRetryForDurationReturnsResponse(t *testing.T) { 488 client := mocks.NewSender() 489 client.SetAndRepeatError(fmt.Errorf("Faux Error"), -1) 490 491 r, err := SendWithSender(client, mocks.NewRequest(), 492 DoRetryForDuration(10*time.Millisecond, time.Duration(0)), 493 DoCloseIfError()) 494 if err == nil { 495 t.Fatal("autorest: Mock client failed to emit errors") 496 } 497 498 if r == nil { 499 t.Fatal("autorest: DoRetryForDuration failed to return the underlying response") 500 } 501 502 Respond(r, 503 ByDiscardingBody(), 504 ByClosing()) 505} 506 507func TestDelayForBackoff(t *testing.T) { 508 d := 2 * time.Second 509 start := time.Now() 510 DelayForBackoff(d, 0, nil) 511 if time.Since(start) < d { 512 t.Fatal("autorest: DelayForBackoff did not delay as long as expected") 513 } 514} 515 516func TestDelayForBackoffWithCap(t *testing.T) { 517 d := 2 * time.Second 518 start := time.Now() 519 DelayForBackoffWithCap(d, 1*time.Second, 0, nil) 520 if time.Since(start) >= d { 521 t.Fatal("autorest: DelayForBackoffWithCap delayed for too long") 522 } 523} 524 525func TestDelayForBackoff_Cancels(t *testing.T) { 526 cancel := make(chan struct{}) 527 delay := 5 * time.Second 528 529 var wg sync.WaitGroup 530 wg.Add(1) 531 start := time.Now() 532 go func() { 533 wg.Done() 534 DelayForBackoff(delay, 0, cancel) 535 }() 536 wg.Wait() 537 close(cancel) 538 time.Sleep(5 * time.Millisecond) 539 if time.Since(start) >= delay { 540 t.Fatal("autorest: DelayForBackoff failed to cancel") 541 } 542} 543 544func TestDelayForBackoffWithinReason(t *testing.T) { 545 d := 5 * time.Second 546 maxCoefficient := 2 547 start := time.Now() 548 DelayForBackoff(d, 0, nil) 549 if time.Since(start) > (time.Duration(maxCoefficient) * d) { 550 551 t.Fatalf("autorest: DelayForBackoff delayed too long (exceeded %d times the specified duration)", maxCoefficient) 552 } 553} 554 555func TestDoPollForStatusCodes_IgnoresUnspecifiedStatusCodes(t *testing.T) { 556 client := mocks.NewSender() 557 558 r, _ := SendWithSender(client, mocks.NewRequest(), 559 DoPollForStatusCodes(time.Duration(0), time.Duration(0))) 560 561 if client.Attempts() != 1 { 562 t.Fatalf("autorest: Sender#DoPollForStatusCodes polled for unspecified status code") 563 } 564 565 Respond(r, 566 ByDiscardingBody(), 567 ByClosing()) 568} 569 570func TestDoPollForStatusCodes_PollsForSpecifiedStatusCodes(t *testing.T) { 571 client := mocks.NewSender() 572 client.AppendResponse(newAcceptedResponse()) 573 574 r, _ := SendWithSender(client, mocks.NewRequest(), 575 DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) 576 577 if client.Attempts() != 2 { 578 t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to poll for specified status code") 579 } 580 581 Respond(r, 582 ByDiscardingBody(), 583 ByClosing()) 584} 585 586func TestDoPollForStatusCodes_CanBeCanceled(t *testing.T) { 587 cancel := make(chan struct{}) 588 delay := 5 * time.Second 589 590 r := mocks.NewResponse() 591 mocks.SetAcceptedHeaders(r) 592 client := mocks.NewSender() 593 client.AppendAndRepeatResponse(r, 100) 594 595 var wg sync.WaitGroup 596 wg.Add(1) 597 start := time.Now() 598 go func() { 599 wg.Done() 600 r, _ := SendWithSender(client, mocks.NewRequest(), 601 DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) 602 Respond(r, 603 ByDiscardingBody(), 604 ByClosing()) 605 }() 606 wg.Wait() 607 close(cancel) 608 time.Sleep(5 * time.Millisecond) 609 if time.Since(start) >= delay { 610 t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to cancel") 611 } 612} 613 614func TestDoPollForStatusCodes_ClosesAllNonreturnedResponseBodiesWhenPolling(t *testing.T) { 615 resp := newAcceptedResponse() 616 617 client := mocks.NewSender() 618 client.AppendAndRepeatResponse(resp, 2) 619 620 r, _ := SendWithSender(client, mocks.NewRequest(), 621 DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) 622 623 if resp.Body.(*mocks.Body).IsOpen() || resp.Body.(*mocks.Body).CloseAttempts() < 2 { 624 t.Fatalf("autorest: Sender#DoPollForStatusCodes did not close unreturned response bodies") 625 } 626 627 Respond(r, 628 ByDiscardingBody(), 629 ByClosing()) 630} 631 632func TestDoPollForStatusCodes_LeavesLastResponseBodyOpen(t *testing.T) { 633 client := mocks.NewSender() 634 client.AppendResponse(newAcceptedResponse()) 635 636 r, _ := SendWithSender(client, mocks.NewRequest(), 637 DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) 638 639 if !r.Body.(*mocks.Body).IsOpen() { 640 t.Fatalf("autorest: Sender#DoPollForStatusCodes did not leave open the body of the last response") 641 } 642 643 Respond(r, 644 ByDiscardingBody(), 645 ByClosing()) 646} 647 648func TestDoPollForStatusCodes_StopsPollingAfterAnError(t *testing.T) { 649 client := mocks.NewSender() 650 client.AppendAndRepeatResponse(newAcceptedResponse(), 5) 651 client.SetError(fmt.Errorf("Faux Error")) 652 client.SetEmitErrorAfter(1) 653 654 r, _ := SendWithSender(client, mocks.NewRequest(), 655 DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) 656 657 if client.Attempts() > 2 { 658 t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to stop polling after receiving an error") 659 } 660 661 Respond(r, 662 ByDiscardingBody(), 663 ByClosing()) 664} 665 666func TestDoPollForStatusCodes_ReturnsPollingError(t *testing.T) { 667 client := mocks.NewSender() 668 client.AppendAndRepeatResponse(newAcceptedResponse(), 5) 669 client.SetError(fmt.Errorf("Faux Error")) 670 client.SetEmitErrorAfter(1) 671 672 r, err := SendWithSender(client, mocks.NewRequest(), 673 DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) 674 675 if err == nil { 676 t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to return error from polling") 677 } 678 679 Respond(r, 680 ByDiscardingBody(), 681 ByClosing()) 682} 683 684func TestWithLogging_Logs(t *testing.T) { 685 buf := &bytes.Buffer{} 686 logger := log.New(buf, "autorest: ", 0) 687 client := mocks.NewSender() 688 689 r, _ := SendWithSender(client, &http.Request{}, 690 WithLogging(logger)) 691 692 if buf.String() == "" { 693 t.Fatal("autorest: Sender#WithLogging failed to log the request") 694 } 695 696 Respond(r, 697 ByDiscardingBody(), 698 ByClosing()) 699} 700 701func TestWithLogging_HandlesMissingResponse(t *testing.T) { 702 buf := &bytes.Buffer{} 703 logger := log.New(buf, "autorest: ", 0) 704 client := mocks.NewSender() 705 client.AppendResponse(nil) 706 client.SetError(fmt.Errorf("Faux Error")) 707 708 r, err := SendWithSender(client, &http.Request{}, 709 WithLogging(logger)) 710 711 if r != nil || err == nil { 712 t.Fatal("autorest: Sender#WithLogging returned a valid response -- expecting nil") 713 } 714 if buf.String() == "" { 715 t.Fatal("autorest: Sender#WithLogging failed to log the request for a nil response") 716 } 717 718 Respond(r, 719 ByDiscardingBody(), 720 ByClosing()) 721} 722 723func TestDoRetryForStatusCodesWithSuccess(t *testing.T) { 724 client := mocks.NewSender() 725 client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("408 Request Timeout", http.StatusRequestTimeout), 2) 726 client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK)) 727 728 r, _ := SendWithSender(client, mocks.NewRequest(), 729 DoRetryForStatusCodes(5, time.Duration(2*time.Second), http.StatusRequestTimeout), 730 ) 731 732 Respond(r, 733 ByDiscardingBody(), 734 ByClosing()) 735 736 if client.Attempts() != 3 { 737 t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: StatusCode %v in %v attempts; Want: StatusCode 200 OK in 2 attempts -- ", 738 r.Status, client.Attempts()-1) 739 } 740} 741 742func TestDoRetryForStatusCodesWithNoSuccess(t *testing.T) { 743 client := mocks.NewSender() 744 client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("504 Gateway Timeout", http.StatusGatewayTimeout), 5) 745 746 r, _ := SendWithSender(client, mocks.NewRequest(), 747 DoRetryForStatusCodes(2, time.Duration(2*time.Second), http.StatusGatewayTimeout), 748 ) 749 Respond(r, 750 ByDiscardingBody(), 751 ByClosing()) 752 753 if client.Attempts() != 3 { 754 t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: failed stop after %v retry attempts; Want: Stop after 2 retry attempts", 755 client.Attempts()-1) 756 } 757} 758 759func TestDoRetryForStatusCodes_CodeNotInRetryList(t *testing.T) { 760 client := mocks.NewSender() 761 client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("204 No Content", http.StatusNoContent), 1) 762 763 r, _ := SendWithSender(client, mocks.NewRequest(), 764 DoRetryForStatusCodes(6, time.Duration(2*time.Second), http.StatusGatewayTimeout), 765 ) 766 767 Respond(r, 768 ByDiscardingBody(), 769 ByClosing()) 770 771 if client.Attempts() != 1 || r.Status != "204 No Content" { 772 t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: Retry attempts %v for StatusCode %v; Want: 0 attempts for StatusCode 204", 773 client.Attempts(), r.Status) 774 } 775} 776 777func TestDoRetryForStatusCodes_RequestBodyReadError(t *testing.T) { 778 client := mocks.NewSender() 779 client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("204 No Content", http.StatusNoContent), 2) 780 781 r, err := SendWithSender(client, mocks.NewRequestWithCloseBody(), 782 DoRetryForStatusCodes(6, time.Duration(2*time.Second), http.StatusGatewayTimeout), 783 ) 784 785 Respond(r, 786 ByDiscardingBody(), 787 ByClosing()) 788 789 if err == nil || client.Attempts() != 0 { 790 t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: Not failed for request body read error; Want: Failed for body read error - %v", err) 791 } 792} 793 794func newAcceptedResponse() *http.Response { 795 resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) 796 mocks.SetAcceptedHeaders(resp) 797 return resp 798} 799 800func TestDelayWithRetryAfterWithSuccess(t *testing.T) { 801 Count429AsRetry = false 802 defer func() { Count429AsRetry = true }() 803 after, retries := 2, 2 804 totalSecs := after * retries 805 806 client := mocks.NewSender() 807 resp := mocks.NewResponseWithStatus("429 Too many requests", http.StatusTooManyRequests) 808 mocks.SetResponseHeader(resp, "Retry-After", fmt.Sprintf("%v", after)) 809 client.AppendAndRepeatResponse(resp, retries) 810 client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK)) 811 812 d := time.Second * time.Duration(totalSecs) 813 start := time.Now() 814 r, _ := SendWithSender(client, mocks.NewRequest(), 815 DoRetryForStatusCodes(1, time.Duration(time.Second), http.StatusTooManyRequests), 816 ) 817 818 if time.Since(start) < d { 819 t.Fatal("autorest: DelayWithRetryAfter failed stopped too soon") 820 } 821 822 Respond(r, 823 ByDiscardingBody(), 824 ByClosing()) 825 826 if r.StatusCode != http.StatusOK { 827 t.Fatalf("autorest: Sender#DelayWithRetryAfterWithSuccess -- got status code %d, wanted 200", r.StatusCode) 828 } 829 if client.Attempts() != 3 { 830 t.Fatalf("autorest: Sender#DelayWithRetryAfterWithSuccess -- Got: StatusCode %v in %v attempts; Want: StatusCode 200 OK in 2 attempts -- ", 831 r.Status, client.Attempts()-1) 832 } 833} 834 835func TestDelayWithRetryAfterWithFail(t *testing.T) { 836 after, retries := 2, 2 837 totalSecs := after * retries 838 839 client := mocks.NewSender() 840 resp := mocks.NewResponseWithStatus("429 Too many requests", http.StatusTooManyRequests) 841 mocks.SetResponseHeader(resp, "Retry-After", fmt.Sprintf("%v", after)) 842 client.AppendAndRepeatResponse(resp, retries) 843 client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK)) 844 845 d := time.Second * time.Duration(totalSecs) 846 start := time.Now() 847 r, _ := SendWithSender(client, mocks.NewRequest(), 848 DoRetryForStatusCodes(1, time.Duration(time.Second), http.StatusTooManyRequests), 849 ) 850 851 if time.Since(start) < d { 852 t.Fatal("autorest: DelayWithRetryAfter failed stopped too soon") 853 } 854 855 Respond(r, 856 ByDiscardingBody(), 857 ByClosing()) 858 859 if r.StatusCode != http.StatusTooManyRequests { 860 t.Fatalf("autorest: Sender#DelayWithRetryAfterWithFail -- got status code %d, wanted 429", r.StatusCode) 861 } 862 if client.Attempts() != 2 { 863 t.Fatalf("autorest: Sender#DelayWithRetryAfterWithFail -- Got: StatusCode %v in %v attempts; Want: StatusCode 429 OK in 1 attempt -- ", 864 r.Status, client.Attempts()-1) 865 } 866} 867 868func TestDelayWithRetryAfterWithSuccessDateTime(t *testing.T) { 869 resumeAt := time.Now().Add(2 * time.Second).Round(time.Second) 870 871 client := mocks.NewSender() 872 resp := mocks.NewResponseWithStatus("503 Service temporarily unavailable", http.StatusServiceUnavailable) 873 mocks.SetResponseHeader(resp, "Retry-After", resumeAt.Format(time.RFC1123)) 874 client.AppendResponse(resp) 875 client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK)) 876 877 r, _ := SendWithSender(client, mocks.NewRequest(), 878 DoRetryForStatusCodes(1, time.Duration(time.Second), http.StatusServiceUnavailable), 879 ) 880 881 if time.Now().Before(resumeAt) { 882 t.Fatal("autorest: DelayWithRetryAfter failed stopped too soon") 883 } 884 885 Respond(r, 886 ByDiscardingBody(), 887 ByClosing()) 888 889 if client.Attempts() != 2 { 890 t.Fatalf("autorest: Sender#DelayWithRetryAfter -- Got: StatusCode %v in %v attempts; Want: StatusCode 200 OK in 2 attempts -- ", 891 r.Status, client.Attempts()-1) 892 } 893} 894 895type temporaryError struct { 896 message string 897} 898 899func (te temporaryError) Error() string { 900 return te.message 901} 902 903func (te temporaryError) Timeout() bool { 904 return true 905} 906 907func (te temporaryError) Temporary() bool { 908 return true 909} 910 911func TestDoRetryForStatusCodes_NilResponseTemporaryError(t *testing.T) { 912 client := mocks.NewSender() 913 client.AppendResponse(nil) 914 client.SetError(temporaryError{message: "faux error"}) 915 916 r, err := SendWithSender(client, mocks.NewRequest(), 917 DoRetryForStatusCodes(3, time.Duration(1*time.Second), StatusCodesForRetry...), 918 ) 919 920 Respond(r, 921 ByDiscardingBody(), 922 ByClosing()) 923 924 if err != nil || client.Attempts() != 2 { 925 t.Fatalf("autorest: Sender#TestDoRetryForStatusCodes_NilResponseTemporaryError -- Got: non-nil error or wrong number of attempts - %v", err) 926 } 927} 928 929func TestDoRetryForStatusCodes_NilResponseTemporaryError2(t *testing.T) { 930 client := mocks.NewSender() 931 client.AppendResponse(nil) 932 client.SetError(fmt.Errorf("faux error")) 933 934 r, err := SendWithSender(client, mocks.NewRequest(), 935 DoRetryForStatusCodes(3, time.Duration(1*time.Second), StatusCodesForRetry...), 936 ) 937 938 Respond(r, 939 ByDiscardingBody(), 940 ByClosing()) 941 942 if err != nil || client.Attempts() != 2 { 943 t.Fatalf("autorest: Sender#TestDoRetryForStatusCodes_NilResponseTemporaryError2 -- Got: nil error or wrong number of attempts - %v", err) 944 } 945} 946 947type fatalError struct { 948 message string 949} 950 951func (fe fatalError) Error() string { 952 return fe.message 953} 954 955func (fe fatalError) Timeout() bool { 956 return false 957} 958 959func (fe fatalError) Temporary() bool { 960 return false 961} 962 963func TestDoRetryForStatusCodes_NilResponseFatalError(t *testing.T) { 964 const retryAttempts = 3 965 client := mocks.NewSender() 966 client.AppendAndRepeatResponse(nil, retryAttempts+1) 967 client.SetAndRepeatError(fatalError{"fatal error"}, retryAttempts+1) 968 969 r, err := SendWithSender(client, mocks.NewRequest(), 970 DoRetryForStatusCodes(retryAttempts, time.Duration(1*time.Second), StatusCodesForRetry...), 971 ) 972 973 Respond(r, 974 ByDiscardingBody(), 975 ByClosing()) 976 977 if err == nil || client.Attempts() < retryAttempts+1 { 978 t.Fatalf("autorest: Sender#TestDoRetryForStatusCodes_NilResponseFatalError -- Got: nil error or wrong number of attempts - %v", err) 979 } 980} 981 982func TestDoRetryForStatusCodes_Cancel429(t *testing.T) { 983 Count429AsRetry = false 984 defer func() { Count429AsRetry = true }() 985 retries := 6 986 client := mocks.NewSender() 987 resp := mocks.NewResponseWithStatus("429 Too many requests", http.StatusTooManyRequests) 988 client.AppendAndRepeatResponse(resp, retries) 989 990 ctx, cancel := context.WithTimeout(context.Background(), time.Duration(retries/2)*time.Second) 991 defer cancel() 992 req := mocks.NewRequest().WithContext(ctx) 993 r, err := SendWithSender(client, req, 994 DoRetryForStatusCodes(1, time.Duration(time.Second), http.StatusTooManyRequests), 995 ) 996 997 if err == nil { 998 t.Fatal("unexpected nil-error") 999 } 1000 if r == nil { 1001 t.Fatal("unexpected nil response") 1002 } 1003 if r.StatusCode != http.StatusTooManyRequests { 1004 t.Fatalf("expected status code 429, got: %d", r.StatusCode) 1005 } 1006 if client.Attempts() >= retries { 1007 t.Fatalf("too many attempts: %d", client.Attempts()) 1008 } 1009} 1010 1011func TestDoRetryForStatusCodes_Race(t *testing.T) { 1012 // cannot use the mock sender as it's not safe for concurrent use 1013 s := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})) 1014 defer s.Close() 1015 1016 sender := DecorateSender(s.Client(), 1017 DoRetryForStatusCodes(0, 0, http.StatusRequestTimeout)) 1018 1019 runs := 2 1020 errCh := make(chan error, runs) 1021 1022 for i := 0; i < runs; i++ { 1023 go func() { 1024 req, _ := http.NewRequest(http.MethodGet, s.URL, nil) 1025 // cannot use testing.T.Fatal inside a goroutine, send error down channel 1026 _, err := sender.Do(req) 1027 errCh <- err 1028 }() 1029 } 1030 for i := 0; i < runs; i++ { 1031 err := <-errCh 1032 if err != nil { 1033 t.Fatal(err) 1034 } 1035 } 1036 close(errCh) 1037} 1038 1039func TestGetSendDecorators(t *testing.T) { 1040 sd := GetSendDecorators(context.Background()) 1041 if l := len(sd); l != 0 { 1042 t.Fatalf("expected zero length but got %d", l) 1043 } 1044 sd = GetSendDecorators(context.Background(), DoCloseIfError(), DoErrorIfStatusCode()) 1045 if l := len(sd); l != 2 { 1046 t.Fatalf("expected length of two but got %d", l) 1047 } 1048} 1049 1050func TestWithSendDecorators(t *testing.T) { 1051 ctx := WithSendDecorators(context.Background(), []SendDecorator{DoRetryForAttempts(5, 5*time.Second)}) 1052 sd := GetSendDecorators(ctx) 1053 if l := len(sd); l != 1 { 1054 t.Fatalf("expected length of one but got %d", l) 1055 } 1056 sd = GetSendDecorators(ctx, DoCloseIfError(), DoErrorIfStatusCode()) 1057 if l := len(sd); l != 1 { 1058 t.Fatalf("expected length of one but got %d", l) 1059 } 1060} 1061