package models import ( "testing" "time" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func setupTestTrackVersionDB(t *testing.T) (*gorm.DB, func()) { // Setup in-memory SQLite database db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) db.Exec("PRAGMA foreign_keys = ON") sqlDB, _ := db.DB() sqlDB.SetMaxOpenConns(1) // Auto-migrate err = db.AutoMigrate(&User{}, &Track{}, &TrackVersion{}) require.NoError(t, err) // Cleanup function cleanup := func() { // SQLite in-memory database doesn't need explicit cleanup } return db, cleanup } func TestTrackVersion_Create(t *testing.T) { db, cleanup := setupTestTrackVersionDB(t) defer cleanup() userID := uuid.New() // Create user user := &User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: userID, Title: "Test Track", FilePath: "/path/to/track.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create track version version := &TrackVersion{ TrackID: track.ID, VersionNumber: 1, FilePath: "/path/to/track_v1.mp3", FileSize: 1024, Changelog: "Initial version", } err = db.Create(version).Error require.NoError(t, err) // Verify version was created assert.NotEqual(t, uuid.Nil, version.ID) assert.Equal(t, track.ID, version.TrackID) assert.Equal(t, 1, version.VersionNumber) assert.Equal(t, "/path/to/track_v1.mp3", version.FilePath) assert.Equal(t, "Initial version", version.Changelog) assert.False(t, version.CreatedAt.IsZero()) assert.False(t, version.UpdatedAt.IsZero()) } func TestTrackVersion_WithTrack(t *testing.T) { db, cleanup := setupTestTrackVersionDB(t) defer cleanup() userID := uuid.New() // Create user user := &User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: userID, Title: "Test Track", FilePath: "/path/to/track.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create track version version := &TrackVersion{ TrackID: track.ID, VersionNumber: 1, FilePath: "/path/to/track_v1.mp3", FileSize: 1024, Changelog: "Initial version", } err = db.Create(version).Error require.NoError(t, err) // Load version with track relation var versionWithTrack TrackVersion err = db.Preload("Track").First(&versionWithTrack, version.ID).Error require.NoError(t, err) assert.NotNil(t, versionWithTrack.Track) assert.Equal(t, track.ID, versionWithTrack.Track.ID) assert.Equal(t, "Test Track", versionWithTrack.Track.Title) } func TestTrackVersion_MultipleVersions(t *testing.T) { db, cleanup := setupTestTrackVersionDB(t) defer cleanup() userID := uuid.New() // Create user user := &User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: userID, Title: "Test Track", FilePath: "/path/to/track.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create multiple versions version1 := &TrackVersion{ TrackID: track.ID, VersionNumber: 1, FilePath: "/path/to/track_v1.mp3", FileSize: 1024, Changelog: "Initial version", } version2 := &TrackVersion{ TrackID: track.ID, VersionNumber: 2, FilePath: "/path/to/track_v2.mp3", FileSize: 2048, Changelog: "Updated mix", } version3 := &TrackVersion{ TrackID: track.ID, VersionNumber: 3, FilePath: "/path/to/track_v3.mp3", FileSize: 3072, Changelog: "Final version", } err = db.Create(version1).Error require.NoError(t, err) err = db.Create(version2).Error require.NoError(t, err) err = db.Create(version3).Error require.NoError(t, err) // Load all versions for the track var versions []TrackVersion err = db.Where("track_id = ?", track.ID).Order("version_number ASC").Find(&versions).Error require.NoError(t, err) assert.Equal(t, 3, len(versions)) assert.Equal(t, 1, versions[0].VersionNumber) assert.Equal(t, 2, versions[1].VersionNumber) assert.Equal(t, 3, versions[2].VersionNumber) } func TestTrackVersion_CascadeDeleteOnTrack(t *testing.T) { db, cleanup := setupTestTrackVersionDB(t) defer cleanup() userID := uuid.New() // Create user user := &User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: userID, Title: "Test Track", FilePath: "/path/to/track.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create track version version := &TrackVersion{ TrackID: track.ID, VersionNumber: 1, FilePath: "/path/to/track_v1.mp3", FileSize: 1024, Changelog: "Initial version", } err = db.Create(version).Error require.NoError(t, err) versionID := version.ID // Delete track err = db.Unscoped().Delete(track).Error require.NoError(t, err) // Verify version is deleted (cascade) var deletedVersion TrackVersion err = db.First(&deletedVersion, versionID).Error assert.Error(t, err) assert.Equal(t, gorm.ErrRecordNotFound, err) } func TestTrackVersion_UniqueVersionNumber(t *testing.T) { db, cleanup := setupTestTrackVersionDB(t) defer cleanup() userID := uuid.New() // Create user user := &User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: userID, Title: "Test Track", FilePath: "/path/to/track.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create first version version1 := &TrackVersion{ TrackID: track.ID, VersionNumber: 1, FilePath: "/path/to/track_v1.mp3", FileSize: 1024, Changelog: "Initial version", } err = db.Create(version1).Error require.NoError(t, err) // Try to create another version with the same version number version2 := &TrackVersion{ TrackID: track.ID, VersionNumber: 1, // Same version number FilePath: "/path/to/track_v1_dup.mp3", FileSize: 1024, Changelog: "Duplicate version", } err = db.Create(version2).Error // Should fail due to unique constraint assert.Error(t, err) } func TestTrackVersion_TableName(t *testing.T) { version := TrackVersion{} assert.Equal(t, "track_versions", version.TableName()) } func TestTrackVersion_Timestamps(t *testing.T) { db, cleanup := setupTestTrackVersionDB(t) defer cleanup() userID := uuid.New() // Create user user := &User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: userID, Title: "Test Track", FilePath: "/path/to/track.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create version now := time.Now() version := &TrackVersion{ TrackID: track.ID, VersionNumber: 1, FilePath: "/path/to/track_v1.mp3", FileSize: 1024, Changelog: "Initial version", } err = db.Create(version).Error require.NoError(t, err) // Verify timestamps are set assert.True(t, version.CreatedAt.After(now.Add(-time.Second))) assert.True(t, version.CreatedAt.Before(now.Add(time.Second))) assert.True(t, version.UpdatedAt.After(now.Add(-time.Second))) assert.True(t, version.UpdatedAt.Before(now.Add(time.Second))) } func TestTrackVersion_SoftDelete(t *testing.T) { db, cleanup := setupTestTrackVersionDB(t) defer cleanup() userID := uuid.New() // Create user user := &User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: userID, Title: "Test Track", FilePath: "/path/to/track.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create version version := &TrackVersion{ TrackID: track.ID, VersionNumber: 1, FilePath: "/path/to/track_v1.mp3", FileSize: 1024, Changelog: "Initial version", } err = db.Create(version).Error require.NoError(t, err) versionID := version.ID // Soft delete version err = db.Delete(version).Error require.NoError(t, err) // Verify version is soft deleted (not found in normal query) var deletedVersion TrackVersion err = db.First(&deletedVersion, versionID).Error assert.Error(t, err) assert.Equal(t, gorm.ErrRecordNotFound, err) // Verify version exists with Unscoped var unscopedVersion TrackVersion err = db.Unscoped().First(&unscopedVersion, versionID).Error require.NoError(t, err) assert.NotNil(t, unscopedVersion.DeletedAt) } func TestTrackVersion_Relations(t *testing.T) { db, cleanup := setupTestTrackVersionDB(t) defer cleanup() userID := uuid.New() // Create user user := &User{ ID: userID, Username: "testuser", Email: "test@example.com", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) // Create track track := &Track{ UserID: userID, Title: "Test Track", FilePath: "/path/to/track.mp3", FileSize: 1024, Format: "MP3", Duration: 180, IsPublic: true, Status: TrackStatusCompleted, } err = db.Create(track).Error require.NoError(t, err) // Create versions version1 := &TrackVersion{ TrackID: track.ID, VersionNumber: 1, FilePath: "/path/to/track_v1.mp3", FileSize: 1024, Changelog: "Initial version", } version2 := &TrackVersion{ TrackID: track.ID, VersionNumber: 2, FilePath: "/path/to/track_v2.mp3", FileSize: 2048, Changelog: "Updated version", } err = db.Create(version1).Error require.NoError(t, err) err = db.Create(version2).Error require.NoError(t, err) // Load track with versions var trackWithVersions Track err = db.Preload("Versions").First(&trackWithVersions, track.ID).Error require.NoError(t, err) assert.Equal(t, 2, len(trackWithVersions.Versions)) }