backend/pkg/library/centrifugo/centrifugo_test.go

303 lines
7.2 KiB
Go
Raw Normal View History

2026-01-06 07:15:18 +00:00
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 setupServiceTestRedis(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 TestNewService(t *testing.T) {
rds, cleanup := setupServiceTestRedis(t)
defer cleanup()
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
Redis: rds,
})
assert.NotNil(t, svc)
assert.NotNil(t, svc.Client())
assert.NotNil(t, svc.Token())
assert.NotNil(t, svc.Blacklist())
assert.NotNil(t, svc.Online())
}
func TestNewService_WithoutRedis(t *testing.T) {
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
Redis: nil, // 沒有 Redis
})
assert.NotNil(t, svc)
assert.NotNil(t, svc.Client())
assert.NotNil(t, svc.Token())
assert.Nil(t, svc.Blacklist()) // 應該為 nil
assert.Nil(t, svc.Online()) // 應該為 nil
}
func TestNewService_DefaultValues(t *testing.T) {
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
})
assert.NotNil(t, svc)
// 檢查預設值已被應用(通過生成 Token 間接驗證)
token, err := svc.GenerateToken("user-123")
require.NoError(t, err)
assert.NotEmpty(t, token)
}
func TestNewService_WithCustomConfig(t *testing.T) {
rds, cleanup := setupServiceTestRedis(t)
defer cleanup()
customExpire := 24 * time.Hour
customTTL := 10 * time.Minute
customPrefix := "custom:"
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
TokenExpire: customExpire,
Redis: rds,
OnlineTTL: customTTL,
KeyPrefix: customPrefix,
})
assert.NotNil(t, svc)
}
func TestService_GenerateToken(t *testing.T) {
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
})
token, err := svc.GenerateToken("user-123")
require.NoError(t, err)
assert.NotEmpty(t, token)
}
func TestService_GenerateTokenWithInfo(t *testing.T) {
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
})
info := map[string]interface{}{
"name": "Daniel",
"avatar": "https://example.com/avatar.jpg",
}
token, err := svc.GenerateTokenWithInfo("user-123", info)
require.NoError(t, err)
assert.NotEmpty(t, token)
}
func TestService_IsUserOnline_WithoutRedis(t *testing.T) {
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
Redis: nil,
})
_, err := svc.IsUserOnline(context.Background(), "user-123")
assert.Error(t, err)
assert.ErrorIs(t, err, ErrOnlineStoreNotConfigured)
}
func TestService_GetUsersOnlineStatus_WithoutRedis(t *testing.T) {
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
Redis: nil,
})
_, err := svc.GetUsersOnlineStatus(context.Background(), []string{"user-1", "user-2"})
assert.Error(t, err)
assert.ErrorIs(t, err, ErrOnlineStoreNotConfigured)
}
func TestService_IsUserOnline_WithRedis(t *testing.T) {
rds, cleanup := setupServiceTestRedis(t)
defer cleanup()
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
Redis: rds,
})
ctx := context.Background()
userID := "user-123"
// 初始狀態應該是離線
online, err := svc.IsUserOnline(ctx, userID)
require.NoError(t, err)
assert.False(t, online)
// 處理連線事件
err = svc.Online().HandleConnect(ctx, userID)
require.NoError(t, err)
// 現在應該在線
online, err = svc.IsUserOnline(ctx, userID)
require.NoError(t, err)
assert.True(t, online)
// 處理斷線事件
err = svc.Online().HandleDisconnect(ctx, userID)
require.NoError(t, err)
// 現在應該離線
online, err = svc.IsUserOnline(ctx, userID)
require.NoError(t, err)
assert.False(t, online)
}
func TestService_GetUsersOnlineStatus_WithRedis(t *testing.T) {
rds, cleanup := setupServiceTestRedis(t)
defer cleanup()
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
Redis: rds,
})
ctx := context.Background()
// 設置一些用戶在線
err := svc.Online().HandleConnect(ctx, "user-1")
require.NoError(t, err)
err = svc.Online().HandleConnect(ctx, "user-3")
require.NoError(t, err)
// 批量獲取在線狀態
status, err := svc.GetUsersOnlineStatus(ctx, []string{"user-1", "user-2", "user-3"})
require.NoError(t, err)
assert.True(t, status["user-1"])
assert.False(t, status["user-2"])
assert.True(t, status["user-3"])
}
func TestService_Blacklist_Integration(t *testing.T) {
rds, cleanup := setupServiceTestRedis(t)
defer cleanup()
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
Redis: rds,
})
ctx := context.Background()
userID := "user-123"
// 撤銷用戶所有 Token
err := svc.Blacklist().RevokeUserTokens(ctx, userID)
require.NoError(t, err)
// 獲取版本
version, err := svc.Blacklist().GetUserTokenVersion(ctx, userID)
require.NoError(t, err)
assert.Greater(t, version, int64(0))
// 檢查舊版本無效
valid, err := svc.Blacklist().IsTokenVersionValid(ctx, userID, version-1)
require.NoError(t, err)
assert.False(t, valid)
// 檢查當前版本有效
valid, err = svc.Blacklist().IsTokenVersionValid(ctx, userID, version)
require.NoError(t, err)
assert.True(t, valid)
}
func TestService_MultipleConnections(t *testing.T) {
rds, cleanup := setupServiceTestRedis(t)
defer cleanup()
svc := NewService(ServiceConfig{
APIURL: "http://localhost:8000",
APIKey: "test-api-key",
TokenSecret: "test-secret",
Redis: rds,
})
ctx := context.Background()
userID := "user-123"
// 模擬多個設備連線
err := svc.Online().HandleConnect(ctx, userID)
require.NoError(t, err)
err = svc.Online().HandleConnect(ctx, userID)
require.NoError(t, err)
err = svc.Online().HandleConnect(ctx, userID)
require.NoError(t, err)
// 用戶應該在線
online, err := svc.IsUserOnline(ctx, userID)
require.NoError(t, err)
assert.True(t, online)
// 斷開一個設備
err = svc.Online().HandleDisconnect(ctx, userID)
require.NoError(t, err)
// 用戶仍然在線(還有 2 個連線)
online, err = svc.IsUserOnline(ctx, userID)
require.NoError(t, err)
assert.True(t, online)
// 斷開剩餘設備
err = svc.Online().HandleDisconnect(ctx, userID)
require.NoError(t, err)
err = svc.Online().HandleDisconnect(ctx, userID)
require.NoError(t, err)
// 用戶現在離線
online, err = svc.IsUserOnline(ctx, userID)
require.NoError(t, err)
assert.False(t, online)
}