github.com/snowflakedb/gosnowflake@v1.9.0/retry_test.go (about)

     1  // Copyright (c) 2017-2022 Snowflake Computing Inc. All rights reserved.
     2  
     3  package gosnowflake
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"net/url"
    12  	"strconv"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  )
    17  
    18  func fakeRequestFunc(_, _ string, _ io.Reader) (*http.Request, error) {
    19  	return nil, nil
    20  }
    21  
    22  func emptyRequest(method string, urlStr string, body io.Reader) (*http.Request, error) {
    23  	return http.NewRequest(method, urlStr, body)
    24  }
    25  
    26  type fakeHTTPError struct {
    27  	err     string
    28  	timeout bool
    29  }
    30  
    31  func (e *fakeHTTPError) Error() string   { return e.err }
    32  func (e *fakeHTTPError) Timeout() bool   { return e.timeout }
    33  func (e *fakeHTTPError) Temporary() bool { return true }
    34  
    35  type fakeResponseBody struct {
    36  	body []byte
    37  	cnt  int
    38  }
    39  
    40  func (b *fakeResponseBody) Read(p []byte) (n int, err error) {
    41  	if b.cnt == 0 {
    42  		copy(p, b.body)
    43  		b.cnt = 1
    44  		return len(b.body), nil
    45  	}
    46  	b.cnt = 0
    47  	return 0, io.EOF
    48  }
    49  
    50  func (b *fakeResponseBody) Close() error {
    51  	return nil
    52  }
    53  
    54  type fakeHTTPClient struct {
    55  	t                   *testing.T                // for assertions
    56  	cnt                 int                       // number of retry
    57  	success             bool                      // return success after retry in cnt times
    58  	timeout             bool                      // timeout
    59  	body                []byte                    // return body
    60  	reqBody             []byte                    // last request body
    61  	statusCode          int                       // status code
    62  	retryNumber         int                       // consecutive number of  retries
    63  	expectedQueryParams map[int]map[string]string // expected query params per each retry (0-based)
    64  }
    65  
    66  func (c *fakeHTTPClient) Do(req *http.Request) (*http.Response, error) {
    67  	defer func() {
    68  		c.retryNumber++
    69  	}()
    70  	if req != nil {
    71  		buf := new(bytes.Buffer)
    72  		buf.ReadFrom(req.Body)
    73  		c.reqBody = buf.Bytes()
    74  	}
    75  
    76  	if len(c.expectedQueryParams) > 0 {
    77  		expectedQueryParams, ok := c.expectedQueryParams[c.retryNumber]
    78  		if ok {
    79  			for queryParamName, expectedValue := range expectedQueryParams {
    80  				actualValue := req.URL.Query().Get(queryParamName)
    81  				if actualValue != expectedValue {
    82  					c.t.Fatalf("expected query param %v to be %v, got %v", queryParamName, expectedValue, actualValue)
    83  				}
    84  			}
    85  		}
    86  	}
    87  
    88  	c.cnt--
    89  	if c.cnt < 0 {
    90  		c.cnt = 0
    91  	}
    92  	logger.Infof("fakeHTTPClient.cnt: %v", c.cnt)
    93  
    94  	var retcode int
    95  	if c.success && c.cnt == 0 {
    96  		retcode = 200
    97  	} else {
    98  		if c.timeout {
    99  			// simulate timeout
   100  			time.Sleep(time.Second * 1)
   101  			return nil, &fakeHTTPError{
   102  				err:     "Whatever reason (Client.Timeout exceeded while awaiting headers)",
   103  				timeout: true,
   104  			}
   105  		}
   106  		if c.statusCode != 0 {
   107  			retcode = c.statusCode
   108  		} else {
   109  			retcode = 0
   110  		}
   111  	}
   112  
   113  	ret := &http.Response{
   114  		StatusCode: retcode,
   115  		Body:       &fakeResponseBody{body: c.body},
   116  	}
   117  	return ret, nil
   118  }
   119  
   120  func TestRequestGUID(t *testing.T) {
   121  	var ridReplacer requestGUIDReplacer
   122  	var testURL *url.URL
   123  	var actualURL *url.URL
   124  	retryTime := 4
   125  
   126  	// empty url
   127  	testURL = &url.URL{}
   128  	ridReplacer = newRequestGUIDReplace(testURL)
   129  	for i := 0; i < retryTime; i++ {
   130  		actualURL = ridReplacer.replace()
   131  		if actualURL.String() != "" {
   132  			t.Fatalf("empty url not replaced by an empty one, got %s", actualURL)
   133  		}
   134  	}
   135  
   136  	// url with on retry id
   137  	testURL = &url.URL{
   138  		Path: "/" + requestIDKey + "=123-1923-9?param2=value",
   139  	}
   140  	ridReplacer = newRequestGUIDReplace(testURL)
   141  	for i := 0; i < retryTime; i++ {
   142  		actualURL = ridReplacer.replace()
   143  
   144  		if actualURL != testURL {
   145  			t.Fatalf("url without retry id not replaced by origin one, got %s", actualURL)
   146  		}
   147  	}
   148  
   149  	// url with retry id
   150  	// With both prefix and suffix
   151  	prefix := "/" + requestIDKey + "=123-1923-9?" + requestGUIDKey + "="
   152  	suffix := "?param2=value"
   153  	testURL = &url.URL{
   154  		Path: prefix + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + suffix,
   155  	}
   156  	ridReplacer = newRequestGUIDReplace(testURL)
   157  	for i := 0; i < retryTime; i++ {
   158  		actualURL = ridReplacer.replace()
   159  		if (!strings.HasPrefix(actualURL.Path, prefix)) ||
   160  			(!strings.HasSuffix(actualURL.Path, suffix)) ||
   161  			len(testURL.Path) != len(actualURL.Path) {
   162  			t.Fatalf("Retry url not replaced correctedly: \n origin: %s \n result: %s", testURL, actualURL)
   163  		}
   164  	}
   165  
   166  	// With no suffix
   167  	prefix = "/" + requestIDKey + "=123-1923-9?" + requestGUIDKey + "="
   168  	suffix = ""
   169  	testURL = &url.URL{
   170  		Path: prefix + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + suffix,
   171  	}
   172  	ridReplacer = newRequestGUIDReplace(testURL)
   173  	for i := 0; i < retryTime; i++ {
   174  		actualURL = ridReplacer.replace()
   175  		if (!strings.HasPrefix(actualURL.Path, prefix)) ||
   176  			(!strings.HasSuffix(actualURL.Path, suffix)) ||
   177  			len(testURL.Path) != len(actualURL.Path) {
   178  			t.Fatalf("Retry url not replaced correctedly: \n origin: %s \n result: %s", testURL, actualURL)
   179  		}
   180  
   181  	}
   182  	// With no prefix
   183  	prefix = requestGUIDKey + "="
   184  	suffix = "?param2=value"
   185  	testURL = &url.URL{
   186  		Path: prefix + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + suffix,
   187  	}
   188  	ridReplacer = newRequestGUIDReplace(testURL)
   189  	for i := 0; i < retryTime; i++ {
   190  		actualURL = ridReplacer.replace()
   191  		if (!strings.HasPrefix(actualURL.Path, prefix)) ||
   192  			(!strings.HasSuffix(actualURL.Path, suffix)) ||
   193  			len(testURL.Path) != len(actualURL.Path) {
   194  			t.Fatalf("Retry url not replaced correctedly: \n origin: %s \n result: %s", testURL, actualURL)
   195  		}
   196  	}
   197  }
   198  
   199  func TestRetryQuerySuccess(t *testing.T) {
   200  	logger.Info("Retry N times and Success")
   201  	client := &fakeHTTPClient{
   202  		cnt:        3,
   203  		success:    true,
   204  		statusCode: 429,
   205  		expectedQueryParams: map[int]map[string]string{
   206  			0: {
   207  				"retryCount":      "",
   208  				"retryReason":     "",
   209  				"clientStartTime": "",
   210  			},
   211  			1: {
   212  				"retryCount":      "1",
   213  				"retryReason":     "429",
   214  				"clientStartTime": "123456",
   215  			},
   216  			2: {
   217  				"retryCount":      "2",
   218  				"retryReason":     "429",
   219  				"clientStartTime": "123456",
   220  			},
   221  		},
   222  		t: t,
   223  	}
   224  	urlPtr, err := url.Parse("https://fakeaccountretrysuccess.snowflakecomputing.com:443/queries/v1/query-request?" + requestIDKey + "=testid")
   225  	assertNilF(t, err, "failed to parse the test URL")
   226  	_, err = newRetryHTTP(context.Background(),
   227  		client,
   228  		emptyRequest, urlPtr, make(map[string]string), 60*time.Second, 3, constTimeProvider(123456), &Config{IncludeRetryReason: ConfigBoolTrue}).doPost().setBody([]byte{0}).execute()
   229  	assertNilF(t, err, "failed to run retry")
   230  	var values url.Values
   231  	values, err = url.ParseQuery(urlPtr.RawQuery)
   232  	assertNilF(t, err, "failed to parse the test URL")
   233  	retry, err := strconv.Atoi(values.Get(retryCountKey))
   234  	if err != nil {
   235  		t.Fatalf("failed to get retry counter: %v", err)
   236  	}
   237  	if retry < 2 {
   238  		t.Fatalf("not enough retry counter: %v", retry)
   239  	}
   240  }
   241  
   242  func TestRetryQuerySuccessWithRetryReasonDisabled(t *testing.T) {
   243  	logger.Info("Retry N times and Success")
   244  	client := &fakeHTTPClient{
   245  		cnt:        3,
   246  		success:    true,
   247  		statusCode: 429,
   248  		expectedQueryParams: map[int]map[string]string{
   249  			0: {
   250  				"retryCount":      "",
   251  				"retryReason":     "",
   252  				"clientStartTime": "",
   253  			},
   254  			1: {
   255  				"retryCount":      "1",
   256  				"retryReason":     "",
   257  				"clientStartTime": "123456",
   258  			},
   259  			2: {
   260  				"retryCount":      "2",
   261  				"retryReason":     "",
   262  				"clientStartTime": "123456",
   263  			},
   264  		},
   265  		t: t,
   266  	}
   267  	urlPtr, err := url.Parse("https://fakeaccountretrysuccess.snowflakecomputing.com:443/queries/v1/query-request?" + requestIDKey + "=testid")
   268  	assertNilF(t, err, "failed to parse the test URL")
   269  	_, err = newRetryHTTP(context.Background(),
   270  		client,
   271  		emptyRequest, urlPtr, make(map[string]string), 60*time.Second, 3, constTimeProvider(123456), &Config{IncludeRetryReason: ConfigBoolFalse}).doPost().setBody([]byte{0}).execute()
   272  	assertNilF(t, err, "failed to run retry")
   273  	var values url.Values
   274  	values, err = url.ParseQuery(urlPtr.RawQuery)
   275  	assertNilF(t, err, "failed to parse the test URL")
   276  	retry, err := strconv.Atoi(values.Get(retryCountKey))
   277  	if err != nil {
   278  		t.Fatalf("failed to get retry counter: %v", err)
   279  	}
   280  	if retry < 2 {
   281  		t.Fatalf("not enough retry counter: %v", retry)
   282  	}
   283  }
   284  
   285  func TestRetryQuerySuccessWithTimeout(t *testing.T) {
   286  	logger.Info("Retry N times and Success")
   287  	client := &fakeHTTPClient{
   288  		cnt:     3,
   289  		success: true,
   290  		timeout: true,
   291  		expectedQueryParams: map[int]map[string]string{
   292  			0: {
   293  				"retryCount":  "",
   294  				"retryReason": "",
   295  			},
   296  			1: {
   297  				"retryCount":  "1",
   298  				"retryReason": "0",
   299  			},
   300  			2: {
   301  				"retryCount":  "2",
   302  				"retryReason": "0",
   303  			},
   304  		},
   305  		t: t,
   306  	}
   307  	urlPtr, err := url.Parse("https://fakeaccountretrysuccess.snowflakecomputing.com:443/queries/v1/query-request?" + requestIDKey + "=testid")
   308  	assertNilF(t, err, "failed to parse the test URL")
   309  	_, err = newRetryHTTP(context.Background(),
   310  		client,
   311  		emptyRequest, urlPtr, make(map[string]string), 60*time.Second, 3, constTimeProvider(123456), nil).doPost().setBody([]byte{0}).execute()
   312  	assertNilF(t, err, "failed to run retry")
   313  	var values url.Values
   314  	values, err = url.ParseQuery(urlPtr.RawQuery)
   315  	assertNilF(t, err, "failed to parse the test URL")
   316  	retry, err := strconv.Atoi(values.Get(retryCountKey))
   317  	if err != nil {
   318  		t.Fatalf("failed to get retry counter: %v", err)
   319  	}
   320  	if retry < 2 {
   321  		t.Fatalf("not enough retry counter: %v", retry)
   322  	}
   323  }
   324  
   325  func TestRetryQueryFailWithTimeout(t *testing.T) {
   326  	logger.Info("Retry N times until there is a timeout and Fail")
   327  	client := &fakeHTTPClient{
   328  		statusCode: http.StatusTooManyRequests,
   329  		success:    false,
   330  	}
   331  	urlPtr, err := url.Parse("https://fakeaccountretryfail.snowflakecomputing.com:443/queries/v1/query-request?" + requestIDKey)
   332  	assertNilF(t, err, "failed to parse the test URL")
   333  	_, err = newRetryHTTP(context.Background(),
   334  		client,
   335  		emptyRequest, urlPtr, make(map[string]string), 20*time.Second, 100, defaultTimeProvider, nil).doPost().setBody([]byte{0}).execute()
   336  	assertNotNilF(t, err, "should fail to run retry")
   337  	var values url.Values
   338  	values, err = url.ParseQuery(urlPtr.RawQuery)
   339  	assertNilF(t, err, fmt.Sprintf("failed to parse the URL: %v", err))
   340  	retry, err := strconv.Atoi(values.Get(retryCountKey))
   341  	assertNilF(t, err, fmt.Sprintf("failed to get retry counter: %v", err))
   342  	if retry < 2 {
   343  		t.Fatalf("not enough retries: %v", retry)
   344  	}
   345  }
   346  
   347  func TestRetryQueryFailWithMaxRetryCount(t *testing.T) {
   348  	maxRetryCount := 3
   349  	logger.Info("Retry 3 times until retry reaches MaxRetryCount and Fail")
   350  	client := &fakeHTTPClient{
   351  		statusCode: http.StatusTooManyRequests,
   352  		success:    false,
   353  	}
   354  	urlPtr, err := url.Parse("https://fakeaccountretryfail.snowflakecomputing.com:443/queries/v1/query-request?" + requestIDKey)
   355  	assertNilF(t, err, "failed to parse the test URL")
   356  	_, err = newRetryHTTP(context.Background(),
   357  		client,
   358  		emptyRequest, urlPtr, make(map[string]string), 15*time.Hour, maxRetryCount, defaultTimeProvider, nil).doPost().setBody([]byte{0}).execute()
   359  	assertNotNilF(t, err, "should fail to run retry")
   360  	var values url.Values
   361  	values, err = url.ParseQuery(urlPtr.RawQuery)
   362  	if err != nil {
   363  		t.Fatalf("failed to parse the URL: %v", err)
   364  	}
   365  	retryCount, err := strconv.Atoi(values.Get(retryCountKey))
   366  	if err != nil {
   367  		t.Fatalf("failed to get retry counter: %v", err)
   368  	}
   369  	if retryCount < 3 {
   370  		t.Fatalf("not enough retries: %v; expected %v", retryCount, maxRetryCount)
   371  	}
   372  }
   373  
   374  func TestRetryLoginRequest(t *testing.T) {
   375  	logger.Info("Retry N times for timeouts and Success")
   376  	client := &fakeHTTPClient{
   377  		cnt:     3,
   378  		success: true,
   379  		timeout: true,
   380  		t:       t,
   381  		expectedQueryParams: map[int]map[string]string{
   382  			0: {
   383  				"retryCount":  "",
   384  				"retryReason": "",
   385  			},
   386  			1: {
   387  				"retryCount":  "",
   388  				"retryReason": "",
   389  			},
   390  			2: {
   391  				"retryCount":  "",
   392  				"retryReason": "",
   393  			},
   394  		},
   395  	}
   396  	urlPtr, err := url.Parse("https://fakeaccountretrylogin.snowflakecomputing.com:443/login-request?request_id=testid")
   397  	assertNilF(t, err, "failed to parse the test URL")
   398  	_, err = newRetryHTTP(context.Background(),
   399  		client,
   400  		emptyRequest, urlPtr, make(map[string]string), 60*time.Second, 3, defaultTimeProvider, nil).doPost().setBody([]byte{0}).execute()
   401  	assertNilF(t, err, "failed to run retry")
   402  	var values url.Values
   403  	values, err = url.ParseQuery(urlPtr.RawQuery)
   404  	assertNilF(t, err, "failed to parse the test URL")
   405  	if values.Get(retryCountKey) != "" {
   406  		t.Fatalf("no retry counter should be attached: %v", retryCountKey)
   407  	}
   408  	logger.Info("Retry N times for timeouts and Fail")
   409  	client = &fakeHTTPClient{
   410  		success: false,
   411  		timeout: true,
   412  	}
   413  	_, err = newRetryHTTP(context.Background(),
   414  		client,
   415  		emptyRequest, urlPtr, make(map[string]string), 5*time.Second, 3, defaultTimeProvider, nil).doPost().setBody([]byte{0}).execute()
   416  	assertNotNilF(t, err, "should fail to run retry")
   417  	values, err = url.ParseQuery(urlPtr.RawQuery)
   418  	if err != nil {
   419  		t.Fatalf("failed to parse the URL: %v", err)
   420  	}
   421  	if values.Get(retryCountKey) != "" {
   422  		t.Fatalf("no retry counter should be attached: %v", retryCountKey)
   423  	}
   424  }
   425  
   426  func TestRetryAuthLoginRequest(t *testing.T) {
   427  	logger.Info("Retry N times always with newer body")
   428  	client := &fakeHTTPClient{
   429  		cnt:     3,
   430  		success: true,
   431  		timeout: true,
   432  	}
   433  	urlPtr, err := url.Parse("https://fakeaccountretrylogin.snowflakecomputing.com:443/login-request?request_id=testid")
   434  	assertNilF(t, err, "failed to parse the test URL")
   435  	execID := 0
   436  	bodyCreator := func() ([]byte, error) {
   437  		execID++
   438  		return []byte(fmt.Sprintf("execID: %d", execID)), nil
   439  	}
   440  	_, err = newRetryHTTP(context.Background(),
   441  		client,
   442  		http.NewRequest, urlPtr, make(map[string]string), 60*time.Second, 3, defaultTimeProvider, nil).doPost().setBodyCreator(bodyCreator).execute()
   443  	assertNilF(t, err, "failed to run retry")
   444  	if lastReqBody := string(client.reqBody); lastReqBody != "execID: 3" {
   445  		t.Fatalf("body should be updated on each request, expected: execID: 3, last body: %v", lastReqBody)
   446  	}
   447  }
   448  
   449  func TestLoginRetry429(t *testing.T) {
   450  	client := &fakeHTTPClient{
   451  		cnt:        3,
   452  		success:    true,
   453  		statusCode: 429,
   454  	}
   455  	urlPtr, err := url.Parse("https://fakeaccountretrylogin.snowflakecomputing.com:443/login-request?request_id=testid")
   456  	assertNilF(t, err, "failed to parse the test URL")
   457  
   458  	_, err = newRetryHTTP(context.Background(),
   459  		client,
   460  		emptyRequest, urlPtr, make(map[string]string), 60*time.Second, 3, defaultTimeProvider, nil).doPost().setBody([]byte{0}).execute() // enable doRaise4XXX
   461  	assertNilF(t, err, "failed to run retry")
   462  
   463  	var values url.Values
   464  	values, err = url.ParseQuery(urlPtr.RawQuery)
   465  	assertNilF(t, err, fmt.Sprintf("failed to parse the URL: %v", err))
   466  	if values.Get(retryCountKey) != "" {
   467  		t.Fatalf("no retry counter should be attached: %v", retryCountKey)
   468  	}
   469  }
   470  
   471  func TestIsRetryable(t *testing.T) {
   472  	tcs := []struct {
   473  		req      *http.Request
   474  		res      *http.Response
   475  		err      error
   476  		expected bool
   477  	}{
   478  		{
   479  			req:      nil,
   480  			res:      nil,
   481  			err:      nil,
   482  			expected: false,
   483  		},
   484  		{
   485  			req:      nil,
   486  			res:      &http.Response{StatusCode: http.StatusBadRequest},
   487  			err:      nil,
   488  			expected: false,
   489  		},
   490  		{
   491  			req:      &http.Request{URL: &url.URL{Path: loginRequestPath}},
   492  			res:      nil,
   493  			err:      nil,
   494  			expected: false,
   495  		},
   496  		{
   497  			req:      &http.Request{URL: &url.URL{Path: loginRequestPath}},
   498  			res:      &http.Response{StatusCode: http.StatusNotFound},
   499  			expected: false,
   500  		},
   501  		{
   502  			req:      &http.Request{URL: &url.URL{Path: loginRequestPath}},
   503  			res:      nil,
   504  			err:      errUnknownError(),
   505  			expected: true,
   506  		},
   507  		{
   508  			req:      &http.Request{URL: &url.URL{Path: loginRequestPath}},
   509  			res:      &http.Response{StatusCode: http.StatusTooManyRequests},
   510  			err:      nil,
   511  			expected: true,
   512  		},
   513  		{
   514  			req:      &http.Request{URL: &url.URL{Path: queryRequestPath}},
   515  			res:      &http.Response{StatusCode: http.StatusServiceUnavailable},
   516  			err:      nil,
   517  			expected: true,
   518  		},
   519  	}
   520  
   521  	for _, tc := range tcs {
   522  		t.Run(fmt.Sprintf("req %v, resp %v", tc.req, tc.res), func(t *testing.T) {
   523  			result, _ := isRetryableError(tc.req, tc.res, tc.err)
   524  			if result != tc.expected {
   525  				t.Fatalf("expected %v, got %v; request: %v, response: %v", tc.expected, result, tc.req, tc.res)
   526  			}
   527  		})
   528  	}
   529  }
   530  
   531  func TestCalculateRetryWait(t *testing.T) {
   532  	// test for randomly selected attempt and currWaitTime values
   533  	// minSleepTime, maxSleepTime are limit values
   534  	tcs := []struct {
   535  		attempt      int
   536  		currWaitTime float64
   537  		minSleepTime float64
   538  		maxSleepTime float64
   539  	}{
   540  		{
   541  			attempt:      1,
   542  			currWaitTime: 3.346609,
   543  			minSleepTime: 0.326695,
   544  			maxSleepTime: 5.019914,
   545  		},
   546  		{
   547  			attempt:      2,
   548  			currWaitTime: 4.260357,
   549  			minSleepTime: 1.869821,
   550  			maxSleepTime: 6.390536,
   551  		},
   552  		{
   553  			attempt:      3,
   554  			currWaitTime: 7.857728,
   555  			minSleepTime: 3.928864,
   556  			maxSleepTime: 11.928864,
   557  		},
   558  		{
   559  			attempt:      4,
   560  			currWaitTime: 7.249255,
   561  			minSleepTime: 3.624628,
   562  			maxSleepTime: 19.624628,
   563  		},
   564  		{
   565  			attempt:      5,
   566  			currWaitTime: 23.598257,
   567  			minSleepTime: 11.799129,
   568  			maxSleepTime: 43.799129,
   569  		},
   570  		{
   571  			attempt:      8,
   572  			currWaitTime: 27.088613,
   573  			minSleepTime: 13.544306,
   574  			maxSleepTime: 269.544306,
   575  		},
   576  		{
   577  			attempt:      10,
   578  			currWaitTime: 30.879329,
   579  			minSleepTime: 15.439664,
   580  			maxSleepTime: 1039.439664,
   581  		},
   582  		{
   583  			attempt:      12,
   584  			currWaitTime: 39.919798,
   585  			minSleepTime: 19.959899,
   586  			maxSleepTime: 4115.959899,
   587  		},
   588  		{
   589  			attempt:      15,
   590  			currWaitTime: 33.750758,
   591  			minSleepTime: 16.875379,
   592  			maxSleepTime: 32784.875379,
   593  		},
   594  		{
   595  			attempt:      20,
   596  			currWaitTime: 32.357793,
   597  			minSleepTime: 16.178897,
   598  			maxSleepTime: 1048592.178897,
   599  		},
   600  	}
   601  
   602  	for _, tc := range tcs {
   603  		t.Run(fmt.Sprintf("attmept: %v", tc.attempt), func(t *testing.T) {
   604  			result := defaultWaitAlgo.calculateWaitBeforeRetryForAuthRequest(tc.attempt, time.Duration(tc.currWaitTime*float64(time.Second)))
   605  			assertBetweenE(t, result.Seconds(), tc.minSleepTime, tc.maxSleepTime)
   606  		})
   607  	}
   608  }
   609  
   610  func TestCalculateRetryWaitForNonAuthRequests(t *testing.T) {
   611  	// test for randomly selected currWaitTime values
   612  	// maxSleepTime is the limit value
   613  	tcs := []struct {
   614  		currWaitTime float64
   615  		maxSleepTime float64
   616  	}{
   617  		{
   618  			currWaitTime: 3.346609,
   619  			maxSleepTime: 10.039827,
   620  		},
   621  		{
   622  			currWaitTime: 4.260357,
   623  			maxSleepTime: 12.781071,
   624  		},
   625  		{
   626  			currWaitTime: 5.154231,
   627  			maxSleepTime: 15.462693,
   628  		},
   629  		{
   630  			currWaitTime: 7.249255,
   631  			maxSleepTime: 16,
   632  		},
   633  		{
   634  			currWaitTime: 23.598257,
   635  			maxSleepTime: 16,
   636  		},
   637  	}
   638  
   639  	for _, tc := range tcs {
   640  		defaultMinSleepTime := 1
   641  		t.Run(fmt.Sprintf("currWaitTime: %v", tc.currWaitTime), func(t *testing.T) {
   642  			result := defaultWaitAlgo.calculateWaitBeforeRetry(time.Duration(tc.currWaitTime) * time.Second)
   643  			assertBetweenInclusiveE(t, result.Seconds(), float64(defaultMinSleepTime), tc.maxSleepTime)
   644  		})
   645  	}
   646  }