mirror of
https://github.com/bitwarden/android.git
synced 2026-06-01 02:06:52 -05:00
BIT-205: Populate vault with login items (#246)
This commit is contained in:
@@ -45,18 +45,18 @@ class LocalDateTimeSerializerTest {
|
||||
LocalDateTimeData(
|
||||
dataAsLocalDateTime = LocalDateTime.of(
|
||||
2023,
|
||||
10,
|
||||
6,
|
||||
17,
|
||||
22,
|
||||
28,
|
||||
446666700,
|
||||
8,
|
||||
1,
|
||||
16,
|
||||
13,
|
||||
3,
|
||||
502391000,
|
||||
),
|
||||
),
|
||||
json.decodeFromString<LocalDateTimeData>(
|
||||
"""
|
||||
{
|
||||
"dataAsLocalDateTime": "2023-10-06T17:22:28.4466667Z"
|
||||
"dataAsLocalDateTime": "2023-08-01T16:13:03.502391Z"
|
||||
}
|
||||
""",
|
||||
),
|
||||
@@ -69,7 +69,7 @@ class LocalDateTimeSerializerTest {
|
||||
json.parseToJsonElement(
|
||||
"""
|
||||
{
|
||||
"dataAsLocalDateTime": "2023-10-06T17:22:28.44Z"
|
||||
"dataAsLocalDateTime": "2023-10-06T17:22:28.4400000Z"
|
||||
}
|
||||
""",
|
||||
),
|
||||
|
||||
@@ -121,25 +121,49 @@ class VaultSdkSourceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Cipher decryptListCollection should call SDK and return a Result with correct data`() =
|
||||
runBlocking {
|
||||
val mockCiphers = mockk<List<Cipher>>()
|
||||
val expectedResult = mockk<List<CipherListView>>()
|
||||
coEvery {
|
||||
clientVault.ciphers().decryptList(
|
||||
ciphers = mockCiphers,
|
||||
)
|
||||
} returns expectedResult
|
||||
val result = vaultSdkSource.decryptCipherListCollection(
|
||||
cipherList = mockCiphers,
|
||||
)
|
||||
assertEquals(
|
||||
expectedResult.asSuccess(),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientVault.ciphers().decryptList(
|
||||
ciphers = mockCiphers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Cipher decryptList should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val mockCiphers = mockk<List<Cipher>>()
|
||||
val expectedResult = mockk<List<CipherListView>>()
|
||||
val mockCiphers = mockk<Cipher>()
|
||||
val expectedResult = mockk<CipherView>()
|
||||
coEvery {
|
||||
clientVault.ciphers().decryptList(
|
||||
ciphers = mockCiphers,
|
||||
clientVault.ciphers().decrypt(
|
||||
cipher = mockCiphers,
|
||||
)
|
||||
} returns expectedResult
|
||||
val result = vaultSdkSource.decryptCipherList(
|
||||
cipherList = mockCiphers,
|
||||
cipherList = listOf(mockCiphers),
|
||||
)
|
||||
assertEquals(
|
||||
expectedResult.asSuccess(),
|
||||
listOf(expectedResult).asSuccess(),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientVault.ciphers().decryptList(
|
||||
ciphers = mockCiphers,
|
||||
clientVault.ciphers().decrypt(
|
||||
cipher = mockCiphers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||
|
||||
import com.bitwarden.core.AttachmentView
|
||||
import com.bitwarden.core.CardView
|
||||
import com.bitwarden.core.CipherRepromptType
|
||||
import com.bitwarden.core.CipherType
|
||||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.FieldType
|
||||
import com.bitwarden.core.FieldView
|
||||
import com.bitwarden.core.IdentityView
|
||||
import com.bitwarden.core.LoginUriView
|
||||
import com.bitwarden.core.LoginView
|
||||
import com.bitwarden.core.PasswordHistoryView
|
||||
import com.bitwarden.core.SecureNoteType
|
||||
import com.bitwarden.core.SecureNoteView
|
||||
import com.bitwarden.core.UriMatchType
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* Create a mock [CipherView] with a given [number].
|
||||
*/
|
||||
fun createMockCipherView(number: Int): CipherView =
|
||||
CipherView(
|
||||
id = "mockId-$number",
|
||||
organizationId = "mockOrganizationId-$number",
|
||||
folderId = "mockId-$number",
|
||||
collectionIds = listOf("mockCollectionId-$number"),
|
||||
key = "mockKey-$number",
|
||||
name = "mockName-$number",
|
||||
notes = "mockNotes-$number",
|
||||
type = CipherType.LOGIN,
|
||||
login = createMockLoginView(number = number),
|
||||
creationDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
deletedDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
revisionDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
attachments = listOf(createMockAttachmentView(number = number)),
|
||||
card = createMockCardView(number = number),
|
||||
fields = listOf(createMockFieldView(number = number)),
|
||||
identity = createMockIdentityView(number = number),
|
||||
favorite = false,
|
||||
passwordHistory = listOf(createMockPasswordHistoryView(number = number)),
|
||||
reprompt = CipherRepromptType.NONE,
|
||||
secureNote = createMockSecureNoteView(),
|
||||
edit = false,
|
||||
organizationUseTotp = false,
|
||||
viewPassword = false,
|
||||
localData = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [LoginView] with a given [number].
|
||||
*/
|
||||
fun createMockLoginView(number: Int): LoginView =
|
||||
LoginView(
|
||||
username = "mockUsername-$number",
|
||||
password = "mockPassword-$number",
|
||||
passwordRevisionDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
autofillOnPageLoad = false,
|
||||
uris = listOf(createMockUriView(number = number)),
|
||||
totp = "mockTotp-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [LoginUriView] with a given [number].
|
||||
*/
|
||||
fun createMockUriView(number: Int): LoginUriView =
|
||||
LoginUriView(
|
||||
uri = "mockUri-$number",
|
||||
match = UriMatchType.HOST,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [AttachmentView] with a given [number].
|
||||
*/
|
||||
fun createMockAttachmentView(number: Int): AttachmentView =
|
||||
AttachmentView(
|
||||
fileName = "mockFileName-$number",
|
||||
size = "1",
|
||||
sizeName = "mockSizeName-$number",
|
||||
id = "mockId-$number",
|
||||
url = "mockUrl-$number",
|
||||
key = "mockKey-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [CardView] with a given [number].
|
||||
*/
|
||||
fun createMockCardView(number: Int): CardView =
|
||||
CardView(
|
||||
number = "mockNumber-$number",
|
||||
expMonth = "mockExpMonth-$number",
|
||||
code = "mockCode-$number",
|
||||
expYear = "mockExpirationYear-$number",
|
||||
cardholderName = "mockCardholderName-$number",
|
||||
brand = "mockBrand-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [FieldView] with a given [number].
|
||||
*/
|
||||
fun createMockFieldView(number: Int): FieldView =
|
||||
FieldView(
|
||||
linkedId = 100U,
|
||||
name = "mockName-$number",
|
||||
type = FieldType.HIDDEN,
|
||||
value = "mockValue-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [IdentityView] with a given [number].
|
||||
*/
|
||||
fun createMockIdentityView(number: Int): IdentityView =
|
||||
IdentityView(
|
||||
firstName = "mockFirstName-$number",
|
||||
middleName = "mockMiddleName-$number",
|
||||
lastName = "mockLastName-$number",
|
||||
passportNumber = "mockPassportNumber-$number",
|
||||
country = "mockCountry-$number",
|
||||
address1 = "mockAddress1-$number",
|
||||
address2 = "mockAddress2-$number",
|
||||
address3 = "mockAddress3-$number",
|
||||
city = "mockCity-$number",
|
||||
postalCode = "mockPostalCode-$number",
|
||||
title = "mockTitle-$number",
|
||||
ssn = "mockSsn-$number",
|
||||
phone = "mockPhone-$number",
|
||||
company = "mockCompany-$number",
|
||||
licenseNumber = "mockLicenseNumber-$number",
|
||||
state = "mockState-$number",
|
||||
email = "mockEmail-$number",
|
||||
username = "mockUsername-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [PasswordHistoryView] with a given [number].
|
||||
*/
|
||||
fun createMockPasswordHistoryView(number: Int): PasswordHistoryView =
|
||||
PasswordHistoryView(
|
||||
password = "mockPassword-$number",
|
||||
lastUsedDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SecureNoteView] with a given [number].
|
||||
*/
|
||||
fun createMockSecureNoteView(): SecureNoteView =
|
||||
SecureNoteView(
|
||||
type = SecureNoteType.GENERIC,
|
||||
)
|
||||
@@ -18,13 +18,18 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResul
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFolder
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherListView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -51,7 +56,7 @@ class VaultRepositoryTest {
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
@@ -70,7 +75,7 @@ class VaultRepositoryTest {
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
@@ -86,7 +91,7 @@ class VaultRepositoryTest {
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
@@ -101,7 +106,7 @@ class VaultRepositoryTest {
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
@@ -111,7 +116,7 @@ class VaultRepositoryTest {
|
||||
assertEquals(
|
||||
DataState.Pending(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
@@ -120,7 +125,7 @@ class VaultRepositoryTest {
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
@@ -161,7 +166,7 @@ class VaultRepositoryTest {
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns mockException.asFailure()
|
||||
@@ -221,7 +226,7 @@ class VaultRepositoryTest {
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
@@ -236,7 +241,7 @@ class VaultRepositoryTest {
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
@@ -249,7 +254,7 @@ class VaultRepositoryTest {
|
||||
assertEquals(
|
||||
DataState.Pending(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
@@ -258,7 +263,7 @@ class VaultRepositoryTest {
|
||||
assertEquals(
|
||||
DataState.NoNetwork(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
@@ -275,7 +280,7 @@ class VaultRepositoryTest {
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
@@ -310,6 +315,102 @@ class VaultRepositoryTest {
|
||||
coVerify { syncService.sync() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync should be able to be called after unlockVaultAndSync is canceled`() = runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.storeUserKey(
|
||||
userId = "mockUserId",
|
||||
userKey = "mockKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
request = InitCryptoRequest(
|
||||
kdfParams = Kdf.Pbkdf2(iterations = DEFAULT_PBKDF2_ITERATIONS.toUInt()),
|
||||
email = "email",
|
||||
password = "mockPassword-1",
|
||||
userKey = "mockKey-1",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
organizationKeys = mapOf(),
|
||||
),
|
||||
)
|
||||
} coAnswers {
|
||||
delay(Long.MAX_VALUE)
|
||||
Result.success(InitializeCryptoResult.Success)
|
||||
}
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Unconfined)
|
||||
scope.launch {
|
||||
vaultRepository.unlockVaultAndSync(masterPassword = "mockPassword-1")
|
||||
}
|
||||
coVerify(exactly = 0) { syncService.sync() }
|
||||
scope.cancel()
|
||||
vaultRepository.sync()
|
||||
|
||||
coVerify(exactly = 1) { syncService.sync() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync should not be able to be called while unlockVaultAndSync is called`() = runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.storeUserKey(
|
||||
userId = "mockUserId",
|
||||
userKey = "mockKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
request = InitCryptoRequest(
|
||||
kdfParams = Kdf.Pbkdf2(iterations = DEFAULT_PBKDF2_ITERATIONS.toUInt()),
|
||||
email = "email",
|
||||
password = "mockPassword-1",
|
||||
userKey = "mockKey-1",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
organizationKeys = mapOf(),
|
||||
),
|
||||
)
|
||||
} coAnswers {
|
||||
delay(Long.MAX_VALUE)
|
||||
Result.success(InitializeCryptoResult.Success)
|
||||
}
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Unconfined)
|
||||
scope.launch {
|
||||
vaultRepository.unlockVaultAndSync(masterPassword = "mockPassword-1")
|
||||
}
|
||||
// We call sync here but the call to the SyncService should be blocked
|
||||
// by the active call to unlockVaultAndSync
|
||||
vaultRepository.sync()
|
||||
|
||||
scope.cancel()
|
||||
|
||||
coVerify(exactly = 0) { syncService.sync() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVaultAndSync with initializeCrypto failure should return GenericError`() =
|
||||
runTest {
|
||||
@@ -451,7 +552,7 @@ class VaultRepositoryTest {
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
@@ -466,7 +567,7 @@ class VaultRepositoryTest {
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -3,15 +3,31 @@ package com.x8bit.bitwarden.ui.vault.feature.vault
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.AccountSummary
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultViewModelTest : BaseViewModelTest() {
|
||||
|
||||
private val mutableVaultDataStateFlow =
|
||||
MutableStateFlow<DataState<VaultData>>(DataState.Loading)
|
||||
|
||||
private val vaultRepository: VaultRepository =
|
||||
mockk {
|
||||
every { vaultDataStateFlow } returns mutableVaultDataStateFlow
|
||||
every { sync() } returns Unit
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct when not set`() {
|
||||
val viewModel = createViewModel()
|
||||
@@ -82,6 +98,113 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `vaultDataStateFlow Loaded with items should update state to Content`() = runTest {
|
||||
mutableVaultDataStateFlow.tryEmit(
|
||||
value = DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
|
||||
assertEquals(
|
||||
createMockVaultState(
|
||||
viewState = VaultState.ViewState.Content(
|
||||
loginItemsCount = 1,
|
||||
cardItemsCount = 0,
|
||||
identityItemsCount = 0,
|
||||
secureNoteItemsCount = 0,
|
||||
favoriteItems = listOf(),
|
||||
folderItems = listOf(
|
||||
VaultState.ViewState.FolderItem(
|
||||
id = "mockId-1",
|
||||
name = "mockName-1".asText(),
|
||||
itemCount = 1,
|
||||
),
|
||||
),
|
||||
noFolderItems = listOf(),
|
||||
trashItemsCount = 0,
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `vaultDataStateFlow Loaded with empty items should update state to NoItems`() = runTest {
|
||||
mutableVaultDataStateFlow.tryEmit(
|
||||
value = DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherViewList = emptyList(),
|
||||
folderViewList = emptyList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
assertEquals(
|
||||
createMockVaultState(viewState = VaultState.ViewState.NoItems),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `vaultDataStateFlow Loading should update state to Loading`() = runTest {
|
||||
mutableVaultDataStateFlow.tryEmit(value = DataState.Loading)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
|
||||
assertEquals(
|
||||
createMockVaultState(viewState = VaultState.ViewState.Loading),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `vaultDataStateFlow Error should show toast and update state to NoItems`() = runTest {
|
||||
mutableVaultDataStateFlow.tryEmit(
|
||||
value = DataState.Error(
|
||||
error = IllegalStateException(),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
assertEquals(
|
||||
VaultEvent.ShowToast("Vault error state not yet implemented"),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
assertEquals(
|
||||
createMockVaultState(viewState = VaultState.ViewState.NoItems),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `vaultDataStateFlow NoNetwork should show toast and update state to NoItems`() = runTest {
|
||||
mutableVaultDataStateFlow.tryEmit(
|
||||
value = DataState.NoNetwork(),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel()
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
assertEquals(
|
||||
VaultEvent.ShowToast("Vault no network state not yet implemented"),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
assertEquals(
|
||||
createMockVaultState(viewState = VaultState.ViewState.NoItems),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AddItemClick should emit NavigateToAddItemScreen`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
@@ -175,12 +298,19 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
state: VaultState? = DEFAULT_STATE,
|
||||
): VaultViewModel = VaultViewModel(
|
||||
savedStateHandle = SavedStateHandle().apply { set("state", state) },
|
||||
vaultRepository = vaultRepository,
|
||||
)
|
||||
}
|
||||
|
||||
private val DEFAULT_STATE: VaultState = VaultState(
|
||||
avatarColorString = "FF0000FF",
|
||||
initials = "BW",
|
||||
accountSummaries = emptyList(),
|
||||
viewState = VaultState.ViewState.Loading,
|
||||
)
|
||||
private const val DEFAULT_COLOR_STRING: String = "FF0000FF"
|
||||
private const val DEFAULE_INITIALS: String = "BW"
|
||||
private val DEFAULT_STATE: VaultState =
|
||||
createMockVaultState(viewState = VaultState.ViewState.Loading)
|
||||
|
||||
private fun createMockVaultState(viewState: VaultState.ViewState): VaultState =
|
||||
VaultState(
|
||||
avatarColorString = DEFAULT_COLOR_STRING,
|
||||
initials = DEFAULE_INITIALS,
|
||||
accountSummaries = emptyList(),
|
||||
viewState = viewState,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.vault.util
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.VaultState
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultDataExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `toViewState should transform full VaultData into ViewState Content`() {
|
||||
val vaultData = VaultData(
|
||||
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
)
|
||||
|
||||
val actual = vaultData.toViewState()
|
||||
|
||||
assertEquals(
|
||||
VaultState.ViewState.Content(
|
||||
loginItemsCount = 1,
|
||||
cardItemsCount = 0,
|
||||
identityItemsCount = 0,
|
||||
secureNoteItemsCount = 0,
|
||||
favoriteItems = listOf(),
|
||||
folderItems = listOf(
|
||||
VaultState.ViewState.FolderItem(
|
||||
id = "mockId-1",
|
||||
name = "mockName-1".asText(),
|
||||
itemCount = 1,
|
||||
),
|
||||
),
|
||||
noFolderItems = listOf(),
|
||||
trashItemsCount = 0,
|
||||
),
|
||||
actual,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toViewState should transform empty VaultData into ViewState NoItems`() {
|
||||
val vaultData = VaultData(
|
||||
cipherViewList = emptyList(),
|
||||
folderViewList = emptyList(),
|
||||
)
|
||||
|
||||
val actual = vaultData.toViewState()
|
||||
|
||||
assertEquals(
|
||||
VaultState.ViewState.NoItems,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user