Cache DNS answers and paint saved people before launch revalidation
Some checks failed
review/security-claude BLOCK: DnsConnectionRetry crashes app on Jellyfin WebSocket (NoSuchElementException)

This commit is contained in:
justinzeus 2026-11-07 00:09:57 +01:00
parent f4fb6e22c9
commit 327f631f0d
18 changed files with 763 additions and 44 deletions

View file

@ -32,6 +32,8 @@ import io.mockk.mockkStatic
import io.mockk.slot
import io.mockk.unmockkStatic
import io.mockk.verify
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flow
@ -116,9 +118,6 @@ class TestMainActivityViewModel {
signInAutomatically = true
}
every { serverRepository.current } returns MutableStateFlow(currentUser)
// coEvery { serverRepository.tryRestoreSession(serverId, userId) } returns currentUser
// coEvery { serverRepository.restoreLastSession() } returns
// RestoredSession.Success(currentUser)
viewModel.handleAppStart()
@ -132,7 +131,7 @@ class TestMainActivityViewModel {
destination as SetupDestination.AppContent
Assert.assertEquals(currentUser, destination.current)
}
coVerify(exactly = 0) { serverRepository.restoreLastSession() }
coVerify(exactly = 0) { serverRepository.restoreSavedSession() }
coVerify(exactly = 0) { serverRepository.getMostRecentServer() }
}
@ -150,7 +149,7 @@ class TestMainActivityViewModel {
viewModel.handleAppStart()
coVerify(exactly = 0) { serverRepository.tryChangeUser(any(), any()) }
coVerify(exactly = 0) { serverRepository.restoreLastSession() }
coVerify(exactly = 0) { serverRepository.restoreSavedSession() }
coVerify(exactly = 1) { serverRepository.getMostRecentServer() }
val args = mutableListOf<SetupDestination>()
verify { setupNavigationManager.navigateTo(capture(args)) }
@ -165,6 +164,20 @@ class TestMainActivityViewModel {
}
}
@Test
fun savedColdLaunchNavigatesBeforeSessionRevalidationCompletes() = runTest(testDispatcher) {
setupPreferences { signInAutomatically = true }
every { serverRepository.current } returns MutableStateFlow<CurrentUser?>(null)
coEvery { serverRepository.restoreSavedSession() } returns RestoredSession.Success(currentUser)
val release = CompletableDeferred<Unit>()
coEvery { serverRepository.revalidateSession(currentUser) } coAnswers { release.await() }
viewModel.handleAppStart()
runCurrent()
verify { setupNavigationManager.navigateTo(SetupDestination.AppContent(currentUser)) }
assertFalse(release.isCompleted)
release.complete(Unit)
}
@Test
fun `Test auto sign in, hot load, with protected profile`() =
runTest {
@ -178,7 +191,7 @@ class TestMainActivityViewModel {
coEvery {
serverRepository.tryChangeUser(serverId, userId)
} returns null
coEvery { serverRepository.restoreLastSession() } returns
coEvery { serverRepository.restoreSavedSession() } returns
RestoredSession.ServerOnly(server)
viewModel.handleAppStart()
@ -204,7 +217,7 @@ class TestMainActivityViewModel {
signInAutomatically = true
}
every { serverRepository.current } returns MutableStateFlow<CurrentUser?>(null)
coEvery { serverRepository.restoreLastSession() } returns
coEvery { serverRepository.restoreSavedSession() } returns
RestoredSession.ServerOnly(server)
coEvery {
serverRepository.tryChangeUser(serverId, userId)
@ -212,7 +225,7 @@ class TestMainActivityViewModel {
viewModel.handleAppStart()
coVerify(exactly = 1) { serverRepository.restoreLastSession() }
coVerify(exactly = 1) { serverRepository.restoreSavedSession() }
val slot = slot<SetupDestination>()
verify { setupNavigationManager.navigateTo(capture(slot)) }
slot.captured.let { destination ->
@ -232,11 +245,11 @@ class TestMainActivityViewModel {
signInAutomatically = true
}
every { serverRepository.current } returns MutableStateFlow<CurrentUser?>(null)
coEvery { serverRepository.restoreLastSession() } returns RestoredSession.None
coEvery { serverRepository.restoreSavedSession() } returns RestoredSession.None
viewModel.handleAppStart()
coVerify(exactly = 1) { serverRepository.restoreLastSession() }
coVerify(exactly = 1) { serverRepository.restoreSavedSession() }
val slot = slot<SetupDestination>()
verify { setupNavigationManager.navigateTo(capture(slot)) }
slot.captured.let { destination ->
@ -254,7 +267,7 @@ class TestMainActivityViewModel {
signInAutomatically = true
}
every { serverRepository.current } returns MutableStateFlow(currentUser)
coEvery { serverRepository.restoreLastSession() } returns
coEvery { serverRepository.restoreSavedSession() } returns
RestoredSession.Success(currentUser)
coEvery { intentService.parseIntent(any()) } returns IntentResult.NoOp
@ -271,7 +284,7 @@ class TestMainActivityViewModel {
// destination as SetupDestination.AppContent
// Assert.assertEquals(currentUser, destination.current)
// }
coVerify(exactly = 0) { serverRepository.restoreLastSession() }
coVerify(exactly = 0) { serverRepository.restoreSavedSession() }
coVerify(exactly = 0) { serverRepository.getMostRecentServer() }
}