From 004ccf3fc849ee881dec3cc61352fca2e6833716 Mon Sep 17 00:00:00 2001 From: sneak Date: Mon, 28 Sep 2026 17:43:34 +0000 Subject: [PATCH] Test the warn lines of failed cache writes and stats queries (closes #72) A directory where a variant .meta file goes, and dropped tables in the test database, make the variant .meta write, the two Stats count queries and the stats counter updates fail. Each test hands the cache or NewVariantStorage a logger writing to a buffer, checks the warn line, and checks the store and Stats calls still succeed. Model: opus-5-5 --- internal/imgcache/stats_internal_test.go | 82 ++++++++++++++++++++++ internal/imgcache/storage_internal_test.go | 34 +++++++++ 2 files changed, 116 insertions(+) diff --git a/internal/imgcache/stats_internal_test.go b/internal/imgcache/stats_internal_test.go index 4cecd4c..5076179 100644 --- a/internal/imgcache/stats_internal_test.go +++ b/internal/imgcache/stats_internal_test.go @@ -1,9 +1,12 @@ package imgcache import ( + "bytes" "context" "database/sql" + "log/slog" "math" + "strings" "testing" "time" @@ -101,3 +104,82 @@ func TestStats_ZeroCounts(t *testing.T) { 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()) + } + } +} diff --git a/internal/imgcache/storage_internal_test.go b/internal/imgcache/storage_internal_test.go index a72ec94..0465bb1 100644 --- a/internal/imgcache/storage_internal_test.go +++ b/internal/imgcache/storage_internal_test.go @@ -4,8 +4,10 @@ import ( "bytes" "errors" "io" + "log/slog" "os" "path/filepath" + "strings" "testing" ) @@ -404,3 +406,35 @@ func TestCacheKey(t *testing.T) { 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()) + } +}