An empty, fractional, negative or non-numeric value must abort startup naming the variable and the key, never fall back to the default. The config loader already refuses these; this pins it before the default changes (#242). Model: opus-5-5
180 lines
5.4 KiB
Go
180 lines
5.4 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"testing"
|
|
)
|
|
|
|
// The variables that set the two concurrency limits.
|
|
const (
|
|
testMaxConcurrentProcessingVar = "PIXA_MAX_CONCURRENT_PROCESSING"
|
|
testUpstreamConnectionsVar = "PIXA_UPSTREAM_CONNECTIONS"
|
|
)
|
|
|
|
// TestOmittedConcurrencyLimitsUseDefaults checks that an omitted
|
|
// max_concurrent_processing is the number of CPUs Go uses and an omitted
|
|
// upstream_connections is 64.
|
|
func TestOmittedConcurrencyLimitsUseDefaults(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c, err := configFromYAML(t, signingKeyLine)
|
|
if err != nil {
|
|
t.Fatalf("minimal config should be valid, got error: %v", err)
|
|
}
|
|
|
|
if c.MaxConcurrentProcessing != runtime.GOMAXPROCS(0) {
|
|
t.Errorf("MaxConcurrentProcessing = %d, want %d, one per CPU",
|
|
c.MaxConcurrentProcessing, runtime.GOMAXPROCS(0))
|
|
}
|
|
|
|
if c.UpstreamConnections != 64 {
|
|
t.Errorf("UpstreamConnections = %d, want 64", c.UpstreamConnections)
|
|
}
|
|
}
|
|
|
|
// TestExplicitConcurrencyLimitsAreUsed checks that valid values for the
|
|
// two limits are used as given.
|
|
func TestExplicitConcurrencyLimitsAreUsed(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c, err := configFromYAML(t, signingKeyLine+
|
|
"max_concurrent_processing: 3\nupstream_connections: 10\n")
|
|
if err != nil {
|
|
t.Fatalf("valid config should load, got error: %v", err)
|
|
}
|
|
|
|
if c.MaxConcurrentProcessing != 3 {
|
|
t.Errorf("MaxConcurrentProcessing = %d, want 3", c.MaxConcurrentProcessing)
|
|
}
|
|
|
|
if c.UpstreamConnections != 10 {
|
|
t.Errorf("UpstreamConnections = %d, want 10", c.UpstreamConnections)
|
|
}
|
|
}
|
|
|
|
// TestInvalidConcurrencyLimitAbortsStartup checks that a limit that is
|
|
// not a whole number of at least 1, or is null, aborts startup naming the
|
|
// key and the value, and the variable too where the value could have come
|
|
// from it.
|
|
func TestInvalidConcurrencyLimitAbortsStartup(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
processing := keyMaxConcurrentProcessing
|
|
connections := keyUpstreamConnections
|
|
|
|
runAbortCases(t, []abortCase{
|
|
{
|
|
name: "max_concurrent_processing zero",
|
|
yaml: signingKeyLine + processing + ": 0\n",
|
|
wantErrSubstrings: []string{
|
|
processing, testMaxConcurrentProcessingVar, "value 0",
|
|
},
|
|
},
|
|
{
|
|
name: "max_concurrent_processing negative",
|
|
yaml: signingKeyLine + processing + ": -2\n",
|
|
wantErrSubstrings: []string{
|
|
processing, testMaxConcurrentProcessingVar, "value -2",
|
|
},
|
|
},
|
|
{
|
|
name: "max_concurrent_processing not a number",
|
|
yaml: signingKeyLine + processing + ": lots\n",
|
|
wantErrSubstrings: []string{
|
|
processing, testMaxConcurrentProcessingVar, "lots",
|
|
},
|
|
},
|
|
{
|
|
name: "max_concurrent_processing fractional",
|
|
yaml: signingKeyLine + processing + ": 1.5\n",
|
|
wantErrSubstrings: []string{processing, "1.5"},
|
|
},
|
|
{
|
|
name: "max_concurrent_processing null",
|
|
yaml: signingKeyLine + processing + ": null\n",
|
|
wantErrSubstrings: []string{processing, nullValueText},
|
|
},
|
|
{
|
|
name: "upstream_connections zero",
|
|
yaml: signingKeyLine + connections + ": 0\n",
|
|
wantErrSubstrings: []string{
|
|
connections, testUpstreamConnectionsVar, "value 0",
|
|
},
|
|
},
|
|
{
|
|
name: "upstream_connections negative",
|
|
yaml: signingKeyLine + connections + ": -5\n",
|
|
wantErrSubstrings: []string{
|
|
connections, testUpstreamConnectionsVar, "value -5",
|
|
},
|
|
},
|
|
{
|
|
name: "upstream_connections not a number",
|
|
yaml: signingKeyLine + connections + ": many\n",
|
|
wantErrSubstrings: []string{
|
|
connections, testUpstreamConnectionsVar, "many",
|
|
},
|
|
},
|
|
{
|
|
name: "upstream_connections null",
|
|
yaml: signingKeyLine + connections + ": null\n",
|
|
wantErrSubstrings: []string{connections, nullValueText},
|
|
},
|
|
})
|
|
}
|
|
|
|
// TestConcurrencyLimitsFromEnvironment checks that the two variables set
|
|
// the limits over the config file, and that an invalid value in either
|
|
// aborts startup naming the variable and the value.
|
|
func TestConcurrencyLimitsFromEnvironment(t *testing.T) {
|
|
t.Setenv(testMaxConcurrentProcessingVar, "3")
|
|
t.Setenv(testUpstreamConnectionsVar, "10")
|
|
|
|
c, err := configFromYAML(t, signingKeyLine+
|
|
"max_concurrent_processing: 5\nupstream_connections: 50\n")
|
|
if err != nil {
|
|
t.Fatalf("limits from the environment should load: %v", err)
|
|
}
|
|
|
|
if c.MaxConcurrentProcessing != 3 || c.UpstreamConnections != 10 {
|
|
t.Errorf("limits = %d and %d, want 3 and 10 from the environment",
|
|
c.MaxConcurrentProcessing, c.UpstreamConnections)
|
|
}
|
|
|
|
cases := []struct {
|
|
variable string
|
|
value string
|
|
}{
|
|
{testMaxConcurrentProcessingVar, "lots"},
|
|
{testMaxConcurrentProcessingVar, "0"},
|
|
{testUpstreamConnectionsVar, "-1"},
|
|
{testUpstreamConnectionsVar, "ten"},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.variable+"="+tc.value, func(t *testing.T) {
|
|
t.Setenv(tc.variable, tc.value)
|
|
|
|
_, err := configFromYAML(t, signingKeyLine)
|
|
wantStartupError(t, err, tc.variable, tc.value)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestInvalidMaxConcurrentProcessingFromEnvironmentAbortsStartup checks
|
|
// that a PIXA_MAX_CONCURRENT_PROCESSING that is not a whole number of at
|
|
// least 1, an empty one included, aborts startup naming the variable and
|
|
// the key instead of taking the default.
|
|
func TestInvalidMaxConcurrentProcessingFromEnvironmentAbortsStartup(t *testing.T) {
|
|
for _, value := range []string{"", " ", "0.5", "half", "-1", "4 images"} {
|
|
t.Run(fmt.Sprintf("value %q", value), func(t *testing.T) {
|
|
t.Setenv(testMaxConcurrentProcessingVar, value)
|
|
|
|
_, err := configFromYAML(t, signingKeyLine)
|
|
wantStartupError(t, err,
|
|
testMaxConcurrentProcessingVar, keyMaxConcurrentProcessing)
|
|
})
|
|
}
|
|
}
|