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") // } // }