package imgcache import ( "bytes" "errors" "io" "log/slog" "os" "path/filepath" "strings" "testing" "sneak.berlin/go/pixa/internal/imagerequest" ) func TestContentStorage_StoreAndLoad(t *testing.T) { t.Parallel() tmpDir := t.TempDir() storage, err := NewContentStorage(tmpDir) if err != nil { t.Fatalf("NewContentStorage() error = %v", err) } content := []byte("hello world") key := imagerequest.Source{Host: testHostCDN, Path: testPathCat}.Key() size, err := storage.Store(key, content) if err != nil { t.Fatalf("Store() error = %v", err) } if size != int64(len(content)) { t.Errorf("Store() size = %d, want %d", size, len(content)) } // Verify file exists at expected path keyStr := string(key) expectedPath := filepath.Join(tmpDir, keyStr[0:2], keyStr[2:4], keyStr) _, err = os.Stat(expectedPath) if err != nil { t.Errorf("File not at expected path %s: %v", expectedPath, err) } // Load and verify content r, err := storage.Load(key) if err != nil { t.Fatalf("Load() error = %v", err) } defer func() { _ = r.Close() }() loaded, err := io.ReadAll(r) if err != nil { t.Fatalf("ReadAll() error = %v", err) } if !bytes.Equal(loaded, content) { t.Errorf("Load() content = %q, want %q", loaded, content) } } func TestContentStorage_LoadNotFound(t *testing.T) { t.Parallel() tmpDir := t.TempDir() storage, err := NewContentStorage(tmpDir) if err != nil { t.Fatalf("NewContentStorage() error = %v", err) } _, err = storage.Load(imagerequest.SourceKey("nonexistent")) if !errors.Is(err, ErrNotFound) { t.Errorf("Load() error = %v, want ErrNotFound", err) } } func TestContentStorage_Delete(t *testing.T) { t.Parallel() tmpDir := t.TempDir() storage, err := NewContentStorage(tmpDir) if err != nil { t.Fatalf("NewContentStorage() error = %v", err) } content := []byte("to be deleted") key := imagerequest.Source{Host: testHostCDN, Path: testPathCat}.Key() _, err = storage.Store(key, content) if err != nil { t.Fatalf("Store() error = %v", err) } if !storage.Exists(key) { t.Error("Exists() = false, want true") } err = storage.Delete(key) if err != nil { t.Fatalf("Delete() error = %v", err) } if storage.Exists(key) { t.Error("Exists() = true after delete, want false") } } func TestContentStorage_DeleteNonexistent(t *testing.T) { t.Parallel() tmpDir := t.TempDir() storage, err := NewContentStorage(tmpDir) if err != nil { t.Fatalf("NewContentStorage() error = %v", err) } // Should not error err = storage.Delete(imagerequest.SourceKey("nonexistent")) if err != nil { t.Errorf("Delete() error = %v, want nil", err) } } func TestContentStorage_KeyToPath(t *testing.T) { t.Parallel() tmpDir := t.TempDir() storage, err := NewContentStorage(tmpDir) if err != nil { t.Fatalf("NewContentStorage() error = %v", err) } // Test by storing and verifying the resulting path structure content := []byte("test content for path verification") key := imagerequest.Source{Host: testHostCDN, Path: testPathCat}.Key() _, err = storage.Store(key, content) if err != nil { t.Fatalf("Store() error = %v", err) } keyStr := string(key) expectedPath := filepath.Join(tmpDir, keyStr[0:2], keyStr[2:4], keyStr) _, err = os.Stat(expectedPath) if err != nil { t.Errorf("File not at expected path %s: %v", expectedPath, err) } } // 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 := imagerequest.CacheKey(&imagerequest.Request{ Source: imagerequest.Source{Host: testHostCDN, Path: 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()) } } // storeTestVariant stores one variant, with its .meta file, in a new // VariantStorage and returns the storage and the variant's key. func storeTestVariant(t *testing.T) (*VariantStorage, imagerequest.VariantKey) { t.Helper() storage, err := NewVariantStorage(t.TempDir(), slog.New(slog.DiscardHandler)) if err != nil { t.Fatalf("NewVariantStorage() error = %v", err) } key := imagerequest.CacheKey(&imagerequest.Request{ Source: imagerequest.Source{Host: testHostCDN, Path: testPathCat}, }) _, err = storage.Store(key, bytes.NewReader([]byte("variant data")), "image/webp") if err != nil { t.Fatalf("Store() error = %v", err) } return storage, key } // TestVariantStorage_DeleteRemovesMeta verifies that Delete removes the // variant's .meta file along with the variant file. func TestVariantStorage_DeleteRemovesMeta(t *testing.T) { t.Parallel() storage, key := storeTestVariant(t) metaPath := storage.keyToPath(key) + ".meta" _, err := os.Stat(metaPath) if err != nil { t.Fatalf("Store() wrote no .meta file: %v", err) } err = storage.Delete(key) if err != nil { t.Fatalf("Delete() error = %v", err) } if storage.Exists(key) { t.Error("Exists() = true after delete, want false") } _, err = os.Stat(metaPath) if !os.IsNotExist(err) { t.Errorf(".meta file left after Delete() (stat err=%v)", err) } } // TestVariantStorage_DeleteWithoutMeta verifies that Delete succeeds for a // variant whose .meta file is missing. func TestVariantStorage_DeleteWithoutMeta(t *testing.T) { t.Parallel() storage, key := storeTestVariant(t) err := os.Remove(storage.keyToPath(key) + ".meta") if err != nil { t.Fatalf("removing .meta file: %v", err) } err = storage.Delete(key) if err != nil { t.Fatalf("Delete() error = %v, want nil", err) } if storage.Exists(key) { t.Error("Exists() = true after delete, want false") } }