| | 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())
|
| |
|
| |
|
| |
|
| |
|
| | }
|
| |
|