Files
2026-05-30 12:29:45 +03:30

274 lines
6.8 KiB
Go

package authorization
import (
"context"
"testing"
"time"
"tm/pkg/logger"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
// MockRedisClient is a mock implementation of the Redis client interface
type MockRedisClient struct {
mock.Mock
}
func (m *MockRedisClient) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
args := m.Called(ctx, key, value, expiration)
return args.Error(0)
}
func (m *MockRedisClient) Get(ctx context.Context, key string) (string, error) {
args := m.Called(ctx, key)
return args.String(0), args.Error(1)
}
func (m *MockRedisClient) IncrWithExpire(ctx context.Context, key string, expiration time.Duration) (int64, error) {
args := m.Called(ctx, key, expiration)
return args.Get(0).(int64), args.Error(1)
}
func (m *MockRedisClient) Del(ctx context.Context, keys ...string) error {
args := m.Called(ctx, keys)
return args.Error(0)
}
func (m *MockRedisClient) Exists(ctx context.Context, keys ...string) (int64, error) {
args := m.Called(ctx, keys)
return args.Get(0).(int64), args.Error(1)
}
func (m *MockRedisClient) Close() error {
args := m.Called()
return args.Error(0)
}
// MockLogger is a mock implementation of the logger interface
type MockLogger struct {
mock.Mock
}
func (m *MockLogger) Debug(msg string, fields map[string]interface{}) {
m.Called(msg, fields)
}
func (m *MockLogger) Info(msg string, fields map[string]interface{}) {
m.Called(msg, fields)
}
func (m *MockLogger) Warn(msg string, fields map[string]interface{}) {
m.Called(msg, fields)
}
func (m *MockLogger) Error(msg string, fields map[string]interface{}) {
m.Called(msg, fields)
}
func (m *MockLogger) Fatal(msg string, fields map[string]interface{}) {
m.Called(msg, fields)
}
func (m *MockLogger) Sync() error {
args := m.Called()
return args.Error(0)
}
func (m *MockLogger) WithFields(fields map[string]interface{}) logger.Logger {
args := m.Called(fields)
return args.Get(0).(logger.Logger)
}
func TestGenerateTokenHash(t *testing.T) {
service := &Service{}
token1 := "test-token-1"
token2 := "test-token-2"
hash1 := service.generateTokenHash(token1)
hash2 := service.generateTokenHash(token2)
// Hash should be consistent for the same input
assert.Equal(t, service.generateTokenHash(token1), hash1)
// Different tokens should have different hashes
assert.NotEqual(t, hash1, hash2)
// Hash should be a valid hex string
assert.Len(t, hash1, 64) // SHA-256 produces 32 bytes = 64 hex chars
}
func TestInvalidateToken_WithRedis(t *testing.T) {
mockRedis := new(MockRedisClient)
mockLogger := new(MockLogger)
config := &AuthorizationConfig{
AccessTokenSecret: "test-secret",
RefreshTokenSecret: "test-refresh-secret",
AccessTokenExpiry: 15 * time.Minute,
RefreshTokenExpiry: 7 * 24 * time.Hour,
Issuer: "test",
Audience: "test-api",
}
service := &Service{
config: config,
logger: mockLogger,
redis: mockRedis,
}
// Generate a valid token first
token, err := service.GenerateAccessToken("user123", "test@example.com", "user", "company123")
assert.NoError(t, err)
// Mock Redis Set call
mockRedis.On("Set", mock.Anything, mock.Anything, "1", mock.Anything).Return(nil)
// Mock logger calls
mockLogger.On("Info", mock.Anything, mock.Anything).Return()
// Test invalidation
err = service.InvalidateToken(token)
assert.NoError(t, err)
// Verify Redis was called
mockRedis.AssertExpectations(t)
}
func TestInvalidateToken_WithoutRedis(t *testing.T) {
mockLogger := new(MockLogger)
config := &AuthorizationConfig{
AccessTokenSecret: "test-secret",
RefreshTokenSecret: "test-refresh-secret",
AccessTokenExpiry: 15 * time.Minute,
RefreshTokenExpiry: 7 * 24 * time.Hour,
Issuer: "test",
Audience: "test-api",
}
service := &Service{
config: config,
logger: mockLogger,
redis: nil, // No Redis client
}
// Mock logger calls
mockLogger.On("Warn", mock.Anything, mock.Anything).Return()
// Test invalidation without Redis
err := service.InvalidateToken("test-token")
assert.NoError(t, err)
// Verify warning was logged
mockLogger.AssertExpectations(t)
}
func TestIsTokenBlacklisted_WithRedis(t *testing.T) {
mockRedis := new(MockRedisClient)
mockLogger := new(MockLogger)
config := &AuthorizationConfig{
AccessTokenSecret: "test-secret",
RefreshTokenSecret: "test-refresh-secret",
AccessTokenExpiry: 15 * time.Minute,
RefreshTokenExpiry: 7 * 24 * time.Hour,
Issuer: "test",
Audience: "test-api",
}
service := &Service{
config: config,
logger: mockLogger,
redis: mockRedis,
}
token := "test-token"
tokenHash := service.generateTokenHash(token)
// Test case 1: Token is blacklisted
mockRedis.On("Exists", mock.Anything, []string{"blacklist:" + tokenHash}).Return(int64(1), nil).Once()
mockLogger.On("Debug", mock.Anything, mock.Anything).Return()
isBlacklisted, err := service.IsTokenBlacklisted(token)
assert.NoError(t, err)
assert.True(t, isBlacklisted)
// Test case 2: Token is not blacklisted
mockRedis.On("Exists", mock.Anything, []string{"blacklist:" + tokenHash}).Return(int64(0), nil).Once()
mockLogger.On("Debug", mock.Anything, mock.Anything).Return()
isBlacklisted, err = service.IsTokenBlacklisted(token)
assert.NoError(t, err)
assert.False(t, isBlacklisted)
mockRedis.AssertExpectations(t)
}
func TestIsTokenBlacklisted_WithoutRedis(t *testing.T) {
mockLogger := new(MockLogger)
config := &AuthorizationConfig{
AccessTokenSecret: "test-secret",
RefreshTokenSecret: "test-refresh-secret",
AccessTokenExpiry: 15 * time.Minute,
RefreshTokenExpiry: 7 * 24 * time.Hour,
Issuer: "test",
Audience: "test-api",
}
service := &Service{
config: config,
logger: mockLogger,
redis: nil, // No Redis client
}
// Test blacklist check without Redis
isBlacklisted, err := service.IsTokenBlacklisted("test-token")
assert.NoError(t, err)
assert.False(t, isBlacklisted)
}
func TestClearExpiredTokens(t *testing.T) {
mockRedis := new(MockRedisClient)
mockLogger := new(MockLogger)
service := &Service{
logger: mockLogger,
redis: mockRedis,
}
// Mock logger call
mockLogger.On("Info", mock.Anything, mock.Anything).Return()
// Test clearing expired tokens
err := service.ClearExpiredTokens()
assert.NoError(t, err)
mockLogger.AssertExpectations(t)
}
func TestGetBlacklistStats(t *testing.T) {
mockRedis := new(MockRedisClient)
mockLogger := new(MockLogger)
service := &Service{
logger: mockLogger,
redis: mockRedis,
}
ctx := context.Background()
// Test with Redis available
stats, err := service.GetBlacklistStats(ctx)
assert.NoError(t, err)
assert.Equal(t, true, stats["redis_available"])
// Test without Redis
service.redis = nil
stats, err = service.GetBlacklistStats(ctx)
assert.NoError(t, err)
assert.Equal(t, false, stats["redis_available"])
}