veza/veza-backend-api/internal/handlers/playback_websocket_handler_test.go

272 lines
6.9 KiB
Go

package handlers
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"go.uber.org/zap/zaptest"
"veza-backend-api/internal/models"
"veza-backend-api/internal/services"
)
// MockPlaybackAnalyticsService is a mock implementation of PlaybackAnalyticsService
type MockPlaybackAnalyticsService struct {
mock.Mock
}
func (m *MockPlaybackAnalyticsService) RecordPlayback(ctx context.Context, analytics *models.PlaybackAnalytics) error {
args := m.Called(ctx, analytics)
return args.Error(0)
}
func (m *MockPlaybackAnalyticsService) GetTrackStats(ctx context.Context, trackID uuid.UUID) (*services.PlaybackStats, error) {
args := m.Called(ctx, trackID)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*services.PlaybackStats), args.Error(1)
}
func TestNewPlaybackWebSocketHandler(t *testing.T) {
// Setup
logger := zaptest.NewLogger(t)
mockService := &MockPlaybackAnalyticsService{}
// Execute
handler := NewPlaybackWebSocketHandlerWithInterface(mockService, logger)
// Assert
assert.NotNil(t, handler)
assert.NotNil(t, handler.analyticsService)
assert.NotNil(t, handler.logger)
assert.NotNil(t, handler.clients)
assert.NotNil(t, handler.broadcast)
assert.Equal(t, 0, handler.GetTotalConnectedClientsCount())
}
func TestNewPlaybackWebSocketHandler_NilLogger(t *testing.T) {
// Setup
mockService := &MockPlaybackAnalyticsService{}
// Execute
handler := NewPlaybackWebSocketHandlerWithInterface(mockService, nil)
// Assert
assert.NotNil(t, handler)
assert.NotNil(t, handler.logger)
}
func TestPlaybackWebSocketHandler_GetConnectedClientsCount(t *testing.T) {
// Setup
logger := zaptest.NewLogger(t)
mockService := &MockPlaybackAnalyticsService{}
handler := NewPlaybackWebSocketHandlerWithInterface(mockService, logger)
// Execute - No clients connected
count := handler.GetConnectedClientsCount(123)
// Assert
assert.Equal(t, 0, count)
}
func TestPlaybackWebSocketHandler_GetTotalConnectedClientsCount(t *testing.T) {
// Setup
logger := zaptest.NewLogger(t)
mockService := &MockPlaybackAnalyticsService{}
handler := NewPlaybackWebSocketHandlerWithInterface(mockService, logger)
// Execute
total := handler.GetTotalConnectedClientsCount()
// Assert
assert.Equal(t, 0, total)
}
func TestPlaybackWebSocketHandler_BroadcastAnalyticsUpdate(t *testing.T) {
// Setup
logger := zaptest.NewLogger(t)
mockService := &MockPlaybackAnalyticsService{}
handler := NewPlaybackWebSocketHandlerWithInterface(mockService, logger)
trackID := int64(123)
trackUUID := uuid.New()
analytics := &models.PlaybackAnalytics{
TrackID: trackUUID,
UserID: uuid.New(),
PlayTime: 45,
PauseCount: 2,
SeekCount: 1,
CompletionRate: 50.0,
StartedAt: time.Now(),
}
// Execute - Should not panic even with no clients
handler.BroadcastAnalyticsUpdate(trackID, analytics)
// Assert - No error should occur
assert.True(t, true)
}
func TestPlaybackWebSocketHandler_BroadcastAnalyticsUpdate_NilAnalytics(t *testing.T) {
// Setup
logger := zaptest.NewLogger(t)
mockService := &MockPlaybackAnalyticsService{}
handler := NewPlaybackWebSocketHandlerWithInterface(mockService, logger)
// Execute - Should not panic with nil analytics
handler.BroadcastAnalyticsUpdate(123, nil)
// Assert - No error should occur
assert.True(t, true)
}
func TestPlaybackWebSocketHandler_BroadcastStatsUpdate(t *testing.T) {
// Setup
logger := zaptest.NewLogger(t)
mockService := &MockPlaybackAnalyticsService{}
handler := NewPlaybackWebSocketHandlerWithInterface(mockService, logger)
stats := &services.PlaybackStats{
TotalSessions: 100,
TotalPlayTime: 3000,
AveragePlayTime: 30.0,
TotalPauses: 50,
AveragePauses: 0.5,
TotalSeeks: 25,
AverageSeeks: 0.25,
AverageCompletion: 75.0,
CompletionRate: 60.0,
}
// Execute - Should not panic even with no clients
handler.BroadcastStatsUpdate(123, stats)
// Assert - No error should occur
assert.True(t, true)
}
func TestPlaybackWebSocketHandler_BroadcastStatsUpdate_NilStats(t *testing.T) {
// Setup
logger := zaptest.NewLogger(t)
mockService := &MockPlaybackAnalyticsService{}
handler := NewPlaybackWebSocketHandlerWithInterface(mockService, logger)
// Execute - Should not panic with nil stats
handler.BroadcastStatsUpdate(123, nil)
// Assert - No error should occur
assert.True(t, true)
}
func TestPlaybackWebSocketHandler_WebSocketHandler_Unauthorized(t *testing.T) {
// Setup
gin.SetMode(gin.TestMode)
router := gin.New()
logger := zaptest.NewLogger(t)
mockService := &MockPlaybackAnalyticsService{}
handler := NewPlaybackWebSocketHandlerWithInterface(mockService, logger)
router.GET("/ws", handler.WebSocketHandler)
// Execute - Request without user ID
req, _ := http.NewRequest("GET", "/ws", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusUnauthorized, w.Code)
}
func TestBroadcastMessage_MarshalJSON(t *testing.T) {
// Setup
msg := &BroadcastMessage{
TrackID: 123,
Type: "analytics_update",
Data: gin.H{"test": "data"},
Timestamp: time.Now(),
}
// Execute
data, err := json.Marshal(msg)
// Assert
assert.NoError(t, err)
assert.NotEmpty(t, data)
var unmarshaled BroadcastMessage
err = json.Unmarshal(data, &unmarshaled)
assert.NoError(t, err)
assert.Equal(t, msg.TrackID, unmarshaled.TrackID)
assert.Equal(t, msg.Type, unmarshaled.Type)
}
func TestIncomingWebSocketMessage_MarshalJSON(t *testing.T) {
// Setup
msg := IncomingWebSocketMessage{
Type: "subscribe",
TrackID: "123",
Data: json.RawMessage(`{"test": "data"}`),
}
// Execute
data, err := json.Marshal(msg)
// Assert
assert.NoError(t, err)
assert.NotEmpty(t, data)
var unmarshaled IncomingWebSocketMessage
err = json.Unmarshal(data, &unmarshaled)
assert.NoError(t, err)
assert.Equal(t, msg.Type, unmarshaled.Type)
assert.Equal(t, msg.TrackID, unmarshaled.TrackID)
}
func TestPlaybackWebSocketHandler_BroadcastChannelFull(t *testing.T) {
// Setup
logger := zaptest.NewLogger(t)
mockService := &MockPlaybackAnalyticsService{}
handler := NewPlaybackWebSocketHandlerWithInterface(mockService, logger)
// Fill the broadcast channel
trackID := int64(123)
for i := 0; i < 257; i++ {
msg := &BroadcastMessage{
TrackID: trackID,
Type: "test",
Data: gin.H{"index": i},
Timestamp: time.Now(),
}
select {
case handler.broadcast <- msg:
default:
// Channel is full, this is expected
}
}
trackUUID := uuid.New()
analytics := &models.PlaybackAnalytics{
TrackID: trackUUID,
UserID: uuid.New(),
PlayTime: 45,
PauseCount: 2,
SeekCount: 1,
CompletionRate: 50.0,
StartedAt: time.Now(),
}
// Execute - Should not panic even when channel is full
handler.BroadcastAnalyticsUpdate(123, analytics)
// Assert - No error should occur
assert.True(t, true)
}