feat: add smart_playlists table migration and CRUD methods

feat/music-streaming
Laurent 1 month ago
parent 124a48a07c
commit e6ae6c5266
  1. 59
      Music/Services/DatabaseService.swift
  2. 61
      MusicTests/SmartPlaylistTests.swift

@ -95,6 +95,15 @@ nonisolated final class DatabaseService: Sendable {
) )
} }
migrator.registerMigration("v3-create-smart-playlists") { db in
try db.create(table: "smart_playlists") { t in
t.autoIncrementedPrimaryKey("id")
t.column("name", .text).notNull().unique()
t.column("searchQuery", .text).notNull()
t.column("createdAt", .datetime).notNull()
}
}
try migrator.migrate(db) try migrator.migrate(db)
} }
@ -340,4 +349,54 @@ nonisolated final class DatabaseService: Sendable {
) )
} }
} }
// MARK: - Smart Playlists
func createSmartPlaylist(name: String, searchQuery: String) throws -> SmartPlaylist {
try dbPool.write { db in
var smartPlaylist = SmartPlaylist(
id: nil, name: name, searchQuery: searchQuery, createdAt: Date()
)
try smartPlaylist.insert(db)
return smartPlaylist
}
}
func renameSmartPlaylist(id: Int64, name: String) throws {
try dbPool.write { db in
try db.execute(
sql: "UPDATE smart_playlists SET name = ? WHERE id = ?",
arguments: [name, id]
)
}
}
func updateSmartPlaylistQuery(id: Int64, searchQuery: String) throws {
try dbPool.write { db in
try db.execute(
sql: "UPDATE smart_playlists SET searchQuery = ? WHERE id = ?",
arguments: [searchQuery, id]
)
}
}
func deleteSmartPlaylist(id: Int64) throws {
try dbPool.write { db in
try db.execute(sql: "DELETE FROM smart_playlists WHERE id = ?", arguments: [id])
}
}
func fetchSmartPlaylists() throws -> [SmartPlaylist] {
try dbPool.read { db in
try SmartPlaylist.fetchAll(
db, sql: "SELECT * FROM smart_playlists ORDER BY name COLLATE NOCASE ASC"
)
}
}
func fetchSmartPlaylists(db: Database) throws -> [SmartPlaylist] {
try SmartPlaylist.fetchAll(
db, sql: "SELECT * FROM smart_playlists ORDER BY name COLLATE NOCASE ASC"
)
}
} }

@ -15,4 +15,65 @@ struct SmartPlaylistTests {
#expect(sp.searchQuery == "miles davis") #expect(sp.searchQuery == "miles davis")
#expect(sp.isSmartPlaylist == true) #expect(sp.isSmartPlaylist == true)
} }
// Creates a smart playlist in the database and fetches it back.
@Test func createAndFetchSmartPlaylist() throws {
let db = try DatabaseService(inMemory: true)
let sp = try db.createSmartPlaylist(name: "Jazz Vibes", searchQuery: "jazz")
#expect(sp.id != nil)
#expect(sp.name == "Jazz Vibes")
#expect(sp.searchQuery == "jazz")
let all = try db.fetchSmartPlaylists()
#expect(all.count == 1)
#expect(all[0].name == "Jazz Vibes")
}
// Renames a smart playlist and verifies the new name persists.
@Test func renameSmartPlaylist() throws {
let db = try DatabaseService(inMemory: true)
let sp = try db.createSmartPlaylist(name: "Old Name", searchQuery: "old")
try db.renameSmartPlaylist(id: sp.id!, name: "New Name")
let all = try db.fetchSmartPlaylists()
#expect(all[0].name == "New Name")
}
// Updates the search query of a smart playlist.
@Test func updateSmartPlaylistQuery() throws {
let db = try DatabaseService(inMemory: true)
let sp = try db.createSmartPlaylist(name: "Jazz", searchQuery: "jazz")
try db.updateSmartPlaylistQuery(id: sp.id!, searchQuery: "jazz fusion")
let all = try db.fetchSmartPlaylists()
#expect(all[0].searchQuery == "jazz fusion")
}
// Deletes a smart playlist and verifies it's gone.
@Test func deleteSmartPlaylist() throws {
let db = try DatabaseService(inMemory: true)
let sp = try db.createSmartPlaylist(name: "To Delete", searchQuery: "delete")
try db.deleteSmartPlaylist(id: sp.id!)
let all = try db.fetchSmartPlaylists()
#expect(all.isEmpty)
}
// Verifies smart playlist tracks are computed via FTS5 search (not stored).
@Test func smartPlaylistReturnsDynamicResults() throws {
let db = try DatabaseService(inMemory: true)
var t1 = Track.fixture(fileURL: "/a.mp3", title: "Kind of Blue", artist: "Miles Davis")
var t2 = Track.fixture(fileURL: "/b.mp3", title: "Hotel California", artist: "Eagles")
var t3 = Track.fixture(fileURL: "/c.mp3", title: "Bitches Brew", artist: "Miles Davis")
try db.insert(&t1)
try db.insert(&t2)
try db.insert(&t3)
// Smart playlist uses the existing fetchTracks with its searchQuery
let results = try db.fetchTracks(search: "miles davis", sortColumn: "title", ascending: true)
#expect(results.count == 2)
#expect(results[0].title == "Bitches Brew")
#expect(results[1].title == "Kind of Blue")
}
} }

Loading…
Cancel
Save