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

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