104 lines
3.4 KiB
Go
104 lines
3.4 KiB
Go
//go:build integration
|
|
|
|
package repository
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type RedeemCacheSuite struct {
|
|
IntegrationRedisSuite
|
|
cache *redeemCache
|
|
}
|
|
|
|
func (s *RedeemCacheSuite) SetupTest() {
|
|
s.IntegrationRedisSuite.SetupTest()
|
|
s.cache = NewRedeemCache(s.rdb).(*redeemCache)
|
|
}
|
|
|
|
func (s *RedeemCacheSuite) TestGetRedeemAttemptCount_Missing() {
|
|
missingUserID := int64(99999)
|
|
count, err := s.cache.GetRedeemAttemptCount(s.ctx, missingUserID)
|
|
require.NoError(s.T(), err, "expected nil error for missing rate-limit key")
|
|
require.Equal(s.T(), 0, count, "expected zero count for missing key")
|
|
}
|
|
|
|
func (s *RedeemCacheSuite) TestIncrementAndGetRedeemAttemptCount() {
|
|
userID := int64(1)
|
|
key := fmt.Sprintf("%s%d", redeemRateLimitKeyPrefix, userID)
|
|
|
|
require.NoError(s.T(), s.cache.IncrementRedeemAttemptCount(s.ctx, userID), "IncrementRedeemAttemptCount")
|
|
count, err := s.cache.GetRedeemAttemptCount(s.ctx, userID)
|
|
require.NoError(s.T(), err, "GetRedeemAttemptCount")
|
|
require.Equal(s.T(), 1, count, "count mismatch")
|
|
|
|
ttl, err := s.rdb.TTL(s.ctx, key).Result()
|
|
require.NoError(s.T(), err, "TTL")
|
|
s.AssertTTLWithin(ttl, 1*time.Second, redeemRateLimitDuration)
|
|
}
|
|
|
|
func (s *RedeemCacheSuite) TestMultipleIncrements() {
|
|
userID := int64(2)
|
|
|
|
require.NoError(s.T(), s.cache.IncrementRedeemAttemptCount(s.ctx, userID))
|
|
require.NoError(s.T(), s.cache.IncrementRedeemAttemptCount(s.ctx, userID))
|
|
require.NoError(s.T(), s.cache.IncrementRedeemAttemptCount(s.ctx, userID))
|
|
|
|
count, err := s.cache.GetRedeemAttemptCount(s.ctx, userID)
|
|
require.NoError(s.T(), err)
|
|
require.Equal(s.T(), 3, count, "count after 3 increments")
|
|
}
|
|
|
|
func (s *RedeemCacheSuite) TestAcquireAndReleaseRedeemLock() {
|
|
ok, err := s.cache.AcquireRedeemLock(s.ctx, "CODE", 10*time.Second)
|
|
require.NoError(s.T(), err, "AcquireRedeemLock")
|
|
require.True(s.T(), ok)
|
|
|
|
// Second acquire should fail
|
|
ok, err = s.cache.AcquireRedeemLock(s.ctx, "CODE", 10*time.Second)
|
|
require.NoError(s.T(), err, "AcquireRedeemLock 2")
|
|
require.False(s.T(), ok, "expected lock to be held")
|
|
|
|
// Release
|
|
require.NoError(s.T(), s.cache.ReleaseRedeemLock(s.ctx, "CODE"), "ReleaseRedeemLock")
|
|
|
|
// Now acquire should succeed
|
|
ok, err = s.cache.AcquireRedeemLock(s.ctx, "CODE", 10*time.Second)
|
|
require.NoError(s.T(), err, "AcquireRedeemLock after release")
|
|
require.True(s.T(), ok)
|
|
}
|
|
|
|
func (s *RedeemCacheSuite) TestAcquireRedeemLock_TTL() {
|
|
lockKey := redeemLockKeyPrefix + "CODE2"
|
|
lockTTL := 15 * time.Second
|
|
|
|
ok, err := s.cache.AcquireRedeemLock(s.ctx, "CODE2", lockTTL)
|
|
require.NoError(s.T(), err, "AcquireRedeemLock CODE2")
|
|
require.True(s.T(), ok)
|
|
|
|
ttl, err := s.rdb.TTL(s.ctx, lockKey).Result()
|
|
require.NoError(s.T(), err, "TTL lock key")
|
|
s.AssertTTLWithin(ttl, 1*time.Second, lockTTL)
|
|
}
|
|
|
|
func (s *RedeemCacheSuite) TestReleaseRedeemLock_Idempotent() {
|
|
// Release a lock that doesn't exist should not error
|
|
require.NoError(s.T(), s.cache.ReleaseRedeemLock(s.ctx, "NONEXISTENT"))
|
|
|
|
// Acquire, release, release again
|
|
ok, err := s.cache.AcquireRedeemLock(s.ctx, "IDEMPOTENT", 10*time.Second)
|
|
require.NoError(s.T(), err)
|
|
require.True(s.T(), ok)
|
|
require.NoError(s.T(), s.cache.ReleaseRedeemLock(s.ctx, "IDEMPOTENT"))
|
|
require.NoError(s.T(), s.cache.ReleaseRedeemLock(s.ctx, "IDEMPOTENT"), "second release should be idempotent")
|
|
}
|
|
|
|
func TestRedeemCacheSuite(t *testing.T) {
|
|
suite.Run(t, new(RedeemCacheSuite))
|
|
}
|