Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@ package net.opatry.tasks.app.presentation
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.ktor.client.plugins.ResponseException
import io.ktor.client.statement.bodyAsText
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import net.opatry.google.auth.GoogleAuthenticator
import net.opatry.google.profile.UserInfoApi
import net.opatry.google.profile.UserInfoErrorResponse
import net.opatry.google.profile.model.UserInfo
import net.opatry.logging.Logger
import net.opatry.tasks.CredentialsStorage
Expand Down Expand Up @@ -79,9 +76,9 @@ class UserViewModel(
return try {
userInfoApi.getUserInfo()
} catch (e: ResponseException) {
Json.decodeFromString<UserInfoErrorResponse>(e.response.bodyAsText()).also { response ->
logger.logError("Error while fetching user info: ${response.error.message}", e)
}
// don't assume we can read response accurately (see https://github.com/opatry/taskfolio/issues/262)
// API is poorly documented and 401 & 400 do not return the same data for sure
logger.logError("Web Service error while fetching user info", e)
null
} catch (e: Exception) {
// most likely no network
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

package net.opatry.tasks.presentation

import io.ktor.client.plugins.ResponseException
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
Expand All @@ -41,9 +42,12 @@ import net.opatry.test.MainDispatcherRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.BDDMockito.given
import org.mockito.BDDMockito.then
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnitRunner
import kotlin.test.assertEquals
Expand Down Expand Up @@ -167,13 +171,73 @@ class UserViewModelTest {
assertEquals(UserState.Unsigned, viewModel.state.value)
}

@Test
fun `signIn stores token but updates state to Unsigned on WS failure when fetching user info`() = runTest {
given(nowProvider.now())
.willReturn(Instant.fromEpochMilliseconds(42L))
val exception = mock<ResponseException>()
given(userInfoApi.getUserInfo()).willThrow(exception)

viewModel.signIn(
GoogleAuthenticator.OAuthToken(
accessToken = "accessToken",
expiresIn = 10L,
idToken = "idToken",
refreshToken = "refreshToken",
scope = "scope",
tokenType = GoogleAuthenticator.OAuthToken.TokenType.Bearer,
)
)
advanceUntilIdle()

verify(credentialsStorage).store(
TokenCache(
accessToken = "accessToken",
refreshToken = "refreshToken",
expirationTimeMillis = 42L + 10L.seconds.inWholeMilliseconds,
)
)
verify(logger).logError("Web Service error while fetching user info", exception)
assertEquals(UserState.Unsigned, viewModel.state.value)
}

@Test
fun `signIn stores token but updates state to Unsigned on unknown failure when fetching user info`() = runTest {
given(nowProvider.now())
.willReturn(Instant.fromEpochMilliseconds(42L))
val exception = mock<RuntimeException>()
given(userInfoApi.getUserInfo()).willThrow(exception)

viewModel.signIn(
GoogleAuthenticator.OAuthToken(
accessToken = "accessToken",
expiresIn = 10L,
idToken = "idToken",
refreshToken = "refreshToken",
scope = "scope",
tokenType = GoogleAuthenticator.OAuthToken.TokenType.Bearer,
)
)
advanceUntilIdle()

verify(credentialsStorage).store(
TokenCache(
accessToken = "accessToken",
refreshToken = "refreshToken",
expirationTimeMillis = 42L + 10L.seconds.inWholeMilliseconds,
)
)
verify(logger).logError("Error while fetching user info", exception)
assertEquals(UserState.Unsigned, viewModel.state.value)
}

@Test
fun `signOut clears token clears signed in status and updates state to Unsigned`() = runTest {
viewModel.signOut()
advanceUntilIdle()

then(credentialsStorage).should().store(TokenCache())
then(userDao).should().clearAllSignedInStatus()
verify(credentialsStorage).store(TokenCache())
verify(userDao).clearAllSignedInStatus()
assertEquals(UserState.Unsigned, viewModel.state.value)
}

Expand Down