import Foundation import Testing @testable import Music struct SmartPlaylistTests { // Creates a SmartPlaylist in memory and verifies its properties. @Test func smartPlaylistProperties() throws { let sp = SmartPlaylist( id: nil, name: "Miles Davis", searchQuery: "miles davis", createdAt: Date(), conditions: nil ) #expect(sp.name == "Miles Davis") #expect(sp.searchQuery == "miles davis") #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") } // Creates a SmartPlaylist fixture with conditions and verifies the conditions // field is preserved and the isSmartPlaylist flag is true. @Test func smartPlaylistWithConditions() throws { let conditions = [SmartPlaylistCondition(field: .artist, op: .equals, value: .string("Miles Davis"))] let sp = SmartPlaylist.fixture(conditions: conditions) #expect(sp.conditions?.count == 1) #expect(sp.conditions?[0].field == .artist) #expect(sp.isSmartPlaylist == true) } // Encodes and decodes a SmartPlaylistCondition to/from JSON, // verifying that all fields survive the round-trip. @Test func conditionCodableRoundTrip() throws { let condition = SmartPlaylistCondition( field: .artist, op: .equals, value: .string("Miles Davis") ) let data = try JSONEncoder().encode(condition) let decoded = try JSONDecoder().decode(SmartPlaylistCondition.self, from: data) #expect(decoded.field == .artist) #expect(decoded.op == .equals) if case .string(let s) = decoded.value { #expect(s == "Miles Davis") } else { Issue.record("Expected string value") } } // Encodes and decodes an array of conditions with mixed value types. @Test func conditionsArrayCodableRoundTrip() throws { let conditions: [SmartPlaylistCondition] = [ SmartPlaylistCondition(field: .artist, op: .startsWith, value: .string("Miles")), SmartPlaylistCondition(field: .year, op: .greaterThan, value: .int(1960)), SmartPlaylistCondition(field: .dateAdded, op: .lessThan, value: .date(Date(timeIntervalSince1970: 0))) ] let data = try JSONEncoder().encode(conditions) let decoded = try JSONDecoder().decode([SmartPlaylistCondition].self, from: data) #expect(decoded.count == 3) #expect(decoded[0].field == .artist) #expect(decoded[1].op == .greaterThan) if case .int(let y) = decoded[1].value { #expect(y == 1960) } else { Issue.record("Expected int") } if case .date(let d) = decoded[2].value { #expect(d == Date(timeIntervalSince1970: 0)) } else { Issue.record("Expected date") } } }