check / check (push) Successful in 30m1s
The new setting image_requests_per_minute (PIXA_IMAGE_REQUESTS_PER_MINUTE), default 600, caps the GET and HEAD requests one client sends to /v1/image/ and /v1/e/ per minute; the next is answered 429 with Retry-After. It uses the login limit's RateLimit middleware, so clients are told apart the same way, and sits in the image routes' group behind the CORS middleware, so a preflight is not counted. 0 turns it off; a negative or non-integer value stops startup. TestEnvironmentSetsEveryKey gains the new variable, under the owner's standing approval in #64. To stay within the linters' limits, the sentry_dsn check moves unchanged into validateSentryDSN, and the cache_max_bytes explicit flag is set in one line. Model: opus-5-5
108 lines
3.0 KiB
Go
108 lines
3.0 KiB
Go
package config
|
|
|
|
import "testing"
|
|
|
|
// testImageRequestsPerMinuteVar is the variable that sets
|
|
// image_requests_per_minute.
|
|
const testImageRequestsPerMinuteVar = "PIXA_IMAGE_REQUESTS_PER_MINUTE"
|
|
|
|
// TestImageRequestsPerMinuteValues checks that an omitted
|
|
// image_requests_per_minute is 600, and that 0, which turns the limit off,
|
|
// and any larger whole number are used as given.
|
|
func TestImageRequestsPerMinuteValues(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.ImageRequestsPerMinute != 600 {
|
|
t.Errorf("ImageRequestsPerMinute = %d, want 600", c.ImageRequestsPerMinute)
|
|
}
|
|
|
|
explicit := []struct {
|
|
value string
|
|
want int
|
|
}{
|
|
{"0", 0},
|
|
{"30", 30},
|
|
}
|
|
|
|
for _, tc := range explicit {
|
|
c, err := configFromYAML(t,
|
|
signingKeyLine+"image_requests_per_minute: "+tc.value+"\n")
|
|
if err != nil {
|
|
t.Fatalf("image_requests_per_minute: %s should load, got error: %v",
|
|
tc.value, err)
|
|
}
|
|
|
|
if c.ImageRequestsPerMinute != tc.want {
|
|
t.Errorf("ImageRequestsPerMinute = %d, want %d",
|
|
c.ImageRequestsPerMinute, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestInvalidImageRequestsPerMinuteAbortsStartup checks that a value that is
|
|
// negative, not a whole number, or null aborts startup naming the key and
|
|
// the value, and the variable too where the value could have come from it.
|
|
func TestInvalidImageRequestsPerMinuteAbortsStartup(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
key := keyImageRequestsPerMinute
|
|
|
|
runAbortCases(t, []abortCase{
|
|
{
|
|
name: "negative",
|
|
yaml: signingKeyLine + key + ": -1\n",
|
|
wantErrSubstrings: []string{
|
|
key, testImageRequestsPerMinuteVar, "value -1",
|
|
},
|
|
},
|
|
{
|
|
name: "not a number",
|
|
yaml: signingKeyLine + key + ": plenty\n",
|
|
wantErrSubstrings: []string{
|
|
key, testImageRequestsPerMinuteVar, "plenty",
|
|
},
|
|
},
|
|
{
|
|
name: "fractional",
|
|
yaml: signingKeyLine + key + ": 2.5\n",
|
|
wantErrSubstrings: []string{key, "2.5"},
|
|
},
|
|
{
|
|
name: "explicit null",
|
|
yaml: signingKeyLine + key + ": null\n",
|
|
wantErrSubstrings: []string{key, nullValueText},
|
|
},
|
|
})
|
|
}
|
|
|
|
// TestImageRequestsPerMinuteFromEnvironment checks that the variable sets the
|
|
// limit over the config file, and that an invalid value in it aborts startup
|
|
// naming the variable and the value.
|
|
func TestImageRequestsPerMinuteFromEnvironment(t *testing.T) {
|
|
t.Setenv(testImageRequestsPerMinuteVar, "0")
|
|
|
|
c, err := configFromYAML(t, signingKeyLine+"image_requests_per_minute: 30\n")
|
|
if err != nil {
|
|
t.Fatalf("limit from the environment should load: %v", err)
|
|
}
|
|
|
|
if c.ImageRequestsPerMinute != 0 {
|
|
t.Errorf("ImageRequestsPerMinute = %d, want 0 from the environment",
|
|
c.ImageRequestsPerMinute)
|
|
}
|
|
|
|
for _, value := range []string{"-5", "ten"} {
|
|
t.Run(value, func(t *testing.T) {
|
|
t.Setenv(testImageRequestsPerMinuteVar, value)
|
|
|
|
_, err := configFromYAML(t, signingKeyLine)
|
|
wantStartupError(t, err, testImageRequestsPerMinuteVar, value)
|
|
})
|
|
}
|
|
}
|