-
-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
430 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package store | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
const ( | ||
// FreecacheType represents the storage type as a string value | ||
FreecacheType = "freecache" | ||
// FreecacheTagPattern represents the tag pattern to be used as a key in specified storage | ||
FreecacheTagPattern = "freecache_tag_%s" | ||
) | ||
|
||
// FreecacheClientInterface represents a coocood/freecache client | ||
type FreecacheClientInterface interface { | ||
Get(key []byte) (value []byte, err error) | ||
GetInt(key int64) (value []byte, err error) | ||
Set(key, value []byte, expireSeconds int) (err error) | ||
SetInt(key int64, value []byte, expireSeconds int) (err error) | ||
Del(key []byte) (affected bool) | ||
DelInt(key int64) (affected bool) | ||
Clear() | ||
} | ||
|
||
type FreecacheStore struct { | ||
client FreecacheClientInterface | ||
options *Options | ||
} | ||
|
||
func NewFreecache(client FreecacheClientInterface, options *Options) *FreecacheStore { | ||
if options == nil { | ||
options = &Options{} | ||
} | ||
|
||
return &FreecacheStore{ | ||
client: client, | ||
options: options, | ||
} | ||
} | ||
func (f *FreecacheStore) Get(key interface{}) (interface{}, error) { | ||
var err error | ||
var result interface{} | ||
if k, ok := key.(string); ok { | ||
result, err = f.client.Get([]byte(k)) | ||
if err != nil { | ||
return nil, errors.New("value not found in Freecache store") | ||
} | ||
return result, err | ||
} | ||
|
||
return nil, errors.New("key type not supported by Freecache store") | ||
|
||
} | ||
|
||
// Set sets a key, value and expiration for a cache entry and stores it in the cache. | ||
// If the key is larger than 65535 or value is larger than 1/1024 of the cache size, | ||
// the entry will not be written to the cache. expireSeconds <= 0 means no expire, | ||
// but it can be evicted when cache is full. | ||
func (f *FreecacheStore) Set(key interface{}, value interface{}, options *Options) error { | ||
var err error | ||
var val []byte | ||
|
||
//type check for value, as freecache only supports value of type []byte | ||
switch v := value.(type) { | ||
case []byte: | ||
val = v | ||
default: | ||
return errors.New("value type not supported by Freecache store") | ||
} | ||
|
||
if k, ok := key.(string); ok { | ||
err = f.client.Set([]byte(k), val, int(options.Expiration.Seconds())) | ||
if err != nil { | ||
return fmt.Errorf("size of key: %v, value: %v, err: %v", k, len(val), err) | ||
} | ||
return err | ||
} | ||
return errors.New("key type not supported by Freecache store") | ||
} | ||
|
||
func (f *FreecacheStore) Delete(key interface{}) error { | ||
if v, ok := key.(string); ok { | ||
if f.client.Del([]byte(v)) { | ||
return nil | ||
} | ||
return fmt.Errorf("failed to delete key %v", key) | ||
} | ||
return errors.New("key type not supported by Freecache store") | ||
|
||
} | ||
|
||
func (f *FreecacheStore) Invalidate(options InvalidateOptions) error { | ||
if tags := options.TagsValue(); len(tags) > 0 { | ||
for _, tag := range tags { | ||
var tagKey = fmt.Sprintf(FreecacheTagPattern, tag) | ||
return f.Delete([]byte(tagKey)) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (f *FreecacheStore) Clear() error { | ||
f.client.Clear() | ||
return nil | ||
} | ||
|
||
func (f *FreecacheStore) GetType() string { | ||
return FreecacheType | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package store | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"testing" | ||
"time" | ||
|
||
mocksStore "github.com/eko/gocache/test/mocks/store/clients" | ||
"github.com/golang/mock/gomock" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestNewFreecache(t *testing.T) { | ||
// Given | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
client := mocksStore.NewMockFreecacheClientInterface(ctrl) | ||
options := &Options{ | ||
Expiration: 6 * time.Second, | ||
} | ||
|
||
// When | ||
store := NewFreecache(client, options) | ||
|
||
// Then | ||
assert.IsType(t, new(FreecacheStore), store) | ||
assert.Equal(t, client, store.client) | ||
assert.Equal(t, options, store.options) | ||
} | ||
|
||
func TestFreecacheGet(t *testing.T) { | ||
// Given | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
client := mocksStore.NewMockFreecacheClientInterface(ctrl) | ||
client.EXPECT().Get([]byte("key1")).Return([]byte("val1"), nil) | ||
client.EXPECT().Get([]byte("key2")).Return([]byte("val2"), nil) | ||
|
||
s := NewFreecache(client, nil) | ||
|
||
value, err := s.Get("key1") | ||
assert.Nil(t, err) | ||
assert.Equal(t, []byte("val1"), value) | ||
|
||
value, err = s.Get("key2") | ||
assert.Nil(t, err) | ||
assert.Equal(t, []byte("val2"), value) | ||
} | ||
|
||
func TestFreecacheGetNotFound(t *testing.T) { | ||
// Given | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
client := mocksStore.NewMockFreecacheClientInterface(ctrl) | ||
client.EXPECT().Get([]byte("key1")).Return(nil, errors.New("value not found in Freecache store")) | ||
|
||
s := NewFreecache(client, nil) | ||
|
||
value, err := s.Get("key1") | ||
assert.EqualError(t, err, "value not found in Freecache store") | ||
assert.Nil(t, value) | ||
} | ||
|
||
func TestFreecacheSet(t *testing.T) { | ||
// Given | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
cacheKey := "my-key" | ||
cacheValue := []byte("my-cache-value") | ||
options := &Options{ | ||
Expiration: 6 * time.Second, | ||
} | ||
|
||
client := mocksStore.NewMockFreecacheClientInterface(ctrl) | ||
client.EXPECT().Set([]byte(cacheKey), cacheValue, 6).Return(nil) | ||
|
||
s := NewFreecache(client, options) | ||
err := s.Set(cacheKey, cacheValue, options) | ||
assert.Nil(t, err) | ||
} | ||
|
||
func TestFreecacheSetInvalidValue(t *testing.T) { | ||
// Given | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
cacheKey := "my-key" | ||
cacheValue := "my-cache-value" | ||
options := &Options{ | ||
Expiration: 6 * time.Second, | ||
} | ||
expectedErr := errors.New("value type not supported by Freecache store") | ||
|
||
client := mocksStore.NewMockFreecacheClientInterface(ctrl) | ||
|
||
s := NewFreecache(client, options) | ||
err := s.Set(cacheKey, cacheValue, options) | ||
assert.Equal(t, expectedErr, err) | ||
} | ||
|
||
func TestFreecacheSetInvalidSize(t *testing.T) { | ||
// Given | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
cacheKey := "my-key" | ||
cacheValue := []byte("my-cache-value") | ||
options := &Options{ | ||
Expiration: 6 * time.Second, | ||
} | ||
expectedErr := fmt.Errorf("size of key: %v, value: %v, err: %v", cacheKey, cacheValue, errors.New("")) | ||
client := mocksStore.NewMockFreecacheClientInterface(ctrl) | ||
client.EXPECT().Set([]byte(cacheKey), cacheValue, 6).Return(expectedErr) | ||
|
||
s := NewFreecache(client, options) | ||
err := s.Set(cacheKey, cacheValue, options) | ||
assert.NotNil(t, err) | ||
|
||
} | ||
|
||
func TestFreecacheSetInvalidKey(t *testing.T) { | ||
// Given | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
cacheKey := 1 | ||
cacheValue := []byte("my-cache-value") | ||
options := &Options{ | ||
Expiration: 6 * time.Second, | ||
} | ||
|
||
expectedErr := errors.New("key type not supported by Freecache store") | ||
|
||
client := mocksStore.NewMockFreecacheClientInterface(ctrl) | ||
|
||
s := NewFreecache(client, options) | ||
err := s.Set(cacheKey, cacheValue, options) | ||
assert.Equal(t, expectedErr, err) | ||
} | ||
|
||
func TestFreecacheDelete(t *testing.T) { | ||
// Given | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
cacheKey := "key" | ||
|
||
client := mocksStore.NewMockFreecacheClientInterface(ctrl) | ||
client.EXPECT().Del(gomock.Any()).Return(true) | ||
|
||
s := NewFreecache(client, nil) | ||
err := s.Delete(cacheKey) | ||
assert.Nil(t, err) | ||
} | ||
|
||
func TestFreecacheDeleteFailed(t *testing.T) { | ||
// Given | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
cacheKey := "key" | ||
expectedErr := fmt.Errorf("failed to delete key %v", cacheKey) | ||
client := mocksStore.NewMockFreecacheClientInterface(ctrl) | ||
client.EXPECT().Del(gomock.Any()).Return(false) | ||
|
||
s := NewFreecache(client, nil) | ||
err := s.Delete(cacheKey) | ||
assert.Equal(t, expectedErr, err) | ||
} | ||
|
||
func TestFreecacheDeleteInvalidKey(t *testing.T) { | ||
// Given | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
cacheKey := 1 | ||
expectedErr := errors.New("key type not supported by Freecache store") | ||
client := mocksStore.NewMockFreecacheClientInterface(ctrl) | ||
|
||
s := NewFreecache(client, nil) | ||
err := s.Delete(cacheKey) | ||
assert.Equal(t, expectedErr, err) | ||
} |
Oops, something went wrong.