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

229 lines
7.7 KiB
Go

package services
import (
"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"
)
// setupTestSessionServiceForT0204 crée un SessionService de test avec la table sessions
func setupTestSessionServiceForT0204(t *testing.T) (*SessionService, *gorm.DB, *database.Database) {
gormDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
err = gormDB.AutoMigrate(&models.User{})
require.NoError(t, err)
// Créer la table sessions
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)
user := &models.User{
Email: "test@example.com",
Username: "testuser",
Role: "user",
IsActive: true,
}
err = gormDB.Create(user).Error
require.NoError(t, err)
sqlDB, err := gormDB.DB()
require.NoError(t, err)
testDB := &database.Database{
DB: sqlDB,
}
logger, _ := zap.NewDevelopment()
service := NewSessionService(testDB, logger)
return service, gormDB, testDB
}
// TestUpdateLastActivityIfNeeded_Debounce teste que le debounce fonctionne correctement
func TestUpdateLastActivityIfNeeded_Debounce(t *testing.T) {
service, gormDB, _ := setupTestSessionServiceForT0204(t)
var user models.User
err := gormDB.First(&user).Error
require.NoError(t, err)
// Créer une session
token := "test-token-debounce"
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)
// Récupérer la session initiale
session, err := service.GetSessionWithBIGINT(tokenHash)
require.NoError(t, err)
initialLastActivity := session.LastActivity
// Attendre un peu
time.Sleep(50 * time.Millisecond)
// Première mise à jour (devrait mettre à jour)
err = service.UpdateLastActivityIfNeeded(tokenHash, 100*time.Millisecond)
assert.NoError(t, err)
// Vérifier que last_activity a été mis à jour
session, err = service.GetSessionWithBIGINT(tokenHash)
require.NoError(t, err)
assert.True(t, session.LastActivity.After(initialLastActivity), "First update should update last_activity")
// Deuxième mise à jour immédiatement (devrait être ignorée par debounce)
timeBeforeSecond := session.LastActivity
err = service.UpdateLastActivityIfNeeded(tokenHash, 100*time.Millisecond)
assert.NoError(t, err)
// Vérifier que last_activity n'a pas changé (debounce)
session, err = service.GetSessionWithBIGINT(tokenHash)
require.NoError(t, err)
assert.Equal(t, timeBeforeSecond.Unix(), session.LastActivity.Unix(), "Second update should be debounced")
// Attendre plus que le debounce duration
time.Sleep(150 * time.Millisecond)
// Troisième mise à jour après le debounce (devrait mettre à jour)
err = service.UpdateLastActivityIfNeeded(tokenHash, 100*time.Millisecond)
assert.NoError(t, err)
// Vérifier que last_activity a été mis à jour
session, err = service.GetSessionWithBIGINT(tokenHash)
require.NoError(t, err)
assert.True(t, session.LastActivity.After(timeBeforeSecond), "Third update after debounce should update last_activity")
}
// TestUpdateLastActivityIfNeeded_ErrorHandling teste que les erreurs sont gérées silencieusement
func TestUpdateLastActivityIfNeeded_ErrorHandling(t *testing.T) {
service, _, _ := setupTestSessionServiceForT0204(t)
// Essayer de mettre à jour une session inexistante
// L'erreur ne doit pas être retournée (gestion silencieuse)
tokenHash := hashTokenForTest("non-existent-token")
err := service.UpdateLastActivityIfNeeded(tokenHash, 5*time.Minute)
assert.NoError(t, err, "Error should be handled silently")
}
// TestUpdateLastActivityIfNeeded_FirstUpdateAlwaysUpdates teste que la première mise à jour met toujours à jour
func TestUpdateLastActivityIfNeeded_FirstUpdateAlwaysUpdates(t *testing.T) {
service, gormDB, _ := setupTestSessionServiceForT0204(t)
var user models.User
err := gormDB.First(&user).Error
require.NoError(t, err)
// Créer une session
token := "test-token-first-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)
tokenHash := hashTokenForTest(token)
// Récupérer la session initiale
session, err := service.GetSessionWithBIGINT(tokenHash)
require.NoError(t, err)
initialLastActivity := session.LastActivity
// Attendre un peu
time.Sleep(50 * time.Millisecond)
// Première mise à jour (devrait toujours mettre à jour)
err = service.UpdateLastActivityIfNeeded(tokenHash, 5*time.Minute)
assert.NoError(t, err)
// Vérifier que last_activity a été mis à jour
session, err = service.GetSessionWithBIGINT(tokenHash)
require.NoError(t, err)
assert.True(t, session.LastActivity.After(initialLastActivity), "First update should always update")
}
// TestUpdateLastActivityIfNeeded_MultipleTokens teste que le debounce fonctionne pour plusieurs tokens différents
func TestUpdateLastActivityIfNeeded_MultipleTokens(t *testing.T) {
service, gormDB, _ := setupTestSessionServiceForT0204(t)
var user models.User
err := gormDB.First(&user).Error
require.NoError(t, err)
// Créer deux sessions
token1 := "token-1"
token2 := "token-2"
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)
tokenHash1 := hashTokenForTest(token1)
tokenHash2 := hashTokenForTest(token2)
// Mettre à jour token1
err = service.UpdateLastActivityIfNeeded(tokenHash1, 100*time.Millisecond)
assert.NoError(t, err)
// Mettre à jour token2 immédiatement (devrait fonctionner car c'est un token différent)
err = service.UpdateLastActivityIfNeeded(tokenHash2, 100*time.Millisecond)
assert.NoError(t, err)
// Vérifier que les deux sessions ont été mises à jour
session1, err := service.GetSessionWithBIGINT(tokenHash1)
require.NoError(t, err)
session2, err := service.GetSessionWithBIGINT(tokenHash2)
require.NoError(t, err)
// Les deux devraient avoir été mises à jour (tokens différents)
assert.True(t, time.Since(session1.LastActivity) < 1*time.Second, "Session1 should be updated")
assert.True(t, time.Since(session2.LastActivity) < 1*time.Second, "Session2 should be updated")
}
// TestHashTokenForMiddleware teste que HashTokenForMiddleware retourne le bon hash
func TestHashTokenForMiddleware(t *testing.T) {
service, _, _ := setupTestSessionServiceForT0204(t)
token := "test-token-hash"
hash1 := service.HashTokenForMiddleware(token)
hash2 := service.HashTokenForMiddleware(token)
// Le hash doit être consistant
assert.Equal(t, hash1, hash2, "Hash should be consistent")
// Le hash doit être différent pour un token différent
token2 := "test-token-hash-2"
hash3 := service.HashTokenForMiddleware(token2)
assert.NotEqual(t, hash1, hash3, "Different tokens should have different hashes")
// Le hash doit avoir une longueur raisonnable (SHA256 = 64 caractères hex)
assert.Equal(t, 64, len(hash1), "SHA256 hash should be 64 characters")
}