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

325 lines
9.3 KiB
Go

package handlers
import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
"veza-backend-api/internal/services"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"go.uber.org/zap"
)
// MockBitrateAdaptationService mocks BitrateAdaptationService
type MockBitrateAdaptationService struct {
mock.Mock
}
func (m *MockBitrateAdaptationService) AdaptBitrate(ctx context.Context, trackID, userID uuid.UUID, currentBitrate int, bandwidth int64, bufferLevel float64) (int, error) {
args := m.Called(ctx, trackID, userID, currentBitrate, bandwidth, bufferLevel)
return args.Int(0), args.Error(1)
}
func (m *MockBitrateAdaptationService) GetAnalytics(ctx context.Context, trackID uuid.UUID) (*services.BitrateAnalytics, error) {
args := m.Called(ctx, trackID)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*services.BitrateAnalytics), args.Error(1)
}
func setupTestBitrateRouter(mockService *MockBitrateAdaptationService) *gin.Engine {
gin.SetMode(gin.TestMode)
router := gin.New()
logger := zap.NewNop()
handler := NewBitrateHandlerWithInterface(mockService, logger)
api := router.Group("/api/v1/tracks")
api.Use(func(c *gin.Context) {
userIDStr := c.GetHeader("X-User-ID")
if userIDStr != "" {
uid, err := uuid.Parse(userIDStr)
if err == nil {
c.Set("user_id", uid)
}
}
c.Next()
})
{
api.POST("/:id/bitrate/adapt", handler.AdaptBitrate)
api.GET("/:id/bitrate/analytics", handler.GetAnalytics)
}
return router
}
func TestBitrateHandler_AdaptBitrate_Success(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
userID := uuid.New()
trackID := uuid.New()
reqBody := AdaptBitrateRequest{
CurrentBitrate: 128000,
Bandwidth: 1000000,
BufferLevel: 0.8,
}
mockService.On("AdaptBitrate", mock.Anything, trackID, userID, 128000, int64(1000000), 0.8).Return(256000, nil)
body, _ := json.Marshal(reqBody)
// Execute
req, _ := http.NewRequest("POST", "/api/v1/tracks/"+trackID.String()+"/bitrate/adapt", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusOK, w.Code)
mockService.AssertExpectations(t)
}
func TestBitrateHandler_AdaptBitrate_Unauthorized(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
trackID := uuid.New()
reqBody := AdaptBitrateRequest{
CurrentBitrate: 128000,
Bandwidth: 1000000,
BufferLevel: 0.8,
}
body, _ := json.Marshal(reqBody)
// Execute - No X-User-ID header
req, _ := http.NewRequest("POST", "/api/v1/tracks/"+trackID.String()+"/bitrate/adapt", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert - Handler returns 403 (Forbidden) when user_id is missing
assert.True(t, w.Code == http.StatusUnauthorized || w.Code == http.StatusForbidden, "Expected 401 or 403, got %d", w.Code)
mockService.AssertNotCalled(t, "AdaptBitrate")
}
func TestBitrateHandler_AdaptBitrate_InvalidTrackID(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
userID := uuid.New()
reqBody := AdaptBitrateRequest{
CurrentBitrate: 128000,
Bandwidth: 1000000,
BufferLevel: 0.8,
}
body, _ := json.Marshal(reqBody)
// Execute - Invalid UUID
req, _ := http.NewRequest("POST", "/api/v1/tracks/invalid-id/bitrate/adapt", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusBadRequest, w.Code)
mockService.AssertNotCalled(t, "AdaptBitrate")
}
func TestBitrateHandler_AdaptBitrate_InvalidJSON(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
userID := uuid.New()
trackID := uuid.New()
// Execute - Invalid JSON
req, _ := http.NewRequest("POST", "/api/v1/tracks/"+trackID.String()+"/bitrate/adapt", bytes.NewBuffer([]byte("invalid json")))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusBadRequest, w.Code)
mockService.AssertNotCalled(t, "AdaptBitrate")
}
func TestBitrateHandler_AdaptBitrate_ValidationError(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
userID := uuid.New()
trackID := uuid.New()
reqBody := AdaptBitrateRequest{
CurrentBitrate: 0, // Invalid - required field
Bandwidth: 1000000,
BufferLevel: 0.8,
}
body, _ := json.Marshal(reqBody)
// Execute
req, _ := http.NewRequest("POST", "/api/v1/tracks/"+trackID.String()+"/bitrate/adapt", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusBadRequest, w.Code)
mockService.AssertNotCalled(t, "AdaptBitrate")
}
func TestBitrateHandler_AdaptBitrate_ServiceError(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
userID := uuid.New()
trackID := uuid.New()
reqBody := AdaptBitrateRequest{
CurrentBitrate: 128000,
Bandwidth: 1000000,
BufferLevel: 0.8,
}
mockService.On("AdaptBitrate", mock.Anything, trackID, userID, 128000, int64(1000000), 0.8).Return(0, errors.New("service error"))
body, _ := json.Marshal(reqBody)
// Execute
req, _ := http.NewRequest("POST", "/api/v1/tracks/"+trackID.String()+"/bitrate/adapt", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusInternalServerError, w.Code)
mockService.AssertExpectations(t)
}
func TestBitrateHandler_AdaptBitrate_InvalidTrackIDError(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
userID := uuid.New()
trackID := uuid.New()
reqBody := AdaptBitrateRequest{
CurrentBitrate: 128000,
Bandwidth: 1000000,
BufferLevel: 0.8,
}
mockService.On("AdaptBitrate", mock.Anything, trackID, userID, 128000, int64(1000000), 0.8).Return(0, services.ErrInvalidTrackID)
body, _ := json.Marshal(reqBody)
// Execute
req, _ := http.NewRequest("POST", "/api/v1/tracks/"+trackID.String()+"/bitrate/adapt", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusBadRequest, w.Code)
mockService.AssertExpectations(t)
}
func TestBitrateHandler_GetAnalytics_Success(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
trackID := uuid.New()
expectedAnalytics := &services.BitrateAnalytics{
TotalAdaptations: 5,
Reasons: make(map[string]int64),
AdaptationsOverTime: []services.AdaptationTimePoint{},
}
mockService.On("GetAnalytics", mock.Anything, trackID).Return(expectedAnalytics, nil)
// Execute
req, _ := http.NewRequest("GET", "/api/v1/tracks/"+trackID.String()+"/bitrate/analytics", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusOK, w.Code)
mockService.AssertExpectations(t)
}
func TestBitrateHandler_GetAnalytics_InvalidTrackID(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
// Execute - Invalid UUID
req, _ := http.NewRequest("GET", "/api/v1/tracks/invalid-id/bitrate/analytics", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusBadRequest, w.Code)
mockService.AssertNotCalled(t, "GetAnalytics")
}
func TestBitrateHandler_GetAnalytics_ServiceError(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
trackID := uuid.New()
mockService.On("GetAnalytics", mock.Anything, trackID).Return(nil, errors.New("service error"))
// Execute
req, _ := http.NewRequest("GET", "/api/v1/tracks/"+trackID.String()+"/bitrate/analytics", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusInternalServerError, w.Code)
mockService.AssertExpectations(t)
}
func TestBitrateHandler_GetAnalytics_InvalidTrackIDError(t *testing.T) {
// Setup
mockService := new(MockBitrateAdaptationService)
router := setupTestBitrateRouter(mockService)
trackID := uuid.New()
mockService.On("GetAnalytics", mock.Anything, trackID).Return(nil, services.ErrInvalidTrackID)
// Execute
req, _ := http.NewRequest("GET", "/api/v1/tracks/"+trackID.String()+"/bitrate/analytics", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, http.StatusBadRequest, w.Code)
mockService.AssertExpectations(t)
}