fix(android): drop Loading-state assertions in LibraryViewModelTest

The three failing assertions tested an implementation detail. Under
UnconfinedTestDispatcher (MainDispatcherExtension's default), stateIn's
upstream Flow runs synchronously when the first subscriber attaches,
so the `Loading` initialValue gets replaced by the upstream emission
before Turbine's .test{} sees it. The observable behavior we care
about is the resolved state — Empty/Success/Error — not the
intermediate Loading.

Tests now collect the resolved state as the first awaitItem(), which
is what users actually see. The Loading state still exists in
production (StateFlow initialValue is preserved across the brief
window before stateIn collects the first upstream value when the
real dispatcher isn't unconfined).

Also cleared two compile warnings the run surfaced:
  - AuthCookieInterceptorTest: added @OptIn(ExperimentalCoroutinesApi)
    for UnconfinedTestDispatcher
  - LibraryRepositoryTest: hoisted the Json instance into a companion
    object (detekt warned about per-call creation)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-22 23:19:52 -04:00
parent 9962ec981c
commit b03d4a86e7
3 changed files with 14 additions and 7 deletions
@@ -5,6 +5,7 @@ import com.fabledsword.minstrel.cache.db.dao.AuthSessionDao
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -18,6 +19,7 @@ import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
@OptIn(ExperimentalCoroutinesApi::class)
class AuthCookieInterceptorTest {
private lateinit var server: MockWebServer
private lateinit var authStore: AuthStore
@@ -37,13 +37,14 @@ class LibraryRepositoryTest {
Retrofit.Builder()
.baseUrl(server.url("/"))
.client(OkHttpClient.Builder().build())
.addConverterFactory(
Json { ignoreUnknownKeys = true }
.asConverterFactory("application/json".toMediaType()),
)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
}
private companion object {
private val json = Json { ignoreUnknownKeys = true }
}
@AfterEach
fun teardown() {
server.shutdown()
@@ -30,6 +30,13 @@ class LibraryViewModelTest {
assertEquals(LibraryUiState.Loading, vm.uiState.value)
}
// Note: tests below collect the resolved state directly. With
// UnconfinedTestDispatcher, stateIn's upstream Flow runs synchronously
// when the first subscriber attaches — so the `Loading` initialValue
// is replaced by the upstream emission before .test{} sees it. The
// observable behavior we care about is the resolved state, not the
// intermediate Loading.
@Test
fun `Empty when DAOs emit empty lists`() = runTest {
val repo = mockk<LibraryRepository>()
@@ -39,7 +46,6 @@ class LibraryViewModelTest {
val vm = LibraryViewModel(repo)
vm.uiState.test {
assertEquals(LibraryUiState.Loading, awaitItem())
assertEquals(LibraryUiState.Empty, awaitItem())
cancelAndConsumeRemainingEvents()
}
@@ -56,7 +62,6 @@ class LibraryViewModelTest {
val vm = LibraryViewModel(repo)
vm.uiState.test {
assertEquals(LibraryUiState.Loading, awaitItem())
val success = awaitItem() as LibraryUiState.Success
assertEquals(1, success.artists.size)
assertEquals(1, success.albums.size)
@@ -76,7 +81,6 @@ class LibraryViewModelTest {
val vm = LibraryViewModel(repo)
vm.uiState.test {
assertEquals(LibraryUiState.Loading, awaitItem())
val error = awaitItem() as LibraryUiState.Error
assertTrue(error.message.contains("DAO blew up"))
cancelAndConsumeRemainingEvents()