test: cover refresh rotation and invalid/expired/locked cases

This commit is contained in:
2026-01-08 06:16:30 +01:00
parent 3ca2334932
commit 4888916613

View File

@@ -348,39 +348,54 @@ func TestJWTService_VerifyAccessToken(t *testing.T) {
}) })
} }
func TestJWTService_RefreshAccessToken(t *testing.T) { func TestJWTService_RefreshAccessTokenWithRotation(t *testing.T) {
jwtService, userRepo, refreshRepo := createTestJWTService() jwtService, userRepo, refreshRepo := createTestJWTService()
user := createTestUser() user := createTestUser()
userRepo.users[user.ID] = user userRepo.users[user.ID] = user
t.Run("Successful_Refresh", func(t *testing.T) { refreshToken, err := jwtService.GenerateRefreshToken(user)
if err != nil {
t.Fatalf("Failed to generate refresh token: %v", err)
}
refreshToken, err := jwtService.GenerateRefreshToken(user) accessToken, newRefreshToken, err := jwtService.RefreshAccessTokenWithRotation(refreshToken)
if err != nil { if err != nil {
t.Fatalf("Failed to generate refresh token: %v", err) t.Fatalf("Expected successful token refresh, got error: %v", err)
} }
accessToken, err := jwtService.RefreshAccessToken(refreshToken) if accessToken == "" {
if err != nil { t.Error("Expected non-empty access token")
t.Fatalf("Expected successful token refresh, got error: %v", err) }
} if newRefreshToken == "" {
t.Error("Expected non-empty refresh token")
}
if newRefreshToken == refreshToken {
t.Error("Expected refresh token to rotate")
}
if accessToken == "" { userID, err := jwtService.VerifyAccessToken(accessToken)
t.Error("Expected non-empty access token") if err != nil {
} t.Fatalf("Expected valid access token, got error: %v", err)
}
if userID != user.ID {
t.Errorf("Expected user ID %d, got %d", user.ID, userID)
}
userID, err := jwtService.VerifyAccessToken(accessToken) _, _, err = jwtService.RefreshAccessTokenWithRotation(refreshToken)
if err != nil { if err == nil {
t.Fatalf("Expected valid access token, got error: %v", err) t.Fatal("Expected error for rotated refresh token")
} }
if !errors.Is(err, ErrRefreshTokenInvalid) {
t.Errorf("Expected ErrRefreshTokenInvalid, got %v", err)
}
if userID != user.ID { _, _, err = jwtService.RefreshAccessTokenWithRotation(newRefreshToken)
t.Errorf("Expected user ID %d, got %d", user.ID, userID) if err != nil {
} t.Fatalf("Expected new refresh token to be usable, got error: %v", err)
}) }
t.Run("Invalid_Refresh_Token", func(t *testing.T) { t.Run("Invalid_Refresh_Token", func(t *testing.T) {
_, err := jwtService.RefreshAccessToken("invalid-refresh-token") _, _, err := jwtService.RefreshAccessTokenWithRotation("invalid-refresh-token")
if err == nil { if err == nil {
t.Error("Expected error for invalid refresh token") t.Error("Expected error for invalid refresh token")
} }
@@ -390,7 +405,6 @@ func TestJWTService_RefreshAccessToken(t *testing.T) {
}) })
t.Run("Expired_Refresh_Token", func(t *testing.T) { t.Run("Expired_Refresh_Token", func(t *testing.T) {
refreshToken := &database.RefreshToken{ refreshToken := &database.RefreshToken{
UserID: user.ID, UserID: user.ID,
TokenHash: "expired-token-hash", TokenHash: "expired-token-hash",
@@ -403,7 +417,7 @@ func TestJWTService_RefreshAccessToken(t *testing.T) {
refreshToken.TokenHash = tokenHash refreshToken.TokenHash = tokenHash
refreshRepo.tokens[tokenHash] = refreshToken refreshRepo.tokens[tokenHash] = refreshToken
_, err := jwtService.RefreshAccessToken(testToken) _, _, err := jwtService.RefreshAccessTokenWithRotation(testToken)
if err == nil { if err == nil {
t.Error("Expected error for expired refresh token") t.Error("Expected error for expired refresh token")
} }
@@ -413,7 +427,6 @@ func TestJWTService_RefreshAccessToken(t *testing.T) {
}) })
t.Run("User_Not_Found", func(t *testing.T) { t.Run("User_Not_Found", func(t *testing.T) {
refreshToken, err := jwtService.GenerateRefreshToken(user) refreshToken, err := jwtService.GenerateRefreshToken(user)
if err != nil { if err != nil {
t.Fatalf("Failed to generate refresh token: %v", err) t.Fatalf("Failed to generate refresh token: %v", err)
@@ -421,7 +434,7 @@ func TestJWTService_RefreshAccessToken(t *testing.T) {
delete(userRepo.users, user.ID) delete(userRepo.users, user.ID)
_, err = jwtService.RefreshAccessToken(refreshToken) _, _, err = jwtService.RefreshAccessTokenWithRotation(refreshToken)
if err == nil { if err == nil {
t.Error("Expected error for non-existent user") t.Error("Expected error for non-existent user")
} }
@@ -439,7 +452,7 @@ func TestJWTService_RefreshAccessToken(t *testing.T) {
t.Fatalf("Failed to generate refresh token: %v", err) t.Fatalf("Failed to generate refresh token: %v", err)
} }
_, err = jwtService.RefreshAccessToken(refreshToken) _, _, err = jwtService.RefreshAccessTokenWithRotation(refreshToken)
if err == nil { if err == nil {
t.Error("Expected error for locked user") t.Error("Expected error for locked user")
} }
@@ -937,7 +950,7 @@ func TestJWTService_Integration(t *testing.T) {
t.Errorf("Expected user ID %d, got %d", user.ID, userID) t.Errorf("Expected user ID %d, got %d", user.ID, userID)
} }
newAccessToken, err := jwtService.RefreshAccessToken(refreshToken) newAccessToken, rotatedRefreshToken, err := jwtService.RefreshAccessTokenWithRotation(refreshToken)
if err != nil { if err != nil {
t.Fatalf("Failed to refresh access token: %v", err) t.Fatalf("Failed to refresh access token: %v", err)
} }
@@ -950,12 +963,12 @@ func TestJWTService_Integration(t *testing.T) {
t.Errorf("Expected user ID %d, got %d", user.ID, userID) t.Errorf("Expected user ID %d, got %d", user.ID, userID)
} }
err = jwtService.RevokeRefreshToken(refreshToken) err = jwtService.RevokeRefreshToken(rotatedRefreshToken)
if err != nil { if err != nil {
t.Fatalf("Failed to revoke refresh token: %v", err) t.Fatalf("Failed to revoke refresh token: %v", err)
} }
_, err = jwtService.RefreshAccessToken(refreshToken) _, _, err = jwtService.RefreshAccessTokenWithRotation(rotatedRefreshToken)
if err == nil { if err == nil {
t.Error("Expected error when using revoked refresh token") t.Error("Expected error when using revoked refresh token")
} }