veza/veza-backend-api/internal/services/playlist_follow_service_test.go
2026-03-05 23:03:43 +01:00

390 lines
9.9 KiB
Go

package services
import (
"context"
"testing"
"github.com/google/uuid"
"veza-backend-api/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func setupTestPlaylistFollowService(t *testing.T) (*PlaylistFollowService, *gorm.DB, func()) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
// Enable foreign keys for SQLite
db.Exec("PRAGMA foreign_keys = ON")
// Auto-migrate
err = db.AutoMigrate(&models.User{}, &models.Playlist{}, &models.PlaylistFollow{})
require.NoError(t, err)
logger := zap.NewNop()
service := NewPlaylistFollowService(db, logger)
cleanup := func() {
// Database will be closed automatically
}
return service, db, cleanup
}
func TestPlaylistFollowService_FollowPlaylist(t *testing.T) {
service, db, cleanup := setupTestPlaylistFollowService(t)
defer cleanup()
ctx := context.Background()
// Create test users
user1 := &models.User{
Username: "user1",
Email: "user1@example.com",
PasswordHash: "hash1",
Slug: "user1",
IsActive: true,
}
user2 := &models.User{
Username: "user2",
Email: "user2@example.com",
PasswordHash: "hash2",
Slug: "user2",
IsActive: true,
}
require.NoError(t, db.Create(user1).Error)
require.NoError(t, db.Create(user2).Error)
// Create test playlist
playlist := &models.Playlist{
UserID: user1.ID,
Title: "Test Playlist",
Description: "A test playlist",
IsPublic: true,
FollowerCount: 0,
}
require.NoError(t, db.Create(playlist).Error)
// Test follow
err := service.FollowPlaylist(ctx, user2.ID, playlist.ID)
assert.NoError(t, err)
// Verify follow was created
var follow models.PlaylistFollow
err = db.Where("user_id = ? AND playlist_id = ?", user2.ID, playlist.ID).First(&follow).Error
assert.NoError(t, err)
assert.Equal(t, user2.ID, follow.UserID)
assert.Equal(t, playlist.ID, follow.PlaylistID)
// Verify follower count was updated
var updatedPlaylist models.Playlist
db.First(&updatedPlaylist, playlist.ID)
assert.Equal(t, 1, updatedPlaylist.FollowerCount)
}
func TestPlaylistFollowService_FollowPlaylist_OwnPlaylist(t *testing.T) {
service, db, cleanup := setupTestPlaylistFollowService(t)
defer cleanup()
ctx := context.Background()
// Create test user
user := &models.User{
Username: "user1",
Email: "user1@example.com",
PasswordHash: "hash1",
Slug: "user1",
IsActive: true,
}
require.NoError(t, db.Create(user).Error)
// Create test playlist
playlist := &models.Playlist{
UserID: user.ID,
Title: "Test Playlist",
Description: "A test playlist",
IsPublic: true,
FollowerCount: 0,
}
require.NoError(t, db.Create(playlist).Error)
// Test follow own playlist (should fail)
err := service.FollowPlaylist(ctx, user.ID, playlist.ID)
assert.Error(t, err)
assert.Equal(t, "cannot follow own playlist", err.Error())
}
func TestPlaylistFollowService_FollowPlaylist_NotFound(t *testing.T) {
service, db, cleanup := setupTestPlaylistFollowService(t)
defer cleanup()
ctx := context.Background()
// Create test user
user := &models.User{
Username: "user1",
Email: "user1@example.com",
PasswordHash: "hash1",
Slug: "user1",
IsActive: true,
}
require.NoError(t, db.Create(user).Error)
// Test follow non-existent playlist
err := service.FollowPlaylist(ctx, user.ID, uuid.New())
assert.Error(t, err)
assert.Equal(t, "playlist not found", err.Error())
}
func TestPlaylistFollowService_FollowPlaylist_Idempotent(t *testing.T) {
service, db, cleanup := setupTestPlaylistFollowService(t)
defer cleanup()
ctx := context.Background()
// Create test users
user1 := &models.User{
Username: "user1",
Email: "user1@example.com",
PasswordHash: "hash1",
Slug: "user1",
IsActive: true,
}
user2 := &models.User{
Username: "user2",
Email: "user2@example.com",
PasswordHash: "hash2",
Slug: "user2",
IsActive: true,
}
require.NoError(t, db.Create(user1).Error)
require.NoError(t, db.Create(user2).Error)
// Create test playlist
playlist := &models.Playlist{
UserID: user1.ID,
Title: "Test Playlist",
Description: "A test playlist",
IsPublic: true,
FollowerCount: 0,
}
require.NoError(t, db.Create(playlist).Error)
// Follow twice
err := service.FollowPlaylist(ctx, user2.ID, playlist.ID)
assert.NoError(t, err)
err = service.FollowPlaylist(ctx, user2.ID, playlist.ID)
assert.NoError(t, err) // Should be idempotent
// Verify only one follow exists
var count int64
db.Model(&models.PlaylistFollow{}).Where("user_id = ? AND playlist_id = ?", user2.ID, playlist.ID).Count(&count)
assert.Equal(t, int64(1), count)
}
func TestPlaylistFollowService_UnfollowPlaylist(t *testing.T) {
service, db, cleanup := setupTestPlaylistFollowService(t)
defer cleanup()
ctx := context.Background()
// Create test users
user1 := &models.User{
Username: "user1",
Email: "user1@example.com",
PasswordHash: "hash1",
Slug: "user1",
IsActive: true,
}
user2 := &models.User{
Username: "user2",
Email: "user2@example.com",
PasswordHash: "hash2",
Slug: "user2",
IsActive: true,
}
require.NoError(t, db.Create(user1).Error)
require.NoError(t, db.Create(user2).Error)
// Create test playlist
playlist := &models.Playlist{
UserID: user1.ID,
Title: "Test Playlist",
Description: "A test playlist",
IsPublic: true,
FollowerCount: 0,
}
require.NoError(t, db.Create(playlist).Error)
// Follow first
err := service.FollowPlaylist(ctx, user2.ID, playlist.ID)
require.NoError(t, err)
// Unfollow
err = service.UnfollowPlaylist(ctx, user2.ID, playlist.ID)
assert.NoError(t, err)
// Verify follow was deleted
var count int64
db.Model(&models.PlaylistFollow{}).Where("user_id = ? AND playlist_id = ? AND deleted_at IS NULL", user2.ID, playlist.ID).Count(&count)
assert.Equal(t, int64(0), count)
// Verify follower count was updated
var updatedPlaylist models.Playlist
db.First(&updatedPlaylist, playlist.ID)
assert.Equal(t, 0, updatedPlaylist.FollowerCount)
}
func TestPlaylistFollowService_UnfollowPlaylist_Idempotent(t *testing.T) {
service, db, cleanup := setupTestPlaylistFollowService(t)
defer cleanup()
ctx := context.Background()
// Create test users
user1 := &models.User{
Username: "user1",
Email: "user1@example.com",
PasswordHash: "hash1",
Slug: "user1",
IsActive: true,
}
user2 := &models.User{
Username: "user2",
Email: "user2@example.com",
PasswordHash: "hash2",
Slug: "user2",
IsActive: true,
}
require.NoError(t, db.Create(user1).Error)
require.NoError(t, db.Create(user2).Error)
// Create test playlist
playlist := &models.Playlist{
UserID: user1.ID,
Title: "Test Playlist",
Description: "A test playlist",
IsPublic: true,
FollowerCount: 0,
}
require.NoError(t, db.Create(playlist).Error)
// Unfollow without following first (should be idempotent)
err := service.UnfollowPlaylist(ctx, user2.ID, playlist.ID)
assert.NoError(t, err)
}
func TestPlaylistFollowService_IsFollowing(t *testing.T) {
service, db, cleanup := setupTestPlaylistFollowService(t)
defer cleanup()
ctx := context.Background()
// Create test users
user1 := &models.User{
Username: "user1",
Email: "user1@example.com",
PasswordHash: "hash1",
Slug: "user1",
IsActive: true,
}
user2 := &models.User{
Username: "user2",
Email: "user2@example.com",
PasswordHash: "hash2",
Slug: "user2",
IsActive: true,
}
require.NoError(t, db.Create(user1).Error)
require.NoError(t, db.Create(user2).Error)
// Create test playlist
playlist := &models.Playlist{
UserID: user1.ID,
Title: "Test Playlist",
Description: "A test playlist",
IsPublic: true,
FollowerCount: 0,
}
require.NoError(t, db.Create(playlist).Error)
// Check before following
isFollowing, err := service.IsFollowing(ctx, user2.ID, playlist.ID)
assert.NoError(t, err)
assert.False(t, isFollowing)
// Follow
err = service.FollowPlaylist(ctx, user2.ID, playlist.ID)
require.NoError(t, err)
// Check after following
isFollowing, err = service.IsFollowing(ctx, user2.ID, playlist.ID)
assert.NoError(t, err)
assert.True(t, isFollowing)
}
func TestPlaylistFollowService_GetPlaylistFollowersCount(t *testing.T) {
service, db, cleanup := setupTestPlaylistFollowService(t)
defer cleanup()
ctx := context.Background()
// Create test users
user1 := &models.User{
Username: "user1",
Email: "user1@example.com",
PasswordHash: "hash1",
Slug: "user1",
IsActive: true,
}
user2 := &models.User{
Username: "user2",
Email: "user2@example.com",
PasswordHash: "hash2",
Slug: "user2",
IsActive: true,
}
user3 := &models.User{
Username: "user3",
Email: "user3@example.com",
PasswordHash: "hash3",
Slug: "user3",
IsActive: true,
}
require.NoError(t, db.Create(user1).Error)
require.NoError(t, db.Create(user2).Error)
require.NoError(t, db.Create(user3).Error)
// Create test playlist
playlist := &models.Playlist{
UserID: user1.ID,
Title: "Test Playlist",
Description: "A test playlist",
IsPublic: true,
FollowerCount: 0,
}
require.NoError(t, db.Create(playlist).Error)
// Check count before following
count, err := service.GetPlaylistFollowersCount(ctx, playlist.ID)
assert.NoError(t, err)
assert.Equal(t, int64(0), count)
// Follow by user2
err = service.FollowPlaylist(ctx, user2.ID, playlist.ID)
require.NoError(t, err)
// Follow by user3
err = service.FollowPlaylist(ctx, user3.ID, playlist.ID)
require.NoError(t, err)
// Check count after following
count, err = service.GetPlaylistFollowersCount(ctx, playlist.ID)
assert.NoError(t, err)
assert.Equal(t, int64(2), count)
}