BIT-1128: Expose vault data (#227)

This commit is contained in:
Ramsey Smith
2023-11-09 16:16:06 -07:00
committed by GitHub
parent 22e30b5eac
commit b9d60e8a04
13 changed files with 507 additions and 48 deletions

View File

@@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.platform.datasource.network.util
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
import java.net.UnknownHostException
class NetworkUtilsTest {
@Test
@@ -40,4 +41,20 @@ class NetworkUtilsTest {
"*.*".base64UrlDecodeOrNull(),
)
}
@Test
fun `isNoConnectionError should return return true for UnknownHostException`() {
assertEquals(
true,
UnknownHostException().isNoConnectionError(),
)
}
@Test
fun `isNoConnectionError should return return false for not UnknownHostException`() {
assertEquals(
false,
IllegalStateException().isNoConnectionError(),
)
}
}

View File

@@ -0,0 +1,35 @@
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
import com.bitwarden.core.CipherListView
import com.bitwarden.core.CipherRepromptType
import com.bitwarden.core.CipherType
import java.time.LocalDateTime
import java.time.ZoneOffset
/**
* Create a mock [CipherListView] with a given [number].
*/
fun createMockCipherListView(number: Int): CipherListView =
CipherListView(
id = "mockId-$number",
organizationId = "mockOrganizationId-$number",
folderId = "mockFolderId-$number",
collectionIds = listOf("mockCollectionId-$number"),
name = "mockName-$number",
type = CipherType.LOGIN,
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 = 1U,
favorite = false,
reprompt = CipherRepromptType.NONE,
edit = false,
viewPassword = false,
subTitle = "",
)

View File

@@ -0,0 +1,17 @@
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
import com.bitwarden.core.FolderView
import java.time.LocalDateTime
import java.time.ZoneOffset
/**
* Create a mock [FolderView] with a given [number].
*/
fun createMockFolderView(number: Int): FolderView =
FolderView(
id = "mockId-$number",
name = "mockName-$number",
revisionDate = LocalDateTime
.parse("2023-10-27T12:00:00")
.toInstant(ZoneOffset.UTC),
)

View File

@@ -1,4 +1,4 @@
package com.x8bit.bitwarden.data.vault.datasource.sdk
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
import com.bitwarden.core.Attachment
import com.bitwarden.core.Card

View File

@@ -1,4 +1,4 @@
package com.x8bit.bitwarden.data.vault.datasource.sdk
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
import com.bitwarden.core.Folder
import java.time.LocalDateTime

View File

@@ -1,5 +1,6 @@
package com.x8bit.bitwarden.data.vault.repository
import app.cash.turbine.test
import com.bitwarden.core.InitCryptoRequest
import com.bitwarden.core.Kdf
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
@@ -8,12 +9,18 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
import com.x8bit.bitwarden.data.auth.util.KdfParamsConstants.DEFAULT_PBKDF2_ITERATIONS
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.repository.model.DataState
import com.x8bit.bitwarden.data.platform.util.asFailure
import com.x8bit.bitwarden.data.platform.util.asSuccess
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSyncResponse
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCipher
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkFolder
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.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
@@ -21,6 +28,7 @@ import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import java.net.UnknownHostException
class VaultRepositoryTest {
@@ -36,27 +44,228 @@ class VaultRepositoryTest {
)
@Test
fun `sync when syncService Success should update AuthDiskSource with keys`() = runTest {
coEvery { syncService.sync() } returns Result.success(createMockSyncResponse(number = 1))
coEvery {
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
} returns mockk()
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns mockk()
fakeAuthDiskSource.userState = MOCK_USER_STATE
fun `sync with syncService Success should update AuthDiskSource and vaultDataStateFlow`() =
runTest {
coEvery {
syncService.sync()
} returns Result.success(createMockSyncResponse(number = 1))
coEvery {
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
fakeAuthDiskSource.userState = MOCK_USER_STATE
vaultRepository.sync()
vaultRepository.sync()
fakeAuthDiskSource.assertUserKey(
userId = "mockUserId",
userKey = "mockKey-1",
)
fakeAuthDiskSource.assertPrivateKey(
userId = "mockUserId",
privateKey = "mockPrivateKey-1",
)
}
fakeAuthDiskSource.assertUserKey(
userId = "mockUserId",
userKey = "mockKey-1",
)
fakeAuthDiskSource.assertPrivateKey(
userId = "mockUserId",
privateKey = "mockPrivateKey-1",
)
assertEquals(
DataState.Loaded(
data = VaultData(
cipherListViewList = listOf(createMockCipherListView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
vaultRepository.vaultDataStateFlow.value,
)
}
@Test
fun `sync with data should update vaultDataStateFlow to Pending before service sync`() =
runTest {
coEvery {
syncService.sync()
} returns Result.success(createMockSyncResponse(number = 1))
coEvery {
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
fakeAuthDiskSource.userState = MOCK_USER_STATE
vaultRepository.vaultDataStateFlow.test {
assertEquals(
DataState.Loading,
awaitItem(),
)
vaultRepository.sync()
assertEquals(
DataState.Loaded(
data = VaultData(
cipherListViewList = listOf(createMockCipherListView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
awaitItem(),
)
vaultRepository.sync()
assertEquals(
DataState.Pending(
data = VaultData(
cipherListViewList = listOf(createMockCipherListView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
awaitItem(),
)
assertEquals(
DataState.Loaded(
data = VaultData(
cipherListViewList = listOf(createMockCipherListView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
awaitItem(),
)
}
}
@Test
fun `sync with decryptCipherList Failure should update vaultDataStateFlow with Error`() =
runTest {
val mockException = IllegalStateException()
coEvery {
syncService.sync()
} returns Result.success(createMockSyncResponse(number = 1))
coEvery {
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
} returns mockException.asFailure()
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
fakeAuthDiskSource.userState = MOCK_USER_STATE
vaultRepository.sync()
assertEquals(
DataState.Error<VaultData>(error = mockException),
vaultRepository.vaultDataStateFlow.value,
)
}
@Test
fun `sync with decryptFolderList Failure should update vaultDataStateFlow with Error`() =
runTest {
val mockException = IllegalStateException()
coEvery {
syncService.sync()
} returns Result.success(createMockSyncResponse(number = 1))
coEvery {
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns mockException.asFailure()
fakeAuthDiskSource.userState = MOCK_USER_STATE
vaultRepository.sync()
assertEquals(
DataState.Error<VaultData>(error = mockException),
vaultRepository.vaultDataStateFlow.value,
)
}
@Test
fun `sync with syncService Failure should update vaultDataStateFlow with an Error`() =
runTest {
val mockException = IllegalStateException(
"sad",
)
coEvery {
syncService.sync()
} returns mockException.asFailure()
vaultRepository.sync()
assertEquals(
DataState.Error(
error = mockException,
data = null,
),
vaultRepository.vaultDataStateFlow.value,
)
}
@Test
fun `sync with NoNetwork should update vaultDataStateFlow to NoNetwork`() =
runTest {
coEvery {
syncService.sync()
} returns UnknownHostException().asFailure()
vaultRepository.sync()
assertEquals(
DataState.NoNetwork(
data = null,
),
vaultRepository.vaultDataStateFlow.value,
)
}
@Test
fun `sync with NoNetwork data should update vaultDataStateFlow to NoNetwork with data`() =
runTest {
coEvery {
syncService.sync()
} returns Result.success(createMockSyncResponse(number = 1))
coEvery {
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
fakeAuthDiskSource.userState = MOCK_USER_STATE
vaultRepository.vaultDataStateFlow.test {
assertEquals(
DataState.Loading,
awaitItem(),
)
vaultRepository.sync()
assertEquals(
DataState.Loaded(
data = VaultData(
cipherListViewList = listOf(createMockCipherListView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
awaitItem(),
)
coEvery {
syncService.sync()
} returns UnknownHostException().asFailure()
vaultRepository.sync()
assertEquals(
DataState.Pending(
data = VaultData(
cipherListViewList = listOf(createMockCipherListView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
awaitItem(),
)
assertEquals(
DataState.NoNetwork(
data = VaultData(
cipherListViewList = listOf(createMockCipherListView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
awaitItem(),
)
}
}
@Test
fun `unlockVaultAndSync with initializeCrypto Success should sync and return Success`() =
@@ -66,10 +275,10 @@ class VaultRepositoryTest {
} returns Result.success(createMockSyncResponse(number = 1))
coEvery {
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
} returns mockk()
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns mockk()
} returns listOf(createMockFolderView(number = 1)).asSuccess()
fakeAuthDiskSource.storePrivateKey(
userId = "mockUserId",
privateKey = "mockPrivateKey-1",
@@ -233,6 +442,45 @@ class VaultRepositoryTest {
result,
)
}
@Test
fun `clearVaultData should update the vaultDataStateFlow to Loading`() =
runTest {
coEvery {
syncService.sync()
} returns Result.success(createMockSyncResponse(number = 1))
coEvery {
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
fakeAuthDiskSource.userState = MOCK_USER_STATE
vaultRepository.vaultDataStateFlow.test {
assertEquals(
DataState.Loading,
awaitItem(),
)
vaultRepository.sync()
assertEquals(
DataState.Loaded(
data = VaultData(
cipherListViewList = listOf(createMockCipherListView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
awaitItem(),
)
vaultRepository.clearVaultData()
assertEquals(
DataState.Loading,
awaitItem(),
)
}
}
}
private val MOCK_USER_STATE = UserStateJson(

View File

@@ -17,15 +17,15 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockLogin
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPasswordHistory
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSecureNote
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockUri
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkAttachment
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCard
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCipher
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkField
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkIdentity
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkLogin
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkPasswordHistory
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkSecureNote
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkUri
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkAttachment
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCard
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipher
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkField
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkIdentity
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkLogin
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkPasswordHistory
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkSecureNote
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkUri
import org.junit.Assert.assertEquals
import org.junit.Test

View File

@@ -1,7 +1,7 @@
package com.x8bit.bitwarden.data.vault.repository.util
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFolder
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkFolder
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFolder
import org.junit.Assert.assertEquals
import org.junit.Test