360 lines
9.3 KiB
Text
360 lines
9.3 KiB
Text
package services
|
|
|
|
import (
|
|
"context"
|
|
"github.com/google/uuid"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"veza-backend-api/internal/models"
|
|
)
|
|
|
|
func setupTestBatchUpdateDB(t *testing.T) (*TrackService, *gorm.DB, func()) {
|
|
// Setup in-memory SQLite database
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
|
|
// Auto-migrate
|
|
err = db.AutoMigrate(&models.User{}, &models.Track{})
|
|
require.NoError(t, err)
|
|
|
|
// Create test user
|
|
user := &models.User{
|
|
ID: 1,
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
IsActive: true,
|
|
}
|
|
err = db.Create(user).Error
|
|
require.NoError(t, err)
|
|
|
|
// Create another user
|
|
user2 := &models.User{
|
|
ID: 2,
|
|
Username: "otheruser",
|
|
Email: "other@example.com",
|
|
IsActive: true,
|
|
}
|
|
err = db.Create(user2).Error
|
|
require.NoError(t, err)
|
|
|
|
// Create logger
|
|
logger := zap.NewNop()
|
|
|
|
// Create service
|
|
service := NewTrackService(db, logger, "test_uploads")
|
|
|
|
// Cleanup function
|
|
cleanup := func() {
|
|
// SQLite in-memory database doesn't need explicit cleanup
|
|
}
|
|
|
|
return service, db, cleanup
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_Success(t *testing.T) {
|
|
service, db, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create tracks
|
|
track1 := &models.Track{
|
|
UserID: 1,
|
|
Title: "Track 1",
|
|
FilePath: "/path/to/track1.mp3",
|
|
FileSize: 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: false,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
track2 := &models.Track{
|
|
UserID: 1,
|
|
Title: "Track 2",
|
|
FilePath: "/path/to/track2.mp3",
|
|
FileSize: 2048,
|
|
Format: "MP3",
|
|
Duration: 240,
|
|
IsPublic: false,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track1).Error
|
|
require.NoError(t, err)
|
|
err = db.Create(track2).Error
|
|
require.NoError(t, err)
|
|
|
|
// Batch update
|
|
updates := map[string]interface{}{
|
|
"is_public": true,
|
|
"genre": "Electronic",
|
|
}
|
|
result, err := service.BatchUpdateTracks(ctx, []int64{track1.ID, track2.ID}, 1, updates)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, result)
|
|
|
|
// Verify results
|
|
assert.Equal(t, 2, len(result.Updated))
|
|
assert.Contains(t, result.Updated, track1.ID)
|
|
assert.Contains(t, result.Updated, track2.ID)
|
|
assert.Equal(t, 0, len(result.Failed))
|
|
|
|
// Verify tracks are updated in database
|
|
var updatedTrack1, updatedTrack2 models.Track
|
|
db.First(&updatedTrack1, track1.ID)
|
|
db.First(&updatedTrack2, track2.ID)
|
|
assert.True(t, updatedTrack1.IsPublic)
|
|
assert.True(t, updatedTrack2.IsPublic)
|
|
assert.Equal(t, "Electronic", updatedTrack1.Genre)
|
|
assert.Equal(t, "Electronic", updatedTrack2.Genre)
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_EmptyList(t *testing.T) {
|
|
service, _, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
updates := map[string]interface{}{
|
|
"is_public": true,
|
|
}
|
|
result, err := service.BatchUpdateTracks(ctx, []int64{}, 1, updates)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, result)
|
|
assert.Equal(t, 0, len(result.Updated))
|
|
assert.Equal(t, 0, len(result.Failed))
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_ExceedsMaxBatchSize(t *testing.T) {
|
|
service, _, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create a list with more than 100 tracks
|
|
trackIDs := make([]int64, 101)
|
|
for i := range trackIDs {
|
|
trackIDs[i] = int64(i + 1)
|
|
}
|
|
|
|
updates := map[string]interface{}{
|
|
"is_public": true,
|
|
}
|
|
result, err := service.BatchUpdateTracks(ctx, trackIDs, 1, updates)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, result)
|
|
assert.Contains(t, err.Error(), "batch size exceeds maximum")
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_EmptyUpdates(t *testing.T) {
|
|
service, _, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
result, err := service.BatchUpdateTracks(ctx, []int64{1, 2}, 1, map[string]interface{}{})
|
|
assert.Error(t, err)
|
|
assert.Nil(t, result)
|
|
assert.Contains(t, err.Error(), "updates cannot be empty")
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_NotFound(t *testing.T) {
|
|
service, _, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
updates := map[string]interface{}{
|
|
"is_public": true,
|
|
}
|
|
result, err := service.BatchUpdateTracks(ctx, []int64{999, 1000}, 1, updates)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, result)
|
|
|
|
assert.Equal(t, 0, len(result.Updated))
|
|
assert.Equal(t, 2, len(result.Failed))
|
|
assert.Equal(t, int64(999), result.Failed[0].TrackID)
|
|
assert.Equal(t, "track not found", result.Failed[0].Error)
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_Forbidden(t *testing.T) {
|
|
service, db, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create track owned by user 1
|
|
track1 := &models.Track{
|
|
UserID: 1,
|
|
Title: "Track 1",
|
|
FilePath: "/path/to/track1.mp3",
|
|
FileSize: 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: false,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track1).Error
|
|
require.NoError(t, err)
|
|
|
|
// Try to update as user 2
|
|
updates := map[string]interface{}{
|
|
"is_public": true,
|
|
}
|
|
result, err := service.BatchUpdateTracks(ctx, []int64{track1.ID}, 2, updates)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, result)
|
|
|
|
assert.Equal(t, 0, len(result.Updated))
|
|
assert.Equal(t, 1, len(result.Failed))
|
|
assert.Equal(t, track1.ID, result.Failed[0].TrackID)
|
|
assert.Contains(t, result.Failed[0].Error, "forbidden")
|
|
|
|
// Verify track is not updated
|
|
var updatedTrack models.Track
|
|
db.First(&updatedTrack, track1.ID)
|
|
assert.False(t, updatedTrack.IsPublic)
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_PartialSuccess(t *testing.T) {
|
|
service, db, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create tracks
|
|
track1 := &models.Track{
|
|
UserID: 1,
|
|
Title: "Track 1",
|
|
FilePath: "/path/to/track1.mp3",
|
|
FileSize: 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: false,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
track2 := &models.Track{
|
|
UserID: 2, // Owned by different user
|
|
Title: "Track 2",
|
|
FilePath: "/path/to/track2.mp3",
|
|
FileSize: 2048,
|
|
Format: "MP3",
|
|
Duration: 240,
|
|
IsPublic: false,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track1).Error
|
|
require.NoError(t, err)
|
|
err = db.Create(track2).Error
|
|
require.NoError(t, err)
|
|
|
|
// Try to update both as user 1
|
|
updates := map[string]interface{}{
|
|
"is_public": true,
|
|
}
|
|
result, err := service.BatchUpdateTracks(ctx, []int64{track1.ID, track2.ID}, 1, updates)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, result)
|
|
|
|
// Track1 should be updated, track2 should fail
|
|
assert.Equal(t, 1, len(result.Updated))
|
|
assert.Contains(t, result.Updated, track1.ID)
|
|
assert.Equal(t, 1, len(result.Failed))
|
|
assert.Equal(t, track2.ID, result.Failed[0].TrackID)
|
|
assert.Contains(t, result.Failed[0].Error, "forbidden")
|
|
|
|
// Verify track1 is updated, track2 is not
|
|
var updatedTrack1, updatedTrack2 models.Track
|
|
db.First(&updatedTrack1, track1.ID)
|
|
db.First(&updatedTrack2, track2.ID)
|
|
assert.True(t, updatedTrack1.IsPublic)
|
|
assert.False(t, updatedTrack2.IsPublic)
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_InvalidTitle(t *testing.T) {
|
|
service, _, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
updates := map[string]interface{}{
|
|
"title": "", // Empty title
|
|
}
|
|
result, err := service.BatchUpdateTracks(ctx, []int64{1}, 1, updates)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, result)
|
|
assert.Contains(t, err.Error(), "title cannot be empty")
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_InvalidYear(t *testing.T) {
|
|
service, _, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
updates := map[string]interface{}{
|
|
"year": 1800, // Year too old
|
|
}
|
|
result, err := service.BatchUpdateTracks(ctx, []int64{1}, 1, updates)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, result)
|
|
assert.Contains(t, err.Error(), "year must be between")
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_InvalidIsPublic(t *testing.T) {
|
|
service, _, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
updates := map[string]interface{}{
|
|
"is_public": "not a boolean", // Invalid type
|
|
}
|
|
result, err := service.BatchUpdateTracks(ctx, []int64{1}, 1, updates)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, result)
|
|
assert.Contains(t, err.Error(), "invalid value for is_public")
|
|
}
|
|
|
|
func TestTrackService_BatchUpdateTracks_UnauthorizedField(t *testing.T) {
|
|
service, db, cleanup := setupTestBatchUpdateDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create track
|
|
track := &models.Track{
|
|
UserID: 1,
|
|
Title: "Track 1",
|
|
FilePath: "/path/to/track1.mp3",
|
|
FileSize: 1024,
|
|
Format: "MP3",
|
|
Duration: 180,
|
|
IsPublic: false,
|
|
Status: models.TrackStatusCompleted,
|
|
}
|
|
err := db.Create(track).Error
|
|
require.NoError(t, err)
|
|
|
|
// Try to update with unauthorized field (user_id should not be updatable)
|
|
updates := map[string]interface{}{
|
|
"is_public": true,
|
|
"user_id": 999, // This should be ignored
|
|
}
|
|
result, err := service.BatchUpdateTracks(ctx, []int64{track.ID}, 1, updates)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, result)
|
|
|
|
// Should succeed with only is_public updated
|
|
assert.Equal(t, 1, len(result.Updated))
|
|
|
|
// Verify user_id is not changed
|
|
var updatedTrack models.Track
|
|
db.First(&updatedTrack, track.ID)
|
|
assert.Equal(t, int64(1), updatedTrack.UserID)
|
|
assert.True(t, updatedTrack.IsPublic)
|
|
}
|