+
+func (s *clientRetrySuite) TearDownTest(c *check.C) {
+ s.server.Close()
+ requestLimiterQuietPeriod = s.origLimiterQuietPeriod
+}
+
+func (s *clientRetrySuite) TestOK(c *check.C) {
+ s.respStatus <- http.StatusOK
+ err := s.client.RequestAndDecode(&struct{}{}, http.MethodGet, "test", nil, nil)
+ c.Check(err, check.IsNil)
+ c.Check(s.reqs, check.HasLen, 1)
+}
+
+func (s *clientRetrySuite) TestNetworkError(c *check.C) {
+ // Close the stub server to produce a "connection refused" error.
+ s.server.Close()
+
+ start := time.Now()
+ timeout := time.Second
+ ctx, cancel := context.WithDeadline(context.Background(), start.Add(timeout))
+ defer cancel()
+ s.client.Timeout = timeout * 2
+ err := s.client.RequestAndDecodeContext(ctx, &struct{}{}, http.MethodGet, "test", nil, nil)
+ c.Check(err, check.ErrorMatches, `.*dial tcp .* connection refused.*`)
+ delta := time.Since(start)
+ c.Check(delta > timeout, check.Equals, true, check.Commentf("time.Since(start) == %v, timeout = %v", delta, timeout))
+}
+
+func (s *clientRetrySuite) TestNonRetryableError(c *check.C) {
+ s.respStatus <- http.StatusBadRequest
+ err := s.client.RequestAndDecode(&struct{}{}, http.MethodGet, "test", nil, nil)
+ c.Check(err, check.ErrorMatches, `.*400 Bad Request.*`)
+ c.Check(s.reqs, check.HasLen, 1)
+}
+
+func (s *clientRetrySuite) TestNonRetryableAfter503s(c *check.C) {
+ time.AfterFunc(time.Second, func() { s.respStatus <- http.StatusNotFound })
+ err := s.client.RequestAndDecode(&struct{}{}, http.MethodGet, "test", nil, nil)
+ c.Check(err, check.ErrorMatches, `.*404 Not Found.*`)
+}
+
+func (s *clientRetrySuite) TestOKAfter503s(c *check.C) {
+ start := time.Now()
+ delay := time.Second
+ time.AfterFunc(delay, func() { s.respStatus <- http.StatusOK })
+ err := s.client.RequestAndDecode(&struct{}{}, http.MethodGet, "test", nil, nil)
+ c.Check(err, check.IsNil)
+ c.Check(len(s.reqs) > 1, check.Equals, true, check.Commentf("len(s.reqs) == %d", len(s.reqs)))
+ c.Check(time.Since(start) > delay, check.Equals, true)
+}
+
+func (s *clientRetrySuite) TestTimeoutAfter503(c *check.C) {
+ s.respStatus <- http.StatusServiceUnavailable
+ s.respDelay = time.Second * 2
+ s.client.Timeout = time.Second / 2
+ err := s.client.RequestAndDecode(&struct{}{}, http.MethodGet, "test", nil, nil)
+ c.Check(err, check.ErrorMatches, `.*503 Service Unavailable.*`)
+ c.Check(s.reqs, check.HasLen, 2)
+}
+
+func (s *clientRetrySuite) Test503Forever(c *check.C) {
+ err := s.client.RequestAndDecode(&struct{}{}, http.MethodGet, "test", nil, nil)
+ c.Check(err, check.ErrorMatches, `.*503 Service Unavailable.*`)
+ c.Check(len(s.reqs) > 1, check.Equals, true, check.Commentf("len(s.reqs) == %d", len(s.reqs)))
+}
+
+func (s *clientRetrySuite) TestContextAlreadyCanceled(c *check.C) {
+ ctx, cancel := context.WithCancel(context.Background())
+ cancel()
+ err := s.client.RequestAndDecodeContext(ctx, &struct{}{}, http.MethodGet, "test", nil, nil)
+ c.Check(err, check.Equals, context.Canceled)
+}