check / check (push) Failing after 1s
The default cache_max_bytes was 75% of the space free at startup. The cache's own files are not free space, so a fuller cache got a smaller limit after a restart and eviction then deleted most of it. The default is now 75% of the sum of the free space and what the cache already holds by its own size accounting, at least 500 MiB. The cache works it out when it opens, after the database is open, so the computation and its tests moved from internal/config to internal/imgcache. The config only records whether cache_max_bytes was set; newCacheConfig in the handlers turns the disk cache off only for an explicit 0, and is tested for an omitted, a zero and a positive value. Model: opus-5-5
165 lines
4.8 KiB
Go
165 lines
4.8 KiB
Go
package config
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestCacheMaxBytesExplicitValueUsedWithoutFloor verifies that an
|
|
// explicitly configured cache_max_bytes value is used exactly as
|
|
// given: the 500 MiB floor applies only to the computed default, never
|
|
// to explicit values.
|
|
func TestCacheMaxBytesExplicitValueUsedWithoutFloor(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
yamlContent := "signing_key: " + validTestSigningKey + "\ncache_max_bytes: 1024\n"
|
|
|
|
c, err := configFromYAML(t, yamlContent)
|
|
if err != nil {
|
|
t.Fatalf("explicit cache_max_bytes must be accepted, got error: %v", err)
|
|
}
|
|
|
|
if c.CacheMaxBytes != 1024 {
|
|
t.Errorf("CacheMaxBytes = %d, want 1024 (no floor for explicit values)",
|
|
c.CacheMaxBytes)
|
|
}
|
|
}
|
|
|
|
// TestCacheMaxBytesZeroIsValidAndDisablesCache verifies that an
|
|
// explicit zero is a valid value (it disables the disk cache), not an
|
|
// error.
|
|
func TestCacheMaxBytesZeroIsValidAndDisablesCache(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
yamlContent := "signing_key: " + validTestSigningKey + "\ncache_max_bytes: 0\n"
|
|
|
|
c, err := configFromYAML(t, yamlContent)
|
|
if err != nil {
|
|
t.Fatalf("cache_max_bytes: 0 must be accepted, got error: %v", err)
|
|
}
|
|
|
|
if c.CacheMaxBytes != 0 {
|
|
t.Errorf("CacheMaxBytes = %d, want 0", c.CacheMaxBytes)
|
|
}
|
|
}
|
|
|
|
// TestCacheMaxBytesLargeExplicitValueParses verifies that values above
|
|
// 32-bit range parse correctly (the field is an int64 byte count).
|
|
func TestCacheMaxBytesLargeExplicitValueParses(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
yamlContent := "signing_key: " + validTestSigningKey +
|
|
"\ncache_max_bytes: 10737418240\n"
|
|
|
|
c, err := configFromYAML(t, yamlContent)
|
|
if err != nil {
|
|
t.Fatalf("large cache_max_bytes must be accepted, got error: %v", err)
|
|
}
|
|
|
|
if c.CacheMaxBytes != 10737418240 {
|
|
t.Errorf("CacheMaxBytes = %d, want 10737418240", c.CacheMaxBytes)
|
|
}
|
|
}
|
|
|
|
// TestCacheMaxBytesInvalidValuesAbortStartup verifies that a SET but
|
|
// invalid cache_max_bytes value aborts startup naming the key and the
|
|
// offending value, per the no-silent-fallback rule: defaults apply
|
|
// only to omitted keys.
|
|
func TestCacheMaxBytesInvalidValuesAbortStartup(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
signingKeyLine := "signing_key: " + validTestSigningKey + "\n"
|
|
|
|
cases := []struct {
|
|
name string
|
|
yaml string
|
|
// wantErrSubstrings must all appear in the error message.
|
|
wantErrSubstrings []string
|
|
}{
|
|
{
|
|
name: "negative",
|
|
yaml: signingKeyLine + "cache_max_bytes: -1024\n",
|
|
wantErrSubstrings: []string{keyCacheMaxBytes, "-1024"},
|
|
},
|
|
{
|
|
name: "float",
|
|
yaml: signingKeyLine + "cache_max_bytes: 3.5\n",
|
|
wantErrSubstrings: []string{keyCacheMaxBytes, "3.5"},
|
|
},
|
|
{
|
|
name: "non-numeric string",
|
|
yaml: signingKeyLine + "cache_max_bytes: banana\n",
|
|
wantErrSubstrings: []string{keyCacheMaxBytes, "banana"},
|
|
},
|
|
{
|
|
name: "explicit null",
|
|
yaml: signingKeyLine + "cache_max_bytes: null\n",
|
|
wantErrSubstrings: []string{keyCacheMaxBytes, "null"},
|
|
},
|
|
{
|
|
name: "bare key no value",
|
|
yaml: signingKeyLine + "cache_max_bytes:\n",
|
|
wantErrSubstrings: []string{keyCacheMaxBytes, "null"},
|
|
},
|
|
{
|
|
name: "boolean",
|
|
yaml: signingKeyLine + "cache_max_bytes: true\n",
|
|
wantErrSubstrings: []string{keyCacheMaxBytes, "true"},
|
|
},
|
|
{
|
|
name: "list",
|
|
yaml: signingKeyLine + "cache_max_bytes:\n - 1\n",
|
|
wantErrSubstrings: []string{keyCacheMaxBytes},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c, err := configFromYAML(t, tc.yaml)
|
|
if err == nil {
|
|
t.Fatalf(
|
|
"config with %s cache_max_bytes must abort startup, got config: %+v",
|
|
tc.name, c)
|
|
}
|
|
|
|
t.Logf("got expected error: %v", err)
|
|
|
|
for _, want := range tc.wantErrSubstrings {
|
|
if !strings.Contains(err.Error(), want) {
|
|
t.Errorf("error %q does not mention %q", err.Error(), want)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestCacheMaxBytesExplicitIsRecorded verifies that an omitted
|
|
// cache_max_bytes is recorded as not explicit, so the cache works out
|
|
// the default when it opens, and that an explicit zero is recorded as
|
|
// explicit, so it disables the disk cache instead.
|
|
func TestCacheMaxBytesExplicitIsRecorded(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
signingKeyLine := "signing_key: " + validTestSigningKey + "\n"
|
|
|
|
omitted, err := configFromYAML(t, signingKeyLine)
|
|
if err != nil {
|
|
t.Fatalf("minimal config should be valid, got error: %v", err)
|
|
}
|
|
|
|
if omitted.CacheMaxBytesExplicit {
|
|
t.Error("omitted cache_max_bytes recorded as explicit")
|
|
}
|
|
|
|
zero, err := configFromYAML(t, signingKeyLine+"cache_max_bytes: 0\n")
|
|
if err != nil {
|
|
t.Fatalf("cache_max_bytes: 0 must be accepted, got error: %v", err)
|
|
}
|
|
|
|
if !zero.CacheMaxBytesExplicit {
|
|
t.Error("cache_max_bytes: 0 not recorded as explicit")
|
|
}
|
|
}
|