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

499 lines
15 KiB
Go
Raw Normal View History

2025-12-03 19:29:37 +00:00
package services
import (
"context"
"fmt"
"os"
"path/filepath"
"testing"
"time"
2025-12-16 16:23:49 +00:00
"github.com/google/uuid"
"veza-backend-api/internal/models"
2025-12-03 19:29:37 +00:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
func setupTestHLSDir(t *testing.T) (string, func()) {
testDir := filepath.Join(os.TempDir(), fmt.Sprintf("hls_test_%d", time.Now().UnixNano()))
err := os.MkdirAll(testDir, 0755)
require.NoError(t, err)
cleanup := func() {
os.RemoveAll(testDir)
}
return testDir, cleanup
}
func createTestTrack(t *testing.T, filePath string) *models.Track {
// Créer un fichier audio de test minimal
err := os.WriteFile(filePath, []byte("fake audio content"), 0644)
require.NoError(t, err)
// GO-004: Utiliser UUID au lieu de int
trackID := uuid.New()
userID := uuid.New()
return &models.Track{
ID: trackID,
UserID: userID,
Title: "Test Track",
FilePath: filePath,
FileSize: 1024,
Format: "mp3",
Duration: 180,
Status: models.TrackStatusCompleted,
}
}
func TestNewHLSTranscodeService(t *testing.T) {
logger := zaptest.NewLogger(t)
service := NewHLSTranscodeService("/tmp/hls", logger)
assert.NotNil(t, service)
assert.Equal(t, "/tmp/hls", service.outputDir)
assert.Equal(t, []int{128, 192, 320}, service.bitrates)
assert.NotNil(t, service.logger)
}
func TestNewHLSTranscodeService_NilLogger(t *testing.T) {
service := NewHLSTranscodeService("/tmp/hls", nil)
assert.NotNil(t, service)
assert.NotNil(t, service.logger) // Devrait créer un logger Nop
}
func TestHLSTranscodeService_SetBitrates(t *testing.T) {
logger := zaptest.NewLogger(t)
service := NewHLSTranscodeService("/tmp/hls", logger)
customBitrates := []int{64, 128, 256}
service.SetBitrates(customBitrates)
assert.Equal(t, customBitrates, service.bitrates)
}
func TestHLSTranscodeService_TranscodeTrack_NilTrack(t *testing.T) {
logger := zaptest.NewLogger(t)
service := NewHLSTranscodeService("/tmp/hls", logger)
ctx := context.Background()
result, err := service.TranscodeTrack(ctx, nil)
assert.Error(t, err)
assert.Nil(t, result)
assert.Contains(t, err.Error(), "track cannot be nil")
}
func TestHLSTranscodeService_TranscodeTrack_EmptyFilePath(t *testing.T) {
logger := zaptest.NewLogger(t)
service := NewHLSTranscodeService("/tmp/hls", logger)
// GO-004: Utiliser UUID au lieu de int
trackID := uuid.New()
track := &models.Track{
ID: trackID,
FilePath: "",
}
ctx := context.Background()
result, err := service.TranscodeTrack(ctx, track)
assert.Error(t, err)
assert.Nil(t, result)
assert.Contains(t, err.Error(), "file path is empty")
}
func TestHLSTranscodeService_TranscodeTrack_FileNotExists(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
// GO-004: Utiliser UUID au lieu de int
trackID := uuid.New()
track := &models.Track{
ID: trackID,
FilePath: "/nonexistent/file.mp3",
}
ctx := context.Background()
result, err := service.TranscodeTrack(ctx, track)
assert.Error(t, err)
assert.Nil(t, result)
assert.Contains(t, err.Error(), "file does not exist")
}
func TestHLSTranscodeService_TranscodeTrack_CreatesDirectory(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
// Créer un fichier audio de test
testAudioFile := filepath.Join(testDir, "test.mp3")
track := createTestTrack(t, testAudioFile)
ctx := context.Background()
// Note: Ce test échouera si ffmpeg n'est pas installé
// C'est acceptable car c'est un test d'intégration
result, err := service.TranscodeTrack(ctx, track)
// Si ffmpeg n'est pas disponible, on s'attend à une erreur
if err != nil {
// Vérifier que le répertoire a été créé même en cas d'erreur
trackDir := filepath.Join(testDir, fmt.Sprintf("track_%d", track.ID))
// Le répertoire peut ne pas exister si l'erreur survient avant sa création
// ou peut exister si l'erreur survient après
_ = trackDir
assert.Error(t, err)
assert.Nil(t, result)
} else {
// Si ffmpeg est disponible, vérifier que tout a été créé
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, track.ID, result.TrackID)
assert.Contains(t, result.PlaylistURL, "master.m3u8")
assert.Greater(t, result.SegmentsCount, 0)
assert.Equal(t, models.HLSStatusReady, result.Status)
}
}
func TestHLSTranscodeService_CountSegments(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
// Créer une structure de test
trackDir := filepath.Join(testDir, "track_123")
qualityDir1 := filepath.Join(trackDir, "128k")
qualityDir2 := filepath.Join(trackDir, "192k")
require.NoError(t, os.MkdirAll(qualityDir1, 0755))
require.NoError(t, os.MkdirAll(qualityDir2, 0755))
// Créer des segments de test
for i := 0; i < 3; i++ {
segmentPath := filepath.Join(qualityDir1, fmt.Sprintf("segment_%03d.ts", i))
require.NoError(t, os.WriteFile(segmentPath, []byte("test"), 0644))
}
for i := 0; i < 2; i++ {
segmentPath := filepath.Join(qualityDir2, fmt.Sprintf("segment_%03d.ts", i))
require.NoError(t, os.WriteFile(segmentPath, []byte("test"), 0644))
}
count, err := service.countSegments(trackDir)
assert.NoError(t, err)
// Devrait retourner le maximum (3 segments dans 128k)
assert.Equal(t, 3, count)
}
func TestHLSTranscodeService_CountSegments_EmptyDir(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
trackDir := filepath.Join(testDir, "track_123")
require.NoError(t, os.MkdirAll(trackDir, 0755))
// Créer les répertoires de qualité vides
for _, bitrate := range service.bitrates {
qualityDir := filepath.Join(trackDir, fmt.Sprintf("%dk", bitrate))
require.NoError(t, os.MkdirAll(qualityDir, 0755))
}
count, err := service.countSegments(trackDir)
assert.NoError(t, err)
assert.Equal(t, 0, count)
}
func TestHLSTranscodeService_CountSegments_NonexistentDir(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
count, err := service.countSegments("/nonexistent/dir")
assert.Error(t, err)
assert.Equal(t, 0, count)
}
func TestHLSTranscodeService_CountSegments_MultipleBitrates(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
// Créer un répertoire de track avec des segments
trackDir := filepath.Join(testDir, "track_123")
require.NoError(t, os.MkdirAll(trackDir, 0755))
// Créer des répertoires de qualité avec différents nombres de segments
qualityDir128 := filepath.Join(trackDir, "128k")
require.NoError(t, os.MkdirAll(qualityDir128, 0755))
require.NoError(t, os.WriteFile(filepath.Join(qualityDir128, "segment_000.ts"), []byte("data"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(qualityDir128, "segment_001.ts"), []byte("data"), 0644))
qualityDir192 := filepath.Join(trackDir, "192k")
require.NoError(t, os.MkdirAll(qualityDir192, 0755))
require.NoError(t, os.WriteFile(filepath.Join(qualityDir192, "segment_000.ts"), []byte("data"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(qualityDir192, "segment_001.ts"), []byte("data"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(qualityDir192, "segment_002.ts"), []byte("data"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(qualityDir192, "segment_003.ts"), []byte("data"), 0644))
qualityDir320 := filepath.Join(trackDir, "320k")
require.NoError(t, os.MkdirAll(qualityDir320, 0755))
require.NoError(t, os.WriteFile(filepath.Join(qualityDir320, "segment_000.ts"), []byte("data"), 0644))
count, err := service.countSegments(trackDir)
assert.NoError(t, err)
// Devrait retourner le maximum (4 segments dans 192k)
assert.Equal(t, 4, count)
}
func TestHLSTranscodeService_CountSegments_OnlySegmentFiles(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
// Créer un répertoire de track avec des segments
trackDir := filepath.Join(testDir, "track_123")
require.NoError(t, os.MkdirAll(trackDir, 0755))
qualityDir := filepath.Join(trackDir, "128k")
require.NoError(t, os.MkdirAll(qualityDir, 0755))
// Créer des fichiers segment_*.ts
require.NoError(t, os.WriteFile(filepath.Join(qualityDir, "segment_000.ts"), []byte("data"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(qualityDir, "segment_001.ts"), []byte("data"), 0644))
// Créer d'autres fichiers qui ne doivent pas être comptés
require.NoError(t, os.WriteFile(filepath.Join(qualityDir, "playlist.m3u8"), []byte("data"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(qualityDir, "other.ts"), []byte("data"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(qualityDir, "segment_other.txt"), []byte("data"), 0644))
count, err := service.countSegments(trackDir)
assert.NoError(t, err)
// Devrait compter uniquement les fichiers segment_*.ts (2 fichiers)
assert.Equal(t, 2, count)
}
func TestHLSTranscodeService_GetPlaylistDuration(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
// Créer une playlist de test
playlistContent := `#EXTM3U
#EXT-X-VERSION:3
#EXTINF:10.0,
segment_000.ts
#EXTINF:10.5,
segment_001.ts
#EXTINF:9.5,
segment_002.ts
#EXT-X-ENDLIST
`
playlistPath := filepath.Join(testDir, "playlist.m3u8")
require.NoError(t, os.WriteFile(playlistPath, []byte(playlistContent), 0644))
duration := service.getPlaylistDuration(playlistPath)
assert.Equal(t, 30.0, duration)
}
func TestHLSTranscodeService_GetPlaylistDuration_NonexistentFile(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
duration := service.getPlaylistDuration("/nonexistent/playlist.m3u8")
assert.Equal(t, 0.0, duration)
}
func TestHLSTranscodeService_GenerateMasterPlaylist(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
// Créer les répertoires et playlists de qualité
bitrates := []int{128, 192, 320}
for _, bitrate := range bitrates {
qualityDir := filepath.Join(testDir, fmt.Sprintf("%dk", bitrate))
require.NoError(t, os.MkdirAll(qualityDir, 0755))
playlistPath := filepath.Join(qualityDir, "playlist.m3u8")
require.NoError(t, os.WriteFile(playlistPath, []byte("#EXTM3U\n"), 0644))
}
err := service.generateMasterPlaylist(testDir, bitrates)
assert.NoError(t, err)
// Vérifier que le fichier master.m3u8 a été créé
masterPlaylistPath := filepath.Join(testDir, "master.m3u8")
assert.FileExists(t, masterPlaylistPath)
// Vérifier le contenu
content, err := os.ReadFile(masterPlaylistPath)
require.NoError(t, err)
contentStr := string(content)
assert.Contains(t, contentStr, "#EXTM3U")
assert.Contains(t, contentStr, "#EXT-X-VERSION:3")
assert.Contains(t, contentStr, "128k/playlist.m3u8")
assert.Contains(t, contentStr, "192k/playlist.m3u8")
assert.Contains(t, contentStr, "320k/playlist.m3u8")
}
func TestHLSTranscodeService_CleanupTrackDir(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
// Créer un répertoire de track
// GO-004: Utiliser UUID au lieu de int
2025-12-16 16:23:49 +00:00
// Note: CleanupTrackDir uses format "track_<trackID>", so we need to match that
2025-12-03 19:29:37 +00:00
trackID := uuid.New()
2025-12-16 16:23:49 +00:00
trackDir := filepath.Join(testDir, fmt.Sprintf("track_%s", trackID.String()))
2025-12-03 19:29:37 +00:00
require.NoError(t, os.MkdirAll(trackDir, 0755))
require.NoError(t, os.WriteFile(filepath.Join(trackDir, "test.txt"), []byte("test"), 0644))
// Nettoyer
err := service.CleanupTrackDir(trackID)
assert.NoError(t, err)
assert.NoDirExists(t, trackDir)
}
func TestHLSTranscodeService_CleanupTrackDir_Nonexistent(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
// Nettoyer un répertoire qui n'existe pas (ne devrait pas retourner d'erreur)
// GO-004: Utiliser UUID au lieu de int
nonexistentTrackID := uuid.New()
err := service.CleanupTrackDir(nonexistentTrackID)
assert.NoError(t, err)
}
func TestHLSTranscodeService_TranscodeTrack_WithCustomBitrates(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
service.SetBitrates([]int{64, 128})
testAudioFile := filepath.Join(testDir, "test.mp3")
track := createTestTrack(t, testAudioFile)
ctx := context.Background()
result, err := service.TranscodeTrack(ctx, track)
// Si ffmpeg n'est pas disponible, on s'attend à une erreur
if err != nil {
assert.Error(t, err)
assert.Nil(t, result)
} else {
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Len(t, result.Bitrates, 2)
assert.Contains(t, result.Bitrates, 64)
assert.Contains(t, result.Bitrates, 128)
}
}
func TestHLSTranscodeService_GetPlaylistDuration_InvalidFormat(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
// Créer une playlist avec format invalide
playlistContent := `#EXTM3U
#EXTINF:invalid,
segment_000.ts
`
playlistPath := filepath.Join(testDir, "playlist.m3u8")
require.NoError(t, os.WriteFile(playlistPath, []byte(playlistContent), 0644))
duration := service.getPlaylistDuration(playlistPath)
// Devrait retourner 0 pour format invalide
assert.Equal(t, 0.0, duration)
}
func TestHLSTranscodeService_GetPlaylistDuration_EmptyFile(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
playlistPath := filepath.Join(testDir, "empty.m3u8")
require.NoError(t, os.WriteFile(playlistPath, []byte(""), 0644))
duration := service.getPlaylistDuration(playlistPath)
assert.Equal(t, 0.0, duration)
}
func TestHLSTranscodeService_GenerateMasterPlaylist_EmptyBitrates(t *testing.T) {
logger := zaptest.NewLogger(t)
testDir, cleanup := setupTestHLSDir(t)
defer cleanup()
service := NewHLSTranscodeService(testDir, logger)
err := service.generateMasterPlaylist(testDir, []int{})
assert.NoError(t, err)
// Vérifier que le fichier master.m3u8 a été créé
masterPlaylistPath := filepath.Join(testDir, "master.m3u8")
assert.FileExists(t, masterPlaylistPath)
// Vérifier le contenu (devrait contenir seulement le header)
content, err := os.ReadFile(masterPlaylistPath)
require.NoError(t, err)
contentStr := string(content)
assert.Contains(t, contentStr, "#EXTM3U")
assert.Contains(t, contentStr, "#EXT-X-VERSION:3")
}