package test import ( "context" "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/domain" "github.com/kms/api-key-service/internal/services" ) // MockConfig implements ConfigProvider for testing type MockConfig struct { values map[string]string } func NewMockConfig() *MockConfig { return &MockConfig{ values: map[string]string{ "JWT_SECRET": "test-jwt-secret-for-testing-only", }, } } func (m *MockConfig) GetString(key string) string { return m.values[key] } func (m *MockConfig) GetInt(key string) int { return 0 } func (m *MockConfig) GetBool(key string) bool { return false } func (m *MockConfig) GetDuration(key string) time.Duration { return 0 } func (m *MockConfig) GetStringSlice(key string) []string { return nil } func (m *MockConfig) IsSet(key string) bool { return m.values[key] != "" } func (m *MockConfig) Validate() error { return nil } func (m *MockConfig) GetDatabaseDSN() string { return "" } func (m *MockConfig) GetServerAddress() string { return "" } func (m *MockConfig) GetMetricsAddress() string { return "" } func (m *MockConfig) GetJWTSecret() string { return m.GetString("JWT_SECRET") } func (m *MockConfig) IsDevelopment() bool { return true } func (m *MockConfig) IsProduction() bool { return false } func TestJWTManager_GenerateToken(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(config, logger) userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read", "write"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, Claims: map[string]string{ "email": "test@example.com", "name": "Test User", }, } tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) assert.NotEmpty(t, tokenString) // Verify the token can be validated 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(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(config, logger) userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Test valid token claims, err := jwtManager.ValidateToken(tokenString) require.NoError(t, err) assert.Equal(t, userToken.UserID, claims.UserID) assert.Equal(t, userToken.AppID, claims.AppID) // Test invalid token _, err = jwtManager.ValidateToken("invalid-token") assert.Error(t, err) // Test empty token _, err = jwtManager.ValidateToken("") assert.Error(t, err) } func TestJWTManager_ExpiredToken(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(config, logger) // Create an expired token userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read"}, IssuedAt: time.Now().Add(-2 * time.Hour), ExpiresAt: time.Now().Add(-time.Hour), // Expired 1 hour ago MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Validation should fail for expired token _, err = jwtManager.ValidateToken(tokenString) assert.Error(t, err) } func TestJWTManager_MaxValidAtExpired(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(config, logger) // Create a token that's past max valid time userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read"}, IssuedAt: time.Now().Add(-2 * time.Hour), ExpiresAt: time.Now().Add(time.Hour), MaxValidAt: time.Now().Add(-time.Hour), // Max valid time expired TokenType: domain.TokenTypeUser, } tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Validation should fail for token past max valid time _, err = jwtManager.ValidateToken(tokenString) assert.Error(t, err) } func TestJWTManager_RefreshToken(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(config, logger) userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } originalToken, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Refresh the token 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 the 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_ExtractClaims(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(config, logger) userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(-time.Hour), // Expired token MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Extract claims from expired token (should work) claims, err := jwtManager.ExtractClaims(tokenString) require.NoError(t, err) assert.Equal(t, userToken.UserID, claims.UserID) assert.Equal(t, userToken.AppID, claims.AppID) } func TestJWTManager_GetTokenInfo(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(config, logger) userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) 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"]) } func TestAuthenticationService_ValidateJWTToken(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() authService := services.NewAuthenticationService(config, logger) userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read", "write"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, Claims: map[string]string{ "email": "test@example.com", }, } // Generate token tokenString, err := authService.GenerateJWTToken(context.Background(), userToken) require.NoError(t, err) // Validate token authContext, err := authService.ValidateJWTToken(context.Background(), tokenString) require.NoError(t, err) assert.Equal(t, userToken.UserID, authContext.UserID) assert.Equal(t, userToken.AppID, authContext.AppID) assert.Equal(t, userToken.Permissions, authContext.Permissions) assert.Equal(t, userToken.TokenType, authContext.TokenType) assert.Equal(t, userToken.Claims, authContext.Claims) } func TestAuthenticationService_GenerateJWTToken(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() authService := services.NewAuthenticationService(config, logger) userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } tokenString, err := authService.GenerateJWTToken(context.Background(), userToken) require.NoError(t, err) assert.NotEmpty(t, tokenString) // Verify token can be validated authContext, err := authService.ValidateJWTToken(context.Background(), tokenString) require.NoError(t, err) assert.Equal(t, userToken.UserID, authContext.UserID) } func TestAuthenticationService_RefreshJWTToken(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() authService := services.NewAuthenticationService(config, logger) userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } originalToken, err := authService.GenerateJWTToken(context.Background(), userToken) require.NoError(t, err) // Refresh token newExpiration := time.Now().Add(2 * time.Hour) refreshedToken, err := authService.RefreshJWTToken(context.Background(), originalToken, newExpiration) require.NoError(t, err) assert.NotEmpty(t, refreshedToken) assert.NotEqual(t, originalToken, refreshedToken) // Validate refreshed token authContext, err := authService.ValidateJWTToken(context.Background(), refreshedToken) require.NoError(t, err) assert.Equal(t, userToken.UserID, authContext.UserID) } func TestJWTManager_InvalidSecret(t *testing.T) { // Test with empty JWT secret config := &MockConfig{values: map[string]string{"JWT_SECRET": ""}} logger := zap.NewNop() jwtManager := auth.NewJWTManager(config, logger) userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } _, err := jwtManager.GenerateToken(userToken) assert.Error(t, err) } func TestJWTManager_TokenRevocation(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() jwtManager := auth.NewJWTManager(config, logger) userToken := &domain.UserToken{ AppID: "test-app", UserID: "test-user", Permissions: []string{"read"}, IssuedAt: time.Now(), ExpiresAt: time.Now().Add(time.Hour), MaxValidAt: time.Now().Add(24 * time.Hour), TokenType: domain.TokenTypeUser, } tokenString, err := jwtManager.GenerateToken(userToken) require.NoError(t, err) // Check revocation status (should be false initially) revoked, err := jwtManager.IsTokenRevoked(tokenString) require.NoError(t, err) assert.False(t, revoked) // Revoke token (currently just logs, doesn't actually revoke) err = jwtManager.RevokeToken(tokenString) require.NoError(t, err) // Note: Current implementation doesn't actually implement blacklisting, // so this test just verifies the methods don't error }