272 lines
6.2 KiB
Go
272 lines
6.2 KiB
Go
|
|
package centrifugo
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/alicebob/miniredis/v2"
|
||
|
|
"github.com/stretchr/testify/assert"
|
||
|
|
"github.com/stretchr/testify/require"
|
||
|
|
"github.com/zeromicro/go-zero/core/stores/redis"
|
||
|
|
)
|
||
|
|
|
||
|
|
func setupOnlineTestRedis(t *testing.T) (*redis.Redis, func()) {
|
||
|
|
mr, err := miniredis.Run()
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
rds, err := redis.NewRedis(redis.RedisConf{
|
||
|
|
Host: mr.Addr(),
|
||
|
|
Type: "node",
|
||
|
|
})
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
return rds, func() {
|
||
|
|
mr.Close()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestNewRedisOnlineStore(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
|
||
|
|
assert.NotNil(t, store)
|
||
|
|
assert.Equal(t, "online:", store.keyPrefix)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestNewRedisOnlineStoreWithPrefix(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStoreWithPrefix(rds, "custom:online:")
|
||
|
|
|
||
|
|
assert.NotNil(t, store)
|
||
|
|
assert.Equal(t, "custom:online:", store.keyPrefix)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSetOnline(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
ctx := context.Background()
|
||
|
|
|
||
|
|
userID := "user-123"
|
||
|
|
ttl := 5 * time.Minute
|
||
|
|
|
||
|
|
// 設置在線
|
||
|
|
err := store.SetOnline(ctx, userID, ttl)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
// 檢查是否在線
|
||
|
|
online, err := store.IsOnline(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.True(t, online)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSetOffline(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
ctx := context.Background()
|
||
|
|
|
||
|
|
userID := "user-123"
|
||
|
|
|
||
|
|
// 先設置在線
|
||
|
|
err := store.SetOnline(ctx, userID, 5*time.Minute)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
// 設置離線
|
||
|
|
err = store.SetOffline(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
// 檢查是否離線
|
||
|
|
online, err := store.IsOnline(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.False(t, online)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestIsOnline_NotOnline(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
ctx := context.Background()
|
||
|
|
|
||
|
|
// 未設置在線的用戶應該返回 false
|
||
|
|
online, err := store.IsOnline(ctx, "non-existent-user")
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.False(t, online)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetOnlineUsers(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
ctx := context.Background()
|
||
|
|
|
||
|
|
// 設置一些用戶在線
|
||
|
|
err := store.SetOnline(ctx, "user-1", 5*time.Minute)
|
||
|
|
require.NoError(t, err)
|
||
|
|
err = store.SetOnline(ctx, "user-3", 5*time.Minute)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
// 批量獲取在線狀態
|
||
|
|
userIDs := []string{"user-1", "user-2", "user-3"}
|
||
|
|
status, err := store.GetOnlineUsers(ctx, userIDs)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
assert.True(t, status["user-1"])
|
||
|
|
assert.False(t, status["user-2"])
|
||
|
|
assert.True(t, status["user-3"])
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetOnlineUsers_Empty(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
ctx := context.Background()
|
||
|
|
|
||
|
|
// 空列表應該返回空 map
|
||
|
|
status, err := store.GetOnlineUsers(ctx, []string{})
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Empty(t, status)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestIncrClient(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
ctx := context.Background()
|
||
|
|
|
||
|
|
userID := "user-123"
|
||
|
|
|
||
|
|
// 第一次增加
|
||
|
|
count, err := store.IncrClient(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, int64(1), count)
|
||
|
|
|
||
|
|
// 第二次增加
|
||
|
|
count, err = store.IncrClient(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, int64(2), count)
|
||
|
|
|
||
|
|
// 獲取連線數
|
||
|
|
count, err = store.GetClientCount(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, int64(2), count)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestDecrClient(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
ctx := context.Background()
|
||
|
|
|
||
|
|
userID := "user-123"
|
||
|
|
|
||
|
|
// 先增加到 2
|
||
|
|
_, err := store.IncrClient(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
_, err = store.IncrClient(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
// 減少一次
|
||
|
|
count, err := store.DecrClient(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, int64(1), count)
|
||
|
|
|
||
|
|
// 再減少一次
|
||
|
|
count, err = store.DecrClient(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, int64(0), count)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestDecrClient_NegativeProtection(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
ctx := context.Background()
|
||
|
|
|
||
|
|
userID := "user-123"
|
||
|
|
|
||
|
|
// 直接減少(沒有先增加)
|
||
|
|
count, err := store.DecrClient(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, int64(0), count) // 應該被保護為 0
|
||
|
|
|
||
|
|
// 再次減少
|
||
|
|
count, err = store.DecrClient(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, int64(0), count) // 仍然是 0
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestGetClientCount_NoClient(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
ctx := context.Background()
|
||
|
|
|
||
|
|
// 未設置連線數的用戶應該返回 0
|
||
|
|
count, err := store.GetClientCount(ctx, "non-existent-user")
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, int64(0), count)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSetOffline_ClearsClientCount(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
ctx := context.Background()
|
||
|
|
|
||
|
|
userID := "user-123"
|
||
|
|
|
||
|
|
// 設置在線和連線數
|
||
|
|
err := store.SetOnline(ctx, userID, 5*time.Minute)
|
||
|
|
require.NoError(t, err)
|
||
|
|
_, err = store.IncrClient(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
_, err = store.IncrClient(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
// 設置離線
|
||
|
|
err = store.SetOffline(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
// 連線數也應該被清除
|
||
|
|
count, err := store.GetClientCount(ctx, userID)
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Equal(t, int64(0), count)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestKeyGeneration_OnlineStore(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStoreWithPrefix(rds, "test:")
|
||
|
|
|
||
|
|
// 測試 key 生成
|
||
|
|
assert.Equal(t, "test:user-123", store.key("user-123"))
|
||
|
|
assert.Equal(t, "test:clients:user-456", store.clientCountKey("user-456"))
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestOnlineStore_ImplementsInterface(t *testing.T) {
|
||
|
|
rds, cleanup := setupOnlineTestRedis(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
store := NewRedisOnlineStore(rds)
|
||
|
|
|
||
|
|
// 確保 RedisOnlineStore 實現了 OnlineStore 介面
|
||
|
|
var _ OnlineStore = store
|
||
|
|
}
|