diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/serializer/LocalDateTimeSerializer.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/serializer/LocalDateTimeSerializer.kt
index a0cb57568..a9c991182 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/serializer/LocalDateTimeSerializer.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/network/serializer/LocalDateTimeSerializer.kt
@@ -8,31 +8,24 @@ import kotlinx.serialization.encoding.Decoder
 import kotlinx.serialization.encoding.Encoder
 import java.time.LocalDateTime
 import java.time.format.DateTimeFormatter
-import java.time.format.DateTimeParseException
 
 /**
  * Used to serialize and deserialize [LocalDateTime].
  */
 class LocalDateTimeSerializer : KSerializer<LocalDateTime> {
-    private val localDateTimeFormatter =
-        DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SS'Z'")
-    private val localDateTimeFormatterNanoSeconds =
+    private val dateTimeFormatterDeserialization = DateTimeFormatter
+        .ofPattern("yyyy-MM-dd'T'HH:mm:ss.[SSSSSSS][SSSSSS][SSSSS][SSSS][SSS][SS][S]'Z'")
+    private val dateTimeFormatterSerialization =
         DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'")
     override val descriptor: SerialDescriptor
         get() = PrimitiveSerialDescriptor(serialName = "LocalDateTime", kind = PrimitiveKind.STRING)
 
     override fun deserialize(decoder: Decoder): LocalDateTime =
         decoder.decodeString().let { dateString ->
-            try {
-                LocalDateTime
-                    .parse(dateString, localDateTimeFormatter)
-            } catch (exception: DateTimeParseException) {
-                LocalDateTime
-                    .parse(dateString, localDateTimeFormatterNanoSeconds)
-            }
+            LocalDateTime.parse(dateString, dateTimeFormatterDeserialization)
         }
 
     override fun serialize(encoder: Encoder, value: LocalDateTime) {
-        encoder.encodeString(localDateTimeFormatter.format(value))
+        encoder.encodeString(dateTimeFormatterSerialization.format(value))
     }
 }
diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt
index b359acfe5..ab94758b0 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt
@@ -27,7 +27,12 @@ interface VaultSdkSource {
     /**
      * Decrypts a list of [Cipher]s returning a list of [CipherListView] wrapped in a [Result].
      */
-    suspend fun decryptCipherList(cipherList: List<Cipher>): Result<List<CipherListView>>
+    suspend fun decryptCipherListCollection(cipherList: List<Cipher>): Result<List<CipherListView>>
+
+    /**
+     * Decrypts a list of [Cipher]s returning a list of [CipherView] wrapped in a [Result].
+     */
+    suspend fun decryptCipherList(cipherList: List<Cipher>): Result<List<CipherView>>
 
     /**
      * Decrypts a [Folder] returning a [FolderView] wrapped in a [Result].
diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt
index 9d990b2d0..af23b61c1 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt
@@ -35,9 +35,14 @@ class VaultSdkSourceImpl(
     override suspend fun decryptCipher(cipher: Cipher): Result<CipherView> =
         runCatching { clientVault.ciphers().decrypt(cipher) }
 
-    override suspend fun decryptCipherList(cipherList: List<Cipher>): Result<List<CipherListView>> =
+    override suspend fun decryptCipherListCollection(
+        cipherList: List<Cipher>,
+    ): Result<List<CipherListView>> =
         runCatching { clientVault.ciphers().decryptList(cipherList) }
 
+    override suspend fun decryptCipherList(cipherList: List<Cipher>): Result<List<CipherView>> =
+        runCatching { cipherList.map { clientVault.ciphers().decrypt(it) } }
+
     override suspend fun decryptFolder(folder: Folder): Result<FolderView> =
         runCatching { clientVault.folders().decrypt(folder) }
 
diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt
index 5bf22129c..d58253cf6 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt
@@ -20,6 +20,10 @@ import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.update
 import kotlinx.coroutines.launch
 
@@ -37,6 +41,8 @@ class VaultRepositoryImpl constructor(
 
     private var syncJob: Job = Job().apply { complete() }
 
+    private var willSyncAfterUnlock = false
+
     private val vaultDataMutableStateFlow =
         MutableStateFlow<DataState<VaultData>>(DataState.Loading)
 
@@ -48,7 +54,7 @@ class VaultRepositoryImpl constructor(
     }
 
     override fun sync() {
-        if (!syncJob.isCompleted) return
+        if (!syncJob.isCompleted || willSyncAfterUnlock) return
         vaultDataMutableStateFlow.value.data?.let { data ->
             vaultDataMutableStateFlow.update {
                 DataState.Pending(data = data)
@@ -86,10 +92,16 @@ class VaultRepositoryImpl constructor(
     }
 
     override suspend fun unlockVaultAndSync(masterPassword: String): VaultUnlockResult {
-        return initializeCrypto(masterPassword = masterPassword)
-            .also { vaultUnlockedResult ->
-                if (vaultUnlockedResult is VaultUnlockResult.Success) sync()
+        return flow {
+            willSyncAfterUnlock = true
+            emit(initializeCrypto(masterPassword = masterPassword))
+        }
+            .onEach {
+                willSyncAfterUnlock = false
+                if (it is VaultUnlockResult.Success) sync()
             }
+            .onCompletion { willSyncAfterUnlock = false }
+            .first()
     }
 
     private fun storeUserKeyAndPrivateKey(
@@ -156,7 +168,7 @@ class VaultRepositoryImpl constructor(
                 onSuccess = { (decryptedCipherList, decryptedFolderList) ->
                     DataState.Loaded(
                         data = VaultData(
-                            cipherListViewList = decryptedCipherList,
+                            cipherViewList = decryptedCipherList,
                             folderViewList = decryptedFolderList,
                         ),
                     )
diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultData.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultData.kt
index e92504a7b..2d4f9d195 100644
--- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultData.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultData.kt
@@ -1,15 +1,15 @@
 package com.x8bit.bitwarden.data.vault.repository.model
 
-import com.bitwarden.core.CipherListView
+import com.bitwarden.core.CipherView
 import com.bitwarden.core.FolderView
 
 /**
  * Represents decrypted vault data.
  *
- * @param cipherListViewList List of decrypted ciphers.
+ * @param cipherViewList List of decrypted ciphers.
  * @param folderViewList List of decrypted folders.
  */
 data class VaultData(
-    val cipherListViewList: List<CipherListView>,
+    val cipherViewList: List<CipherView>,
     val folderViewList: List<FolderView>,
 )
diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt
index 5c79c293b..95c3c17bb 100644
--- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt
+++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt
@@ -7,12 +7,16 @@ import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.viewModelScope
 import com.x8bit.bitwarden.R
 import com.x8bit.bitwarden.data.auth.repository.model.AccountSummary
+import com.x8bit.bitwarden.data.platform.repository.model.DataState
+import com.x8bit.bitwarden.data.vault.repository.VaultRepository
+import com.x8bit.bitwarden.data.vault.repository.model.VaultData
 import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
 import com.x8bit.bitwarden.ui.platform.base.util.Text
 import com.x8bit.bitwarden.ui.platform.base.util.asText
 import com.x8bit.bitwarden.ui.platform.base.util.concat
 import com.x8bit.bitwarden.ui.platform.base.util.hexToColor
 import com.x8bit.bitwarden.ui.vault.feature.vault.util.initials
+import com.x8bit.bitwarden.ui.vault.feature.vault.util.toViewState
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.collections.immutable.persistentListOf
 import kotlinx.coroutines.delay
@@ -32,8 +36,8 @@ private const val KEY_STATE = "state"
 @HiltViewModel
 class VaultViewModel @Inject constructor(
     private val savedStateHandle: SavedStateHandle,
+    vaultRepository: VaultRepository,
 ) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
-    // TODO retrieve this from the data layer BIT-205
     initialState = savedStateHandle[KEY_STATE] ?: VaultState(
         initials = activeAccountSummary.initials,
         avatarColorString = activeAccountSummary.avatarColorHex,
@@ -46,30 +50,18 @@ class VaultViewModel @Inject constructor(
         stateFlow
             .onEach { savedStateHandle[KEY_STATE] = it }
             .launchIn(viewModelScope)
-
+        vaultRepository
+            .vaultDataStateFlow
+            .onEach { sendAction(VaultAction.Internal.VaultDataReceive(vaultData = it)) }
+            .launchIn(viewModelScope)
+        // TODO remove this block once vault unlocked is implemented in BIT-1082
         viewModelScope.launch {
-            // TODO will need to load actual vault items BIT-205
             @Suppress("MagicNumber")
-            delay(2000)
-            mutableStateFlow.update { currentState ->
-                currentState.copy(
-                    viewState = VaultState.ViewState.Content(
-                        loginItemsCount = 0,
-                        cardItemsCount = 0,
-                        identityItemsCount = 0,
-                        secureNoteItemsCount = 0,
-                        favoriteItems = emptyList(),
-                        folderItems = listOf(
-                            VaultState.ViewState.FolderItem(
-                                id = null,
-                                name = R.string.folder_none.asText(),
-                                itemCount = 0,
-                            ),
-                        ),
-                        // TODO Take into account the max threshold of no folder as well as the
-                        //  case where it is empty, in which case, no folder is a folder. BIT-205
-                        noFolderItems = emptyList(),
-                        trashItemsCount = 0,
+            delay(5000)
+            if (vaultRepository.vaultDataStateFlow.value == DataState.Loading) {
+                sendAction(
+                    VaultAction.Internal.VaultDataReceive(
+                        DataState.Error(error = IllegalStateException()),
                     ),
                 )
             }
@@ -78,17 +70,18 @@ class VaultViewModel @Inject constructor(
 
     override fun handleAction(action: VaultAction) {
         when (action) {
-            VaultAction.AddItemClick -> handleAddItemClick()
-            VaultAction.CardGroupClick -> handleCardClick()
+            is VaultAction.AddItemClick -> handleAddItemClick()
+            is VaultAction.CardGroupClick -> handleCardClick()
             is VaultAction.FolderClick -> handleFolderItemClick(action)
-            VaultAction.IdentityGroupClick -> handleIdentityClick()
-            VaultAction.LoginGroupClick -> handleLoginClick()
-            VaultAction.SearchIconClick -> handleSearchIconClick()
+            is VaultAction.IdentityGroupClick -> handleIdentityClick()
+            is VaultAction.LoginGroupClick -> handleLoginClick()
+            is VaultAction.SearchIconClick -> handleSearchIconClick()
             is VaultAction.AccountSwitchClick -> handleAccountSwitchClick(action)
-            VaultAction.AddAccountClick -> handleAddAccountClick()
-            VaultAction.SecureNoteGroupClick -> handleSecureNoteClick()
-            VaultAction.TrashClick -> handleTrashClick()
+            is VaultAction.AddAccountClick -> handleAddAccountClick()
+            is VaultAction.SecureNoteGroupClick -> handleSecureNoteClick()
+            is VaultAction.TrashClick -> handleTrashClick()
             is VaultAction.VaultItemClick -> handleVaultItemClick(action)
+            is VaultAction.Internal.VaultDataReceive -> handleVaultDataReceive(action)
         }
     }
 
@@ -150,6 +143,41 @@ class VaultViewModel @Inject constructor(
     private fun handleVaultItemClick(action: VaultAction.VaultItemClick) {
         sendEvent(VaultEvent.NavigateToVaultItem(action.vaultItem.id))
     }
+
+    private fun handleVaultDataReceive(action: VaultAction.Internal.VaultDataReceive) {
+        when (val vaultData = action.vaultData) {
+            is DataState.Error -> vaultErrorReceive(vaultData = vaultData)
+            is DataState.Loaded -> vaultLoadedReceive(vaultData = vaultData)
+            is DataState.Loading -> vaultLoadingReceive()
+            is DataState.NoNetwork -> vaultNoNetworkReceive(vaultData = vaultData)
+            is DataState.Pending -> vaultPendingReceive(vaultData = vaultData)
+        }
+    }
+    private fun vaultErrorReceive(vaultData: DataState.Error<VaultData>) {
+        // TODO update state to error state BIT-1157
+        mutableStateFlow.update { it.copy(viewState = VaultState.ViewState.NoItems) }
+        sendEvent(VaultEvent.ShowToast(message = "Vault error state not yet implemented"))
+    }
+
+    private fun vaultLoadedReceive(vaultData: DataState.Loaded<VaultData>) {
+        mutableStateFlow.update { it.copy(viewState = vaultData.data.toViewState()) }
+    }
+
+    private fun vaultLoadingReceive() {
+        mutableStateFlow.update { it.copy(viewState = VaultState.ViewState.Loading) }
+    }
+
+    private fun vaultNoNetworkReceive(vaultData: DataState.NoNetwork<VaultData>) {
+        // TODO update state to no network state BIT-1158
+        mutableStateFlow.update { it.copy(viewState = VaultState.ViewState.NoItems) }
+        sendEvent(VaultEvent.ShowToast(message = "Vault no network state not yet implemented"))
+    }
+
+    private fun vaultPendingReceive(vaultData: DataState.Pending<VaultData>) {
+        // TODO update state to refresh state BIT-505
+        mutableStateFlow.update { it.copy(viewState = vaultData.data.toViewState()) }
+        sendEvent(VaultEvent.ShowToast(message = "Refreshing"))
+    }
     //endregion VaultAction Handlers
 }
 
@@ -499,4 +527,17 @@ sealed class VaultAction {
      * User clicked the trash button.
      */
     data object TrashClick : VaultAction()
+
+    /**
+     * Models actions that the [VaultViewModel] itself might send.
+     */
+    sealed class Internal : VaultAction() {
+
+        /**
+         * Indicates a vault data was received.
+         */
+        data class VaultDataReceive(
+            val vaultData: DataState<VaultData>,
+        ) : Internal()
+    }
 }
diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt
new file mode 100644
index 000000000..66316c1d2
--- /dev/null
+++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt
@@ -0,0 +1,70 @@
+package com.x8bit.bitwarden.ui.vault.feature.vault.util
+
+import com.bitwarden.core.CipherType
+import com.bitwarden.core.CipherView
+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
+
+/**
+ * Transforms a [CipherView] into a [VaultState.ViewState.VaultItem].
+ */
+@Suppress("MagicNumber")
+private fun CipherView.toVaultItem(): VaultState.ViewState.VaultItem =
+    when (type) {
+        CipherType.LOGIN -> VaultState.ViewState.VaultItem.Login(
+            id = id.toString(),
+            name = name.asText(),
+            username = login?.username?.asText(),
+        )
+
+        CipherType.SECURE_NOTE -> VaultState.ViewState.VaultItem.SecureNote(
+            id = id.toString(),
+            name = name.asText(),
+        )
+
+        CipherType.CARD -> VaultState.ViewState.VaultItem.Card(
+            id = id.toString(),
+            name = name.asText(),
+            brand = card?.brand?.asText(),
+            lastFourDigits = card?.number
+                ?.takeLast(4)
+                ?.asText(),
+        )
+
+        CipherType.IDENTITY -> VaultState.ViewState.VaultItem.Identity(
+            id = id.toString(),
+            name = name.asText(),
+            firstName = identity?.firstName?.asText(),
+        )
+    }
+
+/**
+ * Transforms [VaultData] into [VaultState.ViewState].
+ */
+fun VaultData.toViewState(): VaultState.ViewState =
+    if (cipherViewList.isEmpty() && folderViewList.isEmpty()) {
+        VaultState.ViewState.NoItems
+    } else {
+        VaultState.ViewState.Content(
+            loginItemsCount = cipherViewList.count { it.type == CipherType.LOGIN },
+            cardItemsCount = cipherViewList.count { it.type == CipherType.CARD },
+            identityItemsCount = cipherViewList.count { it.type == CipherType.IDENTITY },
+            secureNoteItemsCount = cipherViewList.count { it.type == CipherType.SECURE_NOTE },
+            favoriteItems = cipherViewList
+                .filter { it.favorite }
+                .map { it.toVaultItem() },
+            folderItems = folderViewList.map { folderView ->
+                VaultState.ViewState.FolderItem(
+                    id = folderView.id,
+                    name = folderView.name.asText(),
+                    itemCount = cipherViewList.count { folderView.id == it.folderId },
+                )
+            },
+            noFolderItems = cipherViewList
+                .filter { it.folderId.isNullOrBlank() }
+                .map { it.toVaultItem() },
+            // TODO need to populate trash item count in BIT-969
+            trashItemsCount = 0,
+        )
+    }
diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/network/serializer/LocalDateTimeSerializerTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/network/serializer/LocalDateTimeSerializerTest.kt
index 9c11150f2..58ccb2540 100644
--- a/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/network/serializer/LocalDateTimeSerializerTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/network/serializer/LocalDateTimeSerializerTest.kt
@@ -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"
                 }
                 """,
             ),
diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt
index bb1cdfa1a..1c5f026d7 100644
--- a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt
@@ -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,
             )
         }
     }
diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherViewUtil.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherViewUtil.kt
new file mode 100644
index 000000000..9eb1ea7eb
--- /dev/null
+++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherViewUtil.kt
@@ -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,
+    )
diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt
index 86db11af1..48e4ffc2a 100644
--- a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt
@@ -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)),
                         ),
                     ),
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt
index f1f9b7fb6..247755b7a 100644
--- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt
@@ -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,
+    )
diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt
new file mode 100644
index 000000000..8434a4059
--- /dev/null
+++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt
@@ -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,
+        )
+    }
+}