1035 lines
27 KiB
Go
1035 lines
27 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/google/uuid"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"go.uber.org/zap"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"veza-backend-api/internal/models"
|
|
"veza-backend-api/internal/services"
|
|
)
|
|
|
|
// createTestMP3 creates a minimal valid MP3 file header for testing
|
|
func createTestMP3() ([]byte, error) {
|
|
// MP3 file header (ID3v2 tag)
|
|
header := []byte{
|
|
'I', 'D', '3', // ID3v2 marker
|
|
0x03, 0x00, // Version
|
|
0x00, // Flags
|
|
0x00, 0x00, 0x00, 0x00, // Size (0 for test)
|
|
}
|
|
return header, nil
|
|
}
|
|
|
|
// createTestAudioFile creates a test audio file with specified extension
|
|
func createTestAudioFile(ext string) ([]byte, error) {
|
|
switch ext {
|
|
case ".mp3":
|
|
return createTestMP3()
|
|
case ".flac":
|
|
// FLAC file header
|
|
return []byte{'f', 'L', 'a', 'C', 0x00, 0x00, 0x00, 0x22}, nil
|
|
case ".wav":
|
|
// WAV file header
|
|
return []byte{'R', 'I', 'F', 'F', 0x00, 0x00, 0x00, 0x00, 'W', 'A', 'V', 'E'}, nil
|
|
case ".ogg":
|
|
// OGG file header
|
|
return []byte{'O', 'g', 'g', 'S', 0x00, 0x02, 0x00, 0x00}, nil
|
|
default:
|
|
return createTestMP3()
|
|
}
|
|
}
|
|
|
|
func setupTestTrackHandler(t *testing.T) (*TrackHandler, *gorm.DB, func()) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
// Setup in-memory SQLite database
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
assert.NoError(t, err)
|
|
|
|
// Auto-migrate
|
|
err = db.AutoMigrate(&models.Track{}, &models.User{})
|
|
assert.NoError(t, err)
|
|
|
|
// Create test user
|
|
user := &models.User{
|
|
ID: 123,
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
IsActive: true,
|
|
}
|
|
err = db.Create(user).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Setup logger
|
|
logger := zap.NewNop()
|
|
|
|
// Setup test upload directory
|
|
testUploadDir := "test_uploads/tracks"
|
|
trackService := services.NewTrackService(db, logger, testUploadDir)
|
|
trackUploadService := services.NewTrackUploadService(db, logger)
|
|
chunkService := services.NewTrackChunkService("test_uploads/tracks/chunks", logger)
|
|
trackLikeService := services.NewTrackLikeService(db, logger)
|
|
// Pass nil for streamService in tests
|
|
trackHandler := NewTrackHandler(trackService, trackUploadService, chunkService, trackLikeService, nil)
|
|
|
|
// Cleanup function
|
|
cleanup := func() {
|
|
os.RemoveAll("test_uploads")
|
|
}
|
|
|
|
return trackHandler, db, cleanup
|
|
}
|
|
|
|
func TestTrackHandler_UploadTrack_Success(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Create test MP3 file
|
|
mp3Data, err := createTestAudioFile(".mp3")
|
|
assert.NoError(t, err)
|
|
|
|
// Create multipart form
|
|
body := new(bytes.Buffer)
|
|
writer := multipart.NewWriter(body)
|
|
part, err := writer.CreateFormFile("file", "test.mp3")
|
|
assert.NoError(t, err)
|
|
_, err = part.Write(mp3Data)
|
|
assert.NoError(t, err)
|
|
writer.Close()
|
|
|
|
// Create request
|
|
req := httptest.NewRequest("POST", "/api/v1/tracks", body)
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
|
|
// Set user_id in context (simulating auth middleware)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
|
|
// Execute
|
|
handler.UploadTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response, "track")
|
|
|
|
track := response["track"].(map[string]interface{})
|
|
assert.Equal(t, float64(123), track["user_id"])
|
|
assert.Equal(t, "test", track["title"])
|
|
assert.Equal(t, "MP3", track["format"])
|
|
|
|
// Verify track was created in DB
|
|
var dbTrack models.Track
|
|
err = db.First(&dbTrack, track["id"]).Error
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(123), dbTrack.UserID)
|
|
assert.Equal(t, "test", dbTrack.Title)
|
|
}
|
|
|
|
func TestTrackHandler_UploadTrack_Unauthorized(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Create request without user_id in context
|
|
req := httptest.NewRequest("POST", "/api/v1/tracks/upload", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
// No user_id set
|
|
|
|
// Execute
|
|
handler.UploadTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "unauthorized", response["error"])
|
|
}
|
|
|
|
func TestTrackHandler_UploadTrack_NoFile(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Create request without file
|
|
req := httptest.NewRequest("POST", "/api/v1/tracks/upload", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
|
|
// Execute
|
|
handler.UploadTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response["error"], "no file provided")
|
|
}
|
|
|
|
func TestTrackHandler_UploadTrack_InvalidFormat(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Create test file with invalid format
|
|
invalidData := []byte("not an audio file")
|
|
|
|
// Create multipart form
|
|
body := new(bytes.Buffer)
|
|
writer := multipart.NewWriter(body)
|
|
part, err := writer.CreateFormFile("file", "test.txt")
|
|
assert.NoError(t, err)
|
|
_, err = part.Write(invalidData)
|
|
assert.NoError(t, err)
|
|
writer.Close()
|
|
|
|
// Create request
|
|
req := httptest.NewRequest("POST", "/api/v1/tracks", body)
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
|
|
// Execute
|
|
handler.UploadTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response["error"], "Invalid file format")
|
|
}
|
|
|
|
func TestTrackHandler_UploadTrack_FileTooLarge(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Create a large file (over 100MB)
|
|
largeData := make([]byte, 101*1024*1024) // 101MB
|
|
|
|
// Create multipart form
|
|
body := new(bytes.Buffer)
|
|
writer := multipart.NewWriter(body)
|
|
part, err := writer.CreateFormFile("file", "large.mp3")
|
|
assert.NoError(t, err)
|
|
_, err = part.Write(largeData)
|
|
assert.NoError(t, err)
|
|
writer.Close()
|
|
|
|
// Create request
|
|
req := httptest.NewRequest("POST", "/api/v1/tracks", body)
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
|
|
// Execute
|
|
handler.UploadTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response["error"], "File size exceeds maximum")
|
|
}
|
|
|
|
func TestTrackHandler_UploadTrack_ValidFormats(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
formats := []struct {
|
|
ext string
|
|
expected string
|
|
}{
|
|
{".mp3", "MP3"},
|
|
{".flac", "FLAC"},
|
|
{".wav", "WAV"},
|
|
{".ogg", "OGG"},
|
|
}
|
|
|
|
for _, format := range formats {
|
|
t.Run(format.ext, func(t *testing.T) {
|
|
// Create test audio file
|
|
audioData, err := createTestAudioFile(format.ext)
|
|
assert.NoError(t, err)
|
|
|
|
// Create multipart form
|
|
body := new(bytes.Buffer)
|
|
writer := multipart.NewWriter(body)
|
|
part, err := writer.CreateFormFile("file", "test"+format.ext)
|
|
assert.NoError(t, err)
|
|
_, err = part.Write(audioData)
|
|
assert.NoError(t, err)
|
|
writer.Close()
|
|
|
|
// Create request
|
|
req := httptest.NewRequest("POST", "/api/v1/tracks", body)
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
|
|
// Execute
|
|
handler.UploadTrack(c)
|
|
|
|
// Assert
|
|
if w.Code != http.StatusCreated {
|
|
t.Logf("Response body: %s", w.Body.String())
|
|
}
|
|
assert.Equal(t, http.StatusCreated, w.Code, "Format %s should be accepted", format.ext)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTrackHandler_ListTracks_Success(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer quelques tracks avec statut completed
|
|
track1 := &models.Track{
|
|
UserID: 123,
|
|
Title: "Track 1",
|
|
FilePath: "/test/track1.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Genre: "Rock",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track1).Error
|
|
assert.NoError(t, err)
|
|
|
|
track2 := &models.Track{
|
|
UserID: 123,
|
|
Title: "Track 2",
|
|
FilePath: "/test/track2.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Genre: "Jazz",
|
|
Duration: 200,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err = db.Create(track2).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Créer request
|
|
req := httptest.NewRequest("GET", "/api/v1/tracks?page=1&limit=20", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
|
|
// Execute
|
|
handler.ListTracks(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response, "tracks")
|
|
assert.Contains(t, response, "pagination")
|
|
|
|
tracks := response["tracks"].([]interface{})
|
|
assert.GreaterOrEqual(t, len(tracks), 2)
|
|
|
|
pagination := response["pagination"].(map[string]interface{})
|
|
assert.Equal(t, float64(1), pagination["page"])
|
|
assert.Equal(t, float64(20), pagination["limit"])
|
|
}
|
|
|
|
func TestTrackHandler_ListTracks_WithFilters(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer tracks avec différents genres
|
|
track1 := &models.Track{
|
|
UserID: 123,
|
|
Title: "Rock Track",
|
|
FilePath: "/test/track1.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Genre: "Rock",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track1).Error
|
|
assert.NoError(t, err)
|
|
|
|
track2 := &models.Track{
|
|
UserID: 123,
|
|
Title: "Jazz Track",
|
|
FilePath: "/test/track2.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Genre: "Jazz",
|
|
Duration: 200,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err = db.Create(track2).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Test avec filtre genre
|
|
req := httptest.NewRequest("GET", "/api/v1/tracks?genre=Rock", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
|
|
handler.ListTracks(c)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
|
|
tracks := response["tracks"].([]interface{})
|
|
assert.Equal(t, 1, len(tracks))
|
|
|
|
track := tracks[0].(map[string]interface{})
|
|
assert.Equal(t, "Rock", track["genre"])
|
|
}
|
|
|
|
func TestTrackHandler_ListTracks_WithPagination(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer 5 tracks
|
|
for i := 1; i <= 5; i++ {
|
|
track := &models.Track{
|
|
UserID: 123,
|
|
Title: "Track " + string(rune('0'+i)),
|
|
FilePath: "/test/track" + string(rune('0'+i)) + ".mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track).Error
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// Test page 1 avec limit 2
|
|
req := httptest.NewRequest("GET", "/api/v1/tracks?page=1&limit=2", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
|
|
handler.ListTracks(c)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
|
|
tracks := response["tracks"].([]interface{})
|
|
assert.Equal(t, 2, len(tracks))
|
|
|
|
pagination := response["pagination"].(map[string]interface{})
|
|
assert.Equal(t, float64(1), pagination["page"])
|
|
assert.Equal(t, float64(2), pagination["limit"])
|
|
assert.Equal(t, float64(5), pagination["total"])
|
|
}
|
|
|
|
func TestTrackHandler_ListTracks_WithSorting(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer tracks avec différents titres
|
|
track1 := &models.Track{
|
|
UserID: 123,
|
|
Title: "A Track",
|
|
FilePath: "/test/track1.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track1).Error
|
|
assert.NoError(t, err)
|
|
|
|
track2 := &models.Track{
|
|
UserID: 123,
|
|
Title: "Z Track",
|
|
FilePath: "/test/track2.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err = db.Create(track2).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Test avec tri par titre asc
|
|
req := httptest.NewRequest("GET", "/api/v1/tracks?sort_by=title&sort_order=asc", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
|
|
handler.ListTracks(c)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
|
|
tracks := response["tracks"].([]interface{})
|
|
assert.GreaterOrEqual(t, len(tracks), 2)
|
|
|
|
// Vérifier que le tri est appliqué (A avant Z)
|
|
firstTrack := tracks[0].(map[string]interface{})
|
|
assert.Equal(t, "A Track", firstTrack["title"])
|
|
}
|
|
|
|
func TestTrackHandler_UpdateTrack_Success(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer un track
|
|
track := &models.Track{
|
|
UserID: 123,
|
|
Title: "Original Title",
|
|
FilePath: "/test/track.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Genre: "Rock",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Créer request body
|
|
updateData := map[string]interface{}{
|
|
"title": "Updated Title",
|
|
"genre": "Jazz",
|
|
}
|
|
body, _ := json.Marshal(updateData)
|
|
|
|
// Créer request
|
|
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/v1/tracks/%d", track.ID), bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}
|
|
|
|
// Execute
|
|
handler.UpdateTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response, "track")
|
|
|
|
updatedTrack := response["track"].(map[string]interface{})
|
|
assert.Equal(t, "Updated Title", updatedTrack["title"])
|
|
assert.Equal(t, "Jazz", updatedTrack["genre"])
|
|
}
|
|
|
|
func TestTrackHandler_UpdateTrack_NotFound(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer request body
|
|
updateData := map[string]interface{}{
|
|
"title": "Updated Title",
|
|
}
|
|
body, _ := json.Marshal(updateData)
|
|
|
|
// Créer request avec un ID qui n'existe pas
|
|
req := httptest.NewRequest("PUT", "/api/v1/tracks/99999", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: "99999"}}
|
|
|
|
// Execute
|
|
handler.UpdateTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "track not found", response["error"])
|
|
}
|
|
|
|
func TestTrackHandler_UpdateTrack_Forbidden(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer un track appartenant à l'utilisateur 123
|
|
track := &models.Track{
|
|
UserID: 123,
|
|
Title: "Original Title",
|
|
FilePath: "/test/track.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Créer request body
|
|
updateData := map[string]interface{}{
|
|
"title": "Updated Title",
|
|
}
|
|
body, _ := json.Marshal(updateData)
|
|
|
|
// Créer request avec un autre utilisateur (456)
|
|
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/v1/tracks/%d", track.ID), bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(456))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}
|
|
|
|
// Execute
|
|
handler.UpdateTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusForbidden, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "forbidden", response["error"])
|
|
}
|
|
|
|
func TestTrackHandler_UpdateTrack_Unauthorized(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer request body
|
|
updateData := map[string]interface{}{
|
|
"title": "Updated Title",
|
|
}
|
|
body, _ := json.Marshal(updateData)
|
|
|
|
// Créer request sans user_id
|
|
req := httptest.NewRequest("PUT", "/api/v1/tracks/1", bytes.NewBuffer(body))
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
// Pas de user_id
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: "1"}}
|
|
|
|
// Execute
|
|
handler.UpdateTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "unauthorized", response["error"])
|
|
}
|
|
|
|
func TestTrackHandler_UpdateTrack_InvalidID(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer request body
|
|
updateData := map[string]interface{}{
|
|
"title": "Updated Title",
|
|
}
|
|
body, _ := json.Marshal(updateData)
|
|
|
|
// Créer request avec un ID invalide
|
|
req := httptest.NewRequest("PUT", "/api/v1/tracks/invalid", bytes.NewBuffer(body))
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: "invalid"}}
|
|
|
|
// Execute
|
|
handler.UpdateTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "invalid track id", response["error"])
|
|
}
|
|
|
|
func TestTrackHandler_UpdateTrack_EmptyTitle(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer un track
|
|
track := &models.Track{
|
|
UserID: 123,
|
|
Title: "Original Title",
|
|
FilePath: "/test/track.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Créer request body avec titre vide
|
|
updateData := map[string]interface{}{
|
|
"title": "",
|
|
}
|
|
body, _ := json.Marshal(updateData)
|
|
|
|
// Créer request
|
|
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/v1/tracks/%d", track.ID), bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}
|
|
|
|
// Execute
|
|
handler.UpdateTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response["error"], "title cannot be empty")
|
|
}
|
|
|
|
func TestTrackHandler_DeleteTrack_Success(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer un track
|
|
track := &models.Track{
|
|
UserID: 123,
|
|
Title: "Test Track",
|
|
FilePath: "/test/track.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Créer request
|
|
req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/v1/tracks/%d", track.ID), nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}
|
|
|
|
// Execute
|
|
handler.DeleteTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "track deleted successfully", response["message"])
|
|
|
|
// Vérifier que le track a été supprimé
|
|
var deletedTrack models.Track
|
|
err = db.First(&deletedTrack, track.ID).Error
|
|
assert.Error(t, err)
|
|
assert.Equal(t, gorm.ErrRecordNotFound, err)
|
|
}
|
|
|
|
func TestTrackHandler_DeleteTrack_NotFound(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer request avec un ID qui n'existe pas
|
|
req := httptest.NewRequest("DELETE", "/api/v1/tracks/99999", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: "99999"}}
|
|
|
|
// Execute
|
|
handler.DeleteTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "track not found", response["error"])
|
|
}
|
|
|
|
func TestTrackHandler_DeleteTrack_Forbidden(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer un track appartenant à l'utilisateur 123
|
|
track := &models.Track{
|
|
UserID: 123,
|
|
Title: "Test Track",
|
|
FilePath: "/test/track.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Créer request avec un autre utilisateur (456)
|
|
req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/v1/tracks/%d", track.ID), nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(456))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}
|
|
|
|
// Execute
|
|
handler.DeleteTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusForbidden, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "forbidden", response["error"])
|
|
|
|
// Vérifier que le track n'a pas été supprimé
|
|
var existingTrack models.Track
|
|
err = db.First(&existingTrack, track.ID).Error
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, track.ID, existingTrack.ID)
|
|
}
|
|
|
|
func TestTrackHandler_DeleteTrack_Unauthorized(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer request sans user_id
|
|
req := httptest.NewRequest("DELETE", "/api/v1/tracks/1", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
// Pas de user_id
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: "1"}}
|
|
|
|
// Execute
|
|
handler.DeleteTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "unauthorized", response["error"])
|
|
}
|
|
|
|
func TestTrackHandler_DeleteTrack_InvalidID(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer request avec un ID invalide
|
|
req := httptest.NewRequest("DELETE", "/api/v1/tracks/invalid", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: "invalid"}}
|
|
|
|
// Execute
|
|
handler.DeleteTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "invalid track id", response["error"])
|
|
}
|
|
|
|
func TestTrackHandler_GetTrack_Success(t *testing.T) {
|
|
handler, db, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer un track
|
|
track := &models.Track{
|
|
UserID: 123,
|
|
Title: "Test Track",
|
|
FilePath: "/test/track.mp3",
|
|
FileSize: 5 * 1024 * 1024,
|
|
Format: "MP3",
|
|
Genre: "Rock",
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Créer request
|
|
req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/tracks/%d", track.ID), nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}
|
|
|
|
// Execute
|
|
handler.GetTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response, "track")
|
|
|
|
retrievedTrack := response["track"].(map[string]interface{})
|
|
assert.Equal(t, float64(track.ID), retrievedTrack["id"])
|
|
assert.Equal(t, track.Title, retrievedTrack["title"])
|
|
}
|
|
|
|
func TestTrackHandler_GetTrack_NotFound(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer request avec un ID qui n'existe pas
|
|
req := httptest.NewRequest("GET", "/api/v1/tracks/99999", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: "99999"}}
|
|
|
|
// Execute
|
|
handler.GetTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "track not found", response["error"])
|
|
}
|
|
|
|
func TestTrackHandler_GetTrack_InvalidID(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer request avec un ID invalide
|
|
req := httptest.NewRequest("GET", "/api/v1/tracks/invalid", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{gin.Param{Key: "id", Value: "invalid"}}
|
|
|
|
// Execute
|
|
handler.GetTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "invalid track id", response["error"])
|
|
}
|
|
|
|
func TestTrackHandler_GetTrack_MissingID(t *testing.T) {
|
|
handler, _, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Créer request sans ID
|
|
req := httptest.NewRequest("GET", "/api/v1/tracks/", nil)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
c.Set("user_id", int64(123))
|
|
c.Params = gin.Params{}
|
|
|
|
// Execute
|
|
handler.GetTrack(c)
|
|
|
|
// Assert
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "track id is required", response["error"])
|
|
}
|