478 lines
16 KiB
Go
478 lines
16 KiB
Go
package services
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"github.com/google/uuid"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"veza-backend-api/internal/database"
|
|
"veza-backend-api/internal/models"
|
|
)
|
|
|
|
// setupTestSessionServiceForT0202 crée un SessionService de test avec la table sessions (BIGINT user_id)
|
|
func setupTestSessionServiceForT0202(t *testing.T) (*SessionService, *gorm.DB, *database.Database) {
|
|
// Créer une base de données GORM en mémoire
|
|
gormDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
require.NoError(t, err, "Failed to open test database")
|
|
|
|
// Auto-migrate pour créer la table users
|
|
err = gormDB.AutoMigrate(&models.User{})
|
|
require.NoError(t, err, "Failed to migrate users table")
|
|
|
|
// Créer la table sessions manuellement (selon migration T0201)
|
|
err = gormDB.Exec(`
|
|
CREATE TABLE sessions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
token_hash TEXT NOT NULL UNIQUE,
|
|
ip_address TEXT,
|
|
user_agent TEXT,
|
|
expires_at TIMESTAMP NOT NULL,
|
|
last_activity TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
`).Error
|
|
require.NoError(t, err, "Failed to create sessions table")
|
|
|
|
// Créer les index
|
|
err = gormDB.Exec("CREATE INDEX idx_sessions_user_id ON sessions(user_id)").Error
|
|
require.NoError(t, err)
|
|
err = gormDB.Exec("CREATE INDEX idx_sessions_token_hash ON sessions(token_hash)").Error
|
|
require.NoError(t, err)
|
|
err = gormDB.Exec("CREATE INDEX idx_sessions_expires_at ON sessions(expires_at)").Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer un utilisateur de test
|
|
user := &models.User{
|
|
Email: "test@example.com",
|
|
Username: "testuser",
|
|
Role: "user",
|
|
IsActive: true,
|
|
}
|
|
err = gormDB.Create(user).Error
|
|
require.NoError(t, err, "Failed to create test user")
|
|
|
|
// Obtenir le sql.DB depuis GORM
|
|
sqlDB, err := gormDB.DB()
|
|
require.NoError(t, err, "Failed to get sql.DB from GORM")
|
|
|
|
// Créer un Database wrapper
|
|
testDB := &database.Database{
|
|
DB: sqlDB,
|
|
}
|
|
|
|
// Créer le service
|
|
logger, _ := zap.NewDevelopment()
|
|
service := NewSessionService(testDB, logger)
|
|
|
|
return service, gormDB, testDB
|
|
}
|
|
|
|
// hashToken helper pour les tests
|
|
func hashTokenForTest(token string) string {
|
|
hash := sha256.Sum256([]byte(token))
|
|
return hex.EncodeToString(hash[:])
|
|
}
|
|
|
|
// TestSessionService_CreateSessionForT0202_Success teste la création d'une session
|
|
func TestSessionService_CreateSessionForT0202_Success(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Récupérer l'utilisateur
|
|
var user models.User
|
|
err := gormDB.First(&user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer une session
|
|
token := "test-token-123"
|
|
ipAddress := "192.168.1.1"
|
|
userAgent := "Mozilla/5.0"
|
|
expiresAt := time.Now().Add(24 * time.Hour)
|
|
|
|
err = service.CreateSessionWithBIGINT(user.ID, token, ipAddress, userAgent, expiresAt)
|
|
assert.NoError(t, err, "Should create session successfully")
|
|
|
|
// Vérifier que la session a été créée
|
|
tokenHash := hashTokenForTest(token)
|
|
var count int64
|
|
err = gormDB.Raw("SELECT COUNT(*) FROM sessions WHERE token_hash = ?", tokenHash).Scan(&count).Error
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(1), count, "Session should be created")
|
|
}
|
|
|
|
// TestSessionService_CreateSessionForT0202_InvalidUserID teste avec un user_id invalide
|
|
func TestSessionService_CreateSessionForT0202_InvalidUserID(t *testing.T) {
|
|
service, _, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
token := "test-token-123"
|
|
ipAddress := "192.168.1.1"
|
|
userAgent := "Mozilla/5.0"
|
|
expiresAt := time.Now().Add(24 * time.Hour)
|
|
|
|
// Essayer de créer une session avec un user_id inexistant
|
|
err := service.CreateSessionWithBIGINT(99999, token, ipAddress, userAgent, expiresAt)
|
|
assert.Error(t, err, "Should fail with invalid user_id")
|
|
}
|
|
|
|
// TestSessionService_GetSession_Success teste la récupération d'une session
|
|
func TestSessionService_GetSession_Success(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Récupérer l'utilisateur
|
|
var user models.User
|
|
err := gormDB.First(&user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer une session
|
|
token := "test-token-456"
|
|
ipAddress := "192.168.1.2"
|
|
userAgent := "Chrome"
|
|
expiresAt := time.Now().Add(24 * time.Hour)
|
|
|
|
err = service.CreateSessionWithBIGINT(user.ID, token, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
|
|
// Récupérer la session
|
|
tokenHash := hashTokenForTest(token)
|
|
session, err := service.GetSessionWithBIGINT(tokenHash)
|
|
assert.NoError(t, err, "Should get session successfully")
|
|
assert.NotNil(t, session)
|
|
assert.Equal(t, user.ID, session.UserID)
|
|
assert.Equal(t, tokenHash, session.TokenHash)
|
|
assert.Equal(t, ipAddress, session.IPAddress)
|
|
assert.Equal(t, userAgent, session.UserAgent)
|
|
}
|
|
|
|
// TestSessionService_GetSession_NotFound teste la récupération d'une session inexistante
|
|
func TestSessionService_GetSession_NotFound(t *testing.T) {
|
|
service, _, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Essayer de récupérer une session inexistante
|
|
tokenHash := hashTokenForTest("non-existent-token")
|
|
session, err := service.GetSessionWithBIGINT(tokenHash)
|
|
assert.Error(t, err, "Should return error for non-existent session")
|
|
assert.Nil(t, session)
|
|
assert.Contains(t, err.Error(), "session not found")
|
|
}
|
|
|
|
// TestSessionService_GetSession_Expired teste la récupération d'une session expirée
|
|
func TestSessionService_GetSession_Expired(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Récupérer l'utilisateur
|
|
var user models.User
|
|
err := gormDB.First(&user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer une session expirée directement dans la DB
|
|
token := "expired-token"
|
|
tokenHash := hashTokenForTest(token)
|
|
expiredTime := time.Now().Add(-1 * time.Hour) // Expirée il y a 1 heure
|
|
|
|
err = gormDB.Exec(`
|
|
INSERT INTO sessions (user_id, token_hash, ip_address, user_agent, expires_at, last_activity, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
`, user.ID, tokenHash, "192.168.1.1", "Mozilla/5.0", expiredTime, time.Now(), time.Now()).Error
|
|
require.NoError(t, err)
|
|
|
|
// Essayer de récupérer la session expirée
|
|
session, err := service.GetSessionWithBIGINT(tokenHash)
|
|
assert.Error(t, err, "Should return error for expired session")
|
|
assert.Nil(t, session)
|
|
}
|
|
|
|
// TestSessionService_UpdateLastActivity_Success teste la mise à jour de last_activity
|
|
func TestSessionService_UpdateLastActivity_Success(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Récupérer l'utilisateur
|
|
var user models.User
|
|
err := gormDB.First(&user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer une session
|
|
token := "test-token-update"
|
|
ipAddress := "192.168.1.1"
|
|
userAgent := "Mozilla/5.0"
|
|
expiresAt := time.Now().Add(24 * time.Hour)
|
|
|
|
err = service.CreateSessionWithBIGINT(user.ID, token, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
|
|
// Récupérer la session initiale pour obtenir last_activity
|
|
tokenHash := hashTokenForTest(token)
|
|
sessionBefore, err := service.GetSessionWithBIGINT(tokenHash)
|
|
require.NoError(t, err)
|
|
initialLastActivity := sessionBefore.LastActivity
|
|
|
|
// Attendre un peu pour s'assurer que le temps change
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Mettre à jour last_activity
|
|
err = service.UpdateLastActivity(tokenHash)
|
|
assert.NoError(t, err, "Should update last_activity successfully")
|
|
|
|
// Vérifier que last_activity a été mis à jour
|
|
sessionAfter, err := service.GetSessionWithBIGINT(tokenHash)
|
|
require.NoError(t, err)
|
|
assert.True(t, sessionAfter.LastActivity.After(initialLastActivity), "Last activity should be updated")
|
|
}
|
|
|
|
// TestSessionService_UpdateLastActivity_NotFound teste la mise à jour d'une session inexistante
|
|
func TestSessionService_UpdateLastActivity_NotFound(t *testing.T) {
|
|
service, _, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Essayer de mettre à jour une session inexistante
|
|
tokenHash := hashTokenForTest("non-existent-token")
|
|
err := service.UpdateLastActivity(tokenHash)
|
|
assert.Error(t, err, "Should return error for non-existent session")
|
|
assert.Contains(t, err.Error(), "session not found")
|
|
}
|
|
|
|
// TestSessionService_DeleteSession_Success teste la suppression d'une session
|
|
func TestSessionService_DeleteSession_Success(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Récupérer l'utilisateur
|
|
var user models.User
|
|
err := gormDB.First(&user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer une session
|
|
token := "test-token-delete"
|
|
ipAddress := "192.168.1.1"
|
|
userAgent := "Mozilla/5.0"
|
|
expiresAt := time.Now().Add(24 * time.Hour)
|
|
|
|
err = service.CreateSessionWithBIGINT(user.ID, token, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
|
|
// Vérifier que la session existe
|
|
tokenHash := hashTokenForTest(token)
|
|
session, err := service.GetSessionWithBIGINT(tokenHash)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, session)
|
|
|
|
// Supprimer la session
|
|
err = service.DeleteSession(tokenHash)
|
|
assert.NoError(t, err, "Should delete session successfully")
|
|
|
|
// Vérifier que la session a été supprimée
|
|
session, err = service.GetSessionWithBIGINT(tokenHash)
|
|
assert.Error(t, err, "Session should not exist after deletion")
|
|
assert.Nil(t, session)
|
|
}
|
|
|
|
// TestSessionService_DeleteSession_NotFound teste la suppression d'une session inexistante
|
|
func TestSessionService_DeleteSession_NotFound(t *testing.T) {
|
|
service, _, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Essayer de supprimer une session inexistante
|
|
tokenHash := hashTokenForTest("non-existent-token")
|
|
err := service.DeleteSession(tokenHash)
|
|
assert.Error(t, err, "Should return error for non-existent session")
|
|
assert.Contains(t, err.Error(), "session not found")
|
|
}
|
|
|
|
// TestSessionService_DeleteAllUserSessions_Success teste la suppression de toutes les sessions d'un utilisateur
|
|
func TestSessionService_DeleteAllUserSessions_Success(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Récupérer l'utilisateur
|
|
var user models.User
|
|
err := gormDB.First(&user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer plusieurs sessions
|
|
token1 := "token-1"
|
|
token2 := "token-2"
|
|
token3 := "token-3"
|
|
ipAddress := "192.168.1.1"
|
|
userAgent := "Mozilla/5.0"
|
|
expiresAt := time.Now().Add(24 * time.Hour)
|
|
|
|
err = service.CreateSessionWithBIGINT(user.ID, token1, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
err = service.CreateSessionWithBIGINT(user.ID, token2, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
err = service.CreateSessionWithBIGINT(user.ID, token3, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
|
|
// Vérifier que les sessions existent
|
|
var count int64
|
|
err = gormDB.Raw("SELECT COUNT(*) FROM sessions WHERE user_id = ?", user.ID).Scan(&count).Error
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(3), count, "Should have 3 sessions")
|
|
|
|
// Supprimer toutes les sessions
|
|
err = service.DeleteAllUserSessions(user.ID)
|
|
assert.NoError(t, err, "Should delete all user sessions successfully")
|
|
|
|
// Vérifier que toutes les sessions ont été supprimées
|
|
err = gormDB.Raw("SELECT COUNT(*) FROM sessions WHERE user_id = ?", user.ID).Scan(&count).Error
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(0), count, "All sessions should be deleted")
|
|
}
|
|
|
|
// TestSessionService_DeleteAllUserSessions_NoSessions teste la suppression quand il n'y a pas de sessions
|
|
func TestSessionService_DeleteAllUserSessions_NoSessions(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Récupérer l'utilisateur
|
|
var user models.User
|
|
err := gormDB.First(&user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Supprimer toutes les sessions (il n'y en a pas)
|
|
err = service.DeleteAllUserSessions(user.ID)
|
|
assert.NoError(t, err, "Should not error when no sessions exist")
|
|
}
|
|
|
|
// TestSessionService_DeleteAllUserSessions_MultipleUsers teste que seul l'utilisateur spécifié est affecté
|
|
func TestSessionService_DeleteAllUserSessions_MultipleUsers(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Créer un deuxième utilisateur
|
|
user2 := &models.User{
|
|
Email: "user2@example.com",
|
|
Username: "user2",
|
|
Role: "user",
|
|
IsActive: true,
|
|
}
|
|
err := gormDB.Create(user2).Error
|
|
require.NoError(t, err)
|
|
|
|
// Récupérer le premier utilisateur
|
|
var user1 models.User
|
|
err = gormDB.Where("email = ?", "test@example.com").First(&user1).Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer des sessions pour les deux utilisateurs
|
|
token1 := "token-user1"
|
|
token2 := "token-user2"
|
|
ipAddress := "192.168.1.1"
|
|
userAgent := "Mozilla/5.0"
|
|
expiresAt := time.Now().Add(24 * time.Hour)
|
|
|
|
err = service.CreateSessionWithBIGINT(user1.ID, token1, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
err = service.CreateSessionWithBIGINT(user2.ID, token2, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
|
|
// Supprimer toutes les sessions de user1
|
|
err = service.DeleteAllUserSessions(user1.ID)
|
|
assert.NoError(t, err)
|
|
|
|
// Vérifier que seule la session de user1 a été supprimée
|
|
var count1, count2 int64
|
|
err = gormDB.Raw("SELECT COUNT(*) FROM sessions WHERE user_id = ?", user1.ID).Scan(&count1).Error
|
|
require.NoError(t, err)
|
|
err = gormDB.Raw("SELECT COUNT(*) FROM sessions WHERE user_id = ?", user2.ID).Scan(&count2).Error
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(0), count1, "User1 sessions should be deleted")
|
|
assert.Equal(t, int64(1), count2, "User2 session should still exist")
|
|
}
|
|
|
|
// TestSessionService_CreateSession_UniqueTokenHash teste que le token_hash doit être unique
|
|
func TestSessionService_CreateSession_UniqueTokenHash(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Récupérer l'utilisateur
|
|
var user models.User
|
|
err := gormDB.First(&user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer une première session
|
|
token := "duplicate-token"
|
|
ipAddress := "192.168.1.1"
|
|
userAgent := "Mozilla/5.0"
|
|
expiresAt := time.Now().Add(24 * time.Hour)
|
|
|
|
err = service.CreateSessionWithBIGINT(user.ID, token, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
|
|
// Essayer de créer une deuxième session avec le même token
|
|
err = service.CreateSessionWithBIGINT(user.ID, token, ipAddress, userAgent, expiresAt)
|
|
assert.Error(t, err, "Should fail with duplicate token_hash")
|
|
}
|
|
|
|
// TestSessionService_GetSession_AllFields teste que tous les champs sont correctement récupérés
|
|
func TestSessionService_GetSession_AllFields(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Récupérer l'utilisateur
|
|
var user models.User
|
|
err := gormDB.First(&user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer une session avec tous les champs
|
|
token := "test-token-all-fields"
|
|
ipAddress := "192.168.1.100"
|
|
userAgent := "Custom User Agent/1.0"
|
|
expiresAt := time.Now().Add(48 * time.Hour)
|
|
|
|
err = service.CreateSessionWithBIGINT(user.ID, token, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
|
|
// Récupérer la session
|
|
tokenHash := hashTokenForTest(token)
|
|
session, err := service.GetSessionWithBIGINT(tokenHash)
|
|
require.NoError(t, err)
|
|
|
|
// Vérifier tous les champs
|
|
assert.NotZero(t, session.ID, "ID should be set")
|
|
assert.Equal(t, user.ID, session.UserID, "UserID should match")
|
|
assert.Equal(t, tokenHash, session.TokenHash, "TokenHash should match")
|
|
assert.Equal(t, ipAddress, session.IPAddress, "IPAddress should match")
|
|
assert.Equal(t, userAgent, session.UserAgent, "UserAgent should match")
|
|
assert.False(t, session.ExpiresAt.IsZero(), "ExpiresAt should be set")
|
|
assert.False(t, session.LastActivity.IsZero(), "LastActivity should be set")
|
|
assert.False(t, session.CreatedAt.IsZero(), "CreatedAt should be set")
|
|
}
|
|
|
|
// TestSessionService_UpdateLastActivity_MultipleUpdates teste plusieurs mises à jour
|
|
func TestSessionService_UpdateLastActivity_MultipleUpdates(t *testing.T) {
|
|
service, gormDB, _ := setupTestSessionServiceForT0202(t)
|
|
|
|
// Récupérer l'utilisateur
|
|
var user models.User
|
|
err := gormDB.First(&user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Créer une session
|
|
token := "test-token-multiple-updates"
|
|
ipAddress := "192.168.1.1"
|
|
userAgent := "Mozilla/5.0"
|
|
expiresAt := time.Now().Add(24 * time.Hour)
|
|
|
|
err = service.CreateSessionWithBIGINT(user.ID, token, ipAddress, userAgent, expiresAt)
|
|
require.NoError(t, err)
|
|
|
|
tokenHash := hashTokenForTest(token)
|
|
|
|
// Mettre à jour plusieurs fois
|
|
err = service.UpdateLastActivity(tokenHash)
|
|
assert.NoError(t, err)
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
err = service.UpdateLastActivity(tokenHash)
|
|
assert.NoError(t, err)
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
err = service.UpdateLastActivity(tokenHash)
|
|
assert.NoError(t, err)
|
|
|
|
// Vérifier que la session existe toujours et que last_activity a été mis à jour
|
|
session, err := service.GetSessionWithBIGINT(tokenHash)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, session)
|
|
}
|