package services import ( "context" "errors" "github.com/google/uuid" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/driver/sqlite" "gorm.io/gorm" "veza-backend-api/internal/models" ) func setupTestTrackShareService(t *testing.T) (*TrackShareService, *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.TrackShare{}, &models.Track{}, &models.User{}) require.NoError(t, err) // Create test user user := &models.User{ ID: 123, Username: "testuser", Email: "test@example.com", IsActive: true, } err = db.Create(user).Error require.NoError(t, err) // Setup service service := NewTrackShareService(db) // Cleanup function cleanup := func() { // Database will be closed automatically } return service, db, cleanup } func TestTrackShareService_CreateShare(t *testing.T) { service, db, cleanup := setupTestTrackShareService(t) defer cleanup() ctx := context.Background() // Create test track track := &models.Track{ UserID: 123, 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 share share, err := service.CreateShare(ctx, track.ID, 123, "read,download", nil) assert.NoError(t, err) assert.NotNil(t, share) assert.Equal(t, track.ID, share.TrackID) assert.Equal(t, int64(123), share.UserID) assert.Equal(t, "read,download", share.Permissions) assert.NotEmpty(t, share.ShareToken) } func TestTrackShareService_CreateShare_NotOwner(t *testing.T) { service, db, cleanup := setupTestTrackShareService(t) defer cleanup() ctx := context.Background() // Create test track track := &models.Track{ UserID: 123, 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) // Try to create share as different user share, err := service.CreateShare(ctx, track.ID, 456, "read,download", nil) assert.Error(t, err) assert.Nil(t, share) assert.Equal(t, ErrForbidden, err) } func TestTrackShareService_ValidateShareToken(t *testing.T) { service, db, cleanup := setupTestTrackShareService(t) defer cleanup() ctx := context.Background() // Create test track track := &models.Track{ UserID: 123, 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 share share, err := service.CreateShare(ctx, track.ID, 123, "read,download", nil) require.NoError(t, err) // Validate token validatedShare, err := service.ValidateShareToken(ctx, share.ShareToken) assert.NoError(t, err) assert.NotNil(t, validatedShare) assert.Equal(t, share.ID, validatedShare.ID) assert.Equal(t, int64(1), validatedShare.AccessCount) // Should be incremented } func TestTrackShareService_ValidateShareToken_Expired(t *testing.T) { service, db, cleanup := setupTestTrackShareService(t) defer cleanup() ctx := context.Background() // Create test track track := &models.Track{ UserID: 123, 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 share with expiration in the past expiredTime := time.Now().Add(-1 * time.Hour) share := &models.TrackShare{ TrackID: track.ID, UserID: 123, ShareToken: "test-token-123", Permissions: "read,download", ExpiresAt: &expiredTime, AccessCount: 0, } err = db.Create(share).Error require.NoError(t, err) // Try to validate expired token validatedShare, err := service.ValidateShareToken(ctx, share.ShareToken) assert.Error(t, err) assert.Nil(t, validatedShare) assert.Equal(t, ErrShareExpired, err) } func TestTrackShareService_CheckPermission(t *testing.T) { service, _, cleanup := setupTestTrackShareService(t) defer cleanup() // Test with read permission share := &models.TrackShare{ Permissions: "read", ExpiresAt: nil, } assert.True(t, service.CheckPermission(share, "read")) assert.False(t, service.CheckPermission(share, "download")) // Test with download permission share.Permissions = "download" assert.False(t, service.CheckPermission(share, "read")) assert.True(t, service.CheckPermission(share, "download")) // Test with both permissions share.Permissions = "read,download" assert.True(t, service.CheckPermission(share, "read")) assert.True(t, service.CheckPermission(share, "download")) // Test with expired share expiredTime := time.Now().Add(-1 * time.Hour) share.ExpiresAt = &expiredTime assert.False(t, service.CheckPermission(share, "read")) assert.False(t, service.CheckPermission(share, "download")) } func TestTrackShareService_RevokeShare(t *testing.T) { service, db, cleanup := setupTestTrackShareService(t) defer cleanup() ctx := context.Background() // Create test track track := &models.Track{ UserID: 123, 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 share share, err := service.CreateShare(ctx, track.ID, 123, "read,download", nil) require.NoError(t, err) // Revoke share err = service.RevokeShare(ctx, share.ID, 123) assert.NoError(t, err) // Verify share is deleted var deletedShare models.TrackShare err = db.First(&deletedShare, share.ID).Error assert.Error(t, err) assert.True(t, errors.Is(err, gorm.ErrRecordNotFound)) }