Implement incremental sync and offline recovery
This commit is contained in:
@@ -153,6 +153,36 @@ final class iPhoneLibraryViewModelFavoritesTests: XCTestCase {
|
||||
XCTAssertTrue(try XCTUnwrap(remoteRow(in: viewModel, trackID: secondTrack.trackId)).isFavorite)
|
||||
}
|
||||
|
||||
func testFavoritesRemainIntactAcrossLibrarySync() async throws {
|
||||
let track = makeRemoteTrack(
|
||||
trackId: "remote-sync-favorite",
|
||||
assetId: "asset-sync-favorite",
|
||||
title: "Sync Favorite"
|
||||
)
|
||||
let favoriteStore = InMemoryFavoriteTrackStore()
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [track],
|
||||
downloadStates: [makeDownloadedState(for: track)],
|
||||
favoriteTrackStore: favoriteStore,
|
||||
audioFiles: [localFilePath(for: track): Data([0x5])],
|
||||
apiClient: StubVelodyAPIClient(
|
||||
environment: ServerEnvironment(
|
||||
baseURL: ServerEnvironment.defaultLocalBaseURL,
|
||||
appVersion: "Tests"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
await viewModel.toggleFavorite(trackID: track.trackId)
|
||||
await viewModel.refreshSync()
|
||||
|
||||
XCTAssertTrue(try XCTUnwrap(remoteRow(in: viewModel, trackID: track.trackId)).isFavorite)
|
||||
XCTAssertTrue(try XCTUnwrap(offlineRow(in: viewModel, trackID: track.trackId)).isFavorite)
|
||||
let savedFavorites = try await favoriteStore.loadFavoriteTracks()
|
||||
XCTAssertEqual(savedFavorites.map(\.remoteTrackId), [track.trackId])
|
||||
}
|
||||
|
||||
func testToggleFavoriteRepeatedlyLeavesSingleStableRecord() async throws {
|
||||
let track = makeRemoteTrack(
|
||||
trackId: "remote-repeat",
|
||||
@@ -285,4 +315,41 @@ final class iPhoneLibraryViewModelFavoritesTests: XCTestCase {
|
||||
XCTAssertTrue(remoteTrack.isFavorite)
|
||||
XCTAssertTrue(viewModel.availableOfflineTracks.isEmpty)
|
||||
}
|
||||
|
||||
func testFailedDownloadKeepsFavoriteAndShowsRetry() async throws {
|
||||
let track = makeRemoteTrack(
|
||||
trackId: "remote-interrupted-favorite",
|
||||
assetId: "asset-interrupted-favorite",
|
||||
title: "Interrupted Favorite"
|
||||
)
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [track],
|
||||
downloadStates: [
|
||||
RemoteTrackDownloadState(
|
||||
remoteTrackId: track.trackId,
|
||||
assetId: track.assetId,
|
||||
localFilePath: "",
|
||||
downloadedAt: nil,
|
||||
downloadStatus: .failed,
|
||||
lastDownloadError: "The previous download did not finish. Try again."
|
||||
),
|
||||
],
|
||||
favoriteTrackStore: InMemoryFavoriteTrackStore(tracks: [
|
||||
FavoriteTrackRecord(
|
||||
remoteTrackId: track.trackId,
|
||||
favoritedAt: Date(timeIntervalSince1970: 7_000)
|
||||
),
|
||||
])
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
|
||||
let remoteTrack = try XCTUnwrap(remoteRow(in: viewModel, trackID: track.trackId))
|
||||
|
||||
XCTAssertEqual(remoteTrack.status, .failed)
|
||||
XCTAssertEqual(remoteTrack.downloadButtonTitle, "Retry")
|
||||
XCTAssertTrue(remoteTrack.canDownload)
|
||||
XCTAssertTrue(remoteTrack.isFavorite)
|
||||
XCTAssertTrue(viewModel.availableOfflineTracks.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user