diff --git a/Music/Services/DatabaseService.swift b/Music/Services/DatabaseService.swift index 21a71b2..59d25dc 100644 --- a/Music/Services/DatabaseService.swift +++ b/Music/Services/DatabaseService.swift @@ -201,6 +201,20 @@ nonisolated final class DatabaseService: Sendable { } } + func fetchTracksByIds(_ ids: [Int64]) throws -> [Track] { + guard !ids.isEmpty else { return [] } + let tracks = try dbPool.read { db in + let placeholders = databaseQuestionMarks(count: ids.count) + return try Track.fetchAll( + db, + sql: "SELECT * FROM tracks WHERE id IN (\(placeholders))", + arguments: StatementArguments(ids) + ) + } + let trackMap = Dictionary(uniqueKeysWithValues: tracks.compactMap { t in t.id.map { ($0, t) } }) + return ids.compactMap { trackMap[$0] } + } + func fetchRecentlyAdded(limit: Int) throws -> [Track] { try dbPool.read { db in try Track.fetchAll( diff --git a/MusicTests/DatabaseServiceTests.swift b/MusicTests/DatabaseServiceTests.swift index bd4ffcf..37af9b4 100644 --- a/MusicTests/DatabaseServiceTests.swift +++ b/MusicTests/DatabaseServiceTests.swift @@ -264,6 +264,26 @@ struct DatabaseServiceTests { #expect(total == 0) } + // Inserts 5 tracks, fetches 3 by ID, verifies only those 3 are returned + // in the order of the requested IDs. + @Test func fetchTracksByIds() throws { + let db = try DatabaseService(inMemory: true) + var tracks = (0..<5).map { i in + Track.fixture(fileURL: "/track\(i).mp3", title: "Track \(i)") + } + for i in tracks.indices { + try db.insert(&tracks[i]) + } + + let ids: [Int64] = [tracks[2].id!, tracks[0].id!, tracks[4].id!] + let result = try db.fetchTracksByIds(ids) + + #expect(result.count == 3) + #expect(result[0].id == tracks[2].id) + #expect(result[1].id == tracks[0].id) + #expect(result[2].id == tracks[4].id) + } + // Inserts tracks in different months and verifies fetchMonthlyAdditions returns // the correct per-month counts covering the requested range including empty months. // Uses a UTC calendar to match the implementation, which uses UTC month boundaries