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) }