...

Source file src/github.com/go-resty/resty/v2/retry_test.go

Documentation: github.com/go-resty/resty/v2

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

View as plain text