Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tests): replace mockery by gomock and fix race conditions #17

Merged
merged 1 commit into from Dec 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -16,7 +16,7 @@ install:
- go get -t -v ./...

script:
- go test -cover -coverprofile=coverage.txt -covermode=atomic -v ./...
- go test -race -cover -coverprofile=coverage.txt -covermode=atomic -v ./...

after_success:
- bash <(curl -s https://codecov.io/bash)
18 changes: 8 additions & 10 deletions Makefile
@@ -1,13 +1,11 @@
.PHONY: mocks

mocks:
# mocks
mockery -case=snake -name=CacheInterface -dir=cache/ -output test/mocks/cache/
mockery -case=snake -name=CodecInterface -dir=codec/ -output test/mocks/codec/
mockery -case=snake -name=SetterCacheInterface -dir=cache/ -output test/mocks/cache/
mockery -case=snake -name=MetricsInterface -dir=metrics/ -output test/mocks/metrics/
mockery -case=snake -name=StoreInterface -dir=store/ -output test/mocks/store/
mockery -case=snake -name=BigcacheClientInterface -dir=store/ -output test/mocks/store/clients/
mockery -case=snake -name=MemcacheClientInterface -dir=store/ -output test/mocks/store/clients/
mockery -case=snake -name=RedisClientInterface -dir=store/ -output test/mocks/store/clients/
mockery -case=snake -name=RistrettoClientInterface -dir=store/ -output test/mocks/store/clients/
mockgen -source=cache/interface.go -destination=test/mocks/cache/cache_interface.go -package=mocks
mockgen -source=codec/interface.go -destination=test/mocks/codec/codec_interface.go -package=mocks
mockgen -source=metrics/interface.go -destination=test/mocks/metrics/metrics_interface.go -package=mocks
mockgen -source=store/interface.go -destination=test/mocks/store/store_interface.go -package=mocks
mockgen -source=store/bigcache.go -destination=test/mocks/store/clients/bigcache_interface.go -package=mocks
mockgen -source=store/memcache.go -destination=test/mocks/store/clients/memcache_interface.go -package=mocks
mockgen -source=store/redis.go -destination=test/mocks/store/clients/redis_interface.go -package=mocks
mockgen -source=store/ristretto.go -destination=test/mocks/store/clients/ristretto_interface.go -package=mocks
88 changes: 63 additions & 25 deletions cache/cache_test.go
Expand Up @@ -8,13 +8,17 @@ import (
"github.com/eko/gocache/codec"
"github.com/eko/gocache/store"
mocksStore "github.com/eko/gocache/test/mocks/store"
"github.com/golang/mock/gomock"

"github.com/stretchr/testify/assert"
)

func TestNew(t *testing.T) {
// Given
store := &mocksStore.StoreInterface{}
ctrl := gomock.NewController(t)
defer ctrl.Finish()

store := mocksStore.NewMockStoreInterface(ctrl)

// When
cache := New(store)
Expand All @@ -28,6 +32,9 @@ func TestNew(t *testing.T) {

func TestCacheSet(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

options := &store.Options{
Expiration: 5 * time.Second,
}
Expand All @@ -38,9 +45,8 @@ func TestCacheSet(t *testing.T) {
Hello: "world",
}

store := &mocksStore.StoreInterface{}
store.On("Set", "9b1ac8a6e8ca8ca9477c0a252eb37756", value, options).
Return(nil)
store := mocksStore.NewMockStoreInterface(ctrl)
store.EXPECT().Set("9b1ac8a6e8ca8ca9477c0a252eb37756", value, options).Return(nil)

cache := New(store)

Expand All @@ -51,6 +57,9 @@ func TestCacheSet(t *testing.T) {

func TestCacheSetWhenErrorOccurs(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

options := &store.Options{
Expiration: 5 * time.Second,
}
Expand All @@ -63,9 +72,8 @@ func TestCacheSetWhenErrorOccurs(t *testing.T) {

storeErr := errors.New("An error has occurred while inserting data into store")

store := &mocksStore.StoreInterface{}
store.On("Set", "9b1ac8a6e8ca8ca9477c0a252eb37756", value, options).
Return(storeErr)
store := mocksStore.NewMockStoreInterface(ctrl)
store.EXPECT().Set("9b1ac8a6e8ca8ca9477c0a252eb37756", value, options).Return(storeErr)

cache := New(store)

Expand All @@ -76,14 +84,17 @@ func TestCacheSetWhenErrorOccurs(t *testing.T) {

func TestCacheGet(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

cacheValue := &struct {
Hello string
}{
Hello: "world",
}

store := &mocksStore.StoreInterface{}
store.On("Get", "9b1ac8a6e8ca8ca9477c0a252eb37756").Return(cacheValue, nil)
store := mocksStore.NewMockStoreInterface(ctrl)
store.EXPECT().Get("9b1ac8a6e8ca8ca9477c0a252eb37756").Return(cacheValue, nil)

cache := New(store)

Expand All @@ -97,10 +108,13 @@ func TestCacheGet(t *testing.T) {

func TestCacheGetWhenNotFound(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

returnedErr := errors.New("Unable to find item in store")

store := &mocksStore.StoreInterface{}
store.On("Get", "9b1ac8a6e8ca8ca9477c0a252eb37756").Return(nil, returnedErr)
store := mocksStore.NewMockStoreInterface(ctrl)
store.EXPECT().Get("9b1ac8a6e8ca8ca9477c0a252eb37756").Return(nil, returnedErr)

cache := New(store)

Expand All @@ -114,7 +128,10 @@ func TestCacheGetWhenNotFound(t *testing.T) {

func TestCacheGetCodec(t *testing.T) {
// Given
store := &mocksStore.StoreInterface{}
ctrl := gomock.NewController(t)
defer ctrl.Finish()

store := mocksStore.NewMockStoreInterface(ctrl)

cache := New(store)

Expand All @@ -128,7 +145,10 @@ func TestCacheGetCodec(t *testing.T) {

func TestCacheGetType(t *testing.T) {
// Given
store := &mocksStore.StoreInterface{}
ctrl := gomock.NewController(t)
defer ctrl.Finish()

store := mocksStore.NewMockStoreInterface(ctrl)

cache := New(store)

Expand All @@ -138,8 +158,11 @@ func TestCacheGetType(t *testing.T) {

func TestCacheDelete(t *testing.T) {
// Given
store := &mocksStore.StoreInterface{}
store.On("Delete", "9b1ac8a6e8ca8ca9477c0a252eb37756").Return(nil)
ctrl := gomock.NewController(t)
defer ctrl.Finish()

store := mocksStore.NewMockStoreInterface(ctrl)
store.EXPECT().Delete("9b1ac8a6e8ca8ca9477c0a252eb37756").Return(nil)

cache := New(store)

Expand All @@ -152,12 +175,15 @@ func TestCacheDelete(t *testing.T) {

func TestCacheInvalidate(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

options := store.InvalidateOptions{
Tags: []string{"tag1"},
}

store := &mocksStore.StoreInterface{}
store.On("Invalidate", options).Return(nil)
store := mocksStore.NewMockStoreInterface(ctrl)
store.EXPECT().Invalidate(options).Return(nil)

cache := New(store)

Expand All @@ -170,14 +196,17 @@ func TestCacheInvalidate(t *testing.T) {

func TestCacheInvalidateWhenError(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

options := store.InvalidateOptions{
Tags: []string{"tag1"},
}

expectedErr := errors.New("Unexpected error during invalidation")

store := &mocksStore.StoreInterface{}
store.On("Invalidate", options).Return(expectedErr)
store := mocksStore.NewMockStoreInterface(ctrl)
store.EXPECT().Invalidate(options).Return(expectedErr)

cache := New(store)

Expand All @@ -190,8 +219,11 @@ func TestCacheInvalidateWhenError(t *testing.T) {

func TestCacheClear(t *testing.T) {
// Given
store := &mocksStore.StoreInterface{}
store.On("Clear").Return(nil)
ctrl := gomock.NewController(t)
defer ctrl.Finish()

store := mocksStore.NewMockStoreInterface(ctrl)
store.EXPECT().Clear().Return(nil)

cache := New(store)

Expand All @@ -204,10 +236,13 @@ func TestCacheClear(t *testing.T) {

func TestCacheClearWhenError(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

expectedErr := errors.New("Unexpected error during invalidation")

store := &mocksStore.StoreInterface{}
store.On("Clear").Return(expectedErr)
store := mocksStore.NewMockStoreInterface(ctrl)
store.EXPECT().Clear().Return(expectedErr)

cache := New(store)

Expand All @@ -220,10 +255,13 @@ func TestCacheClearWhenError(t *testing.T) {

func TestCacheDeleteWhenError(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

expectedErr := errors.New("Unable to delete key")

store := &mocksStore.StoreInterface{}
store.On("Delete", "9b1ac8a6e8ca8ca9477c0a252eb37756").Return(expectedErr)
store := mocksStore.NewMockStoreInterface(ctrl)
store.EXPECT().Delete("9b1ac8a6e8ca8ca9477c0a252eb37756").Return(expectedErr)

cache := New(store)

Expand Down
44 changes: 29 additions & 15 deletions cache/chain.go
Expand Up @@ -12,15 +12,40 @@ const (
ChainType = "chain"
)

type chainKeyValue struct {
key interface{}
value interface{}
storeType *string
}

// ChainCache represents the configuration needed by a cache aggregator
type ChainCache struct {
caches []SetterCacheInterface
caches []SetterCacheInterface
setChannel chan *chainKeyValue
}

// NewChain instanciates a new cache aggregator
func NewChain(caches ...SetterCacheInterface) *ChainCache {
return &ChainCache{
caches: caches,
chain := &ChainCache{
caches: caches,
setChannel: make(chan *chainKeyValue, 10000),
}

go chain.setter()

return chain
}

// setter sets a value in available caches, until a given cache layer
func (c *ChainCache) setter() {
for item := range c.setChannel {
for _, cache := range c.caches {
if item.storeType != nil && *item.storeType == cache.GetCodec().GetStore().GetType() {
break
}

cache.Set(item.key, item.value, nil)
}
}
}

Expand All @@ -34,7 +59,7 @@ func (c *ChainCache) Get(key interface{}) (interface{}, error) {
object, err = cache.Get(key)
if err == nil {
// Set the value back until this cache layer
go c.setUntil(key, object, &storeType)
c.setChannel <- &chainKeyValue{key, object, &storeType}
return object, nil
}

Expand Down Expand Up @@ -84,17 +109,6 @@ func (c *ChainCache) Clear() error {
return nil
}

// setUntil sets a value in available caches, eventually until a given cache layer
func (c *ChainCache) setUntil(key, object interface{}, until *string) {
for _, cache := range c.caches {
if until != nil && *until == cache.GetCodec().GetStore().GetType() {
break
}

cache.Set(key, object, nil)
}
}

// GetCaches returns all Chaind caches
func (c *ChainCache) GetCaches() []SetterCacheInterface {
return c.caches
Expand Down