Files
threshold/api/client_test.go
T
Timothy Messier 5329f926b6 fix(api): Correctly apply BOUNDARY_MAX_RETRIES env var (#5385)
The api.Client was first checking for the environment variable, and then
resetting the max retries to 2, thus overriding the environment variable
value. In addition, the cli would set max retries to zero if the
environment variable was not set. This would mean that by default the
max retries would be zero, and if the environment variable was set, the
max retries would be 2, regardless of the actual value for the
environment variable.

This fixes the order in which the environment variables are read, to
ensure they take precedence.
2025-01-08 10:07:09 -05:00

249 lines
5.4 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package api
import (
"crypto/tls"
"math"
"net/http"
"os"
"strconv"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/go-cleanhttp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestConfigSetAddress(t *testing.T) {
type test struct {
name string
input string
address string
err string
}
tests := []test{
{
"ipv4",
"http://127.0.0.1:9200",
"http://127.0.0.1:9200",
"",
},
{
"ipv6",
"http://[::1]:9200",
"http://[::1]:9200",
"",
},
{
"bare with version",
"http://127.0.0.1:9200/v1",
"http://127.0.0.1:9200",
"",
},
{
"bare with version and trailing slash",
"http://127.0.0.1:9200/v1/",
"http://127.0.0.1:9200",
"",
},
{
"valid top level scope",
"http://127.0.0.1:9200/v1/scopes",
"http://127.0.0.1:9200",
"",
},
{
"valid scope",
"http://127.0.0.1:9200/v1/scopes/scopeid",
"http://127.0.0.1:9200",
"",
},
{
"longer path project",
"http://127.0.0.1:9200/v1/auth-methods",
"http://127.0.0.1:9200",
"",
},
{
"valid project",
"http://127.0.0.1:9200/my-install",
"http://127.0.0.1:9200/my-install",
"",
},
{
"valid project path containing v1",
"http://127.0.0.1:9200/randomPathHasv1InIt",
"http://127.0.0.1:9200/randomPathHasv1InIt",
"",
},
}
for _, v := range tests {
t.Run(v.name, func(t *testing.T) {
var c Config
err := c.setAddr(v.input)
if err != nil {
assert.Equal(t, v.err, err.Error())
}
assert.Equal(t, v.address, c.Addr)
})
}
}
func TestReadEnvironmentMaxRetries(t *testing.T) {
tests := []struct {
name string
inp string
expMaxRetries int
expErrContains string
}{
{
name: "invalidNaN",
inp: "bad",
expErrContains: "strconv.ParseUint: parsing \"bad\": invalid syntax",
},
{
name: "invalidNegativeNumber",
inp: "-1",
expErrContains: "strconv.ParseUint: parsing \"-1\": invalid syntax",
},
{
name: "invalidGreaterThanUint32",
inp: strconv.Itoa(math.MaxUint32 + 10),
expErrContains: "strconv.ParseUint: parsing \"4294967305\": value out of range",
},
{
name: "invalidGreaterThanInt32",
inp: strconv.Itoa(math.MaxInt32 + 10),
expErrContains: "max retries must be less than or equal to 2147483647",
},
{
name: "success1",
inp: "0",
expMaxRetries: 0,
},
{
name: "success2",
inp: "10000",
expMaxRetries: 10000,
},
{
name: "successMaxInt32",
inp: strconv.Itoa(math.MaxInt32),
expMaxRetries: math.MaxInt32,
},
{
name: "successNothing",
inp: "",
expMaxRetries: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
os.Setenv(EnvBoundaryMaxRetries, tt.inp)
t.Cleanup(func() { os.Unsetenv(EnvBoundaryMaxRetries) })
var c Config
err := c.ReadEnvironment()
if tt.expErrContains != "" {
require.ErrorContains(t, err, tt.expErrContains)
require.Equal(t, 0, c.MaxRetries)
return
}
require.NoError(t, err)
require.Equal(t, tt.expMaxRetries, c.MaxRetries)
})
}
}
func TestDefaultConfig(t *testing.T) {
tests := []struct {
name string
envvars map[string]string
want *Config
}{
{
name: "noEnvVarsSet",
envvars: nil,
want: &Config{
Addr: "http://127.0.0.1:9200",
Token: "",
RecoveryKmsWrapper: nil,
HttpClient: func() *http.Client {
client := cleanhttp.DefaultPooledClient()
client.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}
return client
}(),
TLSConfig: &TLSConfig{},
Headers: map[string][]string{},
MaxRetries: 2,
Timeout: time.Second * 60,
Backoff: RateLimitLinearJitterBackoff,
CheckRetry: nil,
Limiter: nil,
OutputCurlString: false,
SRVLookup: false,
},
},
{
name: "maxRetries",
envvars: map[string]string{
"BOUNDARY_MAX_RETRIES": "5",
},
want: &Config{
Addr: "http://127.0.0.1:9200",
Token: "",
RecoveryKmsWrapper: nil,
HttpClient: func() *http.Client {
client := cleanhttp.DefaultPooledClient()
client.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}
return client
}(),
TLSConfig: &TLSConfig{},
Headers: map[string][]string{},
MaxRetries: 5,
Timeout: time.Second * 60,
Backoff: RateLimitLinearJitterBackoff,
CheckRetry: nil,
Limiter: nil,
OutputCurlString: false,
SRVLookup: false,
},
},
}
for _, tt := range tests {
for k, v := range tt.envvars {
os.Setenv(k, v)
}
t.Cleanup(func() {
for k := range tt.envvars {
os.Unsetenv(k)
}
})
c, err := DefaultConfig()
require.NoError(t, err)
assert.Empty(t,
cmp.Diff(tt.want, c,
cmpopts.IgnoreUnexported(http.Transport{}, tls.Config{}),
// Ignore fields that are functions, since cmp.Diff can't
// correctly compare them if they are non-nil.
cmpopts.IgnoreFields(Config{}, "Backoff"),
cmpopts.IgnoreFields(http.Transport{}, "Proxy", "DialContext"),
))
}
}