diff --git a/Music/Models/TrackContextMenuConfig.swift b/Music/Models/TrackContextMenuConfig.swift new file mode 100644 index 0000000..4313a4b --- /dev/null +++ b/Music/Models/TrackContextMenuConfig.swift @@ -0,0 +1,10 @@ +import Foundation + +struct TrackContextMenuConfig { + let playlists: [Playlist] + let lastUsedPlaylistName: String? + let selectedPlaylist: Playlist? + let onAddToPlaylist: (Track, Playlist) -> Void + let onAddToLastPlaylist: ((Track) -> Void)? + let onRemoveFromPlaylist: ((Track) -> Void)? +} diff --git a/MusicTests/TrackContextMenuConfigTests.swift b/MusicTests/TrackContextMenuConfigTests.swift new file mode 100644 index 0000000..298bbe5 --- /dev/null +++ b/MusicTests/TrackContextMenuConfigTests.swift @@ -0,0 +1,72 @@ +import Testing +@testable import Music + +struct TrackContextMenuConfigTests { + // Builds a config with all fields set and verifies: + // - stored playlists, lastUsedPlaylistName, selectedPlaylist match the inputs + // - onAddToPlaylist callback fires with the correct track and playlist + // - onAddToLastPlaylist callback fires with the correct track + // - onRemoveFromPlaylist callback fires with the correct track + // - when optional callbacks are nil, optionally calling them is safe + + @Test func storesPropertiesAndFiresCallbacks() { + // 1. Create fixture data + let pl1 = Playlist.fixture(id: 1, name: "Favorites") + let pl2 = Playlist.fixture(id: 2, name: "Chill") + let track = Track.fixture(id: 42, title: "Test") + + var addedTrack: Track? = nil + var addedPlaylist: Playlist? = nil + var lastTrack: Track? = nil + var removedTrack: Track? = nil + + // 2. Build config with all callbacks + let config = TrackContextMenuConfig( + playlists: [pl1, pl2], + lastUsedPlaylistName: "Favorites", + selectedPlaylist: pl1, + onAddToPlaylist: { t, p in addedTrack = t; addedPlaylist = p }, + onAddToLastPlaylist: { t in lastTrack = t }, + onRemoveFromPlaylist: { t in removedTrack = t } + ) + + // 3. Verify stored properties + #expect(config.playlists.count == 2) + #expect(config.playlists[0].name == "Favorites") + #expect(config.lastUsedPlaylistName == "Favorites") + #expect(config.selectedPlaylist == pl1) + + // 4. Invoke callbacks and verify they fire correctly + config.onAddToPlaylist(track, pl2) + config.onAddToLastPlaylist?(track) + config.onRemoveFromPlaylist?(track) + + #expect(addedTrack?.id == track.id) + #expect(addedPlaylist?.id == pl2.id) + #expect(lastTrack?.id == track.id) + #expect(removedTrack?.id == track.id) + } + + @Test func nilOptionalCallbacksAreSafe() { + // Verifies that a config with nil optional callbacks does not crash + // when you call them via optional chaining (the normal usage pattern) + let pl = Playlist.fixture(id: 1, name: "Rock") + let track = Track.fixture() + + let config = TrackContextMenuConfig( + playlists: [pl], + lastUsedPlaylistName: nil, + selectedPlaylist: nil, + onAddToPlaylist: { _, _ in }, + onAddToLastPlaylist: nil, + onRemoveFromPlaylist: nil + ) + + // These must not crash + config.onAddToLastPlaylist?(track) + config.onRemoveFromPlaylist?(track) + + #expect(config.lastUsedPlaylistName == nil) + #expect(config.selectedPlaylist == nil) + } +}