veza/veza-backend-api/internal/jobs/cleanup_sessions_test.go

244 lines
6.8 KiB
Go

package jobs
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"veza-backend-api/internal/database"
)
// MockSessionServiceForCleanup pour les tests
type MockSessionServiceForCleanup struct {
mock.Mock
}
func (m *MockSessionServiceForCleanup) CleanupExpiredSessions(ctx context.Context) (int64, error) {
args := m.Called(ctx)
return args.Get(0).(int64), args.Error(1)
}
// TestCleanupExpiredSessions_Success teste le nettoyage réussi des sessions expirées
func TestCleanupExpiredSessions_Success(t *testing.T) {
// Créer une base de données de test
gormDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
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,
token_hash TEXT NOT NULL,
ip_address TEXT,
user_agent TEXT,
expires_at TIMESTAMP NOT NULL,
revoked_at TIMESTAMP,
last_activity TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
`).Error
require.NoError(t, err)
// Insérer des sessions expirées et non expirées
now := time.Now()
expiredTime := now.Add(-1 * time.Hour)
futureTime := now.Add(24 * time.Hour)
err = gormDB.Exec(`
INSERT INTO sessions (user_id, token_hash, expires_at, created_at)
VALUES
(1, 'hash1', ?, ?),
(1, 'hash2', ?, ?),
(2, 'hash3', ?, ?)
`, expiredTime, now, expiredTime, now, futureTime, now).Error
require.NoError(t, err)
sqlDB, err := gormDB.DB()
require.NoError(t, err)
testDB := &database.Database{
DB: sqlDB,
}
logger := zap.NewNop()
// Exécuter le nettoyage
err = CleanupExpiredSessions(testDB, logger)
assert.NoError(t, err)
// Vérifier que les sessions expirées ont été supprimées
var count int64
err = gormDB.Raw("SELECT COUNT(*) FROM sessions").Scan(&count).Error
require.NoError(t, err)
assert.Equal(t, int64(1), count, "Only one non-expired session should remain")
}
// TestCleanupExpiredSessions_NoExpiredSessions teste le cas où il n'y a pas de sessions expirées
func TestCleanupExpiredSessions_NoExpiredSessions(t *testing.T) {
// Créer une base de données de test
gormDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
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,
token_hash TEXT NOT NULL,
expires_at TIMESTAMP NOT NULL,
revoked_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
`).Error
require.NoError(t, err)
// Insérer seulement des sessions non expirées
now := time.Now()
futureTime1 := now.Add(24 * time.Hour)
futureTime2 := now.Add(48 * time.Hour)
err = gormDB.Exec(`
INSERT INTO sessions (user_id, token_hash, expires_at, created_at)
VALUES
(1, 'hash1', ?, ?),
(1, 'hash2', ?, ?)
`, futureTime1, now, futureTime2, now).Error
require.NoError(t, err)
sqlDB, err := gormDB.DB()
require.NoError(t, err)
testDB := &database.Database{
DB: sqlDB,
}
logger := zap.NewNop()
// Exécuter le nettoyage
err = CleanupExpiredSessions(testDB, logger)
assert.NoError(t, err)
// Vérifier que toutes les sessions sont toujours là
var count int64
err = gormDB.Raw("SELECT COUNT(*) FROM sessions").Scan(&count).Error
require.NoError(t, err)
assert.Equal(t, int64(2), count, "All sessions should remain")
}
// TestCleanupExpiredSessions_EmptyDatabase teste le cas où la base de données est vide
func TestCleanupExpiredSessions_EmptyDatabase(t *testing.T) {
// Créer une base de données de test
gormDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
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,
token_hash TEXT NOT NULL,
expires_at TIMESTAMP NOT NULL,
revoked_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
`).Error
require.NoError(t, err)
sqlDB, err := gormDB.DB()
require.NoError(t, err)
testDB := &database.Database{
DB: sqlDB,
}
logger := zap.NewNop()
// Exécuter le nettoyage
err = CleanupExpiredSessions(testDB, logger)
assert.NoError(t, err)
// Vérifier qu'il n'y a pas de sessions
var count int64
err = gormDB.Raw("SELECT COUNT(*) FROM sessions").Scan(&count).Error
require.NoError(t, err)
assert.Equal(t, int64(0), count, "No sessions should exist")
}
// TestScheduleCleanupJob_Execution teste que le job est programmé correctement
func TestScheduleCleanupJob_Execution(t *testing.T) {
// Créer une base de données de test
gormDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
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,
token_hash TEXT NOT NULL,
expires_at TIMESTAMP NOT NULL,
revoked_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
`).Error
require.NoError(t, err)
sqlDB, err := gormDB.DB()
require.NoError(t, err)
testDB := &database.Database{
DB: sqlDB,
}
logger := zap.NewNop()
// Programmer le job avec un ticker très court pour les tests (1 seconde au lieu de 24 heures)
// Note: Dans un vrai test, on pourrait utiliser un mock ticker, mais pour simplifier
// on teste juste que la fonction s'exécute sans erreur
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
executed := make(chan bool, 1)
go func() {
// Exécuter immédiatement au démarrage
if err := CleanupExpiredSessions(testDB, logger); err != nil {
logger.Error("Initial sessions cleanup job failed", zap.Error(err))
}
executed <- true
// Attendre un tick
<-ticker.C
if err := CleanupExpiredSessions(testDB, logger); err != nil {
logger.Error("Scheduled sessions cleanup job failed", zap.Error(err))
}
executed <- true
}()
// Attendre que le job initial soit exécuté
select {
case <-executed:
assert.True(t, true, "Initial cleanup job should execute")
case <-time.After(1 * time.Second):
t.Fatal("Initial cleanup job did not execute in time")
}
// Attendre que le job programmé soit exécuté
select {
case <-executed:
assert.True(t, true, "Scheduled cleanup job should execute")
case <-time.After(2 * time.Second):
t.Fatal("Scheduled cleanup job did not execute in time")
}
// Vérifier que la fonction ScheduleCleanupJob peut être appelée sans erreur
// Note: On ne peut pas vraiment tester qu'elle s'exécute en continu sans bloquer le test
ScheduleSessionCleanupJob(testDB, logger)
time.Sleep(100 * time.Millisecond) // Attendre un peu pour que la goroutine démarre
}