veza/veza-backend-api/internal/services/refresh_token_service_test.go
2025-12-03 20:29:37 +01:00

293 lines
8 KiB
Go

package services
import (
"github.com/google/uuid"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"veza-backend-api/internal/models"
)
// setupTestRefreshTokenService crée un RefreshTokenService de test avec une base de données en mémoire
func setupTestRefreshTokenService(t *testing.T) (*RefreshTokenService, *gorm.DB) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("Failed to open test database: %v", err)
}
// Auto-migrate
err = db.AutoMigrate(&models.User{}, &models.RefreshToken{})
if err != nil {
t.Fatalf("Failed to migrate: %v", err)
}
// Create a test user
user := &models.User{
Email: "test@example.com",
Username: "testuser",
Role: "user",
IsActive: true,
}
db.Create(user)
service := NewRefreshTokenService(db)
return service, db
}
func TestRefreshTokenService_Store(t *testing.T) {
service, db := setupTestRefreshTokenService(t)
var user models.User
db.Where("email = ?", "test@example.com").First(&user)
token := "test-refresh-token-123"
expiresAt := time.Now().Add(30 * 24 * time.Hour)
err := service.Store(user.ID, token, expiresAt)
assert.NoError(t, err)
// Verify token was stored (check by hash)
var storedToken models.RefreshToken
tokenHash := service.hashToken(token)
err = db.Where("user_id = ? AND token_hash = ?", user.ID, tokenHash).First(&storedToken).Error
assert.NoError(t, err)
assert.Equal(t, user.ID, storedToken.UserID)
assert.Equal(t, tokenHash, storedToken.TokenHash)
}
func TestRefreshTokenService_Validate_ValidToken(t *testing.T) {
service, db := setupTestRefreshTokenService(t)
var user models.User
db.Where("email = ?", "test@example.com").First(&user)
token := "valid-refresh-token"
expiresAt := time.Now().Add(30 * 24 * time.Hour)
err := service.Store(user.ID, token, expiresAt)
require.NoError(t, err)
// Validate the token
valid, err := service.Validate(user.ID, token)
assert.NoError(t, err)
assert.True(t, valid)
}
func TestRefreshTokenService_Validate_InvalidToken(t *testing.T) {
service, db := setupTestRefreshTokenService(t)
var user models.User
db.Where("email = ?", "test@example.com").First(&user)
// Try to validate a token that doesn't exist
valid, err := service.Validate(user.ID, "non-existent-token")
assert.NoError(t, err)
assert.False(t, valid)
}
func TestRefreshTokenService_Validate_ExpiredToken(t *testing.T) {
service, db := setupTestRefreshTokenService(t)
var user models.User
db.Where("email = ?", "test@example.com").First(&user)
token := "expired-refresh-token"
expiresAt := time.Now().Add(-1 * time.Hour) // Expired 1 hour ago
err := service.Store(user.ID, token, expiresAt)
require.NoError(t, err)
// Validate the expired token
valid, err := service.Validate(user.ID, token)
assert.NoError(t, err)
assert.False(t, valid, "Expired token should not be valid")
}
func TestRefreshTokenService_Validate_WrongUser(t *testing.T) {
service, db := setupTestRefreshTokenService(t)
var user models.User
db.Where("email = ?", "test@example.com").First(&user)
// Create another user
otherUser := &models.User{
Email: "other@example.com",
Username: "otheruser",
Role: "user",
IsActive: true,
}
db.Create(otherUser)
token := "user-specific-token"
expiresAt := time.Now().Add(30 * 24 * time.Hour)
// Store token for first user
err := service.Store(user.ID, token, expiresAt)
require.NoError(t, err)
// Try to validate with wrong user ID
valid, err := service.Validate(otherUser.ID, token)
assert.NoError(t, err)
assert.False(t, valid, "Token should not be valid for different user")
}
func TestRefreshTokenService_Revoke(t *testing.T) {
service, db := setupTestRefreshTokenService(t)
var user models.User
db.Where("email = ?", "test@example.com").First(&user)
token := "token-to-revoke"
expiresAt := time.Now().Add(30 * 24 * time.Hour)
err := service.Store(user.ID, token, expiresAt)
require.NoError(t, err)
// Verify token exists
valid, err := service.Validate(user.ID, token)
require.NoError(t, err)
assert.True(t, valid)
// Revoke the token
err = service.Revoke(user.ID, token)
assert.NoError(t, err)
// Verify token is no longer valid
valid, err = service.Validate(user.ID, token)
assert.NoError(t, err)
assert.False(t, valid, "Revoked token should not be valid")
}
func TestRefreshTokenService_Revoke_NonExistentToken(t *testing.T) {
service, db := setupTestRefreshTokenService(t)
var user models.User
db.Where("email = ?", "test@example.com").First(&user)
// Try to revoke a token that doesn't exist
err := service.Revoke(user.ID, "non-existent-token")
assert.Error(t, err)
assert.Contains(t, err.Error(), "not found")
}
func TestRefreshTokenService_RevokeAll(t *testing.T) {
service, db := setupTestRefreshTokenService(t)
var user models.User
db.Where("email = ?", "test@example.com").First(&user)
// Store multiple tokens
token1 := "token-1"
token2 := "token-2"
token3 := "token-3"
expiresAt := time.Now().Add(30 * 24 * time.Hour)
err := service.Store(user.ID, token1, expiresAt)
require.NoError(t, err)
err = service.Store(user.ID, token2, expiresAt)
require.NoError(t, err)
err = service.Store(user.ID, token3, expiresAt)
require.NoError(t, err)
// Verify all tokens are valid
valid1, _ := service.Validate(user.ID, token1)
valid2, _ := service.Validate(user.ID, token2)
valid3, _ := service.Validate(user.ID, token3)
assert.True(t, valid1)
assert.True(t, valid2)
assert.True(t, valid3)
// Revoke all tokens
err = service.RevokeAll(user.ID)
assert.NoError(t, err)
// Verify all tokens are revoked
valid1, _ = service.Validate(user.ID, token1)
valid2, _ = service.Validate(user.ID, token2)
valid3, _ = service.Validate(user.ID, token3)
assert.False(t, valid1, "Token 1 should be revoked")
assert.False(t, valid2, "Token 2 should be revoked")
assert.False(t, valid3, "Token 3 should be revoked")
}
func TestRefreshTokenService_hashToken(t *testing.T) {
service, _ := setupTestRefreshTokenService(t)
token := "test-token"
hash1 := service.hashToken(token)
hash2 := service.hashToken(token)
// Same token should produce same hash
assert.Equal(t, hash1, hash2)
assert.Len(t, hash1, 64) // SHA-256 produces 64 hex characters
// Different tokens should produce different hashes
hash3 := service.hashToken("different-token")
assert.NotEqual(t, hash1, hash3)
}
func TestRefreshTokenService_StoreMultipleTokens(t *testing.T) {
service, db := setupTestRefreshTokenService(t)
var user models.User
db.Where("email = ?", "test@example.com").First(&user)
// Store multiple tokens for the same user
token1 := "token-1"
token2 := "token-2"
expiresAt := time.Now().Add(30 * 24 * time.Hour)
err := service.Store(user.ID, token1, expiresAt)
assert.NoError(t, err)
err = service.Store(user.ID, token2, expiresAt)
assert.NoError(t, err)
// Both tokens should be valid
valid1, err := service.Validate(user.ID, token1)
assert.NoError(t, err)
assert.True(t, valid1)
valid2, err := service.Validate(user.ID, token2)
assert.NoError(t, err)
assert.True(t, valid2)
// Verify both tokens are stored in database
var count int64
db.Model(&models.RefreshToken{}).Where("user_id = ?", user.ID).Count(&count)
assert.Equal(t, int64(2), count)
}
func TestRefreshTokenService_Validate_AfterRevokeOne(t *testing.T) {
service, db := setupTestRefreshTokenService(t)
var user models.User
db.Where("email = ?", "test@example.com").First(&user)
token1 := "token-1"
token2 := "token-2"
expiresAt := time.Now().Add(30 * 24 * time.Hour)
err := service.Store(user.ID, token1, expiresAt)
require.NoError(t, err)
err = service.Store(user.ID, token2, expiresAt)
require.NoError(t, err)
// Revoke only token1
err = service.Revoke(user.ID, token1)
assert.NoError(t, err)
// token1 should be invalid
valid1, err := service.Validate(user.ID, token1)
assert.NoError(t, err)
assert.False(t, valid1)
// token2 should still be valid
valid2, err := service.Validate(user.ID, token2)
assert.NoError(t, err)
assert.True(t, valid2)
}