Skip to content

Commit

Permalink
Merge pull request #17 from eko/replace-mockery-with-gomock
Browse files Browse the repository at this point in the history
fix(tests): replace mockery by gomock and fix race conditions
  • Loading branch information
eko authored Dec 8, 2019
2 parents 1b6c810 + 3b46630 commit 99c1da9
Show file tree
Hide file tree
Showing 34 changed files with 1,667 additions and 1,237 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 99c1da9

Please sign in to comment.