| package xcache
|
|
|
| import (
|
| "context"
|
| "testing"
|
| "time"
|
|
|
| "github.com/alicebob/miniredis/v2"
|
| "github.com/eko/gocache/lib/v4/store"
|
| "github.com/redis/go-redis/v9"
|
| "github.com/stretchr/testify/require"
|
|
|
| gocache "github.com/patrickmn/go-cache"
|
|
|
| "github.com/looplj/axonhub/internal/pkg/xredis"
|
| )
|
|
|
| func intPtr(i int) *int {
|
| return &i
|
| }
|
|
|
| func TestNewMemory(t *testing.T) {
|
| client := gocache.New(5*time.Minute, 10*time.Minute)
|
| cache := NewMemory[string](client)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "test-key", "test-value")
|
| require.NoError(t, err)
|
|
|
| value, err := cache.Get(ctx, "test-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "test-value", value)
|
|
|
|
|
| require.Equal(t, "cache", cache.GetType())
|
| }
|
|
|
| func TestNewMemoryWithOptions(t *testing.T) {
|
| cache := NewMemoryWithOptions[int](5*time.Minute, 10*time.Minute)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "number", 42)
|
| require.NoError(t, err)
|
|
|
| value, err := cache.Get(ctx, "number")
|
| require.NoError(t, err)
|
| require.Equal(t, 42, value)
|
| }
|
|
|
| func TestNewRedis(t *testing.T) {
|
|
|
| mr := miniredis.RunT(t)
|
| defer mr.Close()
|
|
|
|
|
| client := redis.NewClient(&redis.Options{
|
| Addr: mr.Addr(),
|
| })
|
|
|
| cache := NewRedis[string](client)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "redis-key", "redis-value")
|
| require.NoError(t, err)
|
|
|
| value, err := cache.Get(ctx, "redis-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "redis-value", value)
|
|
|
|
|
| require.Equal(t, "cache", cache.GetType())
|
| }
|
|
|
| func TestNewRedisWithOptions(t *testing.T) {
|
|
|
| mr := miniredis.RunT(t)
|
| defer mr.Close()
|
|
|
| opts := &redis.Options{
|
| Addr: mr.Addr(),
|
| }
|
|
|
| cache := NewRedisWithOptions[string](opts)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "redis-string", "test-value")
|
| require.NoError(t, err)
|
|
|
| value, err := cache.Get(ctx, "redis-string")
|
| require.NoError(t, err)
|
| require.Equal(t, "test-value", value)
|
| }
|
|
|
| func TestNewTwoLevel(t *testing.T) {
|
|
|
| memClient := gocache.New(5*time.Minute, 10*time.Minute)
|
| memCache := NewMemory[string](memClient)
|
|
|
|
|
| mr := miniredis.RunT(t)
|
| defer mr.Close()
|
|
|
| redisClient := redis.NewClient(&redis.Options{
|
| Addr: mr.Addr(),
|
| })
|
| redisCache := NewRedis[string](redisClient)
|
|
|
|
|
| cache := NewTwoLevel[string](memCache, redisCache)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "two-level-key", "two-level-value")
|
| require.NoError(t, err)
|
|
|
|
|
| value, err := cache.Get(ctx, "two-level-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "two-level-value", value)
|
|
|
|
|
| err = memCache.Clear(ctx)
|
| require.NoError(t, err)
|
|
|
|
|
| value, err = cache.Get(ctx, "two-level-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "two-level-value", value)
|
|
|
|
|
| require.Equal(t, "chain", cache.GetType())
|
| }
|
|
|
| func TestNewTwoLevelWithClients(t *testing.T) {
|
|
|
| mr := miniredis.RunT(t)
|
| defer mr.Close()
|
|
|
| memClient := gocache.New(5*time.Minute, 10*time.Minute)
|
| redisClient := redis.NewClient(&redis.Options{
|
| Addr: mr.Addr(),
|
| })
|
|
|
| cache := NewTwoLevelWithClients[string](
|
| memClient,
|
| redisClient,
|
| []store.Option{},
|
| []store.Option{},
|
| )
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "client-key", "client-value")
|
| require.NoError(t, err)
|
|
|
| value, err := cache.Get(ctx, "client-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "client-value", value)
|
| }
|
|
|
| func TestNewFromConfig_Memory(t *testing.T) {
|
| cfg := Config{
|
| Mode: ModeMemory,
|
| Memory: MemoryConfig{
|
| Expiration: 5 * time.Minute,
|
| CleanupInterval: 10 * time.Minute,
|
| },
|
| }
|
|
|
| cache := NewFromConfig[string](cfg)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "memory-config-key", "memory-config-value")
|
| require.NoError(t, err)
|
|
|
| value, err := cache.Get(ctx, "memory-config-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "memory-config-value", value)
|
|
|
|
|
| require.Equal(t, "cache", cache.GetType())
|
| }
|
|
|
| func TestNewFromConfig_Redis(t *testing.T) {
|
|
|
| mr := miniredis.RunT(t)
|
| defer mr.Close()
|
|
|
| cfg := Config{
|
| Mode: ModeRedis,
|
| Redis: xredis.Config{
|
| Addr: mr.Addr(),
|
| Expiration: 5 * time.Minute,
|
| },
|
| }
|
|
|
| cache := NewFromConfig[string](cfg)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "redis-config-key", "redis-config-value")
|
| require.NoError(t, err)
|
|
|
| value, err := cache.Get(ctx, "redis-config-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "redis-config-value", value)
|
|
|
|
|
| require.Equal(t, "cache", cache.GetType())
|
| }
|
|
|
| func TestNewFromConfig_RedisWithoutAddr(t *testing.T) {
|
| cfg := Config{
|
| Mode: ModeRedis,
|
|
|
| }
|
|
|
| require.Panics(t, func() {
|
| _ = NewFromConfig[string](cfg)
|
| })
|
| }
|
|
|
| func TestNewFromConfig_TwoLevel(t *testing.T) {
|
|
|
| mr := miniredis.RunT(t)
|
| defer mr.Close()
|
|
|
| cfg := Config{
|
| Mode: ModeTwoLevel,
|
| Memory: MemoryConfig{
|
| Expiration: 5 * time.Minute,
|
| CleanupInterval: 10 * time.Minute,
|
| },
|
| Redis: xredis.Config{
|
| Addr: mr.Addr(),
|
| Expiration: 15 * time.Minute,
|
| },
|
| }
|
|
|
| cache := NewFromConfig[string](cfg)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "two-level-config-key", "two-level-config-value")
|
| require.NoError(t, err)
|
|
|
| value, err := cache.Get(ctx, "two-level-config-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "two-level-config-value", value)
|
|
|
|
|
| require.Equal(t, "chain", cache.GetType())
|
| }
|
|
|
| func TestNewFromConfig_TwoLevelWithoutRedis(t *testing.T) {
|
| cfg := Config{
|
| Mode: ModeTwoLevel,
|
| Memory: MemoryConfig{
|
| Expiration: 5 * time.Minute,
|
| CleanupInterval: 10 * time.Minute,
|
| },
|
|
|
| }
|
|
|
| cache := NewFromConfig[string](cfg)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "two-level-fallback-key", "two-level-fallback-value")
|
| require.NoError(t, err)
|
|
|
| value, err := cache.Get(ctx, "two-level-fallback-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "two-level-fallback-value", value)
|
|
|
|
|
| require.Equal(t, "cache", cache.GetType())
|
| }
|
|
|
| func TestNewFromConfig_EmptyMode(t *testing.T) {
|
| cfg := Config{}
|
|
|
| cache := NewFromConfig[string](cfg)
|
|
|
|
|
| require.Equal(t, "noop", cache.GetType())
|
|
|
| ctx := context.Background()
|
| _, err := cache.Get(ctx, "test")
|
| require.Error(t, err)
|
| require.ErrorIs(t, err, ErrCacheNotConfigured)
|
| }
|
|
|
| func TestNewFromConfig_InvalidMode(t *testing.T) {
|
| cfg := Config{
|
| Mode: "invalid-mode",
|
| }
|
|
|
| cache := NewFromConfig[string](cfg)
|
|
|
|
|
| require.Equal(t, "noop", cache.GetType())
|
|
|
| ctx := context.Background()
|
| _, err := cache.Get(ctx, "test")
|
| require.Error(t, err)
|
| require.ErrorIs(t, err, ErrCacheNotConfigured)
|
| }
|
|
|
| func TestCacheWithExpiration(t *testing.T) {
|
|
|
| mr := miniredis.RunT(t)
|
| defer mr.Close()
|
|
|
| cfg := Config{
|
| Mode: ModeRedis,
|
| Redis: xredis.Config{
|
| Addr: mr.Addr(),
|
| Expiration: 100 * time.Millisecond,
|
| },
|
| }
|
|
|
| cache := NewFromConfig[string](cfg)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "expiring-key", "expiring-value")
|
| require.NoError(t, err)
|
|
|
|
|
| value, err := cache.Get(ctx, "expiring-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "expiring-value", value)
|
|
|
|
|
| time.Sleep(150 * time.Millisecond)
|
|
|
|
|
| _, err = cache.Get(ctx, "expiring-key")
|
|
|
| }
|
|
|
| func TestCacheOperations(t *testing.T) {
|
|
|
| mr := miniredis.RunT(t)
|
| defer mr.Close()
|
|
|
| cfg := Config{
|
| Mode: ModeRedis,
|
| Redis: xredis.Config{
|
| Addr: mr.Addr(),
|
| },
|
| }
|
|
|
| cache := NewFromConfig[string](cfg)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "key1", "value1")
|
| require.NoError(t, err)
|
|
|
| err = cache.Set(ctx, "key2", "value2")
|
| require.NoError(t, err)
|
|
|
|
|
| value1, err := cache.Get(ctx, "key1")
|
| require.NoError(t, err)
|
| require.Equal(t, "value1", value1)
|
|
|
| value2, err := cache.Get(ctx, "key2")
|
| require.NoError(t, err)
|
| require.Equal(t, "value2", value2)
|
|
|
|
|
| err = cache.Delete(ctx, "key1")
|
| require.NoError(t, err)
|
|
|
|
|
| _, err = cache.Get(ctx, "key1")
|
| require.Error(t, err)
|
|
|
|
|
| value2, err = cache.Get(ctx, "key2")
|
| require.NoError(t, err)
|
| require.Equal(t, "value2", value2)
|
|
|
|
|
| err = cache.Clear(ctx)
|
| require.NoError(t, err)
|
|
|
|
|
| _, err = cache.Get(ctx, "key2")
|
| require.Error(t, err)
|
| }
|
|
|
| func TestDefaultIfZero(t *testing.T) {
|
|
|
| result := defaultIfZero(0, 5*time.Minute)
|
| require.Equal(t, 5*time.Minute, result)
|
|
|
|
|
| result = defaultIfZero(10*time.Minute, 5*time.Minute)
|
| require.Equal(t, 10*time.Minute, result)
|
| }
|
|
|
| func TestComplexDataTypes(t *testing.T) {
|
| type TestStruct struct {
|
| ID int `json:"id"`
|
| Name string `json:"name"`
|
| }
|
|
|
|
|
| mr := miniredis.RunT(t)
|
| defer mr.Close()
|
|
|
| cfg := Config{
|
| Mode: ModeMemory,
|
| }
|
|
|
| cache := NewFromConfig[TestStruct](cfg)
|
|
|
| ctx := context.Background()
|
|
|
| testData := TestStruct{
|
| ID: 123,
|
| Name: "Test Name",
|
| }
|
|
|
|
|
| err := cache.Set(ctx, "struct-key", testData)
|
| require.NoError(t, err)
|
|
|
| retrievedData, err := cache.Get(ctx, "struct-key")
|
| require.NoError(t, err)
|
| require.Equal(t, testData, retrievedData)
|
| }
|
|
|
| func TestSeparateExpirationConfig(t *testing.T) {
|
|
|
| mr := miniredis.RunT(t)
|
| defer mr.Close()
|
|
|
| cfg := Config{
|
| Mode: ModeTwoLevel,
|
| Memory: MemoryConfig{
|
| Expiration: 100 * time.Millisecond,
|
| CleanupInterval: 10 * time.Minute,
|
| },
|
| Redis: xredis.Config{
|
| Addr: mr.Addr(),
|
| Expiration: 5 * time.Minute,
|
| },
|
| }
|
|
|
| cache := NewFromConfig[string](cfg)
|
|
|
| ctx := context.Background()
|
|
|
|
|
| err := cache.Set(ctx, "separate-exp-key", "separate-exp-value")
|
| require.NoError(t, err)
|
|
|
|
|
| value, err := cache.Get(ctx, "separate-exp-key")
|
| require.NoError(t, err)
|
| require.Equal(t, "separate-exp-value", value)
|
|
|
|
|
| require.Equal(t, "chain", cache.GetType())
|
|
|
|
|
|
|
|
|
| }
|
|
|