package services import ( "context" "fmt" "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 setupTestTrackLikeService(t *testing.T) (*TrackLikeService, *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{}, &models.TrackLike{}) require.NoError(t, err) // Setup logger logger := zap.NewNop() // Setup service service := NewTrackLikeService(db, logger) // Cleanup function cleanup := func() { // Database will be closed automatically } return service, db, cleanup } func TestTrackLikeService_LikeTrack_Success(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test user user := &models.User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create test track track := &models.Track{ UserID: userID, Title: "Test Track", FilePath: "/test/track.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted, LikeCount: 0, } err = db.Create(track).Error require.NoError(t, err) // Like track err = service.LikeTrack(ctx, userID, track.ID) assert.NoError(t, err) // Verify like was created var like models.TrackLike err = db.Where("user_id = ? AND track_id = ?", userID, track.ID).First(&like).Error assert.NoError(t, err) assert.Equal(t, userID, like.UserID) assert.Equal(t, track.ID, like.TrackID) // Verify track like_count was updated var updatedTrack models.Track err = db.First(&updatedTrack, "id = ?", track.ID).Error assert.NoError(t, err) assert.Equal(t, int64(1), updatedTrack.LikeCount) } func TestTrackLikeService_LikeTrack_AlreadyLiked(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test user user := &models.User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create test track track := &models.Track{ UserID: userID, Title: "Test Track", FilePath: "/test/track.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted, LikeCount: 0, } err = db.Create(track).Error require.NoError(t, err) // Like track first time err = service.LikeTrack(ctx, userID, track.ID) assert.NoError(t, err) // Try to like again (should be idempotent) err = service.LikeTrack(ctx, userID, track.ID) assert.NoError(t, err) // Verify only one like exists var count int64 db.Model(&models.TrackLike{}).Where("user_id = ? AND track_id = ?", userID, track.ID).Count(&count) assert.Equal(t, int64(1), count) } func TestTrackLikeService_LikeTrack_TrackNotFound(t *testing.T) { service, _, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() // Try to like non-existent track err := service.LikeTrack(ctx, uuid.New(), uuid.New()) assert.Error(t, err) assert.Contains(t, err.Error(), "track not found") } func TestTrackLikeService_UnlikeTrack_Success(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test user user := &models.User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create test track track := &models.Track{ UserID: userID, Title: "Test Track", FilePath: "/test/track.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted, LikeCount: 1, } err = db.Create(track).Error require.NoError(t, err) // Create like like := &models.TrackLike{ UserID: userID, TrackID: track.ID, } err = db.Create(like).Error require.NoError(t, err) // Unlike track err = service.UnlikeTrack(ctx, userID, track.ID) assert.NoError(t, err) // Verify like was deleted var count int64 db.Model(&models.TrackLike{}).Where("user_id = ? AND track_id = ?", userID, track.ID).Count(&count) assert.Equal(t, int64(0), count) // Verify track like_count was updated var updatedTrack models.Track err = db.First(&updatedTrack, "id = ?", track.ID).Error assert.NoError(t, err) assert.Equal(t, int64(0), updatedTrack.LikeCount) } func TestTrackLikeService_UnlikeTrack_NotLiked(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test user user := &models.User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create test track track := &models.Track{ UserID: userID, Title: "Test Track", FilePath: "/test/track.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted, LikeCount: 0, } err = db.Create(track).Error require.NoError(t, err) // Try to unlike (should be idempotent) err = service.UnlikeTrack(ctx, userID, track.ID) assert.NoError(t, err) } func TestTrackLikeService_IsLiked_True(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test user user := &models.User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create test track track := &models.Track{ UserID: userID, 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 require.NoError(t, err) // Create like like := &models.TrackLike{ UserID: userID, TrackID: track.ID, } err = db.Create(like).Error require.NoError(t, err) // Check if liked isLiked, err := service.IsLiked(ctx, userID, track.ID) assert.NoError(t, err) assert.True(t, isLiked) } func TestTrackLikeService_IsLiked_False(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test user user := &models.User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create test track track := &models.Track{ UserID: userID, 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 require.NoError(t, err) // Check if liked (should be false) isLiked, err := service.IsLiked(ctx, userID, track.ID) assert.NoError(t, err) assert.False(t, isLiked) } func TestTrackLikeService_GetTrackLikesCount(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID1 := uuid.New() userID2 := uuid.New() // Create test users user1 := &models.User{ ID: userID1, Username: "testuser1", Email: "test1@example.com", IsActive: true, } err := db.Create(user1).Error require.NoError(t, err) user2 := &models.User{ ID: userID2, Username: "testuser2", Email: "test2@example.com", IsActive: true, } err = db.Create(user2).Error require.NoError(t, err) // Create test track track := &models.Track{ UserID: userID1, 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 require.NoError(t, err) // Create likes like1 := &models.TrackLike{UserID: userID1, TrackID: track.ID} err = db.Create(like1).Error require.NoError(t, err) like2 := &models.TrackLike{UserID: userID2, TrackID: track.ID} err = db.Create(like2).Error require.NoError(t, err) // Get likes count count, err := service.GetTrackLikesCount(ctx, track.ID) assert.NoError(t, err) assert.Equal(t, int64(2), count) } func TestTrackLikeService_GetTrackLikesCount_Zero(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test track track := &models.Track{ UserID: userID, 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 require.NoError(t, err) // Get likes count (should be 0) count, err := service.GetTrackLikesCount(ctx, track.ID) assert.NoError(t, err) assert.Equal(t, int64(0), count) } func TestTrackLikeService_GetUserLikedTracks(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test user user := &models.User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create test tracks track1 := &models.Track{ UserID: userID, Title: "Track 1", FilePath: "/test/track1.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted, } err = db.Create(track1).Error require.NoError(t, err) track2 := &models.Track{ UserID: userID, Title: "Track 2", FilePath: "/test/track2.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted, } err = db.Create(track2).Error require.NoError(t, err) // Create likes like1 := &models.TrackLike{UserID: userID, TrackID: track1.ID} err = db.Create(like1).Error require.NoError(t, err) like2 := &models.TrackLike{UserID: userID, TrackID: track2.ID} err = db.Create(like2).Error require.NoError(t, err) // Get user liked tracks tracks, err := service.GetUserLikedTracks(ctx, userID, 10, 0) assert.NoError(t, err) assert.Equal(t, 2, len(tracks)) } func TestTrackLikeService_GetUserLikedTracks_WithLimit(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test user user := &models.User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create test tracks for i := 1; i <= 5; i++ { track := &models.Track{ UserID: userID, Title: fmt.Sprintf("Track %d", i), FilePath: fmt.Sprintf("/test/track%d.mp3", i), FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) like := &models.TrackLike{UserID: userID, TrackID: track.ID} err = db.Create(like).Error require.NoError(t, err) } // Get user liked tracks with limit tracks, err := service.GetUserLikedTracks(ctx, userID, 3, 0) assert.NoError(t, err) assert.Equal(t, 3, len(tracks)) } func TestTrackLikeService_GetUserLikedTracks_WithOffset(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test user user := &models.User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create test tracks for i := 1; i <= 5; i++ { track := &models.Track{ UserID: userID, Title: fmt.Sprintf("Track %d", i), FilePath: fmt.Sprintf("/test/track%d.mp3", i), FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) like := &models.TrackLike{UserID: userID, TrackID: track.ID} err = db.Create(like).Error require.NoError(t, err) } // Get user liked tracks with offset tracks, err := service.GetUserLikedTracks(ctx, userID, 3, 2) assert.NoError(t, err) assert.Equal(t, 3, len(tracks)) } func TestTrackLikeService_GetUserLikedTracksCount(t *testing.T) { service, db, cleanup := setupTestTrackLikeService(t) defer cleanup() ctx := context.Background() userID := uuid.New() // Create test user user := &models.User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create test tracks for i := 1; i <= 3; i++ { track := &models.Track{ UserID: userID, Title: fmt.Sprintf("Track %d", i), FilePath: fmt.Sprintf("/test/track%d.mp3", i), FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: models.TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) like := &models.TrackLike{UserID: userID, TrackID: track.ID} err = db.Create(like).Error require.NoError(t, err) } // Get user liked tracks count count, err := service.GetUserLikedTracksCount(ctx, userID) assert.NoError(t, err) assert.Equal(t, int64(3), count) }