refactor: static sentinel errors for #55's config validation paths (err113)

This commit is contained in:
2026-08-09 13:35:52 +00:00
parent 888c3a409a
commit 9218d7e7f6
3 changed files with 24 additions and 12 deletions

View File

@@ -9,6 +9,12 @@ import (
"testing" "testing"
) )
// Static errors returned by the stub free-space probes below.
var (
errTestStatfsFailed = errors.New("statfs failed")
errTestProbeNotExpected = errors.New("probe must not be called")
)
// discardLogger returns a logger that swallows all output, for tests // discardLogger returns a logger that swallows all output, for tests
// that exercise code paths which log. // that exercise code paths which log.
func discardLogger() *slog.Logger { func discardLogger() *slog.Logger {
@@ -205,7 +211,7 @@ func TestComputeDefaultCacheMaxBytesAppliesFloorToComputedDefault(t *testing.T)
func TestComputeDefaultCacheMaxBytesPropagatesProbeError(t *testing.T) { func TestComputeDefaultCacheMaxBytesPropagatesProbeError(t *testing.T) {
t.Parallel() t.Parallel()
probe := func(string) (uint64, error) { return 0, errors.New("statfs failed") } probe := func(string) (uint64, error) { return 0, errTestStatfsFailed }
_, err := ComputeDefaultCacheMaxBytes(t.TempDir(), probe) _, err := ComputeDefaultCacheMaxBytes(t.TempDir(), probe)
if err == nil { if err == nil {
@@ -284,7 +290,7 @@ func TestResolveCacheMaxBytesDoesNotOverrideExplicitValue(t *testing.T) {
probe := func(string) (uint64, error) { probe := func(string) (uint64, error) {
t.Error("free-space probe must not be consulted for explicit values") t.Error("free-space probe must not be consulted for explicit values")
return 0, errors.New("probe must not be called") return 0, errTestProbeNotExpected
} }
err = c.resolveCacheMaxBytes(discardLogger(), probe) err = c.resolveCacheMaxBytes(discardLogger(), probe)

View File

@@ -43,7 +43,7 @@ func defaultFreeSpaceProbe(path string) (uint64, error) {
} }
if stat.Bsize < 0 { if stat.Bsize < 0 {
return 0, fmt.Errorf("statfs reported negative block size %d for %q", stat.Bsize, path) return 0, fmt.Errorf("%w %d for %q", errNegativeBlockSize, stat.Bsize, path)
} }
blockSize := uint64(stat.Bsize) blockSize := uint64(stat.Bsize)

View File

@@ -62,7 +62,11 @@ var (
errTooFewConnections = errors.New("must be at least 1") errTooFewConnections = errors.New("must be at least 1")
errValueTooShort = errors.New("value too short") errValueTooShort = errors.New("value too short")
errMustBeSetTogether = errors.New("must be set together") errMustBeSetTogether = errors.New("must be set together")
errValueNull = errors.New( errMustNotBeNegative = errors.New("must not be negative")
errOverflowsInt64 = errors.New("overflows a 64-bit integer")
errNegativeBlockSize = errors.New(
"statfs reported negative block size")
errValueNull = errors.New(
"value is null; omit the key entirely to use the default") "value is null; omit the key entirely to use the default")
errValuesNull = errors.New( errValuesNull = errors.New(
"value is null; omit a key entirely to use its default") "value is null; omit a key entirely to use its default")
@@ -371,8 +375,8 @@ func (c *Config) validate() error {
// Zero is valid (it disables the disk cache); only negative // Zero is valid (it disables the disk cache); only negative
// values are rejected. No floor applies to explicit values. // values are rejected. No floor applies to explicit values.
if c.CacheMaxBytes < 0 { if c.CacheMaxBytes < 0 {
return fmt.Errorf("config key %q: value %d must not be negative", return fmt.Errorf("config key %q: value %d %w",
"cache_max_bytes", c.CacheMaxBytes) keyCacheMaxBytes, c.CacheMaxBytes, errMustNotBeNegative)
} }
for _, host := range c.AllowlistHosts { for _, host := range c.AllowlistHosts {
@@ -619,27 +623,29 @@ func getInt64(sc *smartconfig.Config, key string, defaultVal int64) (int64, erro
return val, nil return val, nil
case uint64: case uint64:
if val > math.MaxInt64 { if val > math.MaxInt64 {
return 0, fmt.Errorf("config key %q: value %d overflows a 64-bit integer", return 0, fmt.Errorf("config key %q: value %d %w",
key, val) key, val, errOverflowsInt64)
} }
return int64(val), nil return int64(val), nil
case float64: case float64:
if val != math.Trunc(val) { if val != math.Trunc(val) {
return 0, fmt.Errorf("config key %q: value %v is not an integer", key, val) return 0, fmt.Errorf("config key %q: value %v is %w",
key, val, errNotAnInteger)
} }
return int64(val), nil return int64(val), nil
case string: case string:
parsed, err := strconv.ParseInt(strings.TrimSpace(val), 10, 64) parsed, err := strconv.ParseInt(strings.TrimSpace(val), 10, 64)
if err != nil { if err != nil {
return 0, fmt.Errorf("config key %q: value %q is not an integer", key, val) return 0, fmt.Errorf("config key %q: value %q is %w",
key, val, errNotAnInteger)
} }
return parsed, nil return parsed, nil
default: default:
return 0, fmt.Errorf("config key %q: value %v (%T) is not an integer", return 0, fmt.Errorf("config key %q: value %v (%T) is %w",
key, raw, raw) key, raw, raw, errNotAnInteger)
} }
} }