veza/veza-backend-api/internal/services/playback_aggregation_service_test.go
2025-12-16 11:23:49 -05:00

592 lines
16 KiB
Go

package services
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"veza-backend-api/internal/models"
)
func setupTestPlaybackAggregationServiceDB(t *testing.T) *gorm.DB {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
db.Exec("PRAGMA foreign_keys = ON")
err = db.AutoMigrate(&models.User{}, &models.Track{}, &models.PlaybackAnalytics{})
require.NoError(t, err)
return db
}
func TestNewPlaybackAggregationService(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
assert.NotNil(t, service)
assert.Equal(t, db, service.db)
}
func TestPlaybackAggregationService_AggregateByPeriod_Day(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
// Créer test user et track
userID := uuid.New()
user := &models.User{ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
trackID := uuid.New()
track := &models.Track{
ID: trackID,
UserID: userID,
Title: "Test Track",
FilePath: "/test.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(track)
// Créer des sessions sur différentes dates
now := time.Now()
sessions := []models.PlaybackAnalytics{
{
TrackID: trackID,
UserID: userID,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: now.AddDate(0, 0, -2),
CreatedAt: now.AddDate(0, 0, -2),
},
{
TrackID: trackID,
UserID: userID,
PlayTime: 150,
PauseCount: 1,
SeekCount: 2,
CompletionRate: 90.0,
StartedAt: now.AddDate(0, 0, -2),
CreatedAt: now.AddDate(0, 0, -2),
},
{
TrackID: trackID,
UserID: userID,
PlayTime: 100,
PauseCount: 3,
SeekCount: 1,
CompletionRate: 60.0,
StartedAt: now.AddDate(0, 0, -1),
CreatedAt: now.AddDate(0, 0, -1),
},
}
for _, session := range sessions {
db.Create(&session)
}
startDate := now.AddDate(0, 0, -3)
endDate := now
result, err := service.AggregateByPeriod(context.Background(), trackID, PeriodDay, startDate, endDate)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, int64(3), result.TotalSessions)
assert.Equal(t, int64(370), result.TotalPlayTime)
assert.InDelta(t, 123.33, result.AveragePlayTime, 0.1)
// Vérifier qu'il y a 2 périodes (2 jours différents)
assert.Len(t, result.Periods, 2)
// Vérifier la première période (jour -2)
period1 := result.Periods[0]
assert.Equal(t, int64(2), period1.Sessions)
assert.Equal(t, int64(270), period1.TotalPlayTime)
assert.InDelta(t, 135.0, period1.AveragePlayTime, 0.1)
}
func TestPlaybackAggregationService_AggregateByPeriod_Week(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
track := &models.Track{
ID: uuid.New(),
UserID: user.ID,
Title: "Test Track",
FilePath: "/test.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(track)
now := time.Now()
startDate := now.AddDate(0, 0, -14)
endDate := now
// Créer des sessions dans différentes semaines
sessions := []models.PlaybackAnalytics{
{
TrackID: track.ID,
UserID: user.ID,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: startDate.AddDate(0, 0, 1),
CreatedAt: startDate.AddDate(0, 0, 1),
},
{
TrackID: track.ID,
UserID: user.ID,
PlayTime: 150,
PauseCount: 1,
SeekCount: 2,
CompletionRate: 90.0,
StartedAt: startDate.AddDate(0, 0, 8),
CreatedAt: startDate.AddDate(0, 0, 8),
},
}
for _, session := range sessions {
db.Create(&session)
}
result, err := service.AggregateByPeriod(context.Background(), track.ID, PeriodWeek, startDate, endDate)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, int64(2), result.TotalSessions)
}
func TestPlaybackAggregationService_AggregateByPeriod_Month(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
track := &models.Track{
ID: uuid.New(),
UserID: user.ID,
Title: "Test Track",
FilePath: "/test.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(track)
now := time.Now()
startDate := now.AddDate(0, -2, 0)
endDate := now
// Créer des sessions dans différents mois
sessions := []models.PlaybackAnalytics{
{
TrackID: track.ID,
UserID: user.ID,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: startDate.AddDate(0, 0, 1),
CreatedAt: startDate.AddDate(0, 0, 1),
},
{
TrackID: track.ID,
UserID: user.ID,
PlayTime: 150,
PauseCount: 1,
SeekCount: 2,
CompletionRate: 90.0,
StartedAt: startDate.AddDate(0, 1, 0),
CreatedAt: startDate.AddDate(0, 1, 0),
},
}
for _, session := range sessions {
db.Create(&session)
}
result, err := service.AggregateByPeriod(context.Background(), track.ID, PeriodMonth, startDate, endDate)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, int64(2), result.TotalSessions)
}
func TestPlaybackAggregationService_AggregateByPeriod_InvalidTrackID(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
_, err := service.AggregateByPeriod(context.Background(), uuid.Nil, PeriodDay, startDate, endDate)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid track ID")
}
func TestPlaybackAggregationService_AggregateByPeriod_TrackNotFound(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
_, err := service.AggregateByPeriod(context.Background(), uuid.New(), PeriodDay, startDate, endDate)
assert.Error(t, err)
assert.Contains(t, err.Error(), "track not found")
}
func TestPlaybackAggregationService_AggregateByPeriod_InvalidPeriod(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
track := &models.Track{
ID: uuid.New(),
UserID: user.ID,
Title: "Test Track",
FilePath: "/test.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(track)
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
_, err := service.AggregateByPeriod(context.Background(), track.ID, PeriodType("invalid"), startDate, endDate)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid period type")
}
func TestPlaybackAggregationService_AggregateByPeriod_NoData(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
track := &models.Track{
ID: uuid.New(),
UserID: user.ID,
Title: "Test Track",
FilePath: "/test.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(track)
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
result, err := service.AggregateByPeriod(context.Background(), track.ID, PeriodDay, startDate, endDate)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, int64(0), result.TotalSessions)
assert.Len(t, result.Periods, 0)
}
func TestPlaybackAggregationService_AggregateByPeriod_Trends(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
track := &models.Track{
ID: uuid.New(),
UserID: user.ID,
Title: "Test Track",
FilePath: "/test.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(track)
now := time.Now()
// Créer des sessions avec des valeurs croissantes pour tester les tendances
// Important: créer dans des jours différents pour avoir plusieurs périodes
sessions := []models.PlaybackAnalytics{
{
TrackID: track.ID,
UserID: user.ID,
PlayTime: 100,
PauseCount: 1,
SeekCount: 1,
CompletionRate: 50.0,
StartedAt: now.AddDate(0, 0, -3),
CreatedAt: now.AddDate(0, 0, -3),
},
{
TrackID: track.ID,
UserID: user.ID,
PlayTime: 200,
PauseCount: 2,
SeekCount: 2,
CompletionRate: 100.0,
StartedAt: now.AddDate(0, 0, -1),
CreatedAt: now.AddDate(0, 0, -1),
},
}
for _, session := range sessions {
db.Create(&session)
}
startDate := now.AddDate(0, 0, -4)
endDate := now
result, err := service.AggregateByPeriod(context.Background(), track.ID, PeriodDay, startDate, endDate)
require.NoError(t, err)
assert.NotNil(t, result)
// Les tendances ne sont calculées que s'il y a au moins 2 périodes
if len(result.Periods) >= 2 {
assert.NotNil(t, result.Trends)
// Vérifier que les tendances sont calculées (croissance attendue)
if result.Trends != nil {
// Les tendances peuvent être positives (croissance) ou négatives (décroissance)
// On vérifie juste qu'elles sont calculées (non nulles si les valeurs changent)
assert.NotNil(t, result.Trends.SessionsTrend)
assert.NotNil(t, result.Trends.PlayTimeTrend)
assert.NotNil(t, result.Trends.CompletionTrend)
}
} else {
// Si moins de 2 périodes, les tendances ne sont pas calculées
assert.Nil(t, result.Trends)
}
}
func TestPlaybackAggregationService_AggregateByDateRange(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
track := &models.Track{
ID: uuid.New(),
UserID: user.ID,
Title: "Test Track",
FilePath: "/test.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(track)
now := time.Now()
sessions := []models.PlaybackAnalytics{
{
TrackID: track.ID,
UserID: user.ID,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: now.AddDate(0, 0, -2),
CreatedAt: now.AddDate(0, 0, -2),
},
{
TrackID: track.ID,
UserID: user.ID,
PlayTime: 150,
PauseCount: 1,
SeekCount: 2,
CompletionRate: 90.0,
StartedAt: now.AddDate(0, 0, -1),
CreatedAt: now.AddDate(0, 0, -1),
},
}
for _, session := range sessions {
db.Create(&session)
}
startDate := now.AddDate(0, 0, -3)
endDate := now
result, err := service.AggregateByDateRange(context.Background(), track.ID, startDate, endDate)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, int64(2), result.Sessions)
assert.Equal(t, int64(270), result.TotalPlayTime)
assert.InDelta(t, 135.0, result.AveragePlayTime, 0.1)
assert.InDelta(t, 82.5, result.AverageCompletion, 0.1)
}
func TestPlaybackAggregationService_GetTopTracksByPlayback(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
userID := uuid.New()
user := &models.User{ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
// Créer plusieurs tracks
track1ID := uuid.New()
track2ID := uuid.New()
tracks := []models.Track{
{ID: track1ID, UserID: userID, Title: "Track 1", FilePath: "/1.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted},
{ID: track2ID, UserID: userID, Title: "Track 2", FilePath: "/2.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted},
}
for _, track := range tracks {
db.Create(&track)
}
now := time.Now()
// Créer plus de sessions pour le track 1
sessions := []models.PlaybackAnalytics{
{TrackID: track1ID, UserID: userID, PlayTime: 120, CompletionRate: 75.0, StartedAt: now, CreatedAt: now},
{TrackID: track1ID, UserID: userID, PlayTime: 150, CompletionRate: 90.0, StartedAt: now, CreatedAt: now},
{TrackID: track2ID, UserID: userID, PlayTime: 100, CompletionRate: 60.0, StartedAt: now, CreatedAt: now},
}
for _, session := range sessions {
db.Create(&session)
}
result, err := service.GetTopTracksByPlayback(context.Background(), 10, nil, nil)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Len(t, result, 2)
// Vérifier que le track 1 est en premier (plus de sessions)
// track_id is now uuid.UUID, not int64
trackID, ok := result[0]["track_id"].(uuid.UUID)
require.True(t, ok, "track_id should be uuid.UUID")
assert.Equal(t, track1ID, trackID)
assert.Equal(t, int64(2), result[0]["sessions"])
}
func TestPlaybackAggregationService_GetTopTracksByPlayback_WithDateRange(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
track := &models.Track{
ID: uuid.New(),
UserID: user.ID,
Title: "Test Track",
FilePath: "/test.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(track)
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
// Créer une session dans la plage
session := models.PlaybackAnalytics{
TrackID: track.ID,
UserID: user.ID,
PlayTime: 120,
CompletionRate: 75.0,
StartedAt: now.AddDate(0, 0, -3),
CreatedAt: now.AddDate(0, 0, -3),
}
db.Create(&session)
result, err := service.GetTopTracksByPlayback(context.Background(), 10, &startDate, &endDate)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Len(t, result, 1)
assert.Equal(t, track.ID, result[0]["track_id"])
}
func TestPlaybackAggregationService_GetTopTracksByPlayback_DefaultLimit(t *testing.T) {
db := setupTestPlaybackAggregationServiceDB(t)
logger := zaptest.NewLogger(t)
service := NewPlaybackAggregationService(db, logger)
user := &models.User{ID: uuid.New(), Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
// Créer plusieurs tracks
for i := 1; i <= 15; i++ {
trackID := uuid.New()
track := models.Track{
ID: trackID,
UserID: user.ID,
Title: "Track " + string(rune(i)),
FilePath: "/test.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(&track)
session := models.PlaybackAnalytics{
TrackID: trackID,
UserID: user.ID,
PlayTime: 120,
CompletionRate: 75.0,
StartedAt: time.Now(),
CreatedAt: time.Now(),
}
db.Create(&session)
}
result, err := service.GetTopTracksByPlayback(context.Background(), 0, nil, nil)
require.NoError(t, err)
assert.NotNil(t, result)
// Devrait utiliser la limite par défaut de 10
assert.LessOrEqual(t, len(result), 10)
}