veza/veza-backend-api/internal/services/playback_export_service_test.go
2025-12-03 20:29:37 +01:00

508 lines
12 KiB
Go

package services
import (
"github.com/google/uuid"
"os"
"path/filepath"
"testing"
"time"
"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()
analytics := []models.PlaybackAnalytics{
{
ID: 1,
TrackID: 1,
UserID: 1,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
{
ID: 2,
TrackID: 1,
UserID: 2,
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), "1")
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()
analytics := []models.PlaybackAnalytics{
{
ID: 1,
TrackID: 1,
UserID: 1,
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": 1`)
assert.Contains(t, string(data), `"track_id": 1`)
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: 1,
TrackID: 1,
UserID: 1,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
{
ID: 2,
TrackID: 1,
UserID: 2,
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: 1,
TrackID: 1,
UserID: 1,
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: 1,
TrackID: 1,
UserID: 1,
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: 1,
TrackID: 1,
UserID: 1,
PlayTime: 120,
PauseCount: 2,
SeekCount: 3,
CompletionRate: 75.0,
StartedAt: now,
CreatedAt: now,
},
{
ID: 2,
TrackID: 1,
UserID: 2,
PlayTime: 150,
PauseCount: 1,
SeekCount: 2,
CompletionRate: 95.0, // ≥95%
StartedAt: now,
EndedAt: &now,
CreatedAt: now,
},
{
ID: 3,
TrackID: 1,
UserID: 3,
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: 1,
TrackID: 1,
UserID: 1,
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: 1,
TrackID: 1,
UserID: 1,
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), "1,1,1,120,0,0,75.00")
}
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()
analytics := []models.PlaybackAnalytics{
{
ID: 1,
TrackID: 1,
UserID: 1,
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), "1")
}
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()
analytics := []models.PlaybackAnalytics{
{
ID: 1,
TrackID: 1,
UserID: 1,
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": 1`)
}
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: 1,
TrackID: 1,
UserID: 1,
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: 1,
TrackID: 1,
UserID: 1,
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")
}