Files
storage/mockstorage/mockstorage.go
2025-06-25 14:13:21 +03:00

195 lines
4.4 KiB
Go

package mockstorage
import (
"context"
"errors"
"sync"
"time"
)
// Config defines the config for mock storage.
type Config struct {
CustomFuncs *CustomFuncs
}
// Storage is the mock storage adapter.
type Storage struct {
mu sync.RWMutex
data map[string]Entry
custom *CustomFuncs
}
// Entry struct to hold value and expiration time.
type Entry struct {
Value []byte
Exp time.Time
}
// CustomFuncs allows injecting custom behaviors for testing.
type CustomFuncs struct {
GetFunc func(key string) ([]byte, error)
GetWithContext func(ctx context.Context, key string) ([]byte, error)
SetFunc func(key string, val []byte, exp time.Duration) error
SetWithContext func(ctx context.Context, key string, val []byte, exp time.Duration) error
DeleteFunc func(key string) error
DeleteWithContext func(ctx context.Context, key string) error
ResetFunc func() error
ResetWithContext func(ctx context.Context) error
CloseFunc func() error
ConnFunc func() map[string]Entry
KeysFunc func() ([][]byte, error)
}
// New creates a new mock storage with optional configuration.
func New(config ...Config) *Storage {
s := &Storage{
data: make(map[string]Entry),
custom: &CustomFuncs{}, // default no-op
}
if len(config) > 0 && config[0].CustomFuncs != nil {
s.custom = config[0].CustomFuncs
}
return s
}
// Get retrieves the value for a given key.
func (s *Storage) Get(key string) ([]byte, error) {
if s.custom.GetFunc != nil {
return s.custom.GetFunc(key)
}
s.mu.RLock()
defer s.mu.RUnlock()
e, ok := s.data[key]
if !ok {
return nil, errors.New("key not found")
}
if !e.Exp.IsZero() && time.Now().After(e.Exp) {
delete(s.data, key)
return nil, errors.New("key expired")
}
return e.Value, nil
}
// GetWithContext retrieves value by key using a context (functional or fallback)
func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) {
if s.custom.GetWithContext != nil {
return s.custom.GetWithContext(ctx, key)
}
return s.Get(key)
}
// Set sets the value for a given key with expiration.
func (s *Storage) Set(key string, val []byte, exp time.Duration) error {
if s.custom.SetFunc != nil {
return s.custom.SetFunc(key, val, exp)
}
s.mu.Lock()
defer s.mu.Unlock()
var expTime time.Time
if exp > 0 {
expTime = time.Now().Add(exp)
}
s.data[key] = Entry{Value: val, Exp: expTime}
return nil
}
// SetWithContext sets value using context.
func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error {
if s.custom.SetWithContext != nil {
return s.custom.SetWithContext(ctx, key, val, exp)
}
return s.Set(key, val, exp)
}
// Delete removes a key from the storage.
func (s *Storage) Delete(key string) error {
if s.custom.DeleteFunc != nil {
return s.custom.DeleteFunc(key)
}
s.mu.Lock()
defer s.mu.Unlock()
delete(s.data, key)
return nil
}
// DeleteWithContext deletes key using context.
func (s *Storage) DeleteWithContext(ctx context.Context, key string) error {
if s.custom.DeleteWithContext != nil {
return s.custom.DeleteWithContext(ctx, key)
}
return s.Delete(key)
}
// Reset clears all keys.
func (s *Storage) Reset() error {
if s.custom.ResetFunc != nil {
return s.custom.ResetFunc()
}
s.mu.Lock()
defer s.mu.Unlock()
s.data = make(map[string]Entry)
return nil
}
// ResetWithContext resets storage using context.
func (s *Storage) ResetWithContext(ctx context.Context) error {
if s.custom.ResetWithContext != nil {
return s.custom.ResetWithContext(ctx)
}
return s.Reset()
}
// Close closes the mock storage (no-op).
func (s *Storage) Close() error {
if s.custom.CloseFunc != nil {
return s.custom.CloseFunc()
}
return nil
}
// Conn returns internal map.
func (s *Storage) Conn() map[string]Entry {
if s.custom.ConnFunc != nil {
return s.custom.ConnFunc()
}
s.mu.RLock()
defer s.mu.RUnlock()
copyData := make(map[string]Entry)
for k, v := range s.data {
copyData[k] = Entry{Value: v.Value, Exp: v.Exp}
}
return copyData
}
// Keys returns all keys.
func (s *Storage) Keys() ([][]byte, error) {
if s.custom.KeysFunc != nil {
return s.custom.KeysFunc()
}
s.mu.RLock()
defer s.mu.RUnlock()
keys := make([][]byte, 0, len(s.data))
for k := range s.data {
keys = append(keys, []byte(k))
}
return keys, nil
}
// SetCustomFuncs allows runtime injection of function implementations.
func (s *Storage) SetCustomFuncs(custom *CustomFuncs) {
s.custom = custom
}