Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

177 рядки
4.6 KiB

  1. // Copyright 2017 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package gensupport
  15. import (
  16. "errors"
  17. "io"
  18. "net"
  19. "net/http"
  20. "testing"
  21. "time"
  22. "golang.org/x/net/context"
  23. )
  24. func TestRetry(t *testing.T) {
  25. testCases := []struct {
  26. desc string
  27. respStatus []int // HTTP status codes returned (length indicates number of calls we expect).
  28. maxRetry int // Max number of calls allowed by the BackoffStrategy.
  29. wantStatus int // StatusCode of returned response.
  30. }{
  31. {
  32. desc: "First call successful",
  33. respStatus: []int{200},
  34. maxRetry: 3,
  35. wantStatus: 200,
  36. },
  37. {
  38. desc: "Retry before success",
  39. respStatus: []int{500, 500, 500, 200},
  40. maxRetry: 3,
  41. wantStatus: 200,
  42. },
  43. {
  44. desc: "Backoff strategy abandons after 3 retries",
  45. respStatus: []int{500, 500, 500, 500},
  46. maxRetry: 3,
  47. wantStatus: 500,
  48. },
  49. {
  50. desc: "Backoff strategy abandons after 2 retries",
  51. respStatus: []int{500, 500, 500},
  52. maxRetry: 2,
  53. wantStatus: 500,
  54. },
  55. }
  56. for _, tt := range testCases {
  57. // Function consumes tt.respStatus
  58. f := func() (*http.Response, error) {
  59. if len(tt.respStatus) == 0 {
  60. return nil, errors.New("too many requests to function")
  61. }
  62. resp := &http.Response{StatusCode: tt.respStatus[0]}
  63. tt.respStatus = tt.respStatus[1:]
  64. return resp, nil
  65. }
  66. backoff := &LimitRetryStrategy{
  67. Max: tt.maxRetry,
  68. Strategy: NoPauseStrategy,
  69. }
  70. resp, err := Retry(nil, f, backoff)
  71. if err != nil {
  72. t.Errorf("%s: Retry returned err %v", tt.desc, err)
  73. }
  74. if got := resp.StatusCode; got != tt.wantStatus {
  75. t.Errorf("%s: Retry returned response with StatusCode=%d; want %d", tt.desc, got, tt.wantStatus)
  76. }
  77. if len(tt.respStatus) != 0 {
  78. t.Errorf("%s: f was not called enough; status codes remaining: %v", tt.desc, tt.respStatus)
  79. }
  80. }
  81. }
  82. type checkCloseReader struct {
  83. closed bool
  84. }
  85. func (c *checkCloseReader) Read(p []byte) (n int, err error) { return 0, io.EOF }
  86. func (c *checkCloseReader) Close() error {
  87. c.closed = true
  88. return nil
  89. }
  90. func TestRetryClosesBody(t *testing.T) {
  91. var i int
  92. responses := []*http.Response{
  93. {StatusCode: 500, Body: &checkCloseReader{}},
  94. {StatusCode: 500, Body: &checkCloseReader{}},
  95. {StatusCode: 200, Body: &checkCloseReader{}},
  96. }
  97. f := func() (*http.Response, error) {
  98. resp := responses[i]
  99. i++
  100. return resp, nil
  101. }
  102. resp, err := Retry(nil, f, NoPauseStrategy)
  103. if err != nil {
  104. t.Fatalf("Retry returned error: %v", err)
  105. }
  106. if resp != responses[2] {
  107. t.Errorf("Retry returned %v; want %v", resp, responses[2])
  108. }
  109. for i, resp := range responses {
  110. want := i != 2 // Only the last response should not be closed.
  111. got := resp.Body.(*checkCloseReader).closed
  112. if got != want {
  113. t.Errorf("response[%d].Body closed = %t, want %t", i, got, want)
  114. }
  115. }
  116. }
  117. func RetryReturnsOnContextCancel(t *testing.T) {
  118. f := func() (*http.Response, error) {
  119. return nil, io.ErrUnexpectedEOF
  120. }
  121. backoff := UniformPauseStrategy(time.Hour)
  122. ctx, cancel := context.WithCancel(context.Background())
  123. errc := make(chan error, 1)
  124. go func() {
  125. _, err := Retry(ctx, f, backoff)
  126. errc <- err
  127. }()
  128. cancel()
  129. select {
  130. case err := <-errc:
  131. if err != ctx.Err() {
  132. t.Errorf("Retry returned err: %v, want %v", err, ctx.Err())
  133. }
  134. case <-time.After(5 * time.Second):
  135. t.Errorf("Timed out waiting for Retry to return")
  136. }
  137. }
  138. func TestShouldRetry(t *testing.T) {
  139. testCases := []struct {
  140. status int
  141. err error
  142. want bool
  143. }{
  144. {status: 200, want: false},
  145. {status: 308, want: false},
  146. {status: 403, want: false},
  147. {status: 429, want: true},
  148. {status: 500, want: true},
  149. {status: 503, want: true},
  150. {status: 600, want: false},
  151. {err: io.EOF, want: false},
  152. {err: errors.New("random badness"), want: false},
  153. {err: io.ErrUnexpectedEOF, want: true},
  154. {err: &net.AddrError{}, want: false}, // Not temporary.
  155. {err: &net.DNSError{IsTimeout: true}, want: true}, // Temporary.
  156. }
  157. for _, tt := range testCases {
  158. if got := shouldRetry(tt.status, tt.err); got != tt.want {
  159. t.Errorf("shouldRetry(%d, %v) = %t; want %t", tt.status, tt.err, got, tt.want)
  160. }
  161. }
  162. }