Files
Authentication/helper/extract_email_from_token_test.go
T
2025-11-25 15:12:31 +08:00

394 lines
11 KiB
Go

package helper
import (
"authentication/models"
"os"
"strings"
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
)
func TestExtractEmailFromToken_ValidAccessToken(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 TestExtractEmailFromToken_ValidMapClaims(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 TestExtractEmailFromToken_BearerPrefix(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 TestExtractEmailFromToken_EmptyToken(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 TestExtractEmailFromToken_InvalidSignature(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 TestExtractEmailFromToken_ExpiredToken(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 TestExtractEmailFromToken_NoEmailInClaims(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 TestExtractEmailFromToken_InvalidEmailFormat(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 TestExtractEmailFromToken_NoSecretKey(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 TestExtractEmailFromToken_WrongSigningMethod(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 TestExtractEmailFromToken_MalformedToken(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 TestExtractEmailFromToken_MultipleAtSymbols(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 TestExtractEmailFromToken_WhitespaceEmail(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 TestExtractEmailFromToken_CaseInsensitiveBearer(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)
}
}