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

301 lines
8.1 KiB
Go

package services
import (
"context"
"fmt"
"testing"
"github.com/google/uuid"
"go.uber.org/zap"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"veza-backend-api/internal/models"
)
func TestNewHLSStreamingService(t *testing.T) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("Failed to open database: %v", err)
}
logger := zap.NewNop()
service := NewHLSStreamingService(db, "/tmp/hls", logger)
if service == nil {
t.Error("NewHLSStreamingService() returned nil")
}
if service.db == nil {
t.Error("NewHLSStreamingService() returned service with nil db")
}
if service.logger == nil {
t.Error("NewHLSStreamingService() returned service with nil logger")
}
}
func TestHLSStreamingService_parsePlaylistSegments(t *testing.T) {
service := NewHLSStreamingService(nil, "", zap.NewNop())
tests := []struct {
name string
playlist string
expected []string
}{
{
name: "valid playlist with segments",
playlist: `#EXTM3U
#EXT-X-VERSION:3
#EXTINF:10.0,
segment_000.ts
#EXTINF:10.0,
segment_001.ts
#EXTINF:10.0,
segment_002.ts
#EXT-X-ENDLIST`,
expected: []string{"segment_000.ts", "segment_001.ts", "segment_002.ts"},
},
{
name: "playlist with comments only",
playlist: `#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ENDLIST`,
expected: []string{},
},
{
name: "empty playlist",
playlist: "",
expected: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
segments := service.parsePlaylistSegments(tt.playlist)
if len(segments) != len(tt.expected) {
t.Errorf("parsePlaylistSegments() returned %d segments, want %d", len(segments), len(tt.expected))
}
for i, seg := range segments {
if seg != tt.expected[i] {
t.Errorf("parsePlaylistSegments() segment[%d] = %s, want %s", i, seg, tt.expected[i])
}
}
})
}
}
func TestHLSStreamingService_GetStreamURLs(t *testing.T) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("Failed to open database: %v", err)
}
if err := db.AutoMigrate(&models.HLSStream{}); err != nil {
t.Fatalf("Failed to migrate: %v", err)
}
service := NewHLSStreamingService(db, "/tmp/hls", zap.NewNop())
ctx := context.Background()
trackID := uuid.New()
// Create test stream
stream := &models.HLSStream{
TrackID: trackID,
Status: models.HLSStatusReady,
PlaylistURL: "/tmp/hls/track_123/master.m3u8",
Bitrates: models.BitrateList{128, 192, 320},
SegmentsCount: 10,
}
if err := db.Create(stream).Error; err != nil {
t.Fatalf("Failed to create test stream: %v", err)
}
urls, err := service.GetStreamURLs(ctx, trackID, "https://api.example.com")
if err != nil {
t.Fatalf("GetStreamURLs() error = %v", err)
}
if urls == nil {
t.Error("GetStreamURLs() returned nil urls")
}
// Check master playlist URL
if urls["master_playlist"] == "" {
t.Error("GetStreamURLs() missing master_playlist URL")
}
// Check quality URLs
expectedQualities := []int{128, 192, 320}
for _, bitrate := range expectedQualities {
key := fmt.Sprintf("quality_%d", bitrate)
if urls[key] == "" {
t.Errorf("GetStreamURLs() missing quality URL for %dk", bitrate)
}
}
}
func TestHLSStreamingService_CheckStreamExists(t *testing.T) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("Failed to open database: %v", err)
}
if err := db.AutoMigrate(&models.HLSStream{}); err != nil {
t.Fatalf("Failed to migrate: %v", err)
}
service := NewHLSStreamingService(db, "/tmp/hls", zap.NewNop())
ctx := context.Background()
trackID := uuid.New()
// Check non-existent stream
exists, err := service.CheckStreamExists(ctx, trackID)
if err != nil {
t.Fatalf("CheckStreamExists() error = %v", err)
}
if exists {
t.Error("CheckStreamExists() returned true for non-existent stream")
}
// Create stream
stream := &models.HLSStream{
TrackID: trackID,
Status: models.HLSStatusReady,
}
if err := db.Create(stream).Error; err != nil {
t.Fatalf("Failed to create test stream: %v", err)
}
// Check existing stream
exists, err = service.CheckStreamExists(ctx, trackID)
if err != nil {
t.Fatalf("CheckStreamExists() error = %v", err)
}
if !exists {
t.Error("CheckStreamExists() returned false for existing stream")
}
}
func TestHLSStreamingService_UpdateStreamStatus(t *testing.T) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("Failed to open database: %v", err)
}
if err := db.AutoMigrate(&models.HLSStream{}); err != nil {
t.Fatalf("Failed to migrate: %v", err)
}
service := NewHLSStreamingService(db, "/tmp/hls", zap.NewNop())
ctx := context.Background()
trackID := uuid.New()
// Create stream
stream := &models.HLSStream{
TrackID: trackID,
Status: models.HLSStatusProcessing,
}
if err := db.Create(stream).Error; err != nil {
t.Fatalf("Failed to create test stream: %v", err)
}
// Update status
err = service.UpdateStreamStatus(ctx, trackID, models.HLSStatusReady)
if err != nil {
t.Fatalf("UpdateStreamStatus() error = %v", err)
}
// Verify status was updated
var updatedStream models.HLSStream
if err := db.Where("track_id = ?", trackID).First(&updatedStream).Error; err != nil {
t.Fatalf("Failed to query updated stream: %v", err)
}
if updatedStream.Status != models.HLSStatusReady {
t.Errorf("UpdateStreamStatus() status = %v, want %v", updatedStream.Status, models.HLSStatusReady)
}
}
func TestHLSStreamingService_GetStreamStatistics(t *testing.T) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("Failed to open database: %v", err)
}
if err := db.AutoMigrate(&models.HLSStream{}); err != nil {
t.Fatalf("Failed to migrate: %v", err)
}
service := NewHLSStreamingService(db, "/tmp/hls", zap.NewNop())
ctx := context.Background()
// Create test streams with different statuses
streams := []*models.HLSStream{
{TrackID: uuid.New(), Status: models.HLSStatusReady},
{TrackID: uuid.New(), Status: models.HLSStatusReady},
{TrackID: uuid.New(), Status: models.HLSStatusProcessing},
{TrackID: uuid.New(), Status: models.HLSStatusFailed},
}
for _, stream := range streams {
if err := db.Create(stream).Error; err != nil {
t.Fatalf("Failed to create test stream: %v", err)
}
}
stats, err := service.GetStreamStatistics(ctx)
if err != nil {
t.Fatalf("GetStreamStatistics() error = %v", err)
}
if stats == nil {
t.Error("GetStreamStatistics() returned nil stats")
}
// Verify counts
if stats["total_streams"] != int64(4) {
t.Errorf("GetStreamStatistics() total_streams = %v, want 4", stats["total_streams"])
}
if stats["ready_streams"] != int64(2) {
t.Errorf("GetStreamStatistics() ready_streams = %v, want 2", stats["ready_streams"])
}
if stats["processing_streams"] != int64(1) {
t.Errorf("GetStreamStatistics() processing_streams = %v, want 1", stats["processing_streams"])
}
if stats["failed_streams"] != int64(1) {
t.Errorf("GetStreamStatistics() failed_streams = %v, want 1", stats["failed_streams"])
}
}
// Note: Full integration tests would require:
// 1. Real HLS stream files (master.m3u8, playlists, segments)
// 2. File system access for validation
// 3. Verification of stream health checks
//
// Example integration test structure:
// func TestHLSStreamingService_ValidateStream_Integration(t *testing.T) {
// // Setup test database and file system
// db := setupTestDB(t)
// outputDir := setupTestHLSFiles(t)
// defer cleanupTestFiles(t, outputDir)
//
// service := NewHLSStreamingService(db, outputDir, zap.NewNop())
//
// // Create test stream
// trackID := uuid.New()
// stream := createTestHLSStream(t, db, trackID, outputDir)
//
// ctx := context.Background()
// health, err := service.ValidateStream(ctx, trackID)
// if err != nil {
// t.Fatalf("ValidateStream() error = %v", err)
// }
//
// if !health.IsHealthy {
// t.Error("ValidateStream() stream should be healthy")
// }
// if !health.PlaylistExists {
// t.Error("ValidateStream() playlist should exist")
// }
// if !health.SegmentsValid {
// t.Error("ValidateStream() segments should be valid")
// }
// }