veza/veza-backend-api/internal/handlers/playlist_handler_test.go
senke b28d0e7eac [T0-006] test(backend): Ajout tests pour frontend_log_handler
- Tests complets pour frontend_log_handler.go (12 tests)
- Tests couvrent NewFrontendLogHandler et ReceiveLog
- Tests pour tous les niveaux de log (DEBUG, INFO, WARN, ERROR)
- Tests pour gestion des erreurs et validation JSON
- Couverture actuelle: 30.6% (objectif: 80%)

Files: veza-backend-api/internal/handlers/frontend_log_handler_test.go
       VEZA_ROADMAP.json
Hours: 16 estimated, 23 actual
2026-01-04 01:44:22 +01:00

338 lines
11 KiB
Go

package handlers
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"veza-backend-api/internal/models"
"veza-backend-api/internal/services" // Needed for search params
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"go.uber.org/zap"
)
// MockPlaylistService is a mock implementation of PlaylistServiceInterface
type MockPlaylistService struct {
mock.Mock
}
func (m *MockPlaylistService) CreatePlaylist(ctx context.Context, userID uuid.UUID, title, description string, isPublic bool) (*models.Playlist, error) {
args := m.Called(ctx, userID, title, description, isPublic)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*models.Playlist), args.Error(1)
}
func (m *MockPlaylistService) GetPlaylists(ctx context.Context, currentUserID *uuid.UUID, filterUserID *uuid.UUID, page, limit int) ([]*models.Playlist, int64, error) {
args := m.Called(ctx, currentUserID, filterUserID, page, limit)
if args.Get(0) == nil {
return nil, 0, args.Error(2)
}
return args.Get(0).([]*models.Playlist), args.Get(1).(int64), args.Error(2)
}
func (m *MockPlaylistService) GetPlaylist(ctx context.Context, id uuid.UUID, currentUserID *uuid.UUID) (*models.Playlist, error) {
args := m.Called(ctx, id, currentUserID)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*models.Playlist), args.Error(1)
}
func (m *MockPlaylistService) UpdatePlaylist(ctx context.Context, id uuid.UUID, userID uuid.UUID, title, description *string, isPublic *bool) (*models.Playlist, error) {
args := m.Called(ctx, id, userID, title, description, isPublic)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*models.Playlist), args.Error(1)
}
func (m *MockPlaylistService) DeletePlaylist(ctx context.Context, id uuid.UUID, userID uuid.UUID) error {
args := m.Called(ctx, id, userID)
return args.Error(0)
}
func (m *MockPlaylistService) AddTrack(ctx context.Context, playlistID, trackID, userID uuid.UUID) error {
args := m.Called(ctx, playlistID, trackID, userID)
return args.Error(0)
}
func (m *MockPlaylistService) RemoveTrack(ctx context.Context, playlistID, trackID, userID uuid.UUID) error {
args := m.Called(ctx, playlistID, trackID, userID)
return args.Error(0)
}
func (m *MockPlaylistService) ReorderTracks(ctx context.Context, playlistID, userID uuid.UUID, trackIDs []uuid.UUID) error {
args := m.Called(ctx, playlistID, userID, trackIDs)
return args.Error(0)
}
func (m *MockPlaylistService) AddCollaborator(ctx context.Context, playlistID, userID, collaboratorUserID uuid.UUID, permission models.PlaylistPermission) (*models.PlaylistCollaborator, error) {
args := m.Called(ctx, playlistID, userID, collaboratorUserID, permission)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*models.PlaylistCollaborator), args.Error(1)
}
func (m *MockPlaylistService) RemoveCollaborator(ctx context.Context, playlistID, userID, collaboratorUserID uuid.UUID) error {
args := m.Called(ctx, playlistID, userID, collaboratorUserID)
return args.Error(0)
}
func (m *MockPlaylistService) UpdateCollaboratorPermission(ctx context.Context, playlistID, userID, collaboratorUserID uuid.UUID, permission models.PlaylistPermission) error {
args := m.Called(ctx, playlistID, userID, collaboratorUserID, permission)
return args.Error(0)
}
func (m *MockPlaylistService) GetCollaborators(ctx context.Context, playlistID, userID uuid.UUID) ([]*models.PlaylistCollaborator, error) {
args := m.Called(ctx, playlistID, userID)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]*models.PlaylistCollaborator), args.Error(1)
}
func (m *MockPlaylistService) CreateShareLink(ctx context.Context, playlistID, userID uuid.UUID, expiresAt *time.Time) (*models.PlaylistShareLink, error) {
args := m.Called(ctx, playlistID, userID, expiresAt)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*models.PlaylistShareLink), args.Error(1)
}
func (m *MockPlaylistService) FollowPlaylist(ctx context.Context, playlistID, userID uuid.UUID) error {
args := m.Called(ctx, playlistID, userID)
return args.Error(0)
}
func (m *MockPlaylistService) UnfollowPlaylist(ctx context.Context, playlistID, userID uuid.UUID) error {
args := m.Called(ctx, playlistID, userID)
return args.Error(0)
}
func (m *MockPlaylistService) CheckPermission(ctx context.Context, playlistID, userID uuid.UUID, permission models.PlaylistPermission) (bool, error) {
args := m.Called(ctx, playlistID, userID, permission)
return args.Bool(0), args.Error(1)
}
func (m *MockPlaylistService) SearchPlaylists(ctx context.Context, params services.SearchPlaylistsParams) ([]*models.Playlist, int64, error) {
args := m.Called(ctx, params)
if args.Get(0) == nil {
return nil, 0, args.Error(2)
}
return args.Get(0).([]*models.Playlist), args.Get(1).(int64), args.Error(2)
}
func setupPlaylistTestRouter(mockService *MockPlaylistService) *gin.Engine {
gin.SetMode(gin.TestMode)
router := gin.New()
logger := zap.NewNop()
// Use the generic new handler with interface
handler := NewPlaylistHandlerWithInterface(mockService, nil, logger) // db is nil as we use mock service
api := router.Group("/api/v1")
api.Use(func(c *gin.Context) {
// Mock auth middleware manually for simplicity
userIDStr := c.GetHeader("X-User-ID")
if userIDStr != "" {
uid, err := uuid.Parse(userIDStr)
if err == nil {
// Inject user_id into context as middleware would
c.Set("user_id", uid)
}
}
c.Next()
})
{
api.GET("/playlists", handler.GetPlaylists)
api.POST("/playlists", handler.CreatePlaylist)
api.GET("/playlists/:id", handler.GetPlaylist)
api.PUT("/playlists/:id", handler.UpdatePlaylist)
api.DELETE("/playlists/:id", handler.DeletePlaylist)
}
return router
}
func TestPlaylistHandler_GetPlaylists_Success(t *testing.T) {
mockService := new(MockPlaylistService)
router := setupPlaylistTestRouter(mockService)
userID := uuid.New()
expectedPlaylists := []*models.Playlist{
{ID: uuid.New(), Title: "List 1", UserID: userID},
{ID: uuid.New(), Title: "List 2", UserID: userID},
}
// Expect GetPlaylists call
mockService.On("GetPlaylists", mock.Anything, mock.MatchedBy(func(u *uuid.UUID) bool {
return u != nil && *u == userID
}), mock.Anything, 1, 20).Return(expectedPlaylists, int64(2), nil)
req, _ := http.NewRequest("GET", "/api/v1/playlists", nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &response)
data := response["data"].(map[string]interface{})
assert.Equal(t, float64(2), data["total"])
mockService.AssertExpectations(t)
}
func TestPlaylistHandler_CreatePlaylist_Success(t *testing.T) {
mockService := new(MockPlaylistService)
router := setupPlaylistTestRouter(mockService)
userID := uuid.New()
reqBody := CreatePlaylistRequest{
Title: "New Playlist",
Description: "Desc",
IsPublic: true,
}
createdPlaylist := &models.Playlist{
ID: uuid.New(),
UserID: userID,
Title: reqBody.Title,
Description: reqBody.Description,
IsPublic: reqBody.IsPublic,
}
mockService.On("CreatePlaylist", mock.Anything, userID, reqBody.Title, reqBody.Description, reqBody.IsPublic).Return(createdPlaylist, nil)
body, _ := json.Marshal(reqBody)
req, _ := http.NewRequest("POST", "/api/v1/playlists", 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.Equal(t, http.StatusCreated, w.Code)
mockService.AssertExpectations(t)
}
func TestPlaylistHandler_GetPlaylist_NotFound(t *testing.T) {
mockService := new(MockPlaylistService)
router := setupPlaylistTestRouter(mockService)
userID := uuid.New() // Authenticated user
playlistID := uuid.New()
// Error returned by service when not found or access denied
mockService.On("GetPlaylist", mock.Anything, playlistID, mock.MatchedBy(func(u *uuid.UUID) bool {
return u != nil && *u == userID
})).Return(nil, services.ErrPlaylistNotFound)
req, _ := http.NewRequest("GET", "/api/v1/playlists/"+playlistID.String(), nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
mockService.AssertExpectations(t)
}
func TestPlaylistHandler_DeletePlaylist_Success(t *testing.T) {
mockService := new(MockPlaylistService)
router := setupPlaylistTestRouter(mockService)
userID := uuid.New()
playlistID := uuid.New()
mockService.On("DeletePlaylist", mock.Anything, playlistID, userID).Return(nil)
req, _ := http.NewRequest("DELETE", "/api/v1/playlists/"+playlistID.String(), nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
mockService.AssertExpectations(t)
}
func TestPlaylistHandler_DeletePlaylist_Forbidden(t *testing.T) {
mockService := new(MockPlaylistService)
router := setupPlaylistTestRouter(mockService)
userID := uuid.New()
playlistID := uuid.New()
mockService.On("DeletePlaylist", mock.Anything, playlistID, userID).Return(services.ErrAccessDenied)
req, _ := http.NewRequest("DELETE", "/api/v1/playlists/"+playlistID.String(), nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusForbidden, w.Code)
mockService.AssertExpectations(t)
}
func TestPlaylistHandler_UpdatePlaylist_Success(t *testing.T) {
mockService := new(MockPlaylistService)
router := setupPlaylistTestRouter(mockService)
userID := uuid.New()
playlistID := uuid.New()
newTitle := "Updated Title"
reqBody := UpdatePlaylistRequest{
Title: &newTitle,
}
updatedPlaylist := &models.Playlist{
ID: playlistID,
Title: newTitle,
}
mockService.On("UpdatePlaylist", mock.Anything, playlistID, userID,
mock.MatchedBy(func(s *string) bool { return s != nil && *s == "Updated Title" }),
(*string)(nil), (*bool)(nil)).Return(updatedPlaylist, nil)
body, _ := json.Marshal(reqBody)
req, _ := http.NewRequest("PUT", "/api/v1/playlists/"+playlistID.String(), 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.Equal(t, http.StatusOK, w.Code)
mockService.AssertExpectations(t)
}
func TestPlaylistHandler_UpdatePlaylist_ValidationError(t *testing.T) {
mockService := new(MockPlaylistService)
router := setupPlaylistTestRouter(mockService)
userID := uuid.New()
playlistID := uuid.New()
// Title too long or empty if it was required, but here just malformed request maybe?
// Let's send invalid json
req, _ := http.NewRequest("PUT", "/api/v1/playlists/"+playlistID.String(), bytes.NewBuffer([]byte("{invalid")))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
}