package services import ( "context" "testing" "github.com/google/uuid" "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/database" ) func setupTestSocialService(t *testing.T) (*SocialService, *gorm.DB, *database.Database) { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) db.Exec("PRAGMA foreign_keys = ON") // Create tables manually err = db.Exec(` CREATE TABLE follows ( follower_id TEXT NOT NULL, followed_id TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (follower_id, followed_id) ) `).Error require.NoError(t, err) err = db.Exec(` CREATE TABLE likes ( user_id TEXT NOT NULL, track_id TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, track_id) ) `).Error require.NoError(t, err) err = db.Exec(` CREATE TABLE comments ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL, track_id TEXT NOT NULL, parent_id TEXT, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `).Error require.NoError(t, err) // Register a custom SQLite function to generate UUIDs (for testing) // Note: This requires sqlite3 with CGO support, so we'll use a workaround // For now, we'll test CreateComment by inserting directly with a UUID err = db.Exec(` CREATE TABLE user_blocks ( blocker_id TEXT NOT NULL, blocked_id TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (blocker_id, blocked_id) ) `).Error require.NoError(t, err) sqlDB, err := db.DB() require.NoError(t, err) testDB := &database.Database{ DB: sqlDB, } logger := zap.NewNop() service := NewSocialService(testDB, logger) return service, db, testDB } func TestSocialService_FollowUser_Success(t *testing.T) { service, _, _ := setupTestSocialService(t) followerID := uuid.New() followedID := uuid.New() err := service.FollowUser(followerID, followedID) assert.NoError(t, err) } func TestSocialService_FollowUser_Duplicate(t *testing.T) { service, _, _ := setupTestSocialService(t) followerID := uuid.New() followedID := uuid.New() // First follow err := service.FollowUser(followerID, followedID) assert.NoError(t, err) // Try to follow again (should not error due to ON CONFLICT DO NOTHING) err = service.FollowUser(followerID, followedID) assert.NoError(t, err) } func TestSocialService_UnfollowUser_Success(t *testing.T) { service, _, _ := setupTestSocialService(t) followerID := uuid.New() followedID := uuid.New() // First follow err := service.FollowUser(followerID, followedID) assert.NoError(t, err) // Then unfollow err = service.UnfollowUser(followerID, followedID) assert.NoError(t, err) } func TestSocialService_UnfollowUser_NotFollowing(t *testing.T) { service, _, _ := setupTestSocialService(t) followerID := uuid.New() followedID := uuid.New() // Try to unfollow without following first err := service.UnfollowUser(followerID, followedID) assert.NoError(t, err) // Should not error, just do nothing } func TestSocialService_LikeTrack_Success(t *testing.T) { service, _, _ := setupTestSocialService(t) userID := uuid.New() trackID := uuid.New() err := service.LikeTrack(userID, trackID) assert.NoError(t, err) } func TestSocialService_LikeTrack_Duplicate(t *testing.T) { service, _, _ := setupTestSocialService(t) userID := uuid.New() trackID := uuid.New() // First like err := service.LikeTrack(userID, trackID) assert.NoError(t, err) // Try to like again (should not error due to ON CONFLICT DO NOTHING) err = service.LikeTrack(userID, trackID) assert.NoError(t, err) } func TestSocialService_UnlikeTrack_Success(t *testing.T) { service, _, _ := setupTestSocialService(t) userID := uuid.New() trackID := uuid.New() // First like err := service.LikeTrack(userID, trackID) assert.NoError(t, err) // Then unlike err = service.UnlikeTrack(userID, trackID) assert.NoError(t, err) } func TestSocialService_CreateComment_Success(t *testing.T) { _, _, testDB := setupTestSocialService(t) userID := uuid.New() trackID := uuid.New() content := "Great track!" // SQLite doesn't support gen_random_uuid(), so we'll insert directly for testing commentID := uuid.New() _, err := testDB.ExecContext(context.Background(), ` INSERT INTO comments (id, user_id, track_id, parent_id, content) VALUES ($1, $2, $3, $4, $5) `, commentID.String(), userID.String(), trackID.String(), nil, content) require.NoError(t, err) // Verify comment was created var comment Comment err = testDB.QueryRowContext(context.Background(), ` SELECT id, user_id, track_id, parent_id, content, created_at, updated_at FROM comments WHERE id = $1 `, commentID.String()).Scan( &comment.ID, &comment.UserID, &comment.TrackID, &comment.ParentID, &comment.Content, &comment.CreatedAt, &comment.UpdatedAt, ) assert.NoError(t, err) assert.Equal(t, userID, comment.UserID) assert.Equal(t, trackID, comment.TrackID) assert.Equal(t, content, comment.Content) assert.Nil(t, comment.ParentID) } func TestSocialService_CreateComment_WithParent(t *testing.T) { _, _, testDB := setupTestSocialService(t) userID := uuid.New() trackID := uuid.New() parentID := uuid.New() content := "Reply comment" // SQLite doesn't support gen_random_uuid(), so we'll insert directly for testing commentID := uuid.New() _, err := testDB.ExecContext(context.Background(), ` INSERT INTO comments (id, user_id, track_id, parent_id, content) VALUES ($1, $2, $3, $4, $5) `, commentID.String(), userID.String(), trackID.String(), parentID.String(), content) require.NoError(t, err) // Verify comment was created var comment Comment err = testDB.QueryRowContext(context.Background(), ` SELECT id, user_id, track_id, parent_id, content, created_at, updated_at FROM comments WHERE id = $1 `, commentID.String()).Scan( &comment.ID, &comment.UserID, &comment.TrackID, &comment.ParentID, &comment.Content, &comment.CreatedAt, &comment.UpdatedAt, ) assert.NoError(t, err) assert.Equal(t, userID, comment.UserID) assert.Equal(t, trackID, comment.TrackID) assert.Equal(t, content, comment.Content) assert.NotNil(t, comment.ParentID) assert.Equal(t, parentID, *comment.ParentID) } func TestSocialService_GetFollowersCount_Success(t *testing.T) { service, _, _ := setupTestSocialService(t) userID := uuid.New() follower1 := uuid.New() follower2 := uuid.New() // Create follows err := service.FollowUser(follower1, userID) assert.NoError(t, err) err = service.FollowUser(follower2, userID) assert.NoError(t, err) count, err := service.GetFollowersCount(userID) assert.NoError(t, err) assert.Equal(t, 2, count) } func TestSocialService_GetFollowersCount_NoFollowers(t *testing.T) { service, _, _ := setupTestSocialService(t) userID := uuid.New() count, err := service.GetFollowersCount(userID) assert.NoError(t, err) assert.Equal(t, 0, count) } func TestSocialService_GetFollowingCount_Success(t *testing.T) { service, _, _ := setupTestSocialService(t) userID := uuid.New() followed1 := uuid.New() followed2 := uuid.New() // Create follows err := service.FollowUser(userID, followed1) assert.NoError(t, err) err = service.FollowUser(userID, followed2) assert.NoError(t, err) count, err := service.GetFollowingCount(userID) assert.NoError(t, err) assert.Equal(t, 2, count) } func TestSocialService_GetLikesCount_Success(t *testing.T) { service, _, _ := setupTestSocialService(t) trackID := uuid.New() user1 := uuid.New() user2 := uuid.New() // Create likes err := service.LikeTrack(user1, trackID) assert.NoError(t, err) err = service.LikeTrack(user2, trackID) assert.NoError(t, err) count, err := service.GetLikesCount(trackID) assert.NoError(t, err) assert.Equal(t, 2, count) } func TestSocialService_IsFollowing_True(t *testing.T) { service, _, _ := setupTestSocialService(t) followerID := uuid.New() followedID := uuid.New() // Create follow err := service.FollowUser(followerID, followedID) assert.NoError(t, err) isFollowing, err := service.IsFollowing(followerID, followedID) assert.NoError(t, err) assert.True(t, isFollowing) } func TestSocialService_IsFollowing_False(t *testing.T) { service, _, _ := setupTestSocialService(t) followerID := uuid.New() followedID := uuid.New() isFollowing, err := service.IsFollowing(followerID, followedID) assert.NoError(t, err) assert.False(t, isFollowing) } func TestSocialService_IsTrackLiked_True(t *testing.T) { service, _, _ := setupTestSocialService(t) userID := uuid.New() trackID := uuid.New() // Create like err := service.LikeTrack(userID, trackID) assert.NoError(t, err) isLiked, err := service.IsTrackLiked(userID, trackID) assert.NoError(t, err) assert.True(t, isLiked) } func TestSocialService_IsTrackLiked_False(t *testing.T) { service, _, _ := setupTestSocialService(t) userID := uuid.New() trackID := uuid.New() isLiked, err := service.IsTrackLiked(userID, trackID) assert.NoError(t, err) assert.False(t, isLiked) } func TestSocialService_BlockUser_Success(t *testing.T) { service, _, _ := setupTestSocialService(t) blockerID := uuid.New() blockedID := uuid.New() err := service.BlockUser(blockerID, blockedID) assert.NoError(t, err) } func TestSocialService_BlockUser_SelfBlock(t *testing.T) { service, _, _ := setupTestSocialService(t) userID := uuid.New() err := service.BlockUser(userID, userID) assert.Error(t, err) assert.Contains(t, err.Error(), "cannot block yourself") } func TestSocialService_UnblockUser_Success(t *testing.T) { service, _, _ := setupTestSocialService(t) blockerID := uuid.New() blockedID := uuid.New() // First block err := service.BlockUser(blockerID, blockedID) assert.NoError(t, err) // Then unblock err = service.UnblockUser(blockerID, blockedID) assert.NoError(t, err) } func TestSocialService_IsBlocked_True(t *testing.T) { service, _, _ := setupTestSocialService(t) blockerID := uuid.New() blockedID := uuid.New() // Create block err := service.BlockUser(blockerID, blockedID) assert.NoError(t, err) isBlocked, err := service.IsBlocked(blockerID, blockedID) assert.NoError(t, err) assert.True(t, isBlocked) } func TestSocialService_IsBlocked_False(t *testing.T) { service, _, _ := setupTestSocialService(t) blockerID := uuid.New() blockedID := uuid.New() isBlocked, err := service.IsBlocked(blockerID, blockedID) assert.NoError(t, err) assert.False(t, isBlocked) }