Skip to content

Commit

Permalink
feat: Implement memcache adapter
Browse files Browse the repository at this point in the history
* test: Implement memcache test
  • Loading branch information
mehmetumit committed Nov 2, 2023
1 parent 7236bbd commit f4e175c
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 0 deletions.
67 changes: 67 additions & 0 deletions internal/adapters/memcache/memcache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package memcache

import (
"context"
"sync"
"time"

"github.com/google/uuid"
"github.com/mehmetumit/dexus/internal/core/ports"
)

type CacheMap map[string]string
type MemCache struct {
sync.RWMutex //Useful for concurrent nonblocking reads
logger ports.Logger
cacheMap CacheMap
}

func NewMemCache(l ports.Logger) *MemCache {
return &MemCache{
logger: l,
cacheMap: make(CacheMap),
}

}

func (mc *MemCache) expireAfter(key string, ttl time.Duration) {
time.AfterFunc(ttl, func() {
mc.Lock()
defer mc.Unlock()
delete(mc.cacheMap, key)
})
}
func (mc *MemCache) Get(ctx context.Context, key string) (string, error) {
mc.RLock()
defer mc.RUnlock()
val, ok := mc.cacheMap[key]
if !ok {
return "", ports.ErrKeyNotFound
}
return val, nil

}
func (mc *MemCache) Set(ctx context.Context, key string, val string, ttl time.Duration) error {
mc.Lock()
defer mc.Unlock()
mc.cacheMap[key] = val
mc.expireAfter(key, ttl)
return nil
}

func (mc *MemCache) Delete(ctx context.Context, key string) error {
mc.Lock()
defer mc.Unlock()
delete(mc.cacheMap, key)
return nil
}
func (mc *MemCache) GenKey(ctx context.Context, s string) (string, error) {
hashKey := uuid.NewSHA1(uuid.NameSpaceOID, []byte(s)).String()
return hashKey, nil
}
func (mc *MemCache) Flush(ctx context.Context) error {
mc.Lock()
defer mc.Unlock()
clear(mc.cacheMap)
return nil
}
118 changes: 118 additions & 0 deletions internal/adapters/memcache/memcache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package memcache

import (
"context"
"testing"
"time"

"github.com/mehmetumit/dexus/internal/core/ports"
"github.com/mehmetumit/dexus/internal/mocks"
)

func newTestMemCache(t testing.TB) *MemCache {
t.Helper()
mockLogger := mocks.NewMockLogger()
return NewMemCache(mockLogger)
}

func TestMemCache_GenKey_Set_Get_GetNotFound_Flush_ExpireAfter(t *testing.T) {
memCache := newTestMemCache(t)
ctx := context.Background()
cacheTTL := 5 * time.Second
cacheMap := map[string]string{
"a-test-path": "https://test-path.com",
"this/is/test": "https://this-is-test.com",
"test1234": "http://test1234.com",
}
//Hashed key : path key
var keyMap map[string]string = make(map[string]string)

t.Run("GenKey Set Get", func(t *testing.T) {
for k, v := range cacheMap {
hashKey, err := memCache.GenKey(ctx, k)
if err != nil {
t.Errorf("Expected error nil, got %v", err)
}
keyMap[hashKey] = k
memCache.Set(ctx, hashKey, v, cacheTTL)

Check failure on line 37 in internal/adapters/memcache/memcache_test.go

View workflow job for this annotation

GitHub Actions / golangci

Error return value of `memCache.Set` is not checked (errcheck)
}
for k, v := range keyMap {
val, err := memCache.Get(ctx, k)
if err != nil {
t.Errorf("Expected error nil, got %v", err)
}
if val != cacheMap[v] {
t.Errorf("Expected redirection url %v, got %v", cacheMap[v], val)
}
}
})

t.Run("Get Not Found", func(t *testing.T) {

notFoundKeys := []string{
"not-found",
"1234/not/found",
"",
" ",
}
for _, nfk := range notFoundKeys {
_, err := memCache.Get(ctx, nfk)
if err != ports.ErrKeyNotFound {
t.Errorf("Expected error %v, got %v", ports.ErrKeyNotFound, err)
}
}
})
t.Run("Flush", func(t *testing.T) {

err := memCache.Flush(ctx)
if err != nil {
t.Errorf("Expected error nil, got %v", err)
}
for k := range keyMap {
_, err := memCache.Get(ctx, k)
if err != ports.ErrKeyNotFound {
t.Errorf("Expected error %v, got %v", ports.ErrKeyNotFound, err)
}

}
})
t.Run("Delete", func(t *testing.T) {
for k, v := range cacheMap {
hashKey, err := memCache.GenKey(ctx, k)
if err != nil {
t.Errorf("Expected error nil, got %v", err)
}
memCache.Set(ctx, hashKey, v, cacheTTL)

Check failure on line 85 in internal/adapters/memcache/memcache_test.go

View workflow job for this annotation

GitHub Actions / golangci

Error return value of `memCache.Set` is not checked (errcheck)
}
for k := range keyMap {
err := memCache.Delete(ctx, k)
if err != nil {
t.Errorf("Expected error nil, got %v", err)
}
_, err = memCache.Get(ctx, k)
if err != ports.ErrKeyNotFound {
t.Errorf("Expected error %v, got %v", ports.ErrKeyNotFound, err)
}
}

})
t.Run("Expire After", func(t *testing.T) {
ttl := 500 * time.Millisecond
for k, v := range cacheMap {
hashKey, err := memCache.GenKey(ctx, k)
if err != nil {
t.Errorf("Expected error nil, got %v", err)
}
memCache.Set(ctx, hashKey, v, ttl)

Check failure on line 106 in internal/adapters/memcache/memcache_test.go

View workflow job for this annotation

GitHub Actions / golangci

Error return value of `memCache.Set` is not checked (errcheck)
}

time.Sleep(ttl * 2)
for k := range keyMap {
_, err := memCache.Get(ctx, k)
if err != ports.ErrKeyNotFound {
t.Errorf("Expected error %v, got %v", ports.ErrKeyNotFound, err)
}
}
})

}

0 comments on commit f4e175c

Please sign in to comment.