Refuse an unparseable exp with 400; log swallowed cache errors (closes #72) #141
@@ -1,9 +1,12 @@
|
|||||||
package imgcache
|
package imgcache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -101,3 +104,82 @@ func TestStats_ZeroCounts(t *testing.T) {
|
|||||||
t.Errorf("HitRate = %f, want 0.0 for zero counts", stats.HitRate)
|
t.Errorf("HitRate = %f, want 0.0 for zero counts", stats.HitRate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestStats_LogsFailedCountQueries verifies that a failed item count query
|
||||||
|
// and a failed size query are each logged at warn and Stats still succeeds.
|
||||||
|
func TestStats_LogsFailedCountQueries(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db := setupStatsTestDB(t)
|
||||||
|
|
||||||
|
var logBuf bytes.Buffer
|
||||||
|
|
||||||
|
cache, err := NewCache(db, CacheConfig{
|
||||||
|
StateDir: t.TempDir(),
|
||||||
|
CacheTTL: time.Hour,
|
||||||
|
NegativeTTL: 5 * time.Minute,
|
||||||
|
Logger: slog.New(slog.NewJSONHandler(&logBuf, nil)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.ExecContext(t.Context(),
|
||||||
|
`DROP TABLE request_cache; DROP TABLE output_content`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = cache.Stats(t.Context())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Stats() error = %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, msg := range []string{
|
||||||
|
"failed to count cache items for stats",
|
||||||
|
"failed to sum cache size for stats",
|
||||||
|
} {
|
||||||
|
want := `"level":"WARN","msg":"` + msg + `"`
|
||||||
|
if !strings.Contains(logBuf.String(), want) {
|
||||||
|
t.Errorf("log missing %s; got %q", want, logBuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestIncrementStats_LogsFailedUpdates verifies that a failed hit or miss
|
||||||
|
// count update and a failed upstream fetch count update are each logged at
|
||||||
|
// warn.
|
||||||
|
func TestIncrementStats_LogsFailedUpdates(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db := setupStatsTestDB(t)
|
||||||
|
|
||||||
|
var logBuf bytes.Buffer
|
||||||
|
|
||||||
|
cache, err := NewCache(db, CacheConfig{
|
||||||
|
StateDir: t.TempDir(),
|
||||||
|
CacheTTL: time.Hour,
|
||||||
|
NegativeTTL: 5 * time.Minute,
|
||||||
|
Logger: slog.New(slog.NewJSONHandler(&logBuf, nil)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.ExecContext(t.Context(), `DROP TABLE cache_stats`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.IncrementStats(t.Context(), false, 1024)
|
||||||
|
|
||||||
|
for _, msg := range []string{
|
||||||
|
"failed to count cache hit or miss",
|
||||||
|
"failed to count upstream fetch",
|
||||||
|
} {
|
||||||
|
want := `"level":"WARN","msg":"` + msg + `"`
|
||||||
|
if !strings.Contains(logBuf.String(), want) {
|
||||||
|
t.Errorf("log missing %s; got %q", want, logBuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -404,3 +406,35 @@ func TestCacheKey(t *testing.T) {
|
|||||||
t.Error("CacheKey() produced same key for different quality")
|
t.Error("CacheKey() produced same key for different quality")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestVariantStorage_StoreLogsFailedMetaWrite verifies that a .meta write
|
||||||
|
// that fails is logged at warn and the store still succeeds.
|
||||||
|
func TestVariantStorage_StoreLogsFailedMetaWrite(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var logBuf bytes.Buffer
|
||||||
|
|
||||||
|
storage, err := NewVariantStorage(
|
||||||
|
t.TempDir(), slog.New(slog.NewJSONHandler(&logBuf, nil)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewVariantStorage() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := CacheKey(&ImageRequest{SourceHost: testHostCDN, SourcePath: testPathCat})
|
||||||
|
|
||||||
|
// A directory where the .meta file goes makes the .meta write fail.
|
||||||
|
err = os.MkdirAll(storage.keyToPath(key)+".meta", StorageDirPerm)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = storage.Store(key, bytes.NewReader([]byte("variant data")), "image/webp")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Store() error = %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := `"level":"WARN","msg":"failed to write variant metadata sidecar"`
|
||||||
|
if !strings.Contains(logBuf.String(), want) {
|
||||||
|
t.Errorf("log missing %s; got %q", want, logBuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user