Isolate config tests from the shell; any out-of-range PORT is ErrInvalidPort (closes #94)
check / check (push) Successful in 3m17s
check / check (push) Successful in 3m17s
Config tests that unset a variable now restore it when they end, and every test that builds a Config first unsets every variable the config reads, so a value exported in the developer's shell cannot change the result. TestEnvPort checks that the bad value appears in its errors. A PORT of zero or below now wraps ErrInvalidPort, as one above 65535 already did. The README configuration table and the Settings page now say that a RETENTION_SWEEP_INTERVAL that does not parse, or is zero or negative, fails startup. Model: opus-5-5
This commit is contained in:
+45
-11
@@ -38,6 +38,41 @@ const (
|
||||
bindAddressSample = "10.1.2.3"
|
||||
)
|
||||
|
||||
// configEnvKeys is every variable config.New reads. Its tests unset
|
||||
// all of them before setting the ones under test, so a variable
|
||||
// exported in the developer's shell cannot change their outcome.
|
||||
func configEnvKeys() []string {
|
||||
return []string{
|
||||
"WEBHOOKER_ENVIRONMENT",
|
||||
envKeyPort,
|
||||
envKeyBindAddress,
|
||||
"DATA_DIR",
|
||||
envKeyDebug,
|
||||
"METRICS_USERNAME",
|
||||
"METRICS_PASSWORD",
|
||||
envKeySentryDSN,
|
||||
"RETENTION_SWEEP_INTERVAL",
|
||||
"SESSION_IDLE_TIMEOUT",
|
||||
"RECEIVER_RATE_LIMIT",
|
||||
"TRUSTED_PROXIES",
|
||||
"ALLOWED_EGRESS_CIDRS",
|
||||
}
|
||||
}
|
||||
|
||||
// unsetEnv makes each key absent for the rest of the test and puts
|
||||
// back whatever it held when the test ends. t.Setenv registers that
|
||||
// restore; the Unsetenv after it is what makes the key absent, since
|
||||
// a key set to the empty string is still present, and godotenv will
|
||||
// not overwrite a present key.
|
||||
func unsetEnv(t *testing.T, keys ...string) {
|
||||
t.Helper()
|
||||
|
||||
for _, key := range keys {
|
||||
t.Setenv(key, "")
|
||||
require.NoError(t, os.Unsetenv(key))
|
||||
}
|
||||
}
|
||||
|
||||
// envBoolCase is one row of the envBool table.
|
||||
type envBoolCase struct {
|
||||
name string
|
||||
@@ -124,7 +159,7 @@ func TestEnvBool(t *testing.T) {
|
||||
if tt.set {
|
||||
t.Setenv(testEnvKey, tt.value)
|
||||
} else {
|
||||
require.NoError(t, os.Unsetenv(testEnvKey))
|
||||
unsetEnv(t, testEnvKey)
|
||||
}
|
||||
|
||||
got, err := config.EnvBoolForTest(
|
||||
@@ -194,6 +229,7 @@ func TestEnvPositiveInt(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
//nolint:dupl // TestEnvPort makes the same checks, on purpose.
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Cannot use t.Parallel() here because t.Setenv
|
||||
@@ -201,7 +237,7 @@ func TestEnvPositiveInt(t *testing.T) {
|
||||
if tt.set {
|
||||
t.Setenv(testEnvKey, tt.value)
|
||||
} else {
|
||||
require.NoError(t, os.Unsetenv(testEnvKey))
|
||||
unsetEnv(t, testEnvKey)
|
||||
}
|
||||
|
||||
got, err := config.EnvPositiveIntForTest(
|
||||
@@ -264,7 +300,7 @@ func TestEnvPort(t *testing.T) {
|
||||
set: true,
|
||||
value: "0",
|
||||
expectError: true,
|
||||
errIs: config.ErrNonPositiveValue,
|
||||
errIs: config.ErrInvalidPort,
|
||||
},
|
||||
{
|
||||
name: "above the port range is rejected",
|
||||
@@ -275,6 +311,7 @@ func TestEnvPort(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
//nolint:dupl // TestEnvPositiveInt makes the same checks, on purpose.
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Cannot use t.Parallel() here because t.Setenv
|
||||
@@ -282,7 +319,7 @@ func TestEnvPort(t *testing.T) {
|
||||
if tt.set {
|
||||
t.Setenv(testEnvKey, tt.value)
|
||||
} else {
|
||||
require.NoError(t, os.Unsetenv(testEnvKey))
|
||||
unsetEnv(t, testEnvKey)
|
||||
}
|
||||
|
||||
got, err := config.EnvPortForTest(
|
||||
@@ -292,6 +329,7 @@ func TestEnvPort(t *testing.T) {
|
||||
if tt.expectError {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), testEnvKey)
|
||||
assert.Contains(t, err.Error(), tt.value)
|
||||
|
||||
if tt.errIs != nil {
|
||||
require.ErrorIs(t, err, tt.errIs)
|
||||
@@ -322,7 +360,7 @@ func TestEnvBindAddress(t *testing.T) {
|
||||
if tt.set {
|
||||
t.Setenv(testEnvKey, tt.value)
|
||||
} else {
|
||||
require.NoError(t, os.Unsetenv(testEnvKey))
|
||||
unsetEnv(t, testEnvKey)
|
||||
}
|
||||
|
||||
got, err := config.EnvBindAddressForTest(
|
||||
@@ -485,6 +523,7 @@ func TestNewRejectsBadEnvValues(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Cannot use t.Parallel() here because t.Setenv
|
||||
// is incompatible with parallel subtests.
|
||||
unsetEnv(t, configEnvKeys()...)
|
||||
t.Setenv("WEBHOOKER_ENVIRONMENT", "dev")
|
||||
t.Setenv(tt.key, tt.value)
|
||||
|
||||
@@ -646,14 +685,9 @@ func sentryEnvValueCases() []badEnvValueCase {
|
||||
// break the legitimate unset case: absent variables still get their
|
||||
// documented defaults.
|
||||
func TestNewUsesDefaultsWhenUnset(t *testing.T) {
|
||||
unsetEnv(t, configEnvKeys()...)
|
||||
t.Setenv("WEBHOOKER_ENVIRONMENT", "dev")
|
||||
|
||||
for _, key := range []string{
|
||||
envKeyPort, envKeyDebug, envKeyBindAddress, envKeySentryDSN,
|
||||
} {
|
||||
require.NoError(t, os.Unsetenv(key))
|
||||
}
|
||||
|
||||
cfg, err := buildConfig(t)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cfg)
|
||||
|
||||
Reference in New Issue
Block a user