retry_test.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Copyright (c) 2015-2019 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. package resty
  5. import (
  6. "encoding/json"
  7. "errors"
  8. "net/http"
  9. "reflect"
  10. "strconv"
  11. "strings"
  12. "testing"
  13. "time"
  14. )
  15. func TestBackoffSuccess(t *testing.T) {
  16. attempts := 3
  17. externalCounter := 0
  18. retryErr := Backoff(func() (*Response, error) {
  19. externalCounter++
  20. if externalCounter < attempts {
  21. return nil, errors.New("not yet got the number we're after")
  22. }
  23. return nil, nil
  24. })
  25. assertError(t, retryErr)
  26. assertEqual(t, externalCounter, attempts)
  27. }
  28. func TestBackoffTenAttemptsSuccess(t *testing.T) {
  29. attempts := 10
  30. externalCounter := 0
  31. retryErr := Backoff(func() (*Response, error) {
  32. externalCounter++
  33. if externalCounter < attempts {
  34. return nil, errors.New("not yet got the number we're after")
  35. }
  36. return nil, nil
  37. }, Retries(attempts), WaitTime(5), MaxWaitTime(500))
  38. assertError(t, retryErr)
  39. assertEqual(t, externalCounter, attempts)
  40. }
  41. // Check to make sure the conditional of the retry condition is being used
  42. func TestConditionalBackoffCondition(t *testing.T) {
  43. attempts := 3
  44. counter := 0
  45. check := RetryConditionFunc(func(*Response) (bool, error) {
  46. return attempts != counter, nil
  47. })
  48. retryErr := Backoff(func() (*Response, error) {
  49. counter++
  50. return nil, nil
  51. }, RetryConditions([]RetryConditionFunc{check}))
  52. assertError(t, retryErr)
  53. assertEqual(t, counter, attempts)
  54. }
  55. // Check to make sure that errors in the conditional cause a retry
  56. func TestConditionalBackoffConditionError(t *testing.T) {
  57. attempts := 3
  58. counter := 0
  59. check := RetryConditionFunc(func(*Response) (bool, error) {
  60. if attempts != counter {
  61. return false, errors.New("attempts not equal Counter")
  62. }
  63. return false, nil
  64. })
  65. retryErr := Backoff(func() (*Response, error) {
  66. counter++
  67. return nil, nil
  68. }, RetryConditions([]RetryConditionFunc{check}))
  69. assertError(t, retryErr)
  70. assertEqual(t, counter, attempts)
  71. }
  72. // Check to make sure that if the conditional is false we don't retry
  73. func TestConditionalBackoffConditionNonExecution(t *testing.T) {
  74. attempts := 3
  75. counter := 0
  76. retryErr := Backoff(func() (*Response, error) {
  77. counter++
  78. return nil, nil
  79. }, RetryConditions([]RetryConditionFunc{filler}))
  80. assertError(t, retryErr)
  81. assertNotEqual(t, counter, attempts)
  82. }
  83. // Check to make sure the functions added to add conditionals work
  84. func TestConditionalGet(t *testing.T) {
  85. ts := createGetServer(t)
  86. defer ts.Close()
  87. attemptCount := 1
  88. externalCounter := 0
  89. // This check should pass on first run, and let the response through
  90. check := RetryConditionFunc(func(*Response) (bool, error) {
  91. externalCounter++
  92. if attemptCount != externalCounter {
  93. return false, errors.New("attempts not equal Counter")
  94. }
  95. return false, nil
  96. })
  97. client := dc().AddRetryCondition(check).SetRetryCount(1)
  98. resp, err := client.R().
  99. SetQueryParam("request_no", strconv.FormatInt(time.Now().Unix(), 10)).
  100. Get(ts.URL + "/")
  101. assertError(t, err)
  102. assertEqual(t, http.StatusOK, resp.StatusCode())
  103. assertEqual(t, "200 OK", resp.Status())
  104. assertNotNil(t, resp.Body())
  105. assertEqual(t, "TestGet: text response", resp.String())
  106. assertEqual(t, externalCounter, attemptCount)
  107. logResponse(t, resp)
  108. }
  109. // Check to make sure the package Function works.
  110. func TestConditionalGetDefaultClient(t *testing.T) {
  111. ts := createGetServer(t)
  112. defer ts.Close()
  113. attemptCount := 1
  114. externalCounter := 0
  115. // This check should pass on first run, and let the response through
  116. check := RetryConditionFunc(func(*Response) (bool, error) {
  117. externalCounter++
  118. if attemptCount != externalCounter {
  119. return false, errors.New("attempts not equal Counter")
  120. }
  121. return false, nil
  122. })
  123. // Clear the default client.
  124. _ = dc()
  125. // Proceed to check.
  126. client := AddRetryCondition(check).SetRetryCount(1)
  127. resp, err := client.R().
  128. SetQueryParam("request_no", strconv.FormatInt(time.Now().Unix(), 10)).
  129. Get(ts.URL + "/")
  130. assertError(t, err)
  131. assertEqual(t, http.StatusOK, resp.StatusCode())
  132. assertEqual(t, "200 OK", resp.Status())
  133. assertNotNil(t, resp.Body())
  134. assertEqual(t, "TestGet: text response", resp.String())
  135. assertEqual(t, externalCounter, attemptCount)
  136. logResponse(t, resp)
  137. }
  138. func TestClientRetryGet(t *testing.T) {
  139. ts := createGetServer(t)
  140. defer ts.Close()
  141. c := dc()
  142. c.SetHTTPMode().
  143. SetTimeout(time.Second * 3).
  144. SetRetryCount(3)
  145. resp, err := c.R().Get(ts.URL + "/set-retrycount-test")
  146. assertEqual(t, "", resp.Status())
  147. assertEqual(t, 0, resp.StatusCode())
  148. assertEqual(t, 0, len(resp.Cookies()))
  149. assertNotNil(t, resp.Body())
  150. assertEqual(t, 0, len(resp.Header()))
  151. assertEqual(t, true, strings.HasPrefix(err.Error(), "Get "+ts.URL+"/set-retrycount-test"))
  152. }
  153. func TestClientRetryWait(t *testing.T) {
  154. ts := createGetServer(t)
  155. defer ts.Close()
  156. attempt := 0
  157. retryCount := 5
  158. retryIntervals := make([]uint64, retryCount)
  159. // Set retry wait times that do not intersect with default ones
  160. retryWaitTime := time.Duration(3) * time.Second
  161. retryMaxWaitTime := time.Duration(9) * time.Second
  162. c := dc()
  163. c.SetHTTPMode().
  164. SetRetryCount(retryCount).
  165. SetRetryWaitTime(retryWaitTime).
  166. SetRetryMaxWaitTime(retryMaxWaitTime).
  167. AddRetryCondition(
  168. func(r *Response) (bool, error) {
  169. timeSlept, _ := strconv.ParseUint(string(r.Body()), 10, 64)
  170. retryIntervals[attempt] = timeSlept
  171. attempt++
  172. return true, nil
  173. },
  174. )
  175. _, _ = c.R().Get(ts.URL + "/set-retrywaittime-test")
  176. // 5 attempts were made
  177. assertEqual(t, attempt, 5)
  178. // Initial attempt has 0 time slept since last request
  179. assertEqual(t, retryIntervals[0], uint64(0))
  180. for i := 1; i < len(retryIntervals); i++ {
  181. slept := time.Duration(retryIntervals[i])
  182. // Ensure that client has slept some duration between
  183. // waitTime and maxWaitTime for consequent requests
  184. if slept < retryWaitTime || slept > retryMaxWaitTime {
  185. t.Errorf("Client has slept %f seconds before retry %d", slept.Seconds(), i)
  186. }
  187. }
  188. }
  189. func TestClientRetryPost(t *testing.T) {
  190. ts := createPostServer(t)
  191. defer ts.Close()
  192. usersmap := map[string]interface{}{
  193. "user1": map[string]interface{}{"FirstName": "firstname1", "LastName": "lastname1", "ZipCode": "10001"},
  194. }
  195. var users []map[string]interface{}
  196. users = append(users, usersmap)
  197. c := dc()
  198. c.SetRetryCount(3)
  199. c.AddRetryCondition(RetryConditionFunc(func(r *Response) (bool, error) {
  200. if r.StatusCode() >= http.StatusInternalServerError {
  201. return false, errors.New("error")
  202. }
  203. return true, nil
  204. }))
  205. resp, _ := c.R().
  206. SetBody(&users).
  207. Post(ts.URL + "/usersmap?status=500")
  208. if resp != nil {
  209. if resp.StatusCode() == http.StatusInternalServerError {
  210. t.Logf("Got response body: %s", string(resp.body))
  211. var usersResponse []map[string]interface{}
  212. err := json.Unmarshal(resp.body, &usersResponse)
  213. assertError(t, err)
  214. if !reflect.DeepEqual(users, usersResponse) {
  215. t.Errorf("Expected request body to be echoed back as response body. Instead got: %s", string(resp.body))
  216. }
  217. return
  218. }
  219. t.Errorf("Got unexpected response code: %d with body: %s", resp.StatusCode(), string(resp.body))
  220. }
  221. }
  222. func filler(*Response) (bool, error) {
  223. return false, nil
  224. }