134 lines
3.0 KiB
Go
134 lines
3.0 KiB
Go
package redispkg
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestDisabledIsSafe(t *testing.T) {
|
|
r := New("")
|
|
ctx := context.Background()
|
|
if _, ok := r.Get(ctx, "x"); ok {
|
|
t.Fatal("disabled Get must miss")
|
|
}
|
|
r.Set(ctx, "x", "y", time.Second) // 不 panic
|
|
if n := r.IncrWindow(ctx, "k", time.Second); n != 1 {
|
|
t.Fatal("disabled IncrWindow must allow")
|
|
}
|
|
un, ok := r.Lock(ctx, "lk", time.Second)
|
|
if !ok {
|
|
t.Fatal("disabled Lock must always acquire")
|
|
}
|
|
un()
|
|
// ScanLock disabled mode.
|
|
un2, ok2 := r.ScanLock(ctx, "slk", time.Second)
|
|
if !ok2 {
|
|
t.Fatal("disabled ScanLock must always acquire")
|
|
}
|
|
un2()
|
|
}
|
|
|
|
func TestDeadRedisLockFailsOpen(t *testing.T) {
|
|
r := New("redis://127.0.0.1:16399") // 死端口
|
|
un, ok := r.Lock(context.Background(), "lk", time.Second)
|
|
if !ok {
|
|
t.Fatal("Lock on redis error must fail open (spec §9)")
|
|
}
|
|
un()
|
|
}
|
|
|
|
func TestDeadRedisScanLockFailsOpen(t *testing.T) {
|
|
r := New("redis://127.0.0.1:16399")
|
|
un, ok := r.ScanLock(context.Background(), "slk", time.Second)
|
|
if !ok {
|
|
t.Fatal("ScanLock on redis error must fail open")
|
|
}
|
|
un()
|
|
}
|
|
|
|
func newLiveRedis(t *testing.T) *R {
|
|
t.Helper()
|
|
url := os.Getenv("REDIS_URL")
|
|
if url == "" {
|
|
t.Skip("REDIS_URL not set")
|
|
}
|
|
return New(url)
|
|
}
|
|
|
|
func TestIncrWindowSetsTTL(t *testing.T) {
|
|
r := newLiveRedis(t)
|
|
ctx := context.Background()
|
|
key := "test:incrwindow:ttl:" + t.Name()
|
|
r.c.Del(ctx, key)
|
|
|
|
n := r.IncrWindow(ctx, key, 5*time.Second)
|
|
if n != 1 {
|
|
t.Fatalf("first call = %d, want 1", n)
|
|
}
|
|
ttl, err := r.c.TTL(ctx, key).Result()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if ttl <= 0 {
|
|
t.Fatalf("TTL should be positive after first increment, got %v", ttl)
|
|
}
|
|
|
|
// Second call: n=2, TTL should still be positive.
|
|
n = r.IncrWindow(ctx, key, 5*time.Second)
|
|
if n != 2 {
|
|
t.Fatalf("second call = %d, want 2", n)
|
|
}
|
|
r.c.Del(ctx, key)
|
|
}
|
|
|
|
func TestScanLockRenewal(t *testing.T) {
|
|
r := newLiveRedis(t)
|
|
ctx := context.Background()
|
|
key := "test:scanlock:" + t.Name()
|
|
r.c.Del(ctx, key)
|
|
|
|
unlock, ok := r.ScanLock(ctx, key, 2*time.Second)
|
|
if !ok {
|
|
t.Fatal("should acquire")
|
|
}
|
|
// Wait 3 seconds — without renewal, the lock would expire at 2s.
|
|
time.Sleep(3 * time.Second)
|
|
|
|
// A second attempt should fail (lock still held by first, renewed).
|
|
_, ok2 := r.ScanLock(ctx, key, 2*time.Second)
|
|
if ok2 {
|
|
t.Fatal("second acquire should fail — lock should have been renewed")
|
|
}
|
|
unlock()
|
|
r.c.Del(ctx, key)
|
|
}
|
|
|
|
func TestScanLockMutualExclusion(t *testing.T) {
|
|
r := newLiveRedis(t)
|
|
ctx := context.Background()
|
|
key := "test:scanlock:mutex:" + t.Name()
|
|
r.c.Del(ctx, key)
|
|
|
|
unlock1, ok1 := r.ScanLock(ctx, key, 10*time.Second)
|
|
if !ok1 {
|
|
t.Fatal("first should acquire")
|
|
}
|
|
|
|
_, ok2 := r.ScanLock(ctx, key, 10*time.Second)
|
|
if ok2 {
|
|
t.Fatal("second should not acquire while first holds")
|
|
}
|
|
|
|
unlock1()
|
|
// After unlock, a new acquire should succeed.
|
|
time.Sleep(50 * time.Millisecond)
|
|
unlock3, ok3 := r.ScanLock(ctx, key, 10*time.Second)
|
|
if !ok3 {
|
|
t.Fatal("should acquire after unlock")
|
|
}
|
|
unlock3()
|
|
r.c.Del(ctx, key)
|
|
}
|