Some checks failed
CI / test (push) Failing after 6s
CI / frontend (push) Failing after 4s
CI / golangci-lint (push) Failing after 4s
CI / windsurf-platform (macos-latest) (push) Has been cancelled
CI / windsurf-platform (windows-latest) (push) Has been cancelled
Security Scan / backend-security (push) Failing after 1m31s
Security Scan / frontend-security (push) Failing after 7s
165 lines
5.9 KiB
Go
165 lines
5.9 KiB
Go
//go:build integration
|
|
|
|
package repository
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
|
"github.com/redis/go-redis/v9"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type GatewayCacheSuite struct {
|
|
IntegrationRedisSuite
|
|
cache service.GatewayCache
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) SetupTest() {
|
|
s.IntegrationRedisSuite.SetupTest()
|
|
s.cache = NewGatewayCache(s.rdb)
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestGetSessionAccountID_Missing() {
|
|
_, err := s.cache.GetSessionAccountID(s.ctx, 1, "nonexistent")
|
|
require.True(s.T(), errors.Is(err, redis.Nil), "expected redis.Nil for missing session")
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestSetAndGetSessionAccountID() {
|
|
sessionID := "s1"
|
|
accountID := int64(99)
|
|
groupID := int64(1)
|
|
sessionTTL := 1 * time.Minute
|
|
|
|
require.NoError(s.T(), s.cache.SetSessionAccountID(s.ctx, groupID, sessionID, accountID, sessionTTL), "SetSessionAccountID")
|
|
|
|
sid, err := s.cache.GetSessionAccountID(s.ctx, groupID, sessionID)
|
|
require.NoError(s.T(), err, "GetSessionAccountID")
|
|
require.Equal(s.T(), accountID, sid, "session id mismatch")
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestSessionAccountID_TTL() {
|
|
sessionID := "s2"
|
|
accountID := int64(100)
|
|
groupID := int64(1)
|
|
sessionTTL := 1 * time.Minute
|
|
|
|
require.NoError(s.T(), s.cache.SetSessionAccountID(s.ctx, groupID, sessionID, accountID, sessionTTL), "SetSessionAccountID")
|
|
|
|
sessionKey := buildSessionKey(groupID, sessionID)
|
|
ttl, err := s.rdb.TTL(s.ctx, sessionKey).Result()
|
|
require.NoError(s.T(), err, "TTL sessionKey after Set")
|
|
s.AssertTTLWithin(ttl, 1*time.Second, sessionTTL)
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestRefreshSessionTTL() {
|
|
sessionID := "s3"
|
|
accountID := int64(101)
|
|
groupID := int64(1)
|
|
initialTTL := 1 * time.Minute
|
|
refreshTTL := 3 * time.Minute
|
|
|
|
require.NoError(s.T(), s.cache.SetSessionAccountID(s.ctx, groupID, sessionID, accountID, initialTTL), "SetSessionAccountID")
|
|
|
|
require.NoError(s.T(), s.cache.RefreshSessionTTL(s.ctx, groupID, sessionID, refreshTTL), "RefreshSessionTTL")
|
|
|
|
sessionKey := buildSessionKey(groupID, sessionID)
|
|
ttl, err := s.rdb.TTL(s.ctx, sessionKey).Result()
|
|
require.NoError(s.T(), err, "TTL after Refresh")
|
|
s.AssertTTLWithin(ttl, 1*time.Second, refreshTTL)
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestRefreshSessionTTL_MissingKey() {
|
|
// RefreshSessionTTL on a missing key should not error (no-op)
|
|
err := s.cache.RefreshSessionTTL(s.ctx, 1, "missing-session", 1*time.Minute)
|
|
require.NoError(s.T(), err, "RefreshSessionTTL on missing key should not error")
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestDeleteSessionAccountID() {
|
|
sessionID := "openai:s4"
|
|
accountID := int64(102)
|
|
groupID := int64(1)
|
|
sessionTTL := 1 * time.Minute
|
|
|
|
require.NoError(s.T(), s.cache.SetSessionAccountID(s.ctx, groupID, sessionID, accountID, sessionTTL), "SetSessionAccountID")
|
|
require.NoError(s.T(), s.cache.DeleteSessionAccountID(s.ctx, groupID, sessionID), "DeleteSessionAccountID")
|
|
|
|
_, err := s.cache.GetSessionAccountID(s.ctx, groupID, sessionID)
|
|
require.True(s.T(), errors.Is(err, redis.Nil), "expected redis.Nil after delete")
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestGetSessionAccountID_CorruptedValue() {
|
|
sessionID := "corrupted"
|
|
groupID := int64(1)
|
|
sessionKey := buildSessionKey(groupID, sessionID)
|
|
|
|
// Set a non-integer value
|
|
require.NoError(s.T(), s.rdb.Set(s.ctx, sessionKey, "not-a-number", 1*time.Minute).Err(), "Set invalid value")
|
|
|
|
_, err := s.cache.GetSessionAccountID(s.ctx, groupID, sessionID)
|
|
require.Error(s.T(), err, "expected error for corrupted value")
|
|
require.False(s.T(), errors.Is(err, redis.Nil), "expected parsing error, not redis.Nil")
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestGetCascadeID_Missing() {
|
|
id, err := s.cache.GetCascadeID(s.ctx, "nonexistent-cascade-key")
|
|
require.NoError(s.T(), err, "missing cascade key should return nil error")
|
|
require.Equal(s.T(), "", id, "missing cascade key should return empty string")
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestSetAndGetCascadeID() {
|
|
key := "ab12cd34"
|
|
cascadeID := "cascade-uuid-xyz"
|
|
ttl := 30 * time.Minute
|
|
|
|
require.NoError(s.T(), s.cache.SetCascadeID(s.ctx, key, cascadeID, ttl), "SetCascadeID")
|
|
|
|
got, err := s.cache.GetCascadeID(s.ctx, key)
|
|
require.NoError(s.T(), err, "GetCascadeID")
|
|
require.Equal(s.T(), cascadeID, got, "cascade id round-trip mismatch")
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestCascadeID_TTL() {
|
|
key := "ttl-key"
|
|
ttl := 30 * time.Minute
|
|
|
|
require.NoError(s.T(), s.cache.SetCascadeID(s.ctx, key, "cid", ttl), "SetCascadeID")
|
|
|
|
redisKey := buildCascadeKey(key)
|
|
got, err := s.rdb.TTL(s.ctx, redisKey).Result()
|
|
require.NoError(s.T(), err, "TTL after SetCascadeID")
|
|
s.AssertTTLWithin(got, 1*time.Second, ttl)
|
|
}
|
|
|
|
func (s *GatewayCacheSuite) TestDeleteCascadeID() {
|
|
key := "del-key"
|
|
require.NoError(s.T(), s.cache.SetCascadeID(s.ctx, key, "cid", 1*time.Minute), "SetCascadeID")
|
|
require.NoError(s.T(), s.cache.DeleteCascadeID(s.ctx, key), "DeleteCascadeID")
|
|
|
|
got, err := s.cache.GetCascadeID(s.ctx, key)
|
|
require.NoError(s.T(), err, "GetCascadeID after delete should not error")
|
|
require.Equal(s.T(), "", got, "deleted cascade key should return empty")
|
|
}
|
|
|
|
// 验证 cascade key 与 sticky session key 命名空间隔离(前缀不同)。
|
|
func (s *GatewayCacheSuite) TestCascadeID_NamespaceIsolation() {
|
|
commonID := "shared-id"
|
|
require.NoError(s.T(), s.cache.SetCascadeID(s.ctx, commonID, "cascade-value", 1*time.Minute), "SetCascadeID")
|
|
require.NoError(s.T(), s.cache.SetSessionAccountID(s.ctx, 1, commonID, 42, 1*time.Minute), "SetSessionAccountID")
|
|
|
|
gotCascade, err := s.cache.GetCascadeID(s.ctx, commonID)
|
|
require.NoError(s.T(), err, "GetCascadeID")
|
|
require.Equal(s.T(), "cascade-value", gotCascade, "cascade namespace must not be polluted by sticky session")
|
|
|
|
gotAccount, err := s.cache.GetSessionAccountID(s.ctx, 1, commonID)
|
|
require.NoError(s.T(), err, "GetSessionAccountID")
|
|
require.Equal(s.T(), int64(42), gotAccount, "sticky session must not be polluted by cascade write")
|
|
}
|
|
|
|
func TestGatewayCacheSuite(t *testing.T) {
|
|
suite.Run(t, new(GatewayCacheSuite))
|
|
}
|