From c6cb26d543a7e42290efa51463daf1571e6213bb Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 31 Oct 2023 16:59:26 +0100 Subject: [PATCH] Adding app/cache/redis_store.go --- app/cache/redis_store.go | 37 ++++++++++++ app/cache/redis_store_test.go | 103 ++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 app/cache/redis_store.go create mode 100644 app/cache/redis_store_test.go diff --git a/app/cache/redis_store.go b/app/cache/redis_store.go new file mode 100644 index 0000000..5016a15 --- /dev/null +++ b/app/cache/redis_store.go @@ -0,0 +1,37 @@ +package cache + +import ( + "context" + "time" + + "github.com/go-redis/cache/v9" +) + +type RedisStore struct { + c *cache.Cache + ctx context.Context +} + +func NewRedisStore(options *cache.Options) *RedisStore { + return &RedisStore{ + c: cache.New(options), + ctx: context.Background(), + } +} + +func (s *RedisStore) Get(key string, value interface{}) error { + return s.c.Get(s.ctx, key, value) +} + +func (s *RedisStore) Has(key string) bool { + return s.c.Exists(s.ctx, key) +} + +func (s *RedisStore) Set(key string, value any, ttl time.Duration) error { + return s.c.Set(&cache.Item{ + Ctx: s.ctx, + Key: key, + Value: value, + TTL: ttl, + }) +} diff --git a/app/cache/redis_store_test.go b/app/cache/redis_store_test.go new file mode 100644 index 0000000..34e3f34 --- /dev/null +++ b/app/cache/redis_store_test.go @@ -0,0 +1,103 @@ +package cache + +import ( + "testing" + "time" + + "github.com/go-redis/redismock/v9" + + redis_cache "github.com/go-redis/cache/v9" + "github.com/stretchr/testify/assert" +) + +type testItem struct { + Num uint32 + Name string +} + +func TestRedisStore_Set(t *testing.T) { + client, mock := redismock.NewClientMock() + + store := NewRedisStore(&redis_cache.Options{ + Redis: client, + }) + + expected := testItem{ + Num: 24, + Name: "Some Name", + } + + bytes, err := store.c.Marshal(expected) + assert.NoError(t, err) + + mock.ExpectSet("mykey", bytes, time.Minute).SetVal("OK") + + err = store.Set("mykey", expected, time.Minute) + assert.NoError(t, err) + + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestRedisStore_GetMiss(t *testing.T) { + client, mock := redismock.NewClientMock() + + store := NewRedisStore(&redis_cache.Options{ + Redis: client, + }) + + mock.ExpectGet("mykey").SetErr(redis_cache.ErrCacheMiss) + + expected := testItem{} + err := store.Get("mykey", &expected) + assert.ErrorIs(t, err, redis_cache.ErrCacheMiss) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestRedisStore_GetHit(t *testing.T) { + client, mock := redismock.NewClientMock() + + store := NewRedisStore(&redis_cache.Options{ + Redis: client, + }) + + expected := testItem{ + Num: 42, + Name: "MyName", + } + + bytes, err := store.c.Marshal(expected) + assert.NoError(t, err) + + mock.ExpectSet("mykey2", bytes, time.Second*20).SetVal("OK") + mock.ExpectGet("mykey2").SetVal(string(bytes)) + + err = store.Set("mykey2", expected, time.Second*20) + assert.NoError(t, err) + + actual := testItem{} + err = store.Get("mykey2", &actual) + assert.NoError(t, err) + assert.Equal(t, expected, actual) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestRedisStore_Has(t *testing.T) { + client, mock := redismock.NewClientMock() + + store := NewRedisStore(&redis_cache.Options{ + Redis: client, + }) + + bytes, err := store.c.Marshal("value") + assert.NoError(t, err) + + mock.ExpectSet("key1", bytes, time.Minute*15).SetVal("OK") + mock.ExpectGet("key1").SetVal(string(bytes)) + mock.ExpectGet("key2").RedisNil() + + err = store.Set("key1", "value", time.Minute*15) + assert.NoError(t, err) + assert.True(t, store.Has("key1")) + assert.False(t, store.Has("key2")) + assert.NoError(t, mock.ExpectationsWereMet()) +}