520 lines
13 KiB
Go
520 lines
13 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()
|
|
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")
|
|
}
|