Phase 1 - Backend tests: - Add PlaybackAnalytics to AutoMigrate in setupTestTrackHandler - Create migration 081_create_playback_analytics.sql for production - PlaybackAnalyticsService: return ErrTrackNotFound for missing track - RecordPlay handler: return 404 when track not found - CreateShare: use RespondSuccess, fix services.ErrTrackNotFound/ErrForbidden - GetTrackLikes, UnlikeTrack: use RespondSuccess for consistent response - GetUserLikedTracks test: fix route /users/:id/likes and params - GetSharedTrack_InvalidToken: set share service in test Phase 4 - Job queue transcoding: - Add EnqueueTranscodingJob to JobEnqueuer interface - Add TypeTranscoding and processTranscodingJob (stub) in JobWorker - MockJobEnqueuer: implement EnqueueTranscodingJob Phase 5 - Gamification cleanup: - Move api_manager.go to internal/api/archive/ - Add archive/README.md documenting archived modules - Update TODOS_AUDIT.md and FEATURE_STATUS.md
359 lines
9.5 KiB
Go
359 lines
9.5 KiB
Go
package track
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"veza-backend-api/internal/models"
|
|
"veza-backend-api/internal/services"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap/zaptest"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// setupTestTrackHandler creates a test handler with real services and in-memory database
|
|
func setupTestTrackHandler(t *testing.T) (*TrackHandler, *gorm.DB, *gin.Engine, func()) {
|
|
gin.SetMode(gin.TestMode)
|
|
logger := zaptest.NewLogger(t)
|
|
|
|
// Setup in-memory SQLite database
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
db.Exec("PRAGMA foreign_keys = ON")
|
|
|
|
// Auto-migrate models
|
|
err = db.AutoMigrate(
|
|
&models.User{},
|
|
&models.Track{},
|
|
&models.TrackLike{},
|
|
&models.TrackComment{},
|
|
&models.TrackPlay{},
|
|
&models.TrackShare{},
|
|
&models.TrackHistory{},
|
|
&models.Role{},
|
|
&models.Permission{},
|
|
&models.UserRole{},
|
|
&models.RolePermission{},
|
|
&models.PlaybackAnalytics{},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Setup services
|
|
trackService := NewTrackService(db, logger, "/tmp/test-uploads")
|
|
trackUploadService := services.NewTrackUploadService(db, logger)
|
|
chunkService := services.NewTrackChunkService("/tmp/test-chunks", nil, logger)
|
|
likeService := services.NewTrackLikeService(db, logger)
|
|
streamService := services.NewStreamService("http://localhost:8082", logger)
|
|
|
|
handler := NewTrackHandler(
|
|
trackService,
|
|
trackUploadService,
|
|
chunkService,
|
|
likeService,
|
|
streamService,
|
|
)
|
|
historyService := services.NewTrackHistoryService(db, logger)
|
|
handler.SetHistoryService(historyService)
|
|
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
// Mock auth middleware - set user_id from header if present
|
|
userIDStr := c.GetHeader("X-User-ID")
|
|
if userIDStr != "" {
|
|
uid, err := uuid.Parse(userIDStr)
|
|
if err == nil {
|
|
c.Set("user_id", uid)
|
|
}
|
|
}
|
|
c.Next()
|
|
})
|
|
|
|
cleanup := func() {
|
|
// Database cleanup handled by test
|
|
}
|
|
|
|
return handler, db, router, cleanup
|
|
}
|
|
|
|
// Helper to create a test track
|
|
func createTestTrack(id uuid.UUID, userID uuid.UUID) *models.Track {
|
|
return &models.Track{
|
|
ID: id,
|
|
UserID: userID,
|
|
Title: "Test Track",
|
|
Artist: "Test Artist",
|
|
FilePath: "/tmp/test-uploads/test.mp3",
|
|
Format: "mp3",
|
|
FileSize: 1024,
|
|
Duration: 180,
|
|
IsPublic: true,
|
|
Status: models.TrackStatusCompleted,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
}
|
|
|
|
// TestTrackHandler_GetTrack_Success tests successful track retrieval
|
|
func TestTrackHandler_GetTrack_Success(t *testing.T) {
|
|
handler, db, router, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Create a test user first
|
|
userID := uuid.New()
|
|
user := &models.User{
|
|
ID: userID,
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
}
|
|
err := db.Create(user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Create a test track
|
|
trackID := uuid.New()
|
|
track := createTestTrack(trackID, userID)
|
|
err = db.Create(track).Error
|
|
require.NoError(t, err)
|
|
|
|
router.GET("/tracks/:id", handler.GetTrack)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/tracks/%s", trackID.String()), nil)
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
require.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
// TestTrackHandler_GetTrack_NotFound tests track not found scenario
|
|
func TestTrackHandler_GetTrack_NotFound(t *testing.T) {
|
|
handler, _, router, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
router.GET("/tracks/:id", handler.GetTrack)
|
|
|
|
nonExistentID := uuid.New()
|
|
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/tracks/%s", nonExistentID.String()), nil)
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
}
|
|
|
|
// TestTrackHandler_GetTrack_InvalidID tests invalid track ID format
|
|
func TestTrackHandler_GetTrack_InvalidID(t *testing.T) {
|
|
handler, _, router, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
router.GET("/tracks/:id", handler.GetTrack)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/tracks/invalid-id", nil)
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// TestTrackHandler_ListTracks_Success tests successful track listing
|
|
func TestTrackHandler_ListTracks_Success(t *testing.T) {
|
|
handler, db, router, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Create test user first
|
|
userID := uuid.New()
|
|
user := &models.User{
|
|
ID: userID,
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
}
|
|
err := db.Create(user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Create test tracks
|
|
for i := 0; i < 3; i++ {
|
|
track := createTestTrack(uuid.New(), userID)
|
|
track.Title = fmt.Sprintf("Track %d", i+1)
|
|
err := db.Create(track).Error
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
router.GET("/tracks", handler.ListTracks)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/tracks?page=1&limit=10", nil)
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
require.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
// TestTrackHandler_UpdateTrack_Success tests successful track update
|
|
func TestTrackHandler_UpdateTrack_Success(t *testing.T) {
|
|
handler, db, router, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Create test user and track
|
|
userID := uuid.New()
|
|
user := &models.User{
|
|
ID: userID,
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
}
|
|
err := db.Create(user).Error
|
|
require.NoError(t, err)
|
|
|
|
trackID := uuid.New()
|
|
track := createTestTrack(trackID, userID)
|
|
err = db.Create(track).Error
|
|
require.NoError(t, err)
|
|
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", userID)
|
|
c.Next()
|
|
})
|
|
router.PUT("/tracks/:id", handler.UpdateTrack)
|
|
|
|
updateReq := UpdateTrackRequest{
|
|
Title: stringPtr("Updated Title"),
|
|
Artist: stringPtr("Updated Artist"),
|
|
}
|
|
body, _ := json.Marshal(updateReq)
|
|
req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/tracks/%s", trackID.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)
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
require.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
// TestTrackHandler_DeleteTrack_Success tests successful track deletion
|
|
func TestTrackHandler_DeleteTrack_Success(t *testing.T) {
|
|
handler, db, router, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Create test user and track
|
|
userID := uuid.New()
|
|
user := &models.User{
|
|
ID: userID,
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
}
|
|
err := db.Create(user).Error
|
|
require.NoError(t, err)
|
|
|
|
trackID := uuid.New()
|
|
track := createTestTrack(trackID, userID)
|
|
err = db.Create(track).Error
|
|
require.NoError(t, err)
|
|
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", userID)
|
|
c.Next()
|
|
})
|
|
router.DELETE("/tracks/:id", handler.DeleteTrack)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/tracks/%s", trackID.String()), 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{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
require.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
// TestTrackHandler_LikeTrack_Success tests successful track like
|
|
func TestTrackHandler_LikeTrack_Success(t *testing.T) {
|
|
handler, db, router, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Create test user first
|
|
userID := uuid.New()
|
|
user := &models.User{
|
|
ID: userID,
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
}
|
|
err := db.Create(user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Create test track
|
|
trackID := uuid.New()
|
|
track := createTestTrack(trackID, userID)
|
|
err = db.Create(track).Error
|
|
require.NoError(t, err)
|
|
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", userID)
|
|
c.Next()
|
|
})
|
|
router.POST("/tracks/:id/like", handler.LikeTrack)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/tracks/%s/like", trackID.String()), nil)
|
|
req.Header.Set("X-User-ID", userID.String())
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
// TestTrackHandler_SearchTracks_Success tests successful track search
|
|
func TestTrackHandler_SearchTracks_Success(t *testing.T) {
|
|
handler, db, router, cleanup := setupTestTrackHandler(t)
|
|
defer cleanup()
|
|
|
|
// Setup search service
|
|
searchService := services.NewTrackSearchService(db)
|
|
handler.SetSearchService(searchService)
|
|
|
|
// Create test user first
|
|
userID := uuid.New()
|
|
user := &models.User{
|
|
ID: userID,
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
}
|
|
err := db.Create(user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Create test tracks
|
|
track1 := createTestTrack(uuid.New(), userID)
|
|
track1.Title = "Test Song"
|
|
err = db.Create(track1).Error
|
|
require.NoError(t, err)
|
|
|
|
router.GET("/tracks/search", handler.SearchTracks)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/tracks/search?q=Test&limit=10&offset=0", nil)
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
// Helper function to create string pointer
|
|
func stringPtr(s string) *string {
|
|
return &s
|
|
}
|