veza/veza-backend-api/internal/services/playback_export_service_test.go
2026-03-05 23:03:43 +01:00

521 lines
13 KiB
Go

package services
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
"veza-backend-api/internal/models"
)
func TestNewPlaybackExportService(t *testing.T) {
logger := zaptest.NewLogger(t)
service := NewPlaybackExportService(logger)
assert.NotNil(t, service)
assert.NotNil(t, service.logger)
}
func TestNewPlaybackExportService_NilLogger(t *testing.T) {
service := NewPlaybackExportService(nil)
assert.NotNil(t, service)
assert.NotNil(t, service.logger)
}
func TestPlaybackExportService_ExportCSV_Success(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
// Créer un répertoire temporaire
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "test.csv")
// Créer des données de test
now := time.Now()
id1 := uuid.New()
trackID := uuid.New()
userID1 := uuid.New()
id2 := uuid.New()
userID2 := uuid.New()
analytics := []models.PlaybackAnalytics{
{
ID: id1,
TrackID: trackID,
UserID: userID1,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
{
ID: id2,
TrackID: trackID,
UserID: userID2,
PlayTime: 150,
PauseCount: 1,
SeekCount: 2,
CompletionRate: 90.0,
StartedAt: now,
EndedAt: &now,
CreatedAt: now,
},
}
err := service.ExportCSV(analytics, filename)
require.NoError(t, err)
// Vérifier que le fichier existe
_, err = os.Stat(filename)
assert.NoError(t, err)
// Vérifier le contenu du fichier
data, err := os.ReadFile(filename)
require.NoError(t, err)
assert.Contains(t, string(data), "ID")
assert.Contains(t, string(data), "Track ID")
assert.Contains(t, string(data), id1.String())
assert.Contains(t, string(data), "120")
}
func TestPlaybackExportService_ExportCSV_EmptyData(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "test.csv")
err := service.ExportCSV([]models.PlaybackAnalytics{}, filename)
assert.Error(t, err)
assert.Contains(t, err.Error(), "no analytics data")
}
func TestPlaybackExportService_ExportJSON_Success(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "test.json")
now := time.Now()
id := uuid.New()
trackID := uuid.New()
userID := uuid.New()
analytics := []models.PlaybackAnalytics{
{
ID: id,
TrackID: trackID,
UserID: userID,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
}
err := service.ExportJSON(analytics, filename)
require.NoError(t, err)
// Vérifier que le fichier existe
_, err = os.Stat(filename)
assert.NoError(t, err)
// Vérifier que c'est du JSON valide
data, err := os.ReadFile(filename)
require.NoError(t, err)
// Le JSON est indenté, donc les valeurs peuvent avoir des espaces
assert.Contains(t, string(data), `"id": "`+id.String()+`"`)
assert.Contains(t, string(data), `"track_id": "`+trackID.String()+`"`)
assert.Contains(t, string(data), `"play_time": 120`)
}
func TestPlaybackExportService_ExportJSON_EmptyData(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "test.json")
err := service.ExportJSON([]models.PlaybackAnalytics{}, filename)
assert.Error(t, err)
assert.Contains(t, err.Error(), "no analytics data")
}
func TestPlaybackExportService_ExportReport_CSV(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "report.csv")
now := time.Now()
analytics := []models.PlaybackAnalytics{
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 171, // 95% de 180
PauseCount: 1,
SeekCount: 2,
CompletionRate: 95.0,
StartedAt: now,
EndedAt: &now,
CreatedAt: now,
},
}
err := service.ExportReport(analytics, filename, FormatCSV)
require.NoError(t, err)
// Vérifier que le fichier existe
_, err = os.Stat(filename)
assert.NoError(t, err)
// Vérifier le contenu
data, err := os.ReadFile(filename)
require.NoError(t, err)
assert.Contains(t, string(data), "Total Sessions")
assert.Contains(t, string(data), "Average Play Time")
assert.Contains(t, string(data), "Completion Rate")
}
func TestPlaybackExportService_ExportReport_JSON(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "report.json")
now := time.Now()
analytics := []models.PlaybackAnalytics{
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
}
err := service.ExportReport(analytics, filename, FormatJSON)
require.NoError(t, err)
// Vérifier que le fichier existe
_, err = os.Stat(filename)
assert.NoError(t, err)
// Vérifier que c'est du JSON valide avec statistiques
data, err := os.ReadFile(filename)
require.NoError(t, err)
assert.Contains(t, string(data), `"statistics"`)
assert.Contains(t, string(data), `"analytics"`)
assert.Contains(t, string(data), `"total_sessions"`)
}
func TestPlaybackExportService_ExportReport_InvalidFormat(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "report.txt")
now := time.Now()
analytics := []models.PlaybackAnalytics{
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 120,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
}
err := service.ExportReport(analytics, filename, ExportFormat("invalid"))
assert.Error(t, err)
assert.Contains(t, err.Error(), "unsupported export format")
}
func TestPlaybackExportService_ExportReport_EmptyData(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "report.csv")
err := service.ExportReport([]models.PlaybackAnalytics{}, filename, FormatCSV)
assert.Error(t, err)
assert.Contains(t, err.Error(), "no analytics data")
}
func TestPlaybackExportService_calculateReportStats(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
now := time.Now()
analytics := []models.PlaybackAnalytics{
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 150,
PauseCount: 1,
SeekCount: 2,
CompletionRate: 95.0, // ≥95%
StartedAt: now,
EndedAt: &now,
CreatedAt: now,
},
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 100,
PauseCount: 0,
SeekCount: 1,
CompletionRate: 92.0, // ≥90% mais <95%
StartedAt: now,
CreatedAt: now,
},
}
stats := service.calculateReportStats(analytics)
assert.Equal(t, 3, stats.TotalSessions)
assert.Equal(t, int64(370), stats.TotalPlayTime)
assert.InDelta(t, 123.33, stats.AveragePlayTime, 0.1)
assert.Equal(t, int64(3), stats.TotalPauses)
assert.InDelta(t, 1.0, stats.AveragePauses, 0.1)
assert.Equal(t, int64(6), stats.TotalSeeks)
assert.InDelta(t, 2.0, stats.AverageSeeks, 0.1)
assert.InDelta(t, 87.33, stats.AverageCompletion, 0.1)
// 2 sessions avec ≥90% completion (95% et 92%)
assert.InDelta(t, 66.67, stats.CompletionRate, 0.1)
// 1 session avec ≥95% completion
assert.Equal(t, 1, stats.CompletedSessions)
}
func TestPlaybackExportService_calculateReportStats_Empty(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
stats := service.calculateReportStats([]models.PlaybackAnalytics{})
assert.Equal(t, 0, stats.TotalSessions)
assert.Equal(t, int64(0), stats.TotalPlayTime)
assert.Equal(t, 0.0, stats.AveragePlayTime)
}
func TestPlaybackExportService_ExportCSV_WithEndedAt(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "test.csv")
now := time.Now()
endedAt := now.Add(5 * time.Minute)
analytics := []models.PlaybackAnalytics{
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 120,
CompletionRate: 75.0,
StartedAt: now,
EndedAt: &endedAt,
CreatedAt: now,
},
}
err := service.ExportCSV(analytics, filename)
require.NoError(t, err)
// Vérifier que EndedAt est dans le fichier
data, err := os.ReadFile(filename)
require.NoError(t, err)
assert.Contains(t, string(data), endedAt.Format(time.RFC3339))
}
func TestPlaybackExportService_ExportCSV_WithoutEndedAt(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "test.csv")
now := time.Now()
analytics := []models.PlaybackAnalytics{
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 120,
CompletionRate: 75.0,
StartedAt: now,
EndedAt: nil,
CreatedAt: now,
},
}
err := service.ExportCSV(analytics, filename)
require.NoError(t, err)
// Vérifier que le fichier contient une ligne avec EndedAt vide
data, err := os.ReadFile(filename)
require.NoError(t, err)
// La ligne devrait avoir une colonne vide pour EndedAt
assert.Contains(t, string(data), ",120,0,0,75.00") // Part of the CSV line we can match safely
}
func TestPlaybackExportService_ExportToWriter_CSV(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "test.csv")
file, err := os.Create(filename)
require.NoError(t, err)
defer file.Close()
now := time.Now()
id := uuid.New()
analytics := []models.PlaybackAnalytics{
{
ID: id,
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 120,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
}
err = service.ExportToWriter(analytics, FormatCSV, file)
require.NoError(t, err)
file.Close()
// Vérifier le contenu
data, err := os.ReadFile(filename)
require.NoError(t, err)
assert.Contains(t, string(data), "ID")
assert.Contains(t, string(data), id.String())
}
func TestPlaybackExportService_ExportToWriter_JSON(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "test.json")
file, err := os.Create(filename)
require.NoError(t, err)
defer file.Close()
now := time.Now()
id := uuid.New()
analytics := []models.PlaybackAnalytics{
{
ID: id,
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 120,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
}
err = service.ExportToWriter(analytics, FormatJSON, file)
require.NoError(t, err)
file.Close()
// Vérifier le contenu
data, err := os.ReadFile(filename)
require.NoError(t, err)
// Le JSON est indenté, donc les valeurs peuvent avoir des espaces
assert.Contains(t, string(data), `"id": "`+id.String()+`"`)
}
func TestPlaybackExportService_ExportToWriter_InvalidFormat(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
tmpDir := t.TempDir()
filename := filepath.Join(tmpDir, "test.txt")
file, err := os.Create(filename)
require.NoError(t, err)
defer file.Close()
now := time.Now()
analytics := []models.PlaybackAnalytics{
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 120,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
}
err = service.ExportToWriter(analytics, ExportFormat("invalid"), file)
assert.Error(t, err)
assert.Contains(t, err.Error(), "unsupported export format")
}
func TestPlaybackExportService_ExportToWriter_InvalidWriter(t *testing.T) {
service := NewPlaybackExportService(zaptest.NewLogger(t))
now := time.Now()
analytics := []models.PlaybackAnalytics{
{
ID: uuid.New(),
TrackID: uuid.New(),
UserID: uuid.New(),
PlayTime: 120,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
}
// Passer un writer invalide
err := service.ExportToWriter(analytics, FormatCSV, "invalid")
assert.Error(t, err)
assert.Contains(t, err.Error(), "writer must be")
}