address review: dedupe and trim genres, add a starred queue reference, fix the offline source switch

This commit is contained in:
austinried
2026-10-04 00:30:06 +00:00
parent 4779bfc88b
commit 3e260aa170
16 changed files with 157 additions and 40 deletions
@@ -217,6 +217,27 @@ class LibraryHomeDaoTest {
assertEquals(page.map { it.song.id }, dao.songsByGenreIds(sourceId, "Rock"))
}
@Test
fun genreSongOrdinalMatchesTheListOrder() =
runTest {
val sourceId = source()
val dao = db.libraryDao()
dao.upsertSongs(
listOf(
song(sourceId, "s1", "Alpha"),
song(sourceId, "s2", "Bravo"),
song(sourceId, "s3", "Charlie"),
),
)
insertGenre(sourceId, "s1", 0, "Rock")
insertGenre(sourceId, "s2", 0, "Rock")
insertGenre(sourceId, "s3", 0, "Rock")
assertEquals(0L, dao.genreSongOrdinal(sourceId, "Rock", "Alpha", "s1"))
assertEquals(1L, dao.genreSongOrdinal(sourceId, "Rock", "Bravo", "s2"))
assertEquals(2L, dao.genreSongOrdinal(sourceId, "Rock", "Charlie", "s3"))
}
@Test
fun downloadedSongsOrderByNewestDownload() =
runTest {
@@ -134,6 +134,25 @@ class MigrationsTest {
}
}
@Test
fun theGenreBackfillTrimsAndSkipsBlankSingularGenres() =
runTest {
driver.open(":memory:").use { migrated ->
applySchema(migrated, versions.first())
MIGRATIONS.filter { it.endVersion <= 23 }.sortedBy { it.startVersion }.forEach { it.migrate(migrated) }
migrated.execSQL(
"INSERT INTO sources (id, name, address, isActive, createdAt) VALUES (1, 's', 'http://x', 1, 0)",
)
migrated.execSQL("INSERT INTO songs (sourceId, id, title, genre) VALUES (1, 's1', 'Song', ' Rock ')")
migrated.execSQL("INSERT INTO songs (sourceId, id, title, genre) VALUES (1, 's2', 'Blank', ' ')")
MIGRATIONS.first { it.endVersion == 24 }.migrate(migrated)
assertEquals("Rock", text(migrated, "SELECT genre FROM song_genres WHERE songId = 's1'"))
assertEquals(0L, long(migrated, "SELECT COUNT(*) FROM song_genres WHERE songId = 's2'"))
}
}
private fun text(
connection: SQLiteConnection,
sql: String,
@@ -87,6 +87,24 @@ class QueueRepositoryTest {
assertEquals(listOf("s1", "s3"), resolveAll(snapshot))
}
@Test
fun starredEntryResolvesStarredTracksNewestFirst() =
runTest {
seedLibrary()
db.libraryDao().upsertSongs(
listOf(
song("s1", "al1", track = 1, starred = 100),
song("s2", "al1", track = 2, starred = 300),
song("s3", "al1", track = 3, starred = 200),
),
)
val snapshot = repository.snapshotAfter(listOf(repository.starredEntry(1)))
assertEquals(3, snapshot.size)
assertEquals(listOf("s2", "s3", "s1"), resolveAll(snapshot))
}
@Test
fun rangeLimitsAnEntryToASlice() =
runTest {
@@ -326,7 +326,7 @@ class SyncServiceTest {
}
@Test
fun albumGenresUnionsSongGenresAndFallsBackToTheSingleValue() =
fun syncNormalizesSingularGenresIntoTheGenreRows() =
runTest {
insertSource()
val source =
@@ -342,7 +342,24 @@ class SyncServiceTest {
SyncService(db, source).sync()
assertEquals(listOf("Jazz", "Pop", "Rock"), db.libraryDao().albumGenres(1, "al1").first())
assertEquals(listOf("Rock", "Pop"), db.libraryDao().songGenres(1, "s1").first())
assertEquals(listOf("Jazz"), db.libraryDao().songGenres(1, "s2").first())
assertEquals(listOf("Rock"), db.libraryDao().songGenres(1, "s3").first())
}
@Test
fun thePluralGenresWinOverTheSingularAttribute() =
runTest {
insertSource()
val source =
FakeMusicSource(
albums = listOf(album("al1")),
songs = listOf(song("s1").copy(genres = listOf("Rock", "Pop"), genre = "Jazz")),
)
SyncService(db, source).sync()
assertEquals(listOf("Rock", "Pop"), db.libraryDao().songGenres(1, "s1").first())
}
@Test
@@ -2537,6 +2537,7 @@ class PlaybackControllerTest {
await { handle.operations.any { it.startsWith("add") } }
assertFalse(handle.operations.any { it.startsWith("setWindow") })
assertFalse(handle.operations.any { it == "prepare" || it == "play" || it == "pause" })
assertEquals(
"s5",
controller.state.value.item
@@ -2804,6 +2805,7 @@ class PlaybackControllerTest {
await { handle.operations.any { it.startsWith("remove") || it.startsWith("add") } }
assertFalse(handle.operations.any { it.startsWith("setWindow") })
assertFalse(handle.operations.any { it == "prepare" || it == "play" || it == "pause" })
assertEquals(
"s2",
controller.state.value.item