1
0
Fork 0
mirror of https://github.com/eosswedenorg/thalos synced 2026-06-19 04:50:02 +02:00

rename app folder to internal.

This commit is contained in:
Henrik Hautakoski 2024-02-14 13:00:33 +01:00
parent afb90af1db
commit 9974bfe3fd
28 changed files with 23 additions and 23 deletions

31
internal/cache/cache.go vendored Normal file
View file

@ -0,0 +1,31 @@
package cache
import (
"context"
"time"
)
type Cache struct {
store Store
prefix string
}
// Create a new cache
func NewCache(prefix string, store Store) *Cache {
return &Cache{
store: store,
prefix: prefix,
}
}
func (cache *Cache) Get(ctx context.Context, key string, value any) error {
return cache.store.Get(ctx, cache.key(key), value)
}
func (cache *Cache) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
return cache.store.Set(ctx, cache.key(key), value, ttl)
}
func (cache *Cache) key(key string) string {
return cache.prefix + "::" + key
}

61
internal/cache/memory_store.go vendored Normal file
View file

@ -0,0 +1,61 @@
package cache
import (
"context"
"fmt"
"reflect"
"time"
)
// Store time function in a variable.
// Makes it easy to travel in time when testing.
var now = time.Now
type memoryStoreItem struct {
// Actual value stored.
value any
// Cache expiration time.
expired time.Time
}
type MemoryStore struct {
data map[string]memoryStoreItem
}
func NewMemoryStore() *MemoryStore {
return &MemoryStore{make(map[string]memoryStoreItem)}
}
func (s *MemoryStore) Get(ctx context.Context, key string, value any) error {
if item, ok := s.data[key]; ok {
if item.expired.Before(now()) {
delete(s.data, key)
return fmt.Errorf("key: %s does not exist", key)
}
v := reflect.ValueOf(value)
if v.Kind() != reflect.Pointer {
return fmt.Errorf("value must be of pointer type, '%s' passed", v.Kind().String())
}
v.Elem().Set(reflect.ValueOf(item.value))
return nil
}
return fmt.Errorf("key: %s does not exist", key)
}
func (s *MemoryStore) Has(ctx context.Context, key string) bool {
_, hit := s.data[key]
return hit
}
func (s *MemoryStore) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
s.data[key] = memoryStoreItem{
value: value,
expired: now().Add(ttl),
}
return nil
}

88
internal/cache/memory_store_test.go vendored Normal file
View file

@ -0,0 +1,88 @@
package cache
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
type memoryTestItem struct {
String string
Num uint32
Float float32
}
func TestMemoryStore_Set(t *testing.T) {
now = func() time.Time { return time.Unix(1581315270, 0) }
item := memoryTestItem{
String: "MyString",
Num: 23,
Float: 3.14,
}
expected := map[string]memoryStoreItem{
"key1": {
value: item,
expired: now().Add(time.Hour),
},
}
store := NewMemoryStore()
err := store.Set(context.Background(), "key1", item, time.Hour)
assert.NoError(t, err)
assert.Equal(t, expected, store.data)
}
func TestMemoryStore_GetMiss(t *testing.T) {
store := NewMemoryStore()
var v any
err := store.Get(context.Background(), "Key2", &v)
assert.Error(t, err)
}
func TestMemoryStore_GetHit(t *testing.T) {
expected := memoryTestItem{
String: "MyString",
Num: 23,
Float: 3.14,
}
store := NewMemoryStore()
err := store.Set(context.Background(), "key1", expected, time.Hour)
assert.NoError(t, err)
var actual memoryTestItem
err = store.Get(context.Background(), "key1", &actual)
assert.NoError(t, err)
assert.Equal(t, expected, actual)
}
func TestMemoryStore_GetNonPointer(t *testing.T) {
expected := memoryTestItem{
String: "MyString",
Num: 23,
Float: 3.14,
}
store := NewMemoryStore()
err := store.Set(context.Background(), "key1", expected, time.Hour)
assert.NoError(t, err)
var actual string
err = store.Get(context.Background(), "key1", actual)
assert.EqualError(t, err, "value must be of pointer type, 'string' passed")
}
func TestMemoryStore_Has(t *testing.T) {
store := NewMemoryStore()
err := store.Set(context.Background(), "key1", "value", time.Hour)
assert.NoError(t, err)
assert.True(t, store.Has(context.Background(), "key1"))
assert.False(t, store.Has(context.Background(), "key2"))
}

35
internal/cache/redis_store.go vendored Normal file
View file

@ -0,0 +1,35 @@
package cache
import (
"context"
"time"
"github.com/go-redis/cache/v9"
)
type RedisStore struct {
c *cache.Cache
}
func NewRedisStore(options *cache.Options) *RedisStore {
return &RedisStore{
c: cache.New(options),
}
}
func (s *RedisStore) Get(ctx context.Context, key string, value interface{}) error {
return s.c.Get(ctx, key, value)
}
func (s *RedisStore) Has(ctx context.Context, key string) bool {
return s.c.Exists(ctx, key)
}
func (s *RedisStore) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
return s.c.Set(&cache.Item{
Ctx: ctx,
Key: key,
Value: value,
TTL: ttl,
})
}

104
internal/cache/redis_store_test.go vendored Normal file
View file

@ -0,0 +1,104 @@
package cache
import (
"context"
"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(context.Background(), "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(context.Background(), "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(context.Background(), "mykey2", expected, time.Second*20)
assert.NoError(t, err)
actual := testItem{}
err = store.Get(context.Background(), "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(context.Background(), "key1", "value", time.Minute*15)
assert.NoError(t, err)
assert.True(t, store.Has(context.Background(), "key1"))
assert.False(t, store.Has(context.Background(), "key2"))
assert.NoError(t, mock.ExpectationsWereMet())
}

19
internal/cache/store.go vendored Normal file
View file

@ -0,0 +1,19 @@
package cache
import (
"context"
"time"
)
// Interface to a cache storage.
type Store interface {
// Set an item in the store.
Set(ctx context.Context, key string, value any, TTL time.Duration) error
// Get an item from the store.
// returns an error if key is not found or there is other problems.
Get(ctx context.Context, key string, value any) error
// Check if a key exist in the store.
Has(ctx context.Context, key string) bool
}