2025-12-03 19:29:37 +00:00
|
|
|
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,
|
2025-12-06 16:21:59 +00:00
|
|
|
revoked_at TIMESTAMP,
|
2025-12-03 19:29:37 +00:00
|
|
|
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,
|
2025-12-06 16:21:59 +00:00
|
|
|
revoked_at TIMESTAMP,
|
2025-12-03 19:29:37 +00:00
|
|
|
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,
|
2025-12-06 16:21:59 +00:00
|
|
|
revoked_at TIMESTAMP,
|
2025-12-03 19:29:37 +00:00
|
|
|
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,
|
2025-12-06 16:21:59 +00:00
|
|
|
revoked_at TIMESTAMP,
|
2025-12-03 19:29:37 +00:00
|
|
|
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
|
|
|
|
|
}
|