package imagestore_test import ( "context" "errors" "net/http" "net/http/httptest" "testing" "github.com/distribution/distribution/v3/registry/storage/driver" godigest "github.com/opencontainers/go-digest" . "github.com/smartystreets/goconvey/convey" zerr "zotregistry.dev/zot/v2/errors" "zotregistry.dev/zot/v2/pkg/extensions/monitoring" zlog "zotregistry.dev/zot/v2/pkg/log" "zotregistry.dev/zot/v2/pkg/storage/gcs" "zotregistry.dev/zot/v2/pkg/storage/imagestore" "zotregistry.dev/zot/v2/pkg/storage/local" "zotregistry.dev/zot/v2/pkg/test/mocks" ) func TestGetBlobRedirectURL(t *testing.T) { Convey("GetBlobRedirectURL", t, func() { log := zlog.NewTestLogger() metrics := monitoring.NewMetricsServer(false, log) Convey("returns bad digest for invalid digest", func() { store := imagestore.NewImageStore(t.TempDir(), "", false, false, log, metrics, nil, local.New(true), nil, nil, nil) url, err := store.GetBlobRedirectURL(nil, "repo", godigest.Digest("not-a-digest")) So(url, ShouldEqual, "") So(errors.Is(err, zerr.ErrBadBlobDigest), ShouldBeTrue) }) Convey("returns empty URL for local storage", func() { store := imagestore.NewImageStore(t.TempDir(), "", false, false, log, metrics, nil, local.New(true), nil, nil, nil) digest := godigest.FromString("blob-content") // Local driver has no external signed URL endpoint, so redirect is intentionally empty. url, err := store.GetBlobRedirectURL(nil, "repo", digest) So(err, ShouldBeNil) So(url, ShouldEqual, "") }) Convey("returns redirect URL for remote storage", func() { rootDir := t.TempDir() storeMock := &mocks.StorageDriverMock{} remoteDriver := gcs.New(storeMock) store := imagestore.NewImageStore(rootDir, "", false, false, log, metrics, nil, remoteDriver, nil, nil, nil) repo := "repo" digest := godigest.FromString("blob-content") expectedBlobPath := store.BlobPath(repo, digest) expectedURL := "https://example.com/signed/blob" storeMock.StatFn = func(_ context.Context, path string) (driver.FileInfo, error) { So(path, ShouldEqual, expectedBlobPath) return &mocks.FileInfoMock{ PathFn: func() string { return path }, SizeFn: func() int64 { return 42 }, }, nil } storeMock.RedirectURLFn = func(_ *http.Request, path string) (string, error) { So(path, ShouldEqual, expectedBlobPath) return expectedURL, nil } req := httptest.NewRequestWithContext(context.Background(), http.MethodGet, "http://localhost/v2/repo/blobs/sha256:deadbeef", nil) url, err := store.GetBlobRedirectURL(req, repo, digest) So(err, ShouldBeNil) So(url, ShouldEqual, expectedURL) }) Convey("returns blob not found when blob path does not exist", func() { rootDir := t.TempDir() storeMock := &mocks.StorageDriverMock{} remoteDriver := gcs.New(storeMock) store := imagestore.NewImageStore(rootDir, "", false, false, log, metrics, nil, remoteDriver, nil, nil, nil) storeMock.StatFn = func(_ context.Context, path string) (driver.FileInfo, error) { return nil, driver.PathNotFoundError{Path: path} } digest := godigest.FromString("blob-content") url, err := store.GetBlobRedirectURL(nil, "repo", digest) So(url, ShouldEqual, "") So(errors.Is(err, zerr.ErrBlobNotFound), ShouldBeTrue) }) }) }