Pure value type bundling playlists and callbacks needed to build a track context menu; both tests pass (TDD).feat/music-streaming
parent
793fe036ad
commit
c9cdf80a14
@ -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)? |
||||||
|
} |
||||||
@ -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) |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue