package ratelimit import ( "context" "testing" "time" "github.com/alicebob/miniredis/v2" "github.com/redis/go-redis/v9" ) func TestRedisLimiterSharesOneChannelBudget(t *testing.T) { mr := miniredis.RunT(t) client := redis.NewClient(&redis.Options{Addr: mr.Addr()}) limiter := NewWithClient(client) first, err := limiter.Wait(context.Background(), "channel-a", 20) if err != nil { t.Fatalf("first wait: %v", err) } second, err := limiter.Wait(context.Background(), "channel-a", 20) if err != nil { t.Fatalf("second wait: %v", err) } if first != 0 { t.Fatalf("first delay = %v, want 0", first) } if second < 40*time.Millisecond { t.Fatalf("second delay = %v, want a shared channel delay", second) } } func TestRedisLimiterUsesIndependentChannelBudgets(t *testing.T) { mr := miniredis.RunT(t) client := redis.NewClient(&redis.Options{Addr: mr.Addr()}) limiter := NewWithClient(client) if _, err := limiter.Wait(context.Background(), "channel-a", 1); err != nil { t.Fatalf("channel a wait: %v", err) } delay, err := limiter.Wait(context.Background(), "channel-b", 1) if err != nil { t.Fatalf("channel b wait: %v", err) } if delay != 0 { t.Fatalf("channel b delay = %v, want 0", delay) } } func TestRedisLimiterConfiguredRateIsAuthoritativeUpperBound(t *testing.T) { mr := miniredis.RunT(t) client := redis.NewClient(&redis.Options{Addr: mr.Addr()}) limiter := NewWithClient(client) if err := limiter.Configure(context.Background(), "channel-a", 10); err != nil { t.Fatalf("configure: %v", err) } if _, err := limiter.Wait(context.Background(), "channel-a", 1000); err != nil { t.Fatalf("first wait: %v", err) } delay, err := limiter.Wait(context.Background(), "channel-a", 1000) if err != nil { t.Fatalf("second wait: %v", err) } if delay < 90*time.Millisecond { t.Fatalf("delay = %v, want configured 10 TPS ceiling", delay) } }