veza/veza-backend-api/internal/services/playback_comparison_service_test.go

611 lines
18 KiB
Go

package services
import (
"context"
"github.com/google/uuid"
"testing"
"time"
"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 setupTestPlaybackComparisonServiceDB(t *testing.T) (*gorm.DB, *PlaybackComparisonService) {
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)
logger := zaptest.NewLogger(t)
service := NewPlaybackComparisonService(db, logger)
return db, service
}
func TestNewPlaybackComparisonService(t *testing.T) {
db, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
logger := zaptest.NewLogger(t)
service := NewPlaybackComparisonService(db, logger)
assert.NotNil(t, service)
assert.Equal(t, db, service.db)
assert.NotNil(t, service.logger)
}
func TestNewPlaybackComparisonService_NilLogger(t *testing.T) {
db, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
service := NewPlaybackComparisonService(db, nil)
assert.NotNil(t, service)
assert.NotNil(t, service.logger)
}
func TestPlaybackComparisonService_ComparePeriods(t *testing.T) {
db, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
// Créer 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 analytics pour la période 1 (il y a 2 semaines)
now := time.Now()
period1Start := now.AddDate(0, 0, -14)
analytics1 := &models.PlaybackAnalytics{
TrackID: trackID,
UserID: userID,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 66.67,
StartedAt: period1Start.AddDate(0, 0, 1),
CreatedAt: period1Start.AddDate(0, 0, 1),
}
analytics2 := &models.PlaybackAnalytics{
TrackID: trackID,
UserID: userID,
PlayTime: 150,
PauseCount: 1,
SeekCount: 2,
CompletionRate: 83.33,
StartedAt: period1Start.AddDate(0, 0, 2),
CreatedAt: period1Start.AddDate(0, 0, 2),
}
db.Create(analytics1)
db.Create(analytics2)
// Créer des analytics pour la période 2 (cette semaine)
period2Start := now.AddDate(0, 0, -7)
analytics3 := &models.PlaybackAnalytics{
TrackID: trackID,
UserID: userID,
PlayTime: 180,
PauseCount: 0,
SeekCount: 1,
CompletionRate: 100.0,
StartedAt: period2Start.AddDate(0, 0, 1),
CreatedAt: period2Start.AddDate(0, 0, 1),
}
analytics4 := &models.PlaybackAnalytics{
TrackID: trackID,
UserID: userID,
PlayTime: 170,
PauseCount: 1,
SeekCount: 0,
CompletionRate: 94.44,
StartedAt: period2Start.AddDate(0, 0, 2),
CreatedAt: period2Start.AddDate(0, 0, 2),
}
db.Create(analytics3)
db.Create(analytics4)
// Comparer les périodes
result, err := service.ComparePeriods(ctx, trackID, "week", "week")
require.NoError(t, err)
assert.NotNil(t, result)
assert.NotNil(t, result.Period1)
assert.NotNil(t, result.Period2)
assert.NotNil(t, result.Difference)
assert.NotNil(t, result.PercentageChange)
// Vérifier que period2 a plus de sessions que period1 (car on compare deux semaines différentes)
// Note: Les périodes "week" sont calculées depuis maintenant, donc on compare la même période
// Pour un vrai test, on devrait utiliser des dates personnalisées, mais testons quand même la structure
assert.GreaterOrEqual(t, result.Period2.TotalSessions, int64(0))
}
func TestPlaybackComparisonService_ComparePeriods_InvalidTrackID(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
result, err := service.ComparePeriods(ctx, uuid.Nil, "week", "month")
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid track ID")
assert.Nil(t, result)
}
func TestPlaybackComparisonService_ComparePeriods_TrackNotFound(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
result, err := service.ComparePeriods(ctx, uuid.New(), "week", "month")
assert.Error(t, err)
assert.Contains(t, err.Error(), "track not found")
assert.Nil(t, result)
}
func TestPlaybackComparisonService_ComparePeriods_InvalidPeriod(t *testing.T) {
db, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
// Créer 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)
result, err := service.ComparePeriods(ctx, trackID, "invalid", "week")
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid period")
assert.Nil(t, result)
}
func TestPlaybackComparisonService_CompareTracks(t *testing.T) {
db, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
// Créer user et tracks
userID := uuid.New()
user := &models.User{ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true}
db.Create(user)
track1ID := uuid.New()
track1 := &models.Track{
ID: track1ID,
UserID: userID,
Title: "Track 1",
FilePath: "/track1.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
track2ID := uuid.New()
track2 := &models.Track{
ID: track2ID,
UserID: userID,
Title: "Track 2",
FilePath: "/track2.mp3",
FileSize: 2048,
Format: "MP3",
Duration: 240,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(track1)
db.Create(track2)
// Créer des analytics pour track1
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
analytics1 := &models.PlaybackAnalytics{
TrackID: track1ID,
UserID: userID,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 66.67,
StartedAt: startDate.AddDate(0, 0, 1),
CreatedAt: startDate.AddDate(0, 0, 1),
}
analytics2 := &models.PlaybackAnalytics{
TrackID: track1ID,
UserID: userID,
PlayTime: 150,
PauseCount: 1,
SeekCount: 2,
CompletionRate: 83.33,
StartedAt: startDate.AddDate(0, 0, 2),
CreatedAt: startDate.AddDate(0, 0, 2),
}
db.Create(analytics1)
db.Create(analytics2)
// Créer des analytics pour track2
analytics3 := &models.PlaybackAnalytics{
TrackID: track2ID,
UserID: userID,
PlayTime: 200,
PauseCount: 0,
SeekCount: 1,
CompletionRate: 83.33,
StartedAt: startDate.AddDate(0, 0, 1),
CreatedAt: startDate.AddDate(0, 0, 1),
}
db.Create(analytics3)
// Comparer les tracks
result, err := service.CompareTracks(ctx, track1ID, track2ID, startDate, endDate)
require.NoError(t, err)
assert.NotNil(t, result)
assert.NotNil(t, result.Period1)
assert.NotNil(t, result.Period2)
assert.NotNil(t, result.Difference)
assert.NotNil(t, result.PercentageChange)
// Vérifier que track1 a 2 sessions et track2 a 1 session
assert.Equal(t, int64(2), result.Period1.TotalSessions)
assert.Equal(t, int64(1), result.Period2.TotalSessions)
assert.Equal(t, int64(-1), result.Difference.TotalSessions)
}
func TestPlaybackComparisonService_CompareTracks_InvalidTrackID(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
// Test avec TrackID UUID Nil
result, err := service.CompareTracks(ctx, uuid.Nil, uuid.New(), startDate, endDate)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid track ID 1")
assert.Nil(t, result)
}
func TestPlaybackComparisonService_CompareTracks_TrackNotFound(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
result, err := service.CompareTracks(ctx, uuid.New(), uuid.New(), startDate, endDate)
assert.Error(t, err)
assert.Contains(t, err.Error(), "track not found")
assert.Nil(t, result)
}
func TestPlaybackComparisonService_CompareUsers(t *testing.T) {
db, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
// Créer users et track
user1ID := uuid.New()
user2ID := uuid.New()
user1 := &models.User{ID: user1ID, Username: "user1", Slug: "user1", Email: "user1@example.com", IsActive: true}
user2 := &models.User{ID: user2ID, Username: "user2", Slug: "user2", Email: "user2@example.com", IsActive: true}
db.Create(user1)
db.Create(user2)
trackID := uuid.New()
track := &models.Track{
ID: trackID,
UserID: user1ID,
Title: "Test Track",
FilePath: "/test.mp3",
FileSize: 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
db.Create(track)
// Créer des analytics pour user1
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
analytics1 := &models.PlaybackAnalytics{
TrackID: trackID,
UserID: user1ID,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 66.67,
StartedAt: startDate.AddDate(0, 0, 1),
CreatedAt: startDate.AddDate(0, 0, 1),
}
analytics2 := &models.PlaybackAnalytics{
TrackID: trackID,
UserID: user1ID,
PlayTime: 150,
PauseCount: 1,
SeekCount: 2,
CompletionRate: 83.33,
StartedAt: startDate.AddDate(0, 0, 2),
CreatedAt: startDate.AddDate(0, 0, 2),
}
db.Create(analytics1)
db.Create(analytics2)
// Créer des analytics pour user2
analytics3 := &models.PlaybackAnalytics{
TrackID: trackID,
UserID: user2ID,
PlayTime: 180,
PauseCount: 0,
SeekCount: 1,
CompletionRate: 100.0,
StartedAt: startDate.AddDate(0, 0, 1),
CreatedAt: startDate.AddDate(0, 0, 1),
}
db.Create(analytics3)
// Comparer les users
result, err := service.CompareUsers(ctx, trackID, user1ID, user2ID, startDate, endDate)
require.NoError(t, err)
assert.NotNil(t, result)
assert.NotNil(t, result.Period1)
assert.NotNil(t, result.Period2)
assert.NotNil(t, result.Difference)
assert.NotNil(t, result.PercentageChange)
// Vérifier que user1 a 2 sessions et user2 a 1 session
assert.Equal(t, int64(2), result.Period1.TotalSessions)
assert.Equal(t, int64(1), result.Period2.TotalSessions)
assert.Equal(t, int64(-1), result.Difference.TotalSessions)
}
func TestPlaybackComparisonService_CompareUsers_InvalidTrackID(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
result, err := service.CompareUsers(ctx, uuid.Nil, uuid.New(), uuid.New(), startDate, endDate)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid track ID")
assert.Nil(t, result)
}
func TestPlaybackComparisonService_CompareUsers_InvalidUserID(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
result, err := service.CompareUsers(ctx, uuid.New(), uuid.Nil, uuid.New(), startDate, endDate)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid user ID 1")
assert.Nil(t, result)
}
func TestPlaybackComparisonService_CompareUsers_TrackNotFound(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
now := time.Now()
startDate := now.AddDate(0, 0, -7)
endDate := now
result, err := service.CompareUsers(ctx, uuid.New(), uuid.New(), uuid.New(), startDate, endDate)
assert.Error(t, err)
assert.Contains(t, err.Error(), "track not found")
assert.Nil(t, result)
}
func TestPlaybackComparisonService_CompareUsers_UserNotFound(t *testing.T) {
db, service := setupTestPlaybackComparisonServiceDB(t)
ctx := context.Background()
// Créer user et track
user := &models.User{ID: uuid.New(), Username: "user1", Email: "user1@example.com", IsActive: true}
db.Create(user)
trackID := uuid.New()
track := &models.Track{
ID: trackID,
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.CompareUsers(ctx, trackID, user.ID, uuid.New(), startDate, endDate)
assert.Error(t, err)
assert.Contains(t, err.Error(), "user not found")
assert.Nil(t, result)
}
func TestPlaybackComparisonService_CalculateDifference(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
stats1 := &PlaybackStats{
TotalSessions: 10,
TotalPlayTime: 1000,
AveragePlayTime: 100.0,
TotalPauses: 5,
AveragePauses: 0.5,
TotalSeeks: 8,
AverageSeeks: 0.8,
AverageCompletion: 75.0,
CompletionRate: 60.0,
}
stats2 := &PlaybackStats{
TotalSessions: 15,
TotalPlayTime: 1500,
AveragePlayTime: 100.0,
TotalPauses: 10,
AveragePauses: 0.67,
TotalSeeks: 12,
AverageSeeks: 0.8,
AverageCompletion: 80.0,
CompletionRate: 70.0,
}
difference := service.calculateDifference(stats1, stats2)
assert.NotNil(t, difference)
assert.Equal(t, int64(5), difference.TotalSessions)
assert.Equal(t, int64(500), difference.TotalPlayTime)
assert.Equal(t, float64(0.0), difference.AveragePlayTime)
assert.Equal(t, int64(5), difference.TotalPauses)
assert.InDelta(t, 0.17, difference.AveragePauses, 0.01)
assert.Equal(t, int64(4), difference.TotalSeeks)
assert.Equal(t, float64(0.0), difference.AverageSeeks)
assert.Equal(t, 5.0, difference.AverageCompletion)
assert.Equal(t, 10.0, difference.CompletionRate)
}
func TestPlaybackComparisonService_CalculatePercentageChange(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
stats1 := &PlaybackStats{
TotalSessions: 10,
TotalPlayTime: 1000,
AveragePlayTime: 100.0,
TotalPauses: 5,
AveragePauses: 0.5,
TotalSeeks: 8,
AverageSeeks: 0.8,
AverageCompletion: 75.0,
CompletionRate: 60.0,
}
stats2 := &PlaybackStats{
TotalSessions: 15,
TotalPlayTime: 1500,
AveragePlayTime: 100.0,
TotalPauses: 10,
AveragePauses: 0.67,
TotalSeeks: 12,
AverageSeeks: 0.8,
AverageCompletion: 80.0,
CompletionRate: 70.0,
}
change := service.calculatePercentageChange(stats1, stats2)
assert.NotNil(t, change)
assert.Equal(t, 50.0, change.TotalSessions) // (15-10)/10 * 100 = 50%
assert.Equal(t, 50.0, change.TotalPlayTime) // (1500-1000)/1000 * 100 = 50%
assert.Equal(t, 0.0, change.AveragePlayTime) // (100-100)/100 * 100 = 0%
assert.Equal(t, 100.0, change.TotalPauses) // (10-5)/5 * 100 = 100%
assert.InDelta(t, 34.0, change.AveragePauses, 1.0) // (0.67-0.5)/0.5 * 100 ≈ 34%
assert.Equal(t, 50.0, change.TotalSeeks) // (12-8)/8 * 100 = 50%
assert.Equal(t, 0.0, change.AverageSeeks) // (0.8-0.8)/0.8 * 100 = 0%
assert.InDelta(t, 6.67, change.AverageCompletion, 0.1) // (80-75)/75 * 100 ≈ 6.67%
assert.InDelta(t, 16.67, change.CompletionRate, 0.1) // (70-60)/60 * 100 ≈ 16.67%
}
func TestPlaybackComparisonService_CalculatePercentageChange_ZeroBase(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
stats1 := &PlaybackStats{
TotalSessions: 0,
TotalPlayTime: 0,
}
stats2 := &PlaybackStats{
TotalSessions: 10,
TotalPlayTime: 1000,
}
change := service.calculatePercentageChange(stats1, stats2)
assert.NotNil(t, change)
assert.Equal(t, 100.0, change.TotalSessions) // 100% increase from 0
assert.Equal(t, 100.0, change.TotalPlayTime) // 100% increase from 0
}
func TestPlaybackComparisonService_GetPeriodDates(t *testing.T) {
_, service := setupTestPlaybackComparisonServiceDB(t)
// Test "today"
start, end, err := service.getPeriodDates("today")
require.NoError(t, err)
assert.True(t, start.Before(end) || start.Equal(end))
assert.True(t, end.Before(time.Now().Add(time.Minute)) || end.Equal(time.Now()))
// Test "week"
start, end, err = service.getPeriodDates("week")
require.NoError(t, err)
assert.True(t, start.Before(end))
duration := end.Sub(start)
assert.True(t, duration >= 6*24*time.Hour && duration <= 7*24*time.Hour)
// Test "month"
start, end, err = service.getPeriodDates("month")
require.NoError(t, err)
assert.True(t, start.Before(end))
duration = end.Sub(start)
// La durée peut varier légèrement selon le moment où le test est exécuté
assert.True(t, duration >= 28*24*time.Hour && duration <= 31*24*time.Hour)
// Test "year"
start, end, err = service.getPeriodDates("year")
require.NoError(t, err)
assert.True(t, start.Before(end))
duration = end.Sub(start)
// La durée peut varier légèrement selon le moment où le test est exécuté
assert.True(t, duration >= 363*24*time.Hour && duration <= 366*24*time.Hour)
// Test invalid period
_, _, err = service.getPeriodDates("invalid")
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid period")
}