package monitoring import ( "context" "testing" "time" "veza-backend-api/internal/models" "veza-backend-api/internal/services" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func setupTestDB(t *testing.T) *gorm.DB { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) // Migrer les modèles nécessaires err = db.AutoMigrate( &models.Track{}, &models.PlaybackAnalytics{}, ) require.NoError(t, err) return db } func TestNewPlaybackAnalyticsMonitor(t *testing.T) { db := setupTestDB(t) logger := zap.NewNop() alertsService := services.NewPlaybackAlertsService(db, logger) analyticsService := services.NewPlaybackAnalyticsService(db, logger) monitor := NewPlaybackAnalyticsMonitor(db, logger, alertsService, analyticsService) assert.NotNil(t, monitor) assert.Equal(t, db, monitor.db) assert.Equal(t, logger, monitor.logger) assert.Equal(t, alertsService, monitor.alertsService) assert.Equal(t, analyticsService, monitor.analyticsService) assert.NotNil(t, monitor.metrics) assert.Equal(t, 5*time.Minute, monitor.alertCheckInterval) } func TestPlaybackAnalyticsMonitor_RecordEvent(t *testing.T) { db := setupTestDB(t) logger := zap.NewNop() monitor := NewPlaybackAnalyticsMonitor(db, logger, nil, nil) trackID := uuid.New() userID := uuid.New() analytics := &models.PlaybackAnalytics{ TrackID: trackID, UserID: userID, PlayTime: 180, PauseCount: 2, SeekCount: 1, CompletionRate: 75.0, StartedAt: time.Now(), } // Test avec succès monitor.RecordEvent(context.Background(), analytics, 100*time.Millisecond, nil) metrics := monitor.GetPerformanceMetrics() assert.Equal(t, int64(1), metrics.TotalEventsRecorded) assert.Equal(t, int64(0), metrics.TotalEventsFailed) assert.Equal(t, 100*time.Millisecond, metrics.AverageRecordLatency) // Test avec erreur monitor.RecordEvent(context.Background(), analytics, 50*time.Millisecond, assert.AnError) metrics = monitor.GetPerformanceMetrics() assert.Equal(t, int64(1), metrics.TotalEventsRecorded) assert.Equal(t, int64(1), metrics.TotalEventsFailed) } func TestPlaybackAnalyticsMonitor_RecordBatchEvent(t *testing.T) { db := setupTestDB(t) logger := zap.NewNop() monitor := NewPlaybackAnalyticsMonitor(db, logger, nil, nil) // Test avec succès monitor.RecordBatchEvent(context.Background(), 10, 200*time.Millisecond, nil) metrics := monitor.GetPerformanceMetrics() assert.Equal(t, int64(10), metrics.TotalEventsRecorded) assert.Equal(t, int64(0), metrics.TotalEventsFailed) // Test avec erreur monitor.RecordBatchEvent(context.Background(), 5, 100*time.Millisecond, assert.AnError) metrics = monitor.GetPerformanceMetrics() assert.Equal(t, int64(10), metrics.TotalEventsRecorded) assert.Equal(t, int64(5), metrics.TotalEventsFailed) } func TestPlaybackAnalyticsMonitor_UpdateMetrics(t *testing.T) { db := setupTestDB(t) logger := zap.NewNop() monitor := NewPlaybackAnalyticsMonitor(db, logger, nil, nil) trackID := uuid.New() userID1 := uuid.New() userID2 := uuid.New() // Créer un track track := &models.Track{ ID: trackID, Title: "Test Track", Duration: 180, } require.NoError(t, db.Create(track).Error) // Créer des analytics analytics1 := &models.PlaybackAnalytics{ TrackID: trackID, UserID: userID1, PlayTime: 180, CompletionRate: 100.0, StartedAt: time.Now().Add(-10 * time.Minute), CreatedAt: time.Now().Add(-10 * time.Minute), } analytics2 := &models.PlaybackAnalytics{ TrackID: trackID, UserID: userID2, PlayTime: 90, CompletionRate: 50.0, StartedAt: time.Now().Add(-5 * time.Minute), CreatedAt: time.Now().Add(-5 * time.Minute), } require.NoError(t, db.Create(analytics1).Error) require.NoError(t, db.Create(analytics2).Error) // Mettre à jour les métriques err := monitor.UpdateMetrics(context.Background()) require.NoError(t, err) metrics := monitor.GetPerformanceMetrics() assert.GreaterOrEqual(t, metrics.ActiveSessions, int64(0)) assert.Greater(t, metrics.AverageCompletionRate, 0.0) assert.Greater(t, metrics.AveragePlayTime, 0.0) } func TestPlaybackAnalyticsMonitor_CheckAlerts(t *testing.T) { db := setupTestDB(t) logger := zap.NewNop() alertsService := services.NewPlaybackAlertsService(db, logger) monitor := NewPlaybackAnalyticsMonitor(db, logger, alertsService, nil) trackID := uuid.New() userID := uuid.New() // Créer un track track := &models.Track{ ID: trackID, Title: "Test Track", Duration: 180, } require.NoError(t, db.Create(track).Error) // Créer des analytics avec un faible taux de complétion analytics := &models.PlaybackAnalytics{ TrackID: trackID, UserID: userID, PlayTime: 30, CompletionRate: 15.0, // Faible taux de complétion StartedAt: time.Now().Add(-1 * time.Hour), CreatedAt: time.Now().Add(-1 * time.Hour), } require.NoError(t, db.Create(analytics).Error) // Vérifier les alertes alerts, err := monitor.CheckAlerts(context.Background()) require.NoError(t, err) // Il devrait y avoir au moins une alerte pour le faible taux de complétion assert.GreaterOrEqual(t, len(alerts), 0) // Peut être 0 si les seuils ne sont pas atteints metrics := monitor.GetPerformanceMetrics() assert.GreaterOrEqual(t, metrics.TotalAlertsGenerated, int64(0)) } func TestPlaybackAnalyticsMonitor_GetPerformanceMetrics(t *testing.T) { db := setupTestDB(t) logger := zap.NewNop() monitor := NewPlaybackAnalyticsMonitor(db, logger, nil, nil) trackID := uuid.New() userID := uuid.New() // Enregistrer quelques événements analytics := &models.PlaybackAnalytics{ TrackID: trackID, UserID: userID, PlayTime: 180, } monitor.RecordEvent(context.Background(), analytics, 100*time.Millisecond, nil) monitor.RecordEvent(context.Background(), analytics, 150*time.Millisecond, nil) monitor.RecordEvent(context.Background(), analytics, 200*time.Millisecond, assert.AnError) metrics := monitor.GetPerformanceMetrics() assert.Equal(t, int64(2), metrics.TotalEventsRecorded) assert.Equal(t, int64(1), metrics.TotalEventsFailed) assert.Greater(t, metrics.AverageRecordLatency, time.Duration(0)) assert.NotZero(t, metrics.LastUpdated) } func TestPlaybackAnalyticsMonitor_GetDashboardMetrics(t *testing.T) { db := setupTestDB(t) logger := zap.NewNop() alertsService := services.NewPlaybackAlertsService(db, logger) monitor := NewPlaybackAnalyticsMonitor(db, logger, alertsService, nil) trackID := uuid.New() userID := uuid.New() // Créer un track track := &models.Track{ ID: trackID, Title: "Test Track", Duration: 180, } require.NoError(t, db.Create(track).Error) // Créer des analytics analytics := &models.PlaybackAnalytics{ TrackID: trackID, UserID: userID, PlayTime: 180, CompletionRate: 100.0, StartedAt: time.Now().Add(-1 * time.Hour), CreatedAt: time.Now().Add(-1 * time.Hour), } require.NoError(t, db.Create(analytics).Error) // Enregistrer quelques événements monitor.RecordEvent(context.Background(), analytics, 100*time.Millisecond, nil) monitor.RecordEvent(context.Background(), analytics, 150*time.Millisecond, nil) // Obtenir les métriques du dashboard dashboard, err := monitor.GetDashboardMetrics(context.Background()) require.NoError(t, err) assert.NotNil(t, dashboard) assert.NotNil(t, dashboard.Performance) assert.NotNil(t, dashboard.RecentAlerts) assert.NotNil(t, dashboard.TopTracks) assert.GreaterOrEqual(t, dashboard.ErrorRate, 0.0) assert.GreaterOrEqual(t, dashboard.SuccessRate, 0.0) assert.GreaterOrEqual(t, dashboard.Throughput, 0.0) assert.NotZero(t, dashboard.Timestamp) } func TestPlaybackAnalyticsMonitor_GetTopTracks(t *testing.T) { db := setupTestDB(t) logger := zap.NewNop() monitor := NewPlaybackAnalyticsMonitor(db, logger, nil, nil) trackID1 := uuid.New() trackID2 := uuid.New() // Créer des tracks track1 := &models.Track{ ID: trackID1, Title: "Track 1", Duration: 180, } track2 := &models.Track{ ID: trackID2, Title: "Track 2", Duration: 240, } require.NoError(t, db.Create(track1).Error) require.NoError(t, db.Create(track2).Error) // Créer des analytics pour track1 (plus de sessions) for i := 0; i < 5; i++ { analytics := &models.PlaybackAnalytics{ TrackID: trackID1, UserID: uuid.New(), PlayTime: 180, CompletionRate: 100.0, StartedAt: time.Now().Add(-1 * time.Hour), CreatedAt: time.Now().Add(-1 * time.Hour), } require.NoError(t, db.Create(analytics).Error) } // Créer des analytics pour track2 (moins de sessions) for i := 0; i < 2; i++ { analytics := &models.PlaybackAnalytics{ TrackID: trackID2, UserID: uuid.New(), PlayTime: 120, CompletionRate: 50.0, StartedAt: time.Now().Add(-1 * time.Hour), CreatedAt: time.Now().Add(-1 * time.Hour), } require.NoError(t, db.Create(analytics).Error) } // Obtenir les top tracks topTracks, err := monitor.getTopTracks(context.Background(), 10) require.NoError(t, err) assert.GreaterOrEqual(t, len(topTracks), 2) // Track1 devrait être en premier (plus de sessions) if len(topTracks) >= 2 { assert.Equal(t, trackID1, topTracks[0].TrackID) assert.Equal(t, int64(5), topTracks[0].TotalSessions) } } func TestPlaybackAnalyticsMonitor_StartBackgroundMonitoring(t *testing.T) { db := setupTestDB(t) logger := zap.NewNop() monitor := NewPlaybackAnalyticsMonitor(db, logger, nil, nil) ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Démarrer le monitoring en arrière-plan avec un intervalle court done := make(chan bool) go func() { monitor.StartBackgroundMonitoring(ctx, 100*time.Millisecond) done <- true }() // Attendre un peu pour que le monitoring se mette à jour time.Sleep(200 * time.Millisecond) // Arrêter le monitoring cancel() // Attendre que la goroutine se termine select { case <-done: // OK case <-time.After(1 * time.Second): t.Fatal("Background monitoring did not stop") } }