mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +01:00
address arch review: guard the page cap, coalesce syncs, tighten buffers
- Paged loops now throw when they hit maxPages with a full last page, so a truncated enumeration aborts the entity's collect and skips its prune instead of deleting rows the sync never saw. The cap is injectable for tests. - Move sync serialization into SyncManager (tryLock coalesces duplicate requests) and construct SyncService there; SourceRepository only exposes the active MusicSource. Removes the repo <-> sync import cycle. - Re-throw CancellationException instead of reporting a cancelled sync as failed. - Add buffer(1) to the outer albums/songs/playlistSongs flows so their flowOn channel cannot hold 64 batches. - Rank prefetch is best-effort (missing frequent/recent no longer aborts the album sync). - Skip playlist entries without an id, keeping positions contiguous. - Chunk stale-disc deletes like the other prunes. - Update the implementation plan line that still described the old single-transaction sync.
This commit is contained in:
@@ -179,6 +179,49 @@ class SubsonicSourceTest {
|
||||
assertEquals(500, firstBatch.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun albumsAbortWhenThePageCapIsHit() =
|
||||
runBlocking {
|
||||
server.dispatcher =
|
||||
object : Dispatcher() {
|
||||
override fun dispatch(request: RecordedRequest): MockResponse {
|
||||
val url = request.requestUrl!!
|
||||
if (url.encodedPath != "/rest/getAlbumList2.view") return MockResponse().setResponseCode(404)
|
||||
return if (url.queryParameter("type") == "newest") {
|
||||
MockResponse().setBody(albumPage(0, 500))
|
||||
} else {
|
||||
MockResponse().setBody(emptyAlbumList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val failure =
|
||||
runCatching {
|
||||
SubsonicSource(1, client(), maxPages = 2).albums().toList()
|
||||
}
|
||||
|
||||
assertEquals(true, failure.exceptionOrNull() is SubsonicException)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun playlistEntriesWithoutIdsAreSkipped() =
|
||||
runBlocking {
|
||||
server.enqueue(
|
||||
MockResponse().setBody(
|
||||
"<subsonic-response status=\"ok\"><playlist id=\"p1\">" +
|
||||
"<entry id=\"s1\" title=\"One\"/>" +
|
||||
"<entry title=\"No id\"/>" +
|
||||
"<entry id=\"s2\" title=\"Two\"/>" +
|
||||
"</playlist></subsonic-response>",
|
||||
),
|
||||
)
|
||||
|
||||
val entries = SubsonicSource(1, client()).playlistSongs(listOf("p1")).toList().flatten()
|
||||
|
||||
assertEquals(listOf("s1", "s2"), entries.map { it.songId })
|
||||
assertEquals(listOf(0L, 1L), entries.map { it.position })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun streamUriRequestsTranscodingWhenABitrateIsSet() {
|
||||
val uri = SubsonicSource(1, client(), maxBitrate = 128).streamUri("s1").toString()
|
||||
|
||||
@@ -37,7 +37,7 @@ class SyncManagerTest {
|
||||
.build()
|
||||
sourceRepository = SourceRepository(db, OkHttpClient(), fakeUserPreferences())
|
||||
queueRepository = QueueRepository(db)
|
||||
manager = SyncManager(sourceRepository, queueRepository)
|
||||
manager = SyncManager(db, sourceRepository, queueRepository)
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
Reference in New Issue
Block a user