package store import ( "context" "testing" "time" "github.com/alicebob/miniredis/v2" "github.com/redis/go-redis/v9" ) func rdb(t *testing.T) *redis.Client { t.Helper() mr := miniredis.RunT(t) return redis.NewClient(&redis.Options{Addr: mr.Addr()}) } func TestAllowSessionWindow(t *testing.T) { r := rdb(t) ctx := context.Background() now := time.Now() // 30 次放行,第 31 次拒绝 for i := 0; i < 30; i++ { ok, err := AllowSession(ctx, r, "dev1", now) if err != nil || !ok { t.Fatalf("session %d should pass: ok=%v err=%v", i+1, ok, err) } } if ok, _ := AllowSession(ctx, r, "dev1", now); ok { t.Fatal("31st session should be rejected") } // 其他设备不受影响 if ok, _ := AllowSession(ctx, r, "dev2", now); !ok { t.Fatal("other device should pass") } } func TestAllowAudioSecondsWindow(t *testing.T) { r := rdb(t) ctx := context.Background() now := time.Now() // 1700s 放行 if ok, _ := AllowAudioSeconds(ctx, r, "dev1", 1700, now); !ok { t.Fatal("1700s should pass") } // 再 100s(累计 1800)放行 if ok, _ := AllowAudioSeconds(ctx, r, "dev1", 100, now.Add(time.Second)); !ok { t.Fatal("cumulative 1800s should pass") } // 再 1s 超限拒绝 if ok, _ := AllowAudioSeconds(ctx, r, "dev1", 1, now.Add(2*time.Second)); ok { t.Fatal("1801s should be rejected") } } func TestDeviceSlot(t *testing.T) { r := rdb(t) ctx := context.Background() ok, err := AcquireDeviceSlot(ctx, r, "dev1", "s1") if err != nil || !ok { t.Fatalf("first acquire should pass: %v", err) } if ok, _ := AcquireDeviceSlot(ctx, r, "dev1", "s2"); ok { t.Fatal("second concurrent acquire should fail") } // 非持有者释放无效 if err := ReleaseDeviceSlot(ctx, r, "dev1", "s2"); err != nil { t.Fatal(err) } if ok, _ := AcquireDeviceSlot(ctx, r, "dev1", "s3"); ok { t.Fatal("slot should still be held by s1") } // 持有者释放后可再获取 if err := ReleaseDeviceSlot(ctx, r, "dev1", "s1"); err != nil { t.Fatal(err) } if ok, _ := AcquireDeviceSlot(ctx, r, "dev1", "s3"); !ok { t.Fatal("acquire after release should pass") } }