Add iPhone local library search
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import VelodyDomain
|
||||
|
||||
final class OfflineLibrarySearchTests: XCTestCase {
|
||||
func testSearchByTitleFiltersMatchingTracks() {
|
||||
let snapshot = makeSnapshot()
|
||||
|
||||
let filtered = snapshot.filtered(searchText: "trap")
|
||||
|
||||
XCTAssertEqual(filtered.remoteTracks.map(\.remoteTrack.trackId), ["remote-light-trap"])
|
||||
XCTAssertEqual(filtered.availableTracks.map(\.remoteTrackId), ["remote-light-trap"])
|
||||
}
|
||||
|
||||
func testSearchByArtistFiltersMatchingTracks() {
|
||||
let snapshot = makeSnapshot()
|
||||
|
||||
let filtered = snapshot.filtered(searchText: "unknown")
|
||||
|
||||
XCTAssertEqual(filtered.remoteTracks.map(\.remoteTrack.trackId), ["remote-light-trap"])
|
||||
XCTAssertEqual(filtered.availableTracks.map(\.remoteTrackId), ["remote-light-trap"])
|
||||
}
|
||||
|
||||
func testSearchIsCaseInsensitiveAndTrimsWhitespace() {
|
||||
let snapshot = makeSnapshot()
|
||||
|
||||
let filtered = snapshot.filtered(searchText: " TRAP ")
|
||||
|
||||
XCTAssertEqual(filtered.remoteTracks.map(\.remoteTrack.trackId), ["remote-light-trap"])
|
||||
XCTAssertEqual(filtered.availableTracks.map(\.remoteTrackId), ["remote-light-trap"])
|
||||
}
|
||||
|
||||
func testEmptySearchResultReturnsEmptySections() {
|
||||
let snapshot = makeSnapshot()
|
||||
|
||||
let filtered = snapshot.filtered(searchText: "nope")
|
||||
|
||||
XCTAssertTrue(filtered.remoteTracks.isEmpty)
|
||||
XCTAssertTrue(filtered.availableTracks.isEmpty)
|
||||
}
|
||||
|
||||
func testClearingSearchRestoresFullList() {
|
||||
let snapshot = makeSnapshot()
|
||||
|
||||
let filtered = snapshot.filtered(searchText: "")
|
||||
|
||||
XCTAssertEqual(filtered.remoteTracks, snapshot.remoteTracks)
|
||||
XCTAssertEqual(filtered.availableTracks, snapshot.availableTracks)
|
||||
}
|
||||
|
||||
func testOfflineSectionFilteringUsesAvailableOfflineTracks() {
|
||||
let snapshot = makeSnapshot()
|
||||
|
||||
let filtered = snapshot.filtered(searchText: "harbor")
|
||||
|
||||
XCTAssertEqual(filtered.remoteTracks.map(\.remoteTrack.trackId), ["remote-harbor-lights"])
|
||||
XCTAssertEqual(filtered.availableTracks.map(\.remoteTrackId), ["remote-harbor-lights"])
|
||||
}
|
||||
|
||||
func testRemoteSectionFilteringUsesRemoteLibraryTracks() {
|
||||
let snapshot = makeSnapshot()
|
||||
|
||||
let filtered = snapshot.filtered(searchText: "shadow")
|
||||
|
||||
XCTAssertEqual(filtered.remoteTracks.map(\.remoteTrack.trackId), ["remote-shadow-city"])
|
||||
XCTAssertTrue(filtered.availableTracks.isEmpty)
|
||||
}
|
||||
|
||||
func testFilteringDoesNotMutateUnderlyingSnapshotData() {
|
||||
let snapshot = makeSnapshot()
|
||||
let originalRemoteTracks = snapshot.remoteTracks
|
||||
let originalAvailableTracks = snapshot.availableTracks
|
||||
|
||||
_ = snapshot.filtered(searchText: "light")
|
||||
|
||||
XCTAssertEqual(snapshot.remoteTracks, originalRemoteTracks)
|
||||
XCTAssertEqual(snapshot.availableTracks, originalAvailableTracks)
|
||||
}
|
||||
|
||||
private func makeSnapshot() -> OfflineLibrarySnapshot {
|
||||
let remoteTracks = [
|
||||
makeRemoteLibraryTrack(
|
||||
trackId: "remote-light-trap",
|
||||
assetId: "asset-light-trap",
|
||||
title: "Light Trap",
|
||||
artist: "Unknown Artist",
|
||||
isFileAvailable: true
|
||||
),
|
||||
makeRemoteLibraryTrack(
|
||||
trackId: "remote-shadow-city",
|
||||
assetId: "asset-shadow-city",
|
||||
title: "Shadow City",
|
||||
artist: "Night Runner",
|
||||
isFileAvailable: false
|
||||
),
|
||||
makeRemoteLibraryTrack(
|
||||
trackId: "remote-harbor-lights",
|
||||
assetId: "asset-harbor-lights",
|
||||
title: "Harbor Lights",
|
||||
artist: "The Northline",
|
||||
isFileAvailable: true
|
||||
),
|
||||
]
|
||||
|
||||
let availableTracks = [
|
||||
makeOfflineTrack(
|
||||
trackId: "remote-light-trap",
|
||||
assetId: "asset-light-trap",
|
||||
title: "Light Trap",
|
||||
artist: "Unknown Artist"
|
||||
),
|
||||
makeOfflineTrack(
|
||||
trackId: "remote-harbor-lights",
|
||||
assetId: "asset-harbor-lights",
|
||||
title: "Harbor Lights",
|
||||
artist: "The Northline"
|
||||
),
|
||||
]
|
||||
|
||||
return OfflineLibrarySnapshot(
|
||||
remoteTracks: remoteTracks,
|
||||
availableTracks: availableTracks
|
||||
)
|
||||
}
|
||||
|
||||
private func makeRemoteLibraryTrack(
|
||||
trackId: String,
|
||||
assetId: String,
|
||||
title: String,
|
||||
artist: String,
|
||||
isFileAvailable: Bool
|
||||
) -> OfflineLibraryRemoteTrack {
|
||||
OfflineLibraryRemoteTrack(
|
||||
remoteTrack: RemoteTrack(
|
||||
trackId: trackId,
|
||||
title: title,
|
||||
artist: artist,
|
||||
durationSeconds: 180,
|
||||
sha256: String(repeating: "a", count: 64),
|
||||
assetId: assetId,
|
||||
createdAt: "2026-05-30T10:00:00.000Z",
|
||||
updatedAt: "2026-05-30T10:00:00.000Z"
|
||||
),
|
||||
localFilePath: isFileAvailable ? "/tmp/\(assetId).mp3" : "",
|
||||
downloadedAt: isFileAvailable ? Date(timeIntervalSince1970: 1_000) : nil,
|
||||
isFileAvailable: isFileAvailable,
|
||||
status: isFileAvailable ? .downloaded : .notDownloaded,
|
||||
lastDownloadError: nil
|
||||
)
|
||||
}
|
||||
|
||||
private func makeOfflineTrack(
|
||||
trackId: String,
|
||||
assetId: String,
|
||||
title: String,
|
||||
artist: String
|
||||
) -> OfflineLibraryTrack {
|
||||
OfflineLibraryTrack(
|
||||
remoteTrackId: trackId,
|
||||
assetId: assetId,
|
||||
title: title,
|
||||
artist: artist,
|
||||
durationSeconds: 180,
|
||||
sha256: String(repeating: "b", count: 64),
|
||||
localFilePath: "/tmp/\(assetId).mp3",
|
||||
downloadedAt: Date(timeIntervalSince1970: 1_000),
|
||||
isFileAvailable: true
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user