mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 21:17:58 +08:00
b1842ab9e0
* fix: migrate from github.com/rs/zerolog to golang-native log/slog We have been using zerolog for a really long time. golang now has structured logging using slog. Best to move to this in interests of long-term support. This is a tech debt item. Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix: a few changes on top Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * fix: address comments Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> --------- Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
713 lines
24 KiB
Go
713 lines
24 KiB
Go
package cache_test
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"path"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/alicebob/miniredis/v2"
|
|
"github.com/go-redis/redismock/v9"
|
|
"github.com/redis/go-redis/v9"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
zerr "zotregistry.dev/zot/errors"
|
|
"zotregistry.dev/zot/pkg/log"
|
|
"zotregistry.dev/zot/pkg/storage"
|
|
"zotregistry.dev/zot/pkg/storage/cache"
|
|
"zotregistry.dev/zot/pkg/storage/constants"
|
|
test "zotregistry.dev/zot/pkg/test/common"
|
|
)
|
|
|
|
var ErrTestError = errors.New("TestError")
|
|
|
|
func TestRedisCache(t *testing.T) {
|
|
miniRedis := miniredis.RunT(t)
|
|
|
|
Convey("Make a new cache", t, func() {
|
|
dir := t.TempDir()
|
|
|
|
log := log.NewTestLogger()
|
|
So(log, ShouldNotBeNil)
|
|
|
|
cacheDriver, err := storage.Create("redis", "failTypeAssertion", log)
|
|
So(cacheDriver, ShouldBeNil)
|
|
So(err, ShouldNotBeNil)
|
|
|
|
connOpts, _ := redis.ParseURL("redis://" + miniRedis.Addr())
|
|
client := redis.NewClient(connOpts)
|
|
|
|
cacheDriver, err = storage.Create("redis",
|
|
cache.RedisDriverParameters{client, dir, true, "zot"}, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
name := cacheDriver.Name()
|
|
So(name, ShouldEqual, "redis")
|
|
|
|
val, err := cacheDriver.GetBlob("key")
|
|
So(err, ShouldEqual, zerr.ErrCacheMiss)
|
|
So(val, ShouldBeEmpty)
|
|
|
|
exists := cacheDriver.HasBlob("key", path.Join(dir, "value"))
|
|
So(exists, ShouldBeFalse)
|
|
|
|
exists = cacheDriver.HasBlob("key", "value")
|
|
So(exists, ShouldBeFalse)
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "value"))
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.PutBlob("key", "value")
|
|
So(err, ShouldNotBeNil)
|
|
|
|
exists = cacheDriver.HasBlob("key", path.Join(dir, "value"))
|
|
So(exists, ShouldBeTrue)
|
|
|
|
val, err = cacheDriver.GetBlob("key")
|
|
So(err, ShouldBeNil)
|
|
So(val, ShouldNotBeEmpty)
|
|
|
|
err = cacheDriver.DeleteBlob("bogusKey", "bogusValue")
|
|
So(err, ShouldEqual, zerr.ErrCacheMiss)
|
|
|
|
err = cacheDriver.DeleteBlob("key", "bogusValue")
|
|
So(err, ShouldBeNil)
|
|
|
|
// try to insert empty path
|
|
err = cacheDriver.PutBlob("key", "")
|
|
So(err, ShouldNotBeNil)
|
|
So(err, ShouldEqual, zerr.ErrEmptyValue)
|
|
|
|
connOpts, _ = redis.ParseURL("redis://" + miniRedis.Addr() + "/5")
|
|
client = redis.NewClient(connOpts)
|
|
|
|
cacheDriver, err = storage.Create("redis",
|
|
cache.RedisDriverParameters{client, t.TempDir(), false, "zot"}, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.PutBlob("key1", "originalBlobPath")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.PutBlob("key1", "duplicateBlobPath")
|
|
So(err, ShouldBeNil)
|
|
|
|
val, err = cacheDriver.GetBlob("key1")
|
|
So(val, ShouldEqual, "originalBlobPath")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.DeleteBlob("key1", "duplicateBlobPath")
|
|
So(err, ShouldBeNil)
|
|
|
|
val, err = cacheDriver.GetBlob("key1")
|
|
So(val, ShouldEqual, "originalBlobPath")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.PutBlob("key1", "duplicateBlobPath")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.DeleteBlob("key1", "originalBlobPath")
|
|
So(err, ShouldBeNil)
|
|
|
|
val, err = cacheDriver.GetBlob("key1")
|
|
So(val, ShouldEqual, "duplicateBlobPath")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.DeleteBlob("key1", "duplicateBlobPath")
|
|
So(err, ShouldBeNil)
|
|
|
|
// should be empty
|
|
val, err = cacheDriver.GetBlob("key1")
|
|
So(err, ShouldNotBeNil)
|
|
So(val, ShouldBeEmpty)
|
|
|
|
// try to add three same values
|
|
err = cacheDriver.PutBlob("key2", "duplicate")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.PutBlob("key2", "duplicate")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.PutBlob("key2", "duplicate")
|
|
So(err, ShouldBeNil)
|
|
|
|
val, err = cacheDriver.GetBlob("key2")
|
|
So(val, ShouldEqual, "duplicate")
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.DeleteBlob("key2", "duplicate")
|
|
So(err, ShouldBeNil)
|
|
|
|
// should be empty
|
|
val, err = cacheDriver.GetBlob("key2")
|
|
So(err, ShouldNotBeNil)
|
|
So(val, ShouldBeEmpty)
|
|
})
|
|
|
|
Convey("Test cache.GetAllBlos()", t, func() {
|
|
dir := t.TempDir()
|
|
|
|
log := log.NewTestLogger()
|
|
So(log, ShouldNotBeNil)
|
|
|
|
connOpts, _ := redis.ParseURL("redis://" + miniRedis.Addr())
|
|
client := redis.NewClient(connOpts)
|
|
|
|
cacheDriver, err := storage.Create("redis",
|
|
cache.RedisDriverParameters{client, dir, true, "zot"}, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
name := cacheDriver.Name()
|
|
So(name, ShouldEqual, "redis")
|
|
|
|
blobs, err := cacheDriver.GetAllBlobs("digest")
|
|
So(err, ShouldEqual, zerr.ErrCacheMiss)
|
|
So(blobs, ShouldBeNil)
|
|
|
|
err = cacheDriver.PutBlob("digest", path.Join(dir, "first"))
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.PutBlob("digest", path.Join(dir, "second"))
|
|
So(err, ShouldBeNil)
|
|
|
|
err = cacheDriver.PutBlob("digest", path.Join(dir, "third"))
|
|
So(err, ShouldBeNil)
|
|
|
|
blobs, err = cacheDriver.GetAllBlobs("digest")
|
|
So(err, ShouldBeNil)
|
|
|
|
So(blobs, ShouldResemble, []string{"first", "second", "third"})
|
|
|
|
err = cacheDriver.DeleteBlob("digest", path.Join(dir, "first"))
|
|
So(err, ShouldBeNil)
|
|
|
|
blobs, err = cacheDriver.GetAllBlobs("digest")
|
|
So(err, ShouldBeNil)
|
|
So(len(blobs), ShouldEqual, 2)
|
|
So(blobs, ShouldContain, "second")
|
|
So(blobs, ShouldContain, "third")
|
|
|
|
err = cacheDriver.DeleteBlob("digest", path.Join(dir, "third"))
|
|
So(err, ShouldBeNil)
|
|
|
|
blobs, err = cacheDriver.GetAllBlobs("digest")
|
|
So(err, ShouldBeNil)
|
|
|
|
So(blobs, ShouldResemble, []string{"second"})
|
|
})
|
|
}
|
|
|
|
func TestRedisCacheError(t *testing.T) {
|
|
Convey("Make a new cache", t, func() {
|
|
dir := t.TempDir()
|
|
redisURL := "redis://127.0.0.1:" + test.GetFreePort()
|
|
connOpts, _ := redis.ParseURL(redisURL)
|
|
brokenClient := redis.NewClient(connOpts)
|
|
|
|
log := log.NewTestLogger()
|
|
So(log, ShouldNotBeNil)
|
|
|
|
// redis server is not running
|
|
cacheDriver, err := storage.Create("redis",
|
|
cache.RedisDriverParameters{brokenClient, dir, true, "zot"}, log)
|
|
So(err, ShouldNotBeNil)
|
|
So(cacheDriver, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Redis unreachable", t, func() {
|
|
miniRedis := miniredis.RunT(t)
|
|
dir := t.TempDir()
|
|
|
|
log := log.NewTestLogger()
|
|
So(log, ShouldNotBeNil)
|
|
|
|
connOpts, _ := redis.ParseURL("redis://" + miniRedis.Addr())
|
|
workingClient := redis.NewClient(connOpts)
|
|
|
|
redisURL := "redis://127.0.0.1:" + test.GetFreePort() // must not match miniRedis.Addr()
|
|
connOpts, _ = redis.ParseURL(redisURL)
|
|
brokenClient := redis.NewClient(connOpts)
|
|
|
|
cacheDriver, err := cache.NewRedisCache(
|
|
cache.RedisDriverParameters{workingClient, dir, false, "zot"}, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
// replace the working driver with the broken one
|
|
cacheDriver.SetClient(brokenClient)
|
|
|
|
err = cacheDriver.PutBlob("key", "val")
|
|
So(err, ShouldNotBeNil)
|
|
|
|
found := cacheDriver.HasBlob("key", "val")
|
|
So(found, ShouldEqual, false)
|
|
|
|
_, err = cacheDriver.GetBlob("key")
|
|
So(err, ShouldNotBeNil)
|
|
|
|
_, err = cacheDriver.GetAllBlobs("key")
|
|
So(err, ShouldNotBeNil)
|
|
|
|
err = cacheDriver.DeleteBlob("key", "val")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
}
|
|
|
|
func TestRedisMocked(t *testing.T) {
|
|
Convey("Redis tests using mocks", t, func() {
|
|
dir := t.TempDir()
|
|
|
|
log := log.NewTestLogger()
|
|
So(log, ShouldNotBeNil)
|
|
|
|
tests := []cache.RedisDriverParameters{
|
|
{
|
|
RootDir: dir,
|
|
UseRelPaths: true,
|
|
}, {
|
|
RootDir: dir,
|
|
UseRelPaths: false,
|
|
}, {
|
|
RootDir: dir,
|
|
UseRelPaths: true,
|
|
KeyPrefix: "someprefix",
|
|
}, {
|
|
RootDir: dir,
|
|
UseRelPaths: true,
|
|
KeyPrefix: "zot",
|
|
},
|
|
}
|
|
|
|
for i, redisDriverParams := range tests {
|
|
testID := fmt.Sprintf(" %d", i)
|
|
|
|
keyPrefix := redisDriverParams.KeyPrefix
|
|
if len(keyPrefix) == 0 {
|
|
// check default
|
|
keyPrefix = "zot"
|
|
}
|
|
keyPrefix += ":"
|
|
|
|
// depending on UseRelPaths value we check the relative or absolute value
|
|
// in results using path.Join(pathPrefix, path) in both cases
|
|
pathPrefix := ""
|
|
if !redisDriverParams.UseRelPaths {
|
|
pathPrefix = redisDriverParams.RootDir
|
|
}
|
|
|
|
Convey("PutBlob HExists error"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetErr(ErrTestError)
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val"))
|
|
So(err, ShouldEqual, ErrTestError)
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("PutBlob HSet error"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(false)
|
|
mock.ExpectTxPipeline()
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val")).SetErr(ErrTestError)
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val"))
|
|
So(err, ShouldEqual, ErrTestError)
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("PutBlob SAdd error"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(false)
|
|
mock.ExpectTxPipeline()
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val")).SetVal(1)
|
|
mock.ExpectSAdd(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val")).SetErr(ErrTestError)
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val"))
|
|
So(err, ShouldEqual, ErrTestError)
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("PutBlob succeeds original bucket is created"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(false)
|
|
mock.ExpectTxPipeline()
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val")).SetVal(1)
|
|
mock.ExpectSAdd(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val")).SetVal(1)
|
|
mock.ExpectTxPipelineExec()
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val"))
|
|
So(err, ShouldBeNil)
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("PutBlob succeeds original bucket is reused"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(true)
|
|
mock.ExpectTxPipeline()
|
|
mock.ExpectSAdd(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val")).SetVal(1)
|
|
mock.ExpectTxPipelineExec()
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val"))
|
|
So(err, ShouldBeNil)
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("SMembers error in GetAllBlobs"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.ExpectHGet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(path.Join(pathPrefix, "val"))
|
|
mock.ExpectSMembers(keyPrefix + constants.BlobsCache + ":" + constants.DuplicatesBucket + ":key").
|
|
SetErr(ErrTestError)
|
|
|
|
_, err = cacheDriver.GetAllBlobs("key")
|
|
So(err, ShouldEqual, ErrTestError)
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("GetAllBlobs succeeds"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(false)
|
|
mock.ExpectTxPipeline()
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val1")).SetVal(1)
|
|
mock.ExpectSAdd(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val1")).SetVal(1)
|
|
mock.ExpectTxPipelineExec()
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val1"))
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(false)
|
|
mock.ExpectTxPipeline()
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val2")).SetVal(1)
|
|
mock.ExpectSAdd(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val2")).SetVal(1)
|
|
mock.ExpectTxPipelineExec()
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val2"))
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.ExpectHGet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(path.Join(pathPrefix, "val1"))
|
|
mock.ExpectSMembers(keyPrefix + constants.BlobsCache + ":" + constants.DuplicatesBucket + ":key").
|
|
SetVal([]string{path.Join(pathPrefix, "val1"), path.Join(pathPrefix, "val2")})
|
|
|
|
allBlobs, err := cacheDriver.GetAllBlobs("key")
|
|
So(err, ShouldBeNil)
|
|
So(allBlobs, ShouldResemble, []string{path.Join(pathPrefix, "val1"), path.Join(pathPrefix, "val2")})
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("HasBlob HExists returns error"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.ExpectSIsMember(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val")).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetErr(ErrTestError)
|
|
|
|
ok := cacheDriver.HasBlob("key", path.Join(dir, "val"))
|
|
So(ok, ShouldBeFalse)
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("HasBlob SIsMember returns error"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.ExpectSIsMember(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val")).SetErr(ErrTestError)
|
|
|
|
ok := cacheDriver.HasBlob("key", path.Join(dir, "val"))
|
|
So(ok, ShouldBeFalse)
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("HasBlob HExists returns false"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.ExpectSIsMember(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val")).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(false)
|
|
|
|
ok := cacheDriver.HasBlob("key", path.Join(dir, "val"))
|
|
So(ok, ShouldBeFalse)
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("DeleteBlob tests"+testID, func() {
|
|
// initialize mock client
|
|
cacheDB, mock := redismock.NewClientMock()
|
|
redisDriverParams.Client = cacheDB
|
|
|
|
mock.ExpectPing().SetVal("OK")
|
|
cacheDriver, err := cache.NewRedisCache(redisDriverParams, log)
|
|
So(cacheDriver, ShouldNotBeNil)
|
|
So(err, ShouldBeNil)
|
|
|
|
// Create entry for 1st path
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(false)
|
|
mock.ExpectTxPipeline()
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val1")).SetVal(1)
|
|
mock.ExpectSAdd(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val1")).SetVal(1)
|
|
mock.ExpectTxPipelineExec()
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val1"))
|
|
So(err, ShouldBeNil)
|
|
|
|
Convey("DeleteBlob error in HDel"+testID, func() {
|
|
// If the 2nd path does not exist, HDel is callled
|
|
// Error switching to new path
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectSRem(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val1")).SetVal(1)
|
|
mock.ExpectHGet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(path.Join(pathPrefix, "val1"))
|
|
// failed to get new path
|
|
mock.ExpectSRandMember(keyPrefix + constants.BlobsCache + ":" + constants.DuplicatesBucket + ":key").
|
|
RedisNil()
|
|
mock.ExpectHDel(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetErr(ErrTestError)
|
|
|
|
err = cacheDriver.DeleteBlob("key", path.Join(dir, "val1"))
|
|
So(err, ShouldEqual, ErrTestError)
|
|
})
|
|
|
|
Convey("DeleteBlob succeeds in deleting all data for original blob"+testID, func() {
|
|
// If the 2nd path does not exist, HDel is callled
|
|
// Error switching to new path
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectSRem(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val1")).SetVal(1)
|
|
mock.ExpectHGet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(path.Join(pathPrefix, "val1"))
|
|
// failed to get new path
|
|
mock.ExpectSRandMember(keyPrefix + constants.BlobsCache + ":" + constants.DuplicatesBucket + ":key").
|
|
RedisNil()
|
|
mock.ExpectHDel(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(1)
|
|
|
|
err = cacheDriver.DeleteBlob("key", path.Join(dir, "val1"))
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("DeleteBlob error in SRandMember"+testID, func() {
|
|
// Create entry for 2nd path
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(false)
|
|
mock.ExpectTxPipeline()
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val2")).SetVal(1)
|
|
mock.ExpectSAdd(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val2")).SetVal(1)
|
|
mock.ExpectTxPipelineExec()
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val2"))
|
|
So(err, ShouldBeNil)
|
|
|
|
// Error switching to new path
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectSRem(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val1")).SetVal(1)
|
|
mock.ExpectHGet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(path.Join(pathPrefix, "val1"))
|
|
// failed to get new path
|
|
mock.ExpectSRandMember(keyPrefix + constants.BlobsCache + ":" + constants.DuplicatesBucket + ":key").
|
|
SetErr(ErrTestError)
|
|
|
|
err = cacheDriver.DeleteBlob("key", path.Join(dir, "val1"))
|
|
So(err, ShouldEqual, ErrTestError)
|
|
})
|
|
|
|
Convey("DeleteBlob error in HSet"+testID, func() {
|
|
// Create entry for 2nd path
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(false)
|
|
mock.ExpectTxPipeline()
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val2")).SetVal(1)
|
|
mock.ExpectSAdd(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val2")).SetVal(1)
|
|
mock.ExpectTxPipelineExec()
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val2"))
|
|
So(err, ShouldBeNil)
|
|
|
|
// Error switching to new path
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectSRem(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val1")).SetVal(1)
|
|
mock.ExpectHGet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(path.Join(pathPrefix, "val1"))
|
|
mock.ExpectSRandMember(keyPrefix + constants.BlobsCache + ":" + constants.DuplicatesBucket + ":key").
|
|
SetVal(path.Join(pathPrefix, "val2"))
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val2")).SetErr(ErrTestError)
|
|
|
|
err = cacheDriver.DeleteBlob("key", path.Join(dir, "val1"))
|
|
So(err, ShouldEqual, ErrTestError)
|
|
})
|
|
|
|
Convey("DeleteBlob succeeds in switching original blob path"+testID, func() {
|
|
// Create entry for 2nd path
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectHExists(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(false)
|
|
mock.ExpectTxPipeline()
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val2")).SetVal(1)
|
|
mock.ExpectSAdd(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val2")).SetVal(1)
|
|
mock.ExpectTxPipelineExec()
|
|
|
|
err = cacheDriver.PutBlob("key", path.Join(dir, "val2"))
|
|
So(err, ShouldBeNil)
|
|
|
|
mock.Regexp().ExpectSetNX(keyPrefix+"locks:key", `.*`, 8*time.Second).SetVal(true)
|
|
mock.ExpectSRem(keyPrefix+constants.BlobsCache+":"+constants.DuplicatesBucket+":key",
|
|
path.Join(pathPrefix, "val1")).SetVal(1)
|
|
mock.ExpectHGet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key").
|
|
SetVal(path.Join(pathPrefix, "val1"))
|
|
mock.ExpectSRandMember(keyPrefix + constants.BlobsCache + ":" + constants.DuplicatesBucket + ":key").
|
|
SetVal(path.Join(pathPrefix, "val2"))
|
|
mock.ExpectHSet(keyPrefix+constants.BlobsCache+":"+constants.OriginalBucket, "key",
|
|
path.Join(pathPrefix, "val2")).SetVal(1)
|
|
|
|
err = cacheDriver.DeleteBlob("key", path.Join(dir, "val1"))
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
}
|
|
})
|
|
}
|