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

391 lines
13 KiB
Go

package services
import (
"context"
"testing"
"github.com/google/uuid"
"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 setupTestBitrateAdaptationServiceDB(t *testing.T) (*gorm.DB, uuid.UUID, uuid.UUID) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
// Enable foreign keys for SQLite
db.Exec("PRAGMA foreign_keys = ON")
// Auto-migrate
err = db.AutoMigrate(&models.User{}, &models.Track{}, &models.BitrateAdaptationLog{})
require.NoError(t, err)
userID := uuid.New()
// Create test user
user := &models.User{
ID: userID,
Username: "testuser",
Email: "test@example.com",
IsActive: true,
}
err = db.Create(user).Error
require.NoError(t, err)
// Create test track
trackID := uuid.New()
track := &models.Track{
ID: trackID,
UserID: userID,
Title: "Test Track",
FilePath: "/test/track.mp3",
FileSize: 5 * 1024 * 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
err = db.Create(track).Error
require.NoError(t, err)
return db, userID, trackID
}
func TestNewBitrateAdaptationService(t *testing.T) {
db, _, _ := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
assert.NotNil(t, service)
assert.Equal(t, db, service.db)
assert.Equal(t, bandwidthService, service.bandwidthService)
assert.NotNil(t, service.logger)
}
func TestNewBitrateAdaptationService_NilLogger(t *testing.T) {
db, _, _ := setupTestBitrateAdaptationServiceDB(t)
bandwidthService := NewBandwidthDetectionService(nil)
service := NewBitrateAdaptationService(db, bandwidthService, nil)
assert.NotNil(t, service)
assert.NotNil(t, service.logger)
}
func TestBitrateAdaptationService_AdaptBitrate_NoChange(t *testing.T) {
db, userID, trackID := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
ctx := context.Background()
// Test avec bitrate qui ne change pas
// Bande passante de 2 Mbps = 2097152 bps, avec buffer 20% = 1677 kbps disponible
// Recommandation: 320 kbps
// Current: 320 kbps, donc pas de changement
newBitrate, err := service.AdaptBitrate(ctx, trackID, userID, 320, 2097152, 0.5)
require.NoError(t, err)
assert.Equal(t, 320, newBitrate)
// Vérifier qu'aucun log n'a été créé
var count int64
db.Model(&models.BitrateAdaptationLog{}).Count(&count)
assert.Equal(t, int64(0), count)
}
func TestBitrateAdaptationService_AdaptBitrate_Increase(t *testing.T) {
db, userID, trackID := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
ctx := context.Background()
// Test avec augmentation de bitrate
// Bande passante de 10 Mbps = 10485760 bps, avec buffer 20% = 8388 kbps disponible
// Recommandation: 320 kbps
// Current: 128 kbps, buffer: 0.5 (50%)
newBitrate, err := service.AdaptBitrate(ctx, trackID, userID, 128, 10485760, 0.5)
require.NoError(t, err)
assert.Equal(t, 320, newBitrate)
// Vérifier qu'un log a été créé
var log models.BitrateAdaptationLog
err = db.First(&log).Error
require.NoError(t, err)
assert.Equal(t, trackID, log.TrackID)
assert.Equal(t, userID, log.UserID)
assert.Equal(t, 128, log.OldBitrate)
assert.Equal(t, 320, log.NewBitrate)
assert.Equal(t, models.BitrateReasonNetworkFast, log.Reason)
assert.NotNil(t, log.NetworkBandwidth)
}
func TestBitrateAdaptationService_AdaptBitrate_Decrease(t *testing.T) {
db, userID, trackID := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
ctx := context.Background()
// Test avec diminution de bitrate
// Bande passante de 300 kbps = 307200 bps, avec buffer 20% = 245 kbps disponible
// Recommandation: 192 kbps
// Current: 320 kbps, buffer: 0.5 (50%)
newBitrate, err := service.AdaptBitrate(ctx, trackID, userID, 320, 307200, 0.5)
require.NoError(t, err)
assert.Equal(t, 192, newBitrate)
// Vérifier qu'un log a été créé
var log models.BitrateAdaptationLog
err = db.First(&log).Error
require.NoError(t, err)
assert.Equal(t, 320, log.OldBitrate)
assert.Equal(t, 192, log.NewBitrate)
assert.Equal(t, models.BitrateReasonNetworkSlow, log.Reason)
}
func TestBitrateAdaptationService_AdaptBitrate_LowBuffer_PreventIncrease(t *testing.T) {
db, userID, trackID := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
ctx := context.Background()
// Test avec buffer faible qui empêche l'augmentation
// Bande passante de 10 Mbps = 10485760 bps, recommandation: 320 kbps
// Current: 128 kbps, buffer: 0.15 (15% < 20%)
// L'augmentation devrait être bloquée
newBitrate, err := service.AdaptBitrate(ctx, trackID, userID, 128, 10485760, 0.15)
require.NoError(t, err)
assert.Equal(t, 128, newBitrate) // Pas d'augmentation
// Vérifier qu'aucun log n'a été créé (pas de changement)
var count int64
db.Model(&models.BitrateAdaptationLog{}).Count(&count)
assert.Equal(t, int64(0), count)
}
func TestBitrateAdaptationService_AdaptBitrate_VeryLowBuffer_ForceDecrease(t *testing.T) {
db, userID, trackID := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
ctx := context.Background()
// Test avec buffer très faible qui force la diminution
// Bande passante de 10 Mbps = 10485760 bps, recommandation: 320 kbps
// Current: 320 kbps, buffer: 0.05 (5% < 10%)
// La diminution devrait être forcée
newBitrate, err := service.AdaptBitrate(ctx, trackID, userID, 320, 10485760, 0.05)
require.NoError(t, err)
assert.Equal(t, 192, newBitrate) // Diminution forcée
// Vérifier qu'un log a été créé
var log models.BitrateAdaptationLog
err = db.First(&log).Error
require.NoError(t, err)
assert.Equal(t, 320, log.OldBitrate)
assert.Equal(t, 192, log.NewBitrate)
assert.Equal(t, models.BitrateReasonBufferLow, log.Reason)
}
func TestBitrateAdaptationService_AdaptBitrate_VeryLowBuffer_192to128(t *testing.T) {
db, userID, trackID := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
ctx := context.Background()
// Test avec buffer très faible, passage de 192 à 128
newBitrate, err := service.AdaptBitrate(ctx, trackID, userID, 192, 10485760, 0.05)
require.NoError(t, err)
assert.Equal(t, 128, newBitrate)
// Vérifier qu'un log a été créé
var log models.BitrateAdaptationLog
err = db.First(&log).Error
require.NoError(t, err)
assert.Equal(t, 192, log.OldBitrate)
assert.Equal(t, 128, log.NewBitrate)
}
func TestBitrateAdaptationService_AdaptBitrate_InvalidParameters(t *testing.T) {
db, userID, trackID := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
ctx := context.Background()
// Test avec trackID invalide
_, err := service.AdaptBitrate(ctx, uuid.Nil, userID, 128, 10485760, 0.5)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid track ID")
// Test avec userID invalide
_, err = service.AdaptBitrate(ctx, trackID, uuid.Nil, 128, 10485760, 0.5)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid user ID")
// Test avec currentBitrate invalide
_, err = service.AdaptBitrate(ctx, trackID, userID, 0, 10485760, 0.5)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid current bitrate")
// Test avec bufferLevel invalide (négatif)
_, err = service.AdaptBitrate(ctx, trackID, userID, 128, 10485760, -0.1)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid buffer level")
// Test avec bufferLevel invalide (> 1.0)
_, err = service.AdaptBitrate(ctx, trackID, userID, 128, 10485760, 1.5)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid buffer level")
}
func TestBitrateAdaptationService_DetermineReason(t *testing.T) {
db, _, _ := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
// Test avec buffer faible
reason := service.determineReason(128, 320, 0.15)
assert.Equal(t, models.BitrateReasonBufferLow, reason)
// Test avec augmentation (buffer normal)
reason = service.determineReason(128, 320, 0.5)
assert.Equal(t, models.BitrateReasonNetworkFast, reason)
// Test avec diminution (buffer normal)
reason = service.determineReason(320, 192, 0.5)
assert.Equal(t, models.BitrateReasonNetworkSlow, reason)
// Test avec buffer faible mais augmentation
reason = service.determineReason(128, 192, 0.15)
assert.Equal(t, models.BitrateReasonBufferLow, reason)
}
func TestBitrateAdaptationService_AdaptBitrate_MultipleAdaptations(t *testing.T) {
db, userID, trackID := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
ctx := context.Background()
// Première adaptation: 128 -> 192
// Bande passante de 300 kbps = 307200 bps, avec buffer 20% = 245 kbps disponible
// Recommandation: 192 kbps
newBitrate, err := service.AdaptBitrate(ctx, trackID, userID, 128, 307200, 0.5)
require.NoError(t, err)
assert.Equal(t, 192, newBitrate)
// Deuxième adaptation: 192 -> 320
// Bande passante de 10 Mbps = 10485760 bps, avec buffer 20% = 8388 kbps disponible
// Recommandation: 320 kbps
newBitrate, err = service.AdaptBitrate(ctx, trackID, userID, 192, 10485760, 0.5)
require.NoError(t, err)
assert.Equal(t, 320, newBitrate)
// Vérifier qu'il y a 2 logs
var count int64
db.Model(&models.BitrateAdaptationLog{}).Count(&count)
assert.Equal(t, int64(2), count)
}
func TestBitrateAdaptationService_AdaptBitrate_EdgeCases(t *testing.T) {
db, userID, trackID := setupTestBitrateAdaptationServiceDB(t)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
ctx := context.Background()
// Test avec buffer exactement à 20%
newBitrate, err := service.AdaptBitrate(ctx, trackID, userID, 128, 10485760, 0.2)
require.NoError(t, err)
// À 20%, l'augmentation devrait être permise
assert.Equal(t, 320, newBitrate)
// Nettoyer les logs précédents
db.Exec("DELETE FROM bitrate_adaptation_logs")
// Test avec buffer exactement à 10%
newBitrate, err = service.AdaptBitrate(ctx, trackID, userID, 320, 10485760, 0.1)
require.NoError(t, err)
// À 10%, la diminution devrait être forcée
assert.Equal(t, 192, newBitrate)
// Nettoyer les logs précédents
db.Exec("DELETE FROM bitrate_adaptation_logs")
// Test avec buffer à 0%
newBitrate, err = service.AdaptBitrate(ctx, trackID, userID, 320, 10485760, 0.0)
require.NoError(t, err)
assert.Equal(t, 192, newBitrate)
// Nettoyer les logs précédents
db.Exec("DELETE FROM bitrate_adaptation_logs")
// Test avec buffer à 100%
newBitrate, err = service.AdaptBitrate(ctx, trackID, userID, 128, 10485760, 1.0)
require.NoError(t, err)
assert.Equal(t, 320, newBitrate)
}
func TestBitrateAdaptationService_AdaptBitrate_LogCreationFailure(t *testing.T) {
// Créer une DB qui va échouer lors de la création
// On utilise une table qui n'existe pas pour simuler l'erreur
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
// Ne pas créer la table bitrate_adaptation_logs pour simuler une erreur
// Mais on doit créer User et Track pour que les foreign keys fonctionnent
err = db.AutoMigrate(&models.User{}, &models.Track{})
require.NoError(t, err)
userID := uuid.New()
// Create test user
user := &models.User{
ID: userID,
Username: "testuser",
Email: "test@example.com",
IsActive: true,
}
require.NoError(t, db.Create(user).Error)
// Create test track
track := &models.Track{
UserID: userID,
Title: "Test Track",
FilePath: "/test/track.mp3",
FileSize: 5 * 1024 * 1024,
Format: "MP3",
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
}
require.NoError(t, db.Create(track).Error)
logger := zaptest.NewLogger(t)
bandwidthService := NewBandwidthDetectionService(logger)
service := NewBitrateAdaptationService(db, bandwidthService, logger)
ctx := context.Background()
// L'adaptation devrait quand même fonctionner même si le log échoue
newBitrate, err := service.AdaptBitrate(ctx, track.ID, userID, 128, 10485760, 0.5)
// L'adaptation ne devrait pas retourner d'erreur même si le log échoue
require.NoError(t, err)
assert.Equal(t, 320, newBitrate)
}