package redis // Tests for Redis config and client behavior. import ( "context" "errors" "net" "strconv" "testing" "time" "github.com/alicebob/miniredis/v2" redisv9 "github.com/redis/go-redis/v9" "github.com/stretchr/testify/require" ) // TestDefaultConfig ensures default config behavior is handled correctly. func TestDefaultConfig(t *testing.T) { cfg := DefaultConfig() require.Equal(t, "localhost", cfg.Host) require.Equal(t, 6379, cfg.Port) } // TestAddr ensures addr behavior is handled correctly. func TestAddr(t *testing.T) { cfg := DefaultConfig() require.Equal(t, "localhost:6379", cfg.Addr()) } // newClientForMiniRedis creates in-memory test doubles and deterministic fixtures. func newClientForMiniRedis(t *testing.T) (*Client, *miniredis.Miniredis) { t.Helper() mr := miniredis.RunT(t) cfg := DefaultConfig() host, portStr, err := net.SplitHostPort(mr.Addr()) require.NoError(t, err) port, err := strconv.Atoi(portStr) require.NoError(t, err) cfg.Host = host cfg.Port = port client, err := NewClient(cfg) require.NoError(t, err) t.Cleanup(func() { _ = client.Close() }) return client, mr } // TestHealthCheck ensures health check behavior is handled correctly. func TestHealthCheck(t *testing.T) { client, _ := newClientForMiniRedis(t) require.NoError(t, client.HealthCheck(context.Background())) } // TestSetGet ensures set get behavior is handled correctly. func TestSetGet(t *testing.T) { client, _ := newClientForMiniRedis(t) ctx := context.Background() require.NoError(t, client.Set(ctx, "k", "v", 0)) got, err := client.Get(ctx, "k") require.NoError(t, err) require.Equal(t, "v", got) } // TestDeleteExists ensures delete exists behavior is handled correctly. func TestDeleteExists(t *testing.T) { client, _ := newClientForMiniRedis(t) ctx := context.Background() require.NoError(t, client.Set(ctx, "k", "v", 0)) exists, err := client.Exists(ctx, "k") require.NoError(t, err) require.True(t, exists) require.NoError(t, client.Delete(ctx, "k")) exists, err = client.Exists(ctx, "k") require.NoError(t, err) require.False(t, exists) } // TestIncr ensures incr behavior is handled correctly. func TestIncr(t *testing.T) { client, _ := newClientForMiniRedis(t) ctx := context.Background() n, err := client.Incr(ctx, "counter") require.NoError(t, err) require.Equal(t, int64(1), n) n, err = client.Incr(ctx, "counter") require.NoError(t, err) require.Equal(t, int64(2), n) } // TestExpire ensures expire behavior is handled correctly. func TestExpire(t *testing.T) { client, mr := newClientForMiniRedis(t) ctx := context.Background() require.NoError(t, client.Set(ctx, "temp", "value", 0)) require.NoError(t, client.Expire(ctx, "temp", time.Second)) mr.FastForward(2 * time.Second) _, err := client.Get(ctx, "temp") require.Error(t, err) require.True(t, errors.Is(err, redisv9.Nil)) } // TestGetNotFound ensures get not found behavior is handled correctly. func TestGetNotFound(t *testing.T) { client, _ := newClientForMiniRedis(t) _, err := client.Get(context.Background(), "missing") require.Error(t, err) require.True(t, errors.Is(err, redisv9.Nil)) } // TestConfigFromEnvDefaults ensures config from env defaults behavior is handled correctly. func TestConfigFromEnvDefaults(t *testing.T) { t.Setenv("REDIS_HOST", "") t.Setenv("REDIS_PORT", "") cfg := ConfigFromEnv() def := DefaultConfig() require.Equal(t, def.Host, cfg.Host) require.Equal(t, def.Port, cfg.Port) } // TestConfigFromEnvOverrides ensures config from env overrides behavior is handled correctly. func TestConfigFromEnvOverrides(t *testing.T) { t.Setenv("REDIS_HOST", "cache") t.Setenv("REDIS_PORT", "6380") t.Setenv("REDIS_PASSWORD", "secret") t.Setenv("REDIS_DB", "2") t.Setenv("REDIS_POOL_SIZE", "25") t.Setenv("REDIS_MIN_IDLE_CONNS", "3") t.Setenv("REDIS_DIAL_TIMEOUT", "2s") t.Setenv("REDIS_READ_TIMEOUT", "4s") t.Setenv("REDIS_WRITE_TIMEOUT", "5s") cfg := ConfigFromEnv() require.Equal(t, "cache", cfg.Host) require.Equal(t, 6380, cfg.Port) require.Equal(t, "secret", cfg.Password) require.Equal(t, 2, cfg.DB) require.Equal(t, 25, cfg.PoolSize) require.Equal(t, 3, cfg.MinIdleConns) require.Equal(t, 2*time.Second, cfg.DialTimeout) require.Equal(t, 4*time.Second, cfg.ReadTimeout) require.Equal(t, 5*time.Second, cfg.WriteTimeout) } // TestNewClientInvalidConfig ensures new client invalid config behavior is handled correctly. func TestNewClientInvalidConfig(t *testing.T) { cfg := DefaultConfig() cfg.Port = 0 _, err := NewClient(cfg) require.Error(t, err) }