cover the queue/shuffle invariants the QA review flagged

The shuffled-removal tests mostly removed the *current* track (a path that
was already correct) and asserted `state.item`, which `rebuildWindow` keeps
regardless of the computed cursor, so a wrong position went unnoticed. Add:

- deterministic shuffle setup (`seedShuffledQueue`, or replace + setShuffle
  + setCursor + connect) so shuffle tests assert exact behaviour instead of
  looping over random seeds;
- controller tests that assert the queue position still resolves to the
  playing track after removing a non-current track, with and without a block,
  and that undo restores the shuffled order;
- a repository property test removing every position with a block present,
  checking the play/flat bijection, the combined/contextPlay inverse, the
  block span and that no track repeats;
- a `QueueRow.isPlaying` predicate (position AND song id) extracted and
  unit-tested, and QueueViewModel tests for starting a shuffled queue at the
  top and reconciling after a shuffled removal;
- Shuffle degenerate/out-of-range/MAX_SIZE coverage.
This commit is contained in:
austinried
2026-10-04 00:30:01 +00:00
parent 736e59fec6
commit 7b1a2a8f95
5 changed files with 219 additions and 1 deletions
@@ -80,6 +80,13 @@ data class QueueRow(
val upNext: Boolean = false,
)
// Both must match: the position alone can transiently point at the row above while the list
// reconciles, and the song id alone lights up a duplicate elsewhere in the queue.
internal fun QueueRow.isPlaying(
currentPosition: Long?,
currentSongId: String?,
): Boolean = position == currentPosition && song.song.id == currentSongId
class QueueViewModel(
private val queueRepository: QueueRepository,
private val playbackController: PlaybackController,
@@ -436,7 +443,7 @@ fun QueueScreen(
) { isDragging ->
QueueRowItem(
row = row,
isPlaying = row.position == currentPosition && row.song.song.id == currentSongId,
isPlaying = row.isPlaying(currentPosition, currentSongId),
floating = isDragging,
coverArt = coverArt,
dragHandle =