mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
125 lines
3.4 KiB
Go
125 lines
3.4 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"math"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/shoenig/test/must"
|
|
)
|
|
|
|
type mockHandler struct {
|
|
callsCounter []time.Time
|
|
}
|
|
|
|
func (mh *mockHandler) Handle(rw http.ResponseWriter, req *http.Request) {
|
|
mh.callsCounter = append(mh.callsCounter, time.Now())
|
|
|
|
// return a populated meta after 7 tries to test he retries stops after a
|
|
// successful call
|
|
if len(mh.callsCounter) < 7 {
|
|
http.Error(rw, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
|
|
return
|
|
}
|
|
|
|
rw.WriteHeader(http.StatusOK)
|
|
rw.Header().Set("Content-Type", "application/json")
|
|
|
|
resp := &WriteMeta{}
|
|
jsonResp, _ := json.Marshal(resp)
|
|
|
|
rw.Write(jsonResp)
|
|
return
|
|
}
|
|
|
|
func Test_RetryPut_multiple_calls(t *testing.T) {
|
|
t.Run("successfully retries until no error, delayed capped to 100ms", func(t *testing.T) {
|
|
mh := mockHandler{
|
|
callsCounter: []time.Time{},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(mh.Handle))
|
|
cm, err := NewClient(&Config{
|
|
Address: server.URL,
|
|
retryOptions: &retryOptions{
|
|
delayBase: 10 * time.Millisecond,
|
|
maxRetries: 10,
|
|
maxBackoffDelay: 100 * time.Millisecond,
|
|
},
|
|
})
|
|
must.NoError(t, err)
|
|
|
|
md, err := cm.retryPut(context.TODO(), "/endpoint", nil, nil, &WriteOptions{})
|
|
must.NoError(t, err)
|
|
|
|
must.Len(t, 7, mh.callsCounter)
|
|
|
|
must.NotNil(t, md)
|
|
must.Greater(t, 10*time.Millisecond, mh.callsCounter[1].Sub(mh.callsCounter[0]))
|
|
must.Greater(t, 20*time.Millisecond, mh.callsCounter[2].Sub(mh.callsCounter[1]))
|
|
must.Greater(t, 40*time.Millisecond, mh.callsCounter[3].Sub(mh.callsCounter[2]))
|
|
must.Greater(t, 80*time.Millisecond, mh.callsCounter[4].Sub(mh.callsCounter[3]))
|
|
must.Greater(t, 100*time.Millisecond, mh.callsCounter[5].Sub(mh.callsCounter[4]))
|
|
must.Greater(t, 100*time.Millisecond, mh.callsCounter[6].Sub(mh.callsCounter[5]))
|
|
})
|
|
}
|
|
|
|
func Test_RetryPut_one_call(t *testing.T) {
|
|
t.Run("successfully retries until no error, delayed capped to 100ms", func(t *testing.T) {
|
|
mh := mockHandler{
|
|
callsCounter: []time.Time{},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(mh.Handle))
|
|
cm, err := NewClient(&Config{
|
|
Address: server.URL,
|
|
retryOptions: &retryOptions{
|
|
delayBase: 10 * time.Millisecond,
|
|
maxRetries: 1,
|
|
},
|
|
})
|
|
must.NoError(t, err)
|
|
|
|
md, err := cm.retryPut(context.TODO(), "/endpoint/", nil, nil, &WriteOptions{})
|
|
must.Error(t, err)
|
|
must.Nil(t, md)
|
|
|
|
must.Len(t, 2, mh.callsCounter)
|
|
})
|
|
}
|
|
|
|
func Test_RetryPut_capped_base_too_big(t *testing.T) {
|
|
t.Run("successfully retries until no error, delayed capped to 100ms", func(t *testing.T) {
|
|
mh := mockHandler{
|
|
callsCounter: []time.Time{},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(mh.Handle))
|
|
cm, err := NewClient(&Config{
|
|
Address: server.URL,
|
|
retryOptions: &retryOptions{
|
|
delayBase: math.MaxInt64 * time.Nanosecond,
|
|
maxRetries: 3,
|
|
maxBackoffDelay: 200 * time.Millisecond,
|
|
},
|
|
})
|
|
must.NoError(t, err)
|
|
|
|
md, err := cm.retryPut(context.TODO(), "/endpoint", nil, nil, &WriteOptions{})
|
|
must.Error(t, err)
|
|
|
|
must.Len(t, 4, mh.callsCounter)
|
|
|
|
must.Nil(t, md)
|
|
must.Greater(t, cm.config.retryOptions.maxBackoffDelay, mh.callsCounter[1].Sub(mh.callsCounter[0]))
|
|
must.Greater(t, cm.config.retryOptions.maxBackoffDelay, mh.callsCounter[2].Sub(mh.callsCounter[1]))
|
|
})
|
|
}
|