feat: implemented poke api client with cache
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package pokecache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type cacheEntry struct {
|
||||
val []byte
|
||||
createdAt time.Time
|
||||
}
|
||||
|
||||
type Cache struct {
|
||||
entries map[string]cacheEntry
|
||||
lock sync.Mutex
|
||||
done chan struct{}
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func NewCache(interval time.Duration) *Cache {
|
||||
c := Cache{
|
||||
entries: map[string]cacheEntry{},
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
go c.runReapLoop(interval)
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c *Cache) runReapLoop(interval time.Duration) {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
c.lock.Lock()
|
||||
|
||||
for k, v := range c.entries {
|
||||
if time.Since(v.createdAt) > interval {
|
||||
delete(c.entries, k)
|
||||
}
|
||||
}
|
||||
|
||||
c.lock.Unlock()
|
||||
case <-c.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) Add(key string, value []byte) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.entries[key] = cacheEntry{
|
||||
val: value,
|
||||
createdAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) Get(key string) ([]byte, bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
v, ok := c.entries[key]
|
||||
|
||||
return v.val, ok
|
||||
}
|
||||
|
||||
func (c *Cache) Stop() {
|
||||
c.once.Do(func() {
|
||||
close(c.done)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package pokecache_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/StevanFreeborn/pokedexcli/internal/pokecache"
|
||||
)
|
||||
|
||||
func TestNewCache(t *testing.T) {
|
||||
t.Run("it should return new initialized cache", func(t *testing.T) {
|
||||
cache := pokecache.NewCache(5 * time.Second)
|
||||
defer cache.Stop()
|
||||
|
||||
if cache == nil {
|
||||
t.Errorf("expected result to be non nil value")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("it should start reaping loop", func(t *testing.T) {
|
||||
interval := 1 * time.Second
|
||||
wait := interval + 1
|
||||
key := "key"
|
||||
bytes := []byte("Hello World")
|
||||
|
||||
cache := pokecache.NewCache(1 * time.Second)
|
||||
defer cache.Stop()
|
||||
|
||||
cache.Add(key, bytes)
|
||||
|
||||
time.Sleep(wait)
|
||||
|
||||
_, found := cache.Get(key)
|
||||
|
||||
if found {
|
||||
t.Errorf("expected entry for key %s to have been reaped", key)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
t.Run("it should add entry to cache and allow retrieving entry", func(t *testing.T) {
|
||||
key := "key"
|
||||
bytes := []byte("Hello World")
|
||||
|
||||
cache := pokecache.NewCache(5 * time.Second)
|
||||
defer cache.Stop()
|
||||
|
||||
cache.Add(key, bytes)
|
||||
|
||||
result, found := cache.Get(key)
|
||||
|
||||
if !found {
|
||||
t.Errorf("expected to find entry for key %s", key)
|
||||
}
|
||||
|
||||
expected := string(bytes)
|
||||
strResult := string(result)
|
||||
|
||||
if strResult != expected {
|
||||
t.Errorf("expected to find value %s for key %s but received %s", expected, key, strResult)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user