package models import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func TestTrackShare(t *testing.T) { // Setup in-memory database 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(&User{}, &Track{}, &TrackShare{}) require.NoError(t, err) t.Run("Create TrackShare with all fields", func(t *testing.T) { // Create user user := &User{ Username: "testuser", Email: "test@example.com", PasswordHash: "hash", Slug: "testuser", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: user.ID, Title: "Test Track", FilePath: "/test/track.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create track share expiresAt := time.Now().Add(24 * time.Hour) trackShare := &TrackShare{ TrackID: track.ID, UserID: user.ID, ShareToken: "test-token-123", Permissions: "read,download", ExpiresAt: &expiresAt, AccessCount: 0, } err = db.Create(trackShare).Error require.NoError(t, err) assert.NotZero(t, trackShare.ID) assert.Equal(t, track.ID, trackShare.TrackID) assert.Equal(t, user.ID, trackShare.UserID) assert.Equal(t, "test-token-123", trackShare.ShareToken) assert.Equal(t, "read,download", trackShare.Permissions) assert.NotNil(t, trackShare.ExpiresAt) assert.Equal(t, int64(0), trackShare.AccessCount) assert.False(t, trackShare.CreatedAt.IsZero()) assert.False(t, trackShare.UpdatedAt.IsZero()) }) t.Run("Create TrackShare without expiration", func(t *testing.T) { // Create user user := &User{ Username: "testuser2", Email: "test2@example.com", PasswordHash: "hash", Slug: "testuser2", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: user.ID, Title: "Test Track 2", FilePath: "/test/track2.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create track share without expiration trackShare := &TrackShare{ TrackID: track.ID, UserID: user.ID, ShareToken: "test-token-456", Permissions: "read", ExpiresAt: nil, AccessCount: 0, } err = db.Create(trackShare).Error require.NoError(t, err) assert.NotZero(t, trackShare.ID) assert.Nil(t, trackShare.ExpiresAt) assert.Equal(t, "read", trackShare.Permissions) }) t.Run("TrackShare with unique share_token constraint", func(t *testing.T) { // Create user user := &User{ Username: "testuser3", Email: "test3@example.com", PasswordHash: "hash", Slug: "testuser3", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: user.ID, Title: "Test Track 3", FilePath: "/test/track3.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create first track share trackShare1 := &TrackShare{ TrackID: track.ID, UserID: user.ID, ShareToken: "unique-token-123", Permissions: "read", } err = db.Create(trackShare1).Error require.NoError(t, err) // Try to create second track share with same token trackShare2 := &TrackShare{ TrackID: track.ID, UserID: user.ID, ShareToken: "unique-token-123", Permissions: "read", } err = db.Create(trackShare2).Error assert.Error(t, err) // Should fail due to unique constraint }) t.Run("TrackShare cascade delete on track deletion", func(t *testing.T) { // Create user user := &User{ Username: "testuser4", Email: "test4@example.com", PasswordHash: "hash", Slug: "testuser4", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: user.ID, Title: "Test Track 4", FilePath: "/test/track4.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create track share trackShare := &TrackShare{ TrackID: track.ID, UserID: user.ID, ShareToken: "cascade-token-123", Permissions: "read", } err = db.Create(trackShare).Error require.NoError(t, err) shareID := trackShare.ID // Delete track (hard delete) err = db.Unscoped().Delete(track).Error require.NoError(t, err) // Verify track share is also deleted (cascade) // Note: SQLite in-memory may not enforce foreign key constraints the same way as PostgreSQL // So we check if the share still exists or was soft-deleted var deletedShare TrackShare err = db.Unscoped().First(&deletedShare, shareID).Error // The share should be deleted (either hard or soft delete depending on DB behavior) // In production with PostgreSQL, it will be hard deleted due to CASCADE if err == nil { // If still exists, verify it's at least soft-deleted assert.NotNil(t, deletedShare.DeletedAt) } else { // If not found, it was hard deleted (expected behavior) assert.Equal(t, gorm.ErrRecordNotFound, err) } }) t.Run("TrackShare TableName", func(t *testing.T) { share := &TrackShare{} assert.Equal(t, "track_shares", share.TableName()) }) t.Run("TrackShare with different permissions", func(t *testing.T) { // Create user user := &User{ Username: "testuser5", Email: "test5@example.com", PasswordHash: "hash", Slug: "testuser5", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: user.ID, Title: "Test Track 5", FilePath: "/test/track5.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Test different permission values permissions := []string{"read", "download", "read,download"} for i, perm := range permissions { trackShare := &TrackShare{ TrackID: track.ID, UserID: user.ID, ShareToken: "perm-token-" + string(rune(i)), Permissions: perm, } err = db.Create(trackShare).Error require.NoError(t, err) assert.Equal(t, perm, trackShare.Permissions) } }) t.Run("TrackShare increment access_count", func(t *testing.T) { // Create user user := &User{ Username: "testuser6", Email: "test6@example.com", PasswordHash: "hash", Slug: "testuser6", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: user.ID, Title: "Test Track 6", FilePath: "/test/track6.mp3", FileSize: 5 * 1024 * 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create track share trackShare := &TrackShare{ TrackID: track.ID, UserID: user.ID, ShareToken: "access-token-123", Permissions: "read", AccessCount: 0, } err = db.Create(trackShare).Error require.NoError(t, err) // Increment access count trackShare.AccessCount++ err = db.Save(trackShare).Error require.NoError(t, err) // Verify access count was incremented var updatedShare TrackShare err = db.First(&updatedShare, trackShare.ID).Error require.NoError(t, err) assert.Equal(t, int64(1), updatedShare.AccessCount) }) }