package helper import ( "authentication/models" "os" "strings" "testing" "time" "github.com/golang-jwt/jwt/v5" ) func TestExtractEmailFromTokenValidAccessToken(t *testing.T) { // Set up test environment secretKey := "test-secret-key-123" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") email := "test@example.com" // Create valid AccessToken claims := &models.AccessToken{ Email: email, SessionID: "session123", RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), IssuedAt: jwt.NewNumericDate(time.Now()), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString([]byte(secretKey)) if err != nil { t.Fatalf("Failed to sign token: %v", err) } // Test extraction extractedEmail, err := ExtractEmailFromToken(tokenString) if err != nil { t.Errorf("Expected no error, got: %v", err) } if extractedEmail != email { t.Errorf("Expected email '%s', got '%s'", email, extractedEmail) } } func TestExtractEmailFromTokenValidMapClaims(t *testing.T) { secretKey := "test-secret-key-456" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") email := "mapuser@example.com" // Create token with MapClaims claims := jwt.MapClaims{ "email": email, "exp": time.Now().Add(time.Hour).Unix(), "iat": time.Now().Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString([]byte(secretKey)) if err != nil { t.Fatalf("Failed to sign token: %v", err) } // Test extraction extractedEmail, err := ExtractEmailFromToken(tokenString) if err != nil { t.Errorf("Expected no error, got: %v", err) } if extractedEmail != email { t.Errorf("Expected email '%s', got '%s'", email, extractedEmail) } } func TestExtractEmailFromTokenBearerPrefix(t *testing.T) { secretKey := "test-secret-bearer" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") email := "bearer@example.com" claims := &models.AccessToken{ Email: email, SessionID: "session789", RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString([]byte(secretKey)) if err != nil { t.Fatalf("Failed to sign token: %v", err) } // Test with Bearer prefix bearerToken := "Bearer " + tokenString extractedEmail, err := ExtractEmailFromToken(bearerToken) if err != nil { t.Errorf("Expected no error, got: %v", err) } if extractedEmail != email { t.Errorf("Expected email '%s', got '%s'", email, extractedEmail) } } func TestExtractEmailFromTokenEmptyToken(t *testing.T) { os.Setenv("JWT_SECRET_KEY", "test-key") defer os.Unsetenv("JWT_SECRET_KEY") testCases := []string{"", "null", "undefined"} for _, tokenString := range testCases { _, err := ExtractEmailFromToken(tokenString) if err == nil { t.Errorf("Expected error for token '%s', got nil", tokenString) } if !strings.Contains(err.Error(), "no valid token provided") { t.Errorf("Expected 'no valid token provided' error, got: %v", err) } } } func TestExtractEmailFromTokenInvalidSignature(t *testing.T) { os.Setenv("JWT_SECRET_KEY", "correct-secret") defer os.Unsetenv("JWT_SECRET_KEY") // Create token with wrong secret wrongSecret := "wrong-secret" claims := &models.AccessToken{ Email: "test@example.com", SessionID: "session123", RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, _ := token.SignedString([]byte(wrongSecret)) // Try to extract with different secret _, err := ExtractEmailFromToken(tokenString) if err == nil { t.Error("Expected error for invalid signature") } if !strings.Contains(err.Error(), "invalid token signature") { t.Errorf("Expected 'invalid token signature' error, got: %v", err) } } func TestExtractEmailFromTokenExpiredToken(t *testing.T) { secretKey := "test-expired-key" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") // Create expired token claims := &models.AccessToken{ Email: "expired@example.com", SessionID: "session999", RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(-time.Hour)), // Expired 1 hour ago IssuedAt: jwt.NewNumericDate(time.Now().Add(-2 * time.Hour)), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, _ := token.SignedString([]byte(secretKey)) // Note: The ExtractEmailFromToken function doesn't validate expiration, // it relies on ParseWithClaims which may or may not enforce expiration // depending on jwt library version. We'll just verify it can extract the email. extractedEmail, _ := ExtractEmailFromToken(tokenString) if extractedEmail != "expired@example.com" { t.Logf("Extracted email from expired token: %s", extractedEmail) } } func TestExtractEmailFromTokenNoEmailInClaims(t *testing.T) { secretKey := "test-no-email" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") // Create token without email claims := jwt.MapClaims{ "user_id": "user123", "exp": time.Now().Add(time.Hour).Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, _ := token.SignedString([]byte(secretKey)) _, err := ExtractEmailFromToken(tokenString) if err == nil { t.Error("Expected error for token without email") } if !strings.Contains(err.Error(), "email not found in token") { t.Errorf("Expected 'email not found in token' error, got: %v", err) } } func TestExtractEmailFromTokenInvalidEmailFormat(t *testing.T) { secretKey := "test-invalid-email" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") // Create token with invalid email (no @ symbol) claims := &models.AccessToken{ Email: "notanemail", SessionID: "session123", RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, _ := token.SignedString([]byte(secretKey)) _, err := ExtractEmailFromToken(tokenString) if err == nil { t.Error("Expected error for invalid email format") } } func TestExtractEmailFromTokenNoSecretKey(t *testing.T) { // Ensure no secret key is set os.Unsetenv("JWT_SECRET_KEY") claims := &models.AccessToken{ Email: "test@example.com", SessionID: "session123", RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, _ := token.SignedString([]byte("any-key")) _, err := ExtractEmailFromToken(tokenString) if err == nil { t.Error("Expected error when secret key not set") } } func TestExtractEmailFromTokenWrongSigningMethod(t *testing.T) { secretKey := "test-wrong-method" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") // Try to create token with non-HMAC signing method (would need RSA keys in real scenario) // For simplicity, we'll create a malformed token string malformedToken := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20ifQ.invalid" _, err := ExtractEmailFromToken(malformedToken) if err == nil { t.Error("Expected error for wrong signing method") } } func TestExtractEmailFromTokenMalformedToken(t *testing.T) { os.Setenv("JWT_SECRET_KEY", "test-key") defer os.Unsetenv("JWT_SECRET_KEY") malformedTokens := []string{ "not.a.token", "invalid", "Bearer invalid", "...", "a.b", } for _, tokenString := range malformedTokens { _, err := ExtractEmailFromToken(tokenString) if err == nil { t.Errorf("Expected error for malformed token '%s'", tokenString) } } } func TestExtractEmailFromTokenMultipleAtSymbols(t *testing.T) { secretKey := "test-multiple-at" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") email := "user@sub@example.com" claims := &models.AccessToken{ Email: email, SessionID: "session123", RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, _ := token.SignedString([]byte(secretKey)) // Should extract successfully (just checks for @ presence) extractedEmail, err := ExtractEmailFromToken(tokenString) if err != nil { t.Errorf("Expected no error, got: %v", err) } if extractedEmail != email { t.Errorf("Expected email '%s', got '%s'", email, extractedEmail) } } func TestExtractEmailFromTokenWhitespaceEmail(t *testing.T) { secretKey := "test-whitespace" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") // Email with spaces (should still work if it has @) email := " user@example.com " claims := jwt.MapClaims{ "email": email, "exp": time.Now().Add(time.Hour).Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, _ := token.SignedString([]byte(secretKey)) extractedEmail, err := ExtractEmailFromToken(tokenString) if err != nil { t.Errorf("Expected no error, got: %v", err) } if extractedEmail != email { t.Errorf("Expected email '%s', got '%s'", email, extractedEmail) } } func TestExtractEmailFromTokenCaseInsensitiveBearer(t *testing.T) { secretKey := "test-case-bearer" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") email := "case@example.com" claims := &models.AccessToken{ Email: email, SessionID: "session123", RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, _ := token.SignedString([]byte(secretKey)) // Test with standard "Bearer " prefix bearerToken := "Bearer " + tokenString extractedEmail, err := ExtractEmailFromToken(bearerToken) if err != nil { t.Errorf("Expected no error for Bearer prefix, got: %v", err) } if extractedEmail != email { t.Errorf("Expected email '%s', got '%s'", email, extractedEmail) } } func BenchmarkExtractEmailFromToken(b *testing.B) { secretKey := "benchmark-secret" os.Setenv("JWT_SECRET_KEY", secretKey) defer os.Unsetenv("JWT_SECRET_KEY") claims := &models.AccessToken{ Email: "bench@example.com", SessionID: "sessionBench", RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, _ := token.SignedString([]byte(secretKey)) b.ResetTimer() for i := 0; i < b.N; i++ { ExtractEmailFromToken(tokenString) } }