veza/veza-backend-api/internal/core/track/handler_test.go
senke 7846bbab28 fix(backend): remediation plan — tests, playback_analytics, job queue, gamification
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
2026-02-17 16:01:45 +01:00

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
}