mirror of
https://github.com/bitwarden/android.git
synced 2026-06-02 11:12:00 -05:00
BIT-394 Setup service layer to accommodate get token error parsing (#61)
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package com.x8bit.bitwarden.data.auth.datasource.network.service
|
||||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.api.AccountsApi
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.PreLoginResponseJson
|
||||
import com.x8bit.bitwarden.data.platform.base.BaseServiceTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import retrofit2.create
|
||||
|
||||
class AccountsServiceTest : BaseServiceTest() {
|
||||
|
||||
private val accountsApi: AccountsApi = retrofit.create()
|
||||
private val service = AccountsServiceImpl(accountsApi)
|
||||
|
||||
@Test
|
||||
fun `preLogin should call API`() = runTest {
|
||||
val response = MockResponse().setBody(PRE_LOGIN_RESPONSE_JSON)
|
||||
server.enqueue(response)
|
||||
assertEquals(Result.success(PRE_LOGIN_RESPONSE), service.preLogin(EMAIL))
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EMAIL = "email"
|
||||
}
|
||||
}
|
||||
|
||||
private const val PRE_LOGIN_RESPONSE_JSON = """
|
||||
{
|
||||
"kdf": 1,
|
||||
"kdfIterations": 1,
|
||||
"kdfMemory": 1,
|
||||
"kdfParallelism": 1
|
||||
}
|
||||
"""
|
||||
|
||||
private val PRE_LOGIN_RESPONSE = PreLoginResponseJson(
|
||||
kdf = 1,
|
||||
kdfIterations = 1u,
|
||||
kdfMemory = 1,
|
||||
kdfParallelism = 1,
|
||||
)
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.x8bit.bitwarden.data.auth.datasource.network.service
|
||||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.api.IdentityApi
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson
|
||||
import com.x8bit.bitwarden.data.platform.base.BaseServiceTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import retrofit2.create
|
||||
|
||||
class IdentityServiceTest : BaseServiceTest() {
|
||||
|
||||
private val identityApi: IdentityApi = retrofit.create()
|
||||
|
||||
private val identityService = IdentityServiceImpl(
|
||||
api = identityApi,
|
||||
json = Json,
|
||||
baseUrl = server.url("/").toString(),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `getToken when request response is Success should return Success`() = runTest {
|
||||
server.enqueue(MockResponse().setBody(LOGIN_SUCCESS_JSON))
|
||||
val result = identityService.getToken(email = EMAIL, passwordHash = PASSWORD_HASH)
|
||||
assertEquals(Result.success(LOGIN_SUCCESS), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getToken when request is error should return error`() = runTest {
|
||||
server.enqueue(MockResponse().setResponseCode(500))
|
||||
val result = identityService.getToken(email = EMAIL, passwordHash = PASSWORD_HASH)
|
||||
assertTrue(result.isFailure)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getToken when response is CaptchaRequired should return CaptchaRequired`() = runTest {
|
||||
server.enqueue(MockResponse().setResponseCode(400).setBody(CAPTCHA_BODY_JSON))
|
||||
val result = identityService.getToken(email = EMAIL, passwordHash = PASSWORD_HASH)
|
||||
assertEquals(Result.success(CAPTCHA_BODY), result)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EMAIL = "email"
|
||||
private const val PASSWORD_HASH = "passwordHash"
|
||||
}
|
||||
}
|
||||
|
||||
private const val CAPTCHA_BODY_JSON = """
|
||||
{
|
||||
"HCaptcha_SiteKey": "123"
|
||||
}
|
||||
"""
|
||||
private val CAPTCHA_BODY = GetTokenResponseJson.CaptchaRequired("123")
|
||||
|
||||
private const val LOGIN_SUCCESS_JSON = """
|
||||
{
|
||||
"access_token": "123"
|
||||
}
|
||||
"""
|
||||
private val LOGIN_SUCCESS = GetTokenResponseJson.Success("123")
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.x8bit.bitwarden.data.auth.repository
|
||||
|
||||
import com.bitwarden.core.Kdf
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthState
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.LoginResult
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.PreLoginResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.AccountsService
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.IdentityService
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AuthRepositoryTest {
|
||||
|
||||
private val accountsService: AccountsService = mockk()
|
||||
private val identityService: IdentityService = mockk()
|
||||
private val authInterceptor = mockk<AuthTokenInterceptor>()
|
||||
private val mockBitwardenSdk = mockk<Client> {
|
||||
coEvery {
|
||||
auth().hashPassword(
|
||||
email = EMAIL,
|
||||
password = PASSWORD,
|
||||
kdfParams = Kdf.Pbkdf2(iterations = PRE_LOGIN_SUCCESS.kdfIterations),
|
||||
)
|
||||
} returns PASSWORD_HASH
|
||||
}
|
||||
|
||||
private val repository = AuthRepositoryImpl(
|
||||
accountsService = accountsService,
|
||||
identityService = identityService,
|
||||
bitwardenSdkClient = mockBitwardenSdk,
|
||||
authTokenInterceptor = authInterceptor,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun beforeEach() {
|
||||
clearMocks(identityService, accountsService, authInterceptor)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login when pre login fails should return Error`() = runTest {
|
||||
coEvery { accountsService.preLogin(EMAIL) } returns (Result.failure(RuntimeException()))
|
||||
val result = repository.login(EMAIL, PASSWORD)
|
||||
assertEquals(LoginResult.Error, result)
|
||||
assertEquals(AuthState.Unauthenticated, repository.authStateFlow.value)
|
||||
coVerify { accountsService.preLogin(EMAIL) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login get token fails should return Error`() = runTest {
|
||||
coEvery { accountsService.preLogin(EMAIL) } returns Result.success(PRE_LOGIN_SUCCESS)
|
||||
coEvery { identityService.getToken(EMAIL, PASSWORD_HASH) }
|
||||
.returns(Result.failure(RuntimeException()))
|
||||
val result = repository.login(EMAIL, PASSWORD)
|
||||
assertEquals(LoginResult.Error, result)
|
||||
assertEquals(AuthState.Unauthenticated, repository.authStateFlow.value)
|
||||
coVerify { accountsService.preLogin(EMAIL) }
|
||||
coVerify { identityService.getToken(EMAIL, PASSWORD_HASH) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login get token succeeds should return Success and update AuthState`() = runTest {
|
||||
coEvery { accountsService.preLogin(EMAIL) } returns Result.success(PRE_LOGIN_SUCCESS)
|
||||
coEvery { identityService.getToken(email = EMAIL, passwordHash = PASSWORD_HASH) }
|
||||
.returns(Result.success(GetTokenResponseJson.Success(accessToken = ACCESS_TOKEN)))
|
||||
every { authInterceptor.authToken = ACCESS_TOKEN } returns Unit
|
||||
val result = repository.login(EMAIL, PASSWORD)
|
||||
assertEquals(LoginResult.Success, result)
|
||||
assertEquals(AuthState.Authenticated(ACCESS_TOKEN), repository.authStateFlow.value)
|
||||
verify { authInterceptor.authToken = ACCESS_TOKEN }
|
||||
coVerify { accountsService.preLogin(EMAIL) }
|
||||
coVerify { identityService.getToken(EMAIL, PASSWORD_HASH) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login get token returns captcha request should return CaptchaRequired`() = runTest {
|
||||
coEvery { accountsService.preLogin(EMAIL) } returns Result.success(PRE_LOGIN_SUCCESS)
|
||||
coEvery { identityService.getToken(email = EMAIL, passwordHash = PASSWORD_HASH) }
|
||||
.returns(Result.success(GetTokenResponseJson.CaptchaRequired(CAPTCHA_KEY)))
|
||||
val result = repository.login(EMAIL, PASSWORD)
|
||||
assertEquals(LoginResult.CaptchaRequired(CAPTCHA_KEY), result)
|
||||
assertEquals(AuthState.Unauthenticated, repository.authStateFlow.value)
|
||||
coVerify { accountsService.preLogin(EMAIL) }
|
||||
coVerify { identityService.getToken(EMAIL, PASSWORD_HASH) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EMAIL = "test@test.com"
|
||||
private const val PASSWORD = "password"
|
||||
private const val PASSWORD_HASH = "passwordHash"
|
||||
private const val ACCESS_TOKEN = "accessToken"
|
||||
private const val CAPTCHA_KEY = "captcha"
|
||||
private val PRE_LOGIN_SUCCESS = PreLoginResponseJson(
|
||||
kdf = 1,
|
||||
kdfIterations = 1u,
|
||||
kdfMemory = null,
|
||||
kdfParallelism = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.x8bit.bitwarden.data.platform.base
|
||||
|
||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.core.ResultCallAdapterFactory
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import retrofit2.Retrofit
|
||||
|
||||
/**
|
||||
* Base class for service tests. Provides common mock web server and retrofit setup.
|
||||
*/
|
||||
abstract class BaseServiceTest {
|
||||
|
||||
protected val server = MockWebServer().apply { start() }
|
||||
|
||||
protected val retrofit: Retrofit = Retrofit.Builder()
|
||||
.baseUrl(server.url("/").toString())
|
||||
.addCallAdapterFactory(ResultCallAdapterFactory())
|
||||
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
|
||||
.build()
|
||||
|
||||
@After
|
||||
fun after() {
|
||||
server.shutdown()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user