package test import ( "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "github.com/kms/api-key-service/internal/auth" "github.com/kms/api-key-service/internal/config" "github.com/kms/api-key-service/internal/domain" ) func TestJWTManager_GenerateToken(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read", "write"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(1 * time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, Claims: map[string]string{ "department": "engineering", "role": "developer", }, } tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) assert.NotEmpty(t, tokenString) // Verify token structure (should have 3 parts separated by dots) parts := len(tokenString) assert.Greater(t, parts, 100) // JWT tokens are typically longer than 100 chars } func TestJWTManager_ValidateToken(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read", "write"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(1 * time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, Claims: map[string]string{ "department": "engineering", }, } // Generate token tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Validate token claims, err := jwtManager.ValidateToken(tokenString) require.NoError(t, err) assert.Equal(t, userToken.UserID, claims.UserID) assert.Equal(t, userToken.AppID, claims.AppID) assert.Equal(t, userToken.Permissions, claims.Permissions) assert.Equal(t, userToken.TokenType, claims.TokenType) assert.Equal(t, userToken.Claims, claims.Claims) } func TestJWTManager_ValidateToken_InvalidToken(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) // Test with invalid token _, err := jwtManager.ValidateToken("invalid.token.here") assert.Error(t, err) assert.Contains(t, err.Error(), "Invalid token") } func TestJWTManager_ValidateToken_ExpiredToken(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read"}, IssuedAt: time.Now().Add(-2 * time.Hour), ExpiresAt: time.Now().Add(-1 * time.Hour), // Expired 1 hour ago MaxValidAt: time.Now().Add(-30 * time.Minute), // Max valid also expired TokenType: domain.TokenTypeUser, } // Generate token (this should work even with past dates) tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Validate token (this should fail due to expiration) _, err = jwtManager.ValidateToken(tokenString) assert.Error(t, err) // The error could be either JWT expiration or our custom max valid check assert.True(t, strings.Contains(err.Error(), "expired beyond maximum validity") || strings.Contains(err.Error(), "token is expired"), "Expected expiration error, got: %s", err.Error()) } func TestJWTManager_RefreshToken(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read", "write"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(1 * time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } // Generate original token originalToken, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Refresh token with new expiration newExpiration := time.Now().Add(2 * time.Hour) refreshedToken, err := jwtManager.RefreshToken(originalToken, newExpiration) require.NoError(t, err) assert.NotEmpty(t, refreshedToken) assert.NotEqual(t, originalToken, refreshedToken) // Validate refreshed token claims, err := jwtManager.ValidateToken(refreshedToken) require.NoError(t, err) assert.Equal(t, userToken.UserID, claims.UserID) assert.Equal(t, userToken.AppID, claims.AppID) } func TestJWTManager_RefreshToken_ExpiredMaxValid(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read"}, IssuedAt: time.Now().Add(-2 * time.Hour), ExpiresAt: time.Now().Add(1 * time.Hour), MaxValidAt: time.Now().Add(-30 * time.Minute), // Max valid expired TokenType: domain.TokenTypeUser, } // Generate token tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Try to refresh (should fail due to max valid expiration) newExpiration := time.Now().Add(2 * time.Hour) _, err = jwtManager.RefreshToken(tokenString, newExpiration) assert.Error(t, err) assert.Contains(t, err.Error(), "expired beyond maximum validity") } func TestJWTManager_ExtractClaims(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read", "write"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(-1 * time.Hour), // Expired token MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } // Generate expired token tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Extract claims (should work even for expired tokens) claims, err := jwtManager.ExtractClaims(tokenString) require.NoError(t, err) assert.Equal(t, userToken.UserID, claims.UserID) assert.Equal(t, userToken.AppID, claims.AppID) assert.Equal(t, userToken.Permissions, claims.Permissions) } func TestJWTManager_RevokeToken(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(1 * time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } // Generate token tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Revoke token err = jwtManager.RevokeToken(tokenString) assert.NoError(t, err) // Check if token is revoked revoked, err := jwtManager.IsTokenRevoked(tokenString) assert.NoError(t, err) assert.True(t, revoked) } func TestJWTManager_RevokeToken_AlreadyExpired(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read"}, IssuedAt: time.Now().Add(-2 * time.Hour), ExpiresAt: time.Now().Add(-1 * time.Hour), // Already expired MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } // Generate expired token tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Revoke expired token (should succeed but not add to blacklist) err = jwtManager.RevokeToken(tokenString) assert.NoError(t, err) // Check if token is revoked (should be false since it was already expired) revoked, err := jwtManager.IsTokenRevoked(tokenString) assert.NoError(t, err) assert.False(t, revoked) } func TestJWTManager_IsTokenRevoked_NotRevoked(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(1 * time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } // Generate token tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Check if token is revoked (should be false) revoked, err := jwtManager.IsTokenRevoked(tokenString) assert.NoError(t, err) assert.False(t, revoked) } func TestJWTManager_GetTokenInfo(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read", "write"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(1 * time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, Claims: map[string]string{ "department": "engineering", }, } // Generate token tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Get token info info := jwtManager.GetTokenInfo(tokenString) assert.Equal(t, userToken.UserID, info["user_id"]) assert.Equal(t, userToken.AppID, info["app_id"]) assert.Equal(t, userToken.Permissions, info["permissions"]) assert.Equal(t, userToken.TokenType, info["token_type"]) assert.NotNil(t, info["issued_at"]) assert.NotNil(t, info["expires_at"]) assert.NotNil(t, info["max_valid_at"]) assert.NotNil(t, info["jti"]) } func TestJWTManager_GetTokenInfo_InvalidToken(t *testing.T) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) // Get info for invalid token info := jwtManager.GetTokenInfo("invalid.token.here") assert.Contains(t, info["error"], "Invalid token format") } // Benchmark tests func BenchmarkJWTManager_GenerateToken(b *testing.B) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read", "write"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(1 * time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } b.ResetTimer() for i := 0; i < b.N; i++ { _, err := jwtManager.GenerateToken(userToken) if err != nil { b.Fatal(err) } } } func BenchmarkJWTManager_ValidateToken(b *testing.B) { cfg := config.NewConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(cfg, logger) userToken := &domain.UserToken{ UserID: "test-user-123", AppID: "test-app-456", Permissions: []string{"read", "write"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(1 * time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } tokenString, err := jwtManager.GenerateToken(userToken) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _, err := jwtManager.ValidateToken(tokenString) if err != nil { b.Fatal(err) } } }