mirror of
https://github.com/bitwarden/android.git
synced 2026-06-04 19:56:39 -05:00
BIT-598: Create initial vault database (#399)
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package com.x8bit.bitwarden.data.util
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.di.PlatformNetworkModule
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
||||
/**
|
||||
* Helper method for comparing JSON string and ignoring the formatting.
|
||||
*/
|
||||
fun assertJsonEquals(
|
||||
expected: String,
|
||||
actual: String,
|
||||
json: Json = PlatformNetworkModule.providesJson(),
|
||||
) {
|
||||
assertEquals(
|
||||
json.parseToJsonElement(expected),
|
||||
json.parseToJsonElement(actual),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.x8bit.bitwarden.data.vault.datasource.disk
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.di.PlatformNetworkModule
|
||||
import com.x8bit.bitwarden.data.util.assertJsonEquals
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.FakeCiphersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.CipherEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCipher
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultDiskSourceTest {
|
||||
|
||||
private val json = PlatformNetworkModule.providesJson()
|
||||
private lateinit var ciphersDao: FakeCiphersDao
|
||||
|
||||
private lateinit var vaultDiskSource: VaultDiskSource
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
ciphersDao = FakeCiphersDao()
|
||||
vaultDiskSource = VaultDiskSourceImpl(
|
||||
ciphersDao = ciphersDao,
|
||||
json = json,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getCiphers should emit all dao updates`() = runTest {
|
||||
val cipherEntities = listOf(CIPHER_ENTITY)
|
||||
val ciphers = listOf(CIPHER_1)
|
||||
|
||||
vaultDiskSource
|
||||
.getCiphers(USER_ID)
|
||||
.test {
|
||||
assertEquals(emptyList<SyncResponseJson.Cipher>(), awaitItem())
|
||||
ciphersDao.insertCiphers(cipherEntities)
|
||||
assertEquals(ciphers, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `replaceVaultData should clear the dao and insert the encoded ciphers`() = runTest {
|
||||
assertEquals(ciphersDao.storedCiphers, emptyList<CipherEntity>())
|
||||
|
||||
vaultDiskSource.replaceVaultData(USER_ID, VAULT_DATA)
|
||||
|
||||
assertEquals(1, ciphersDao.storedCiphers.size)
|
||||
val storedEntity = ciphersDao.storedCiphers.first()
|
||||
// We cannot compare the JSON strings directly because of formatting differences
|
||||
// So we split that off into its own assertion.
|
||||
assertEquals(CIPHER_ENTITY.copy(cipherJson = ""), storedEntity.copy(cipherJson = ""))
|
||||
assertJsonEquals(CIPHER_ENTITY.cipherJson, storedEntity.cipherJson)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deleteVaultData should remove all ciphers matching the user ID`() = runTest {
|
||||
assertFalse(ciphersDao.deleteCiphersCalled)
|
||||
vaultDiskSource.deleteVaultData(USER_ID)
|
||||
assertTrue(ciphersDao.deleteCiphersCalled)
|
||||
}
|
||||
}
|
||||
|
||||
private const val USER_ID: String = "test_user_id"
|
||||
|
||||
private val CIPHER_1: SyncResponseJson.Cipher = createMockCipher(1)
|
||||
|
||||
private val VAULT_DATA: SyncResponseJson = SyncResponseJson(
|
||||
folders = null,
|
||||
collections = null,
|
||||
profile = mockk<SyncResponseJson.Profile> {
|
||||
every { id } returns USER_ID
|
||||
},
|
||||
ciphers = listOf(CIPHER_1),
|
||||
policies = null,
|
||||
domains = SyncResponseJson.Domains(
|
||||
globalEquivalentDomains = null,
|
||||
equivalentDomains = null,
|
||||
),
|
||||
sends = null,
|
||||
)
|
||||
|
||||
private const val CIPHER_JSON = """
|
||||
{
|
||||
"notes": "mockNotes-1",
|
||||
"attachments": [
|
||||
{
|
||||
"fileName": "mockFileName-1",
|
||||
"size": 1,
|
||||
"sizeName": "mockSizeName-1",
|
||||
"id": "mockId-1",
|
||||
"url": "mockUrl-1",
|
||||
"key": "mockKey-1"
|
||||
}
|
||||
],
|
||||
"organizationUseTotp": false,
|
||||
"reprompt": 0,
|
||||
"edit": false,
|
||||
"passwordHistory": [
|
||||
{
|
||||
"password": "mockPassword-1",
|
||||
"lastUsedDate": "2023-10-27T12:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"revisionDate": "2023-10-27T12:00:00.000Z",
|
||||
"type": 1,
|
||||
"login": {
|
||||
"uris": [
|
||||
{
|
||||
"match": 1,
|
||||
"uri": "mockUri-1"
|
||||
}
|
||||
],
|
||||
"totp": "mockTotp-1",
|
||||
"password": "mockPassword-1",
|
||||
"passwordRevisionDate": "2023-10-27T12:00:00.000Z",
|
||||
"autofillOnPageLoad": false,
|
||||
"uri": "mockUri-1",
|
||||
"username": "mockUsername-1"
|
||||
},
|
||||
"creationDate": "2023-10-27T12:00:00.000Z",
|
||||
"secureNote": {
|
||||
"type": 0
|
||||
},
|
||||
"folderId": "mockFolderId-1",
|
||||
"organizationId": "mockOrganizationId-1",
|
||||
"deletedDate": "2023-10-27T12:00:00.000Z",
|
||||
"identity": {
|
||||
"passportNumber": "mockPassportNumber-1",
|
||||
"lastName": "mockLastName-1",
|
||||
"address3": "mockAddress3-1",
|
||||
"address2": "mockAddress2-1",
|
||||
"city": "mockCity-1",
|
||||
"country": "mockCountry-1",
|
||||
"address1": "mockAddress1-1",
|
||||
"postalCode": "mockPostalCode-1",
|
||||
"title": "mockTitle-1",
|
||||
"ssn": "mockSsn-1",
|
||||
"firstName": "mockFirstName-1",
|
||||
"phone": "mockPhone-1",
|
||||
"middleName": "mockMiddleName-1",
|
||||
"company": "mockCompany-1",
|
||||
"licenseNumber": "mockLicenseNumber-1",
|
||||
"state": "mockState-1",
|
||||
"email": "mockEmail-1",
|
||||
"username": "mockUsername-1"
|
||||
},
|
||||
"collectionIds": [
|
||||
"mockCollectionId-1"
|
||||
],
|
||||
"name": "mockName-1",
|
||||
"id": "mockId-1",
|
||||
"fields": [
|
||||
{
|
||||
"linkedId": 100,
|
||||
"name": "mockName-1",
|
||||
"type": 1,
|
||||
"value": "mockValue-1"
|
||||
}
|
||||
],
|
||||
"viewPassword": false,
|
||||
"favorite": false,
|
||||
"card": {
|
||||
"number": "mockNumber-1",
|
||||
"expMonth": "mockExpMonth-1",
|
||||
"code": "mockCode-1",
|
||||
"expYear": "mockExpirationYear-1",
|
||||
"cardholderName": "mockCardholderName-1",
|
||||
"brand": "mockBrand-1"
|
||||
},
|
||||
"key": "mockKey-1"
|
||||
}
|
||||
"""
|
||||
|
||||
private val CIPHER_ENTITY = CipherEntity(
|
||||
id = "mockId-1",
|
||||
userId = USER_ID,
|
||||
cipherType = "1",
|
||||
cipherJson = CIPHER_JSON,
|
||||
)
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.x8bit.bitwarden.data.vault.datasource.disk.dao
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.CipherEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class FakeCiphersDao : CiphersDao {
|
||||
|
||||
val storedCiphers = mutableListOf<CipherEntity>()
|
||||
|
||||
var deleteCiphersCalled: Boolean = false
|
||||
|
||||
private val ciphersFlow = MutableSharedFlow<List<CipherEntity>>(
|
||||
replay = 1,
|
||||
extraBufferCapacity = Int.MAX_VALUE,
|
||||
)
|
||||
|
||||
init {
|
||||
ciphersFlow.tryEmit(emptyList())
|
||||
}
|
||||
|
||||
override suspend fun deleteAllCiphers(userId: String) {
|
||||
deleteCiphersCalled = true
|
||||
storedCiphers.removeAll { it.userId == userId }
|
||||
ciphersFlow.tryEmit(storedCiphers.toList())
|
||||
}
|
||||
|
||||
override fun getAllCiphers(userId: String): Flow<List<CipherEntity>> =
|
||||
ciphersFlow.map { ciphers -> ciphers.filter { it.userId == userId } }
|
||||
|
||||
override suspend fun insertCiphers(ciphers: List<CipherEntity>) {
|
||||
storedCiphers.addAll(ciphers)
|
||||
ciphersFlow.tryEmit(ciphers.toList())
|
||||
}
|
||||
|
||||
override suspend fun replaceAllCiphers(userId: String, ciphers: List<CipherEntity>) {
|
||||
storedCiphers.removeAll { it.userId == userId }
|
||||
storedCiphers.addAll(ciphers)
|
||||
ciphersFlow.tryEmit(ciphers.toList())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user