mirror of
https://github.com/bitwarden/android.git
synced 2025-12-10 09:56:45 -06:00
PM-1908: Push notifications for non-active accounts prompt for future sync
This commit is contained in:
parent
4a874668f2
commit
4641fd8709
@ -34,6 +34,8 @@ import java.time.Clock
|
||||
import java.time.ZoneOffset
|
||||
import java.time.ZonedDateTime
|
||||
import javax.inject.Inject
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.toJavaDuration
|
||||
@ -134,7 +136,6 @@ class PushManagerImpl @Inject constructor(
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
private fun onMessageReceived(notification: BitwardenNotification) {
|
||||
if (authDiskSource.uniqueAppId == notification.contextId) return
|
||||
val userId = activeUserId ?: return
|
||||
Timber.d("Push Notification Received: ${notification.notificationType}")
|
||||
|
||||
when (val type = notification.notificationType) {
|
||||
@ -179,11 +180,13 @@ class PushManagerImpl @Inject constructor(
|
||||
.decodeFromString<NotificationPayload.SyncCipherNotification>(
|
||||
string = notification.payload,
|
||||
)
|
||||
.takeIf { isLoggedIn(userId) && it.userMatchesNotification(userId) }
|
||||
?.takeIf { it.cipherId != null && it.revisionDate != null }
|
||||
.takeIf {
|
||||
it.cipherId != null && it.revisionDate != null && isLoggedIn(it.userId)
|
||||
}
|
||||
?.let {
|
||||
mutableSyncCipherUpsertSharedFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = requireNotNull(it.userId),
|
||||
cipherId = requireNotNull(it.cipherId),
|
||||
revisionDate = requireNotNull(it.revisionDate),
|
||||
organizationId = it.organizationId,
|
||||
@ -228,11 +231,13 @@ class PushManagerImpl @Inject constructor(
|
||||
.decodeFromString<NotificationPayload.SyncFolderNotification>(
|
||||
string = notification.payload,
|
||||
)
|
||||
.takeIf { isLoggedIn(userId) && it.userMatchesNotification(userId) }
|
||||
?.takeIf { it.folderId != null && it.revisionDate != null }
|
||||
.takeIf {
|
||||
it.folderId != null && it.revisionDate != null && isLoggedIn(it.userId)
|
||||
}
|
||||
?.let {
|
||||
mutableSyncFolderUpsertSharedFlow.tryEmit(
|
||||
SyncFolderUpsertData(
|
||||
userId = requireNotNull(it.userId),
|
||||
folderId = requireNotNull(it.folderId),
|
||||
revisionDate = requireNotNull(it.revisionDate),
|
||||
isUpdate = type == NotificationType.SYNC_FOLDER_UPDATE,
|
||||
@ -273,11 +278,13 @@ class PushManagerImpl @Inject constructor(
|
||||
.decodeFromString<NotificationPayload.SyncSendNotification>(
|
||||
string = notification.payload,
|
||||
)
|
||||
.takeIf { isLoggedIn(userId) && it.userMatchesNotification(userId) }
|
||||
?.takeIf { it.sendId != null && it.revisionDate != null }
|
||||
.takeIf {
|
||||
it.sendId != null && it.revisionDate != null && isLoggedIn(it.userId)
|
||||
}
|
||||
?.let {
|
||||
mutableSyncSendUpsertSharedFlow.tryEmit(
|
||||
SyncSendUpsertData(
|
||||
userId = requireNotNull(it.userId),
|
||||
sendId = requireNotNull(it.sendId),
|
||||
revisionDate = requireNotNull(it.revisionDate),
|
||||
isUpdate = type == NotificationType.SYNC_SEND_UPDATE,
|
||||
@ -361,11 +368,11 @@ class PushManagerImpl @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
private fun isLoggedIn(
|
||||
userId: String,
|
||||
): Boolean = authDiskSource.getAccountTokens(userId)?.isLoggedIn == true
|
||||
userId: String?,
|
||||
): Boolean {
|
||||
contract { returns(true) implies (userId != null) }
|
||||
return userId?.let { authDiskSource.getAccountTokens(it) }?.isLoggedIn == true
|
||||
}
|
||||
|
||||
private fun NotificationPayload.userMatchesNotification(userId: String): Boolean {
|
||||
return this.userId != null && this.userId == userId
|
||||
}
|
||||
|
||||
@ -5,12 +5,14 @@ import java.time.ZonedDateTime
|
||||
/**
|
||||
* Required data for sync cipher upsert operations.
|
||||
*
|
||||
* @property userId The user ID associated with this update.
|
||||
* @property cipherId The cipher ID.
|
||||
* @property revisionDate The cipher's revision date. This is used to determine if the local copy of
|
||||
* the cipher is out-of-date.
|
||||
* @property isUpdate Whether or not this is an update of an existing cipher.
|
||||
*/
|
||||
data class SyncCipherUpsertData(
|
||||
val userId: String,
|
||||
val cipherId: String,
|
||||
val revisionDate: ZonedDateTime,
|
||||
val organizationId: String?,
|
||||
|
||||
@ -5,12 +5,14 @@ import java.time.ZonedDateTime
|
||||
/**
|
||||
* Required data for sync folder upsert operations.
|
||||
*
|
||||
* @property userId The user ID associated with this update.
|
||||
* @property folderId The folder ID.
|
||||
* @property revisionDate The folder's revision date. This is used to determine if the local copy of
|
||||
* the folder is out-of-date.
|
||||
* @property isUpdate Whether or not this is an update of an existing folder.
|
||||
*/
|
||||
data class SyncFolderUpsertData(
|
||||
val userId: String,
|
||||
val folderId: String,
|
||||
val revisionDate: ZonedDateTime,
|
||||
val isUpdate: Boolean,
|
||||
|
||||
@ -5,12 +5,14 @@ import java.time.ZonedDateTime
|
||||
/**
|
||||
* Required data for sync send upsert operations.
|
||||
*
|
||||
* @property userId The user ID associated with this update.
|
||||
* @property sendId The send ID.
|
||||
* @property revisionDate The send's revision date. This is used to determine if the local copy of
|
||||
* the send is out-of-date.
|
||||
* @property isUpdate Whether or not this is an update of an existing send.
|
||||
*/
|
||||
data class SyncSendUpsertData(
|
||||
val userId: String,
|
||||
val sendId: String,
|
||||
val revisionDate: ZonedDateTime,
|
||||
val isUpdate: Boolean,
|
||||
|
||||
@ -17,6 +17,7 @@ import com.bitwarden.vault.AttachmentView
|
||||
import com.bitwarden.vault.CipherView
|
||||
import com.bitwarden.vault.EncryptionContext
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
|
||||
import com.x8bit.bitwarden.data.platform.manager.PushManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.ReviewPromptManager
|
||||
@ -53,6 +54,7 @@ import java.time.Clock
|
||||
class CipherManagerImpl(
|
||||
private val fileManager: FileManager,
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
private val settingsDiskSource: SettingsDiskSource,
|
||||
private val ciphersService: CiphersService,
|
||||
private val vaultDiskSource: VaultDiskSource,
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
@ -689,7 +691,7 @@ class CipherManagerImpl(
|
||||
* for now.
|
||||
*/
|
||||
private suspend fun syncCipherIfNecessary(syncCipherUpsertData: SyncCipherUpsertData) {
|
||||
val userId = activeUserId ?: return
|
||||
val userId = syncCipherUpsertData.userId
|
||||
val cipherId = syncCipherUpsertData.cipherId
|
||||
val organizationId = syncCipherUpsertData.organizationId
|
||||
val collectionIds = syncCipherUpsertData.collectionIds
|
||||
@ -732,6 +734,12 @@ class CipherManagerImpl(
|
||||
}
|
||||
|
||||
if (!shouldUpdate) return
|
||||
if (activeUserId != userId) {
|
||||
// We cannot update right now since the accounts do not match, so we will
|
||||
// do a full-sync on the next check.
|
||||
settingsDiskSource.storeLastSyncTime(userId = userId, lastSyncTime = null)
|
||||
return
|
||||
}
|
||||
|
||||
ciphersService
|
||||
.getCipher(cipherId = cipherId)
|
||||
|
||||
@ -6,6 +6,7 @@ import com.bitwarden.network.model.UpdateFolderResponseJson
|
||||
import com.bitwarden.network.service.FolderService
|
||||
import com.bitwarden.vault.FolderView
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
|
||||
import com.x8bit.bitwarden.data.platform.manager.PushManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncFolderDeleteData
|
||||
@ -25,8 +26,10 @@ import kotlinx.coroutines.flow.onEach
|
||||
/**
|
||||
* The default implementation of the [FolderManager].
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
class FolderManagerImpl(
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
private val settingsDiskSource: SettingsDiskSource,
|
||||
private val folderService: FolderService,
|
||||
private val vaultDiskSource: VaultDiskSource,
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
@ -148,7 +151,7 @@ class FolderManagerImpl(
|
||||
* are met.
|
||||
*/
|
||||
private suspend fun syncFolderIfNecessary(syncFolderUpsertData: SyncFolderUpsertData) {
|
||||
val userId = activeUserId ?: return
|
||||
val userId = syncFolderUpsertData.userId
|
||||
val folderId = syncFolderUpsertData.folderId
|
||||
val isUpdate = syncFolderUpsertData.isUpdate
|
||||
val revisionDate = syncFolderUpsertData.revisionDate
|
||||
@ -162,6 +165,12 @@ class FolderManagerImpl(
|
||||
localFolder.revisionDate.toEpochSecond() < revisionDate.toEpochSecond()
|
||||
|
||||
if (!isValidCreate && !isValidUpdate) return
|
||||
if (activeUserId != userId) {
|
||||
// We cannot update right now since the accounts do not match, so we will
|
||||
// do a full-sync on the next check.
|
||||
settingsDiskSource.storeLastSyncTime(userId = userId, lastSyncTime = null)
|
||||
return
|
||||
}
|
||||
|
||||
folderService
|
||||
.getFolder(folderId = folderId)
|
||||
|
||||
@ -13,6 +13,7 @@ import com.bitwarden.send.Send
|
||||
import com.bitwarden.send.SendType
|
||||
import com.bitwarden.send.SendView
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
|
||||
import com.x8bit.bitwarden.data.platform.manager.PushManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.ReviewPromptManager
|
||||
@ -38,6 +39,7 @@ import retrofit2.HttpException
|
||||
@Suppress("LongParameterList")
|
||||
class SendManagerImpl(
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
private val settingsDiskSource: SettingsDiskSource,
|
||||
private val vaultDiskSource: VaultDiskSource,
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
private val sendsService: SendsService,
|
||||
@ -265,7 +267,7 @@ class SendManagerImpl(
|
||||
* now.
|
||||
*/
|
||||
private suspend fun syncSendIfNecessary(syncSendUpsertData: SyncSendUpsertData) {
|
||||
val userId = activeUserId ?: return
|
||||
val userId = syncSendUpsertData.userId
|
||||
val sendId = syncSendUpsertData.sendId
|
||||
val isUpdate = syncSendUpsertData.isUpdate
|
||||
val revisionDate = syncSendUpsertData.revisionDate
|
||||
@ -278,6 +280,12 @@ class SendManagerImpl(
|
||||
localSend != null &&
|
||||
localSend.revisionDate.toEpochSecond() < revisionDate.toEpochSecond()
|
||||
if (!isValidCreate && !isValidUpdate) return
|
||||
if (activeUserId != userId) {
|
||||
// We cannot update right now since the accounts do not match, so we will
|
||||
// do a full-sync on the next check.
|
||||
settingsDiskSource.storeLastSyncTime(userId = userId, lastSyncTime = null)
|
||||
return
|
||||
}
|
||||
|
||||
sendsService
|
||||
.getSend(sendId = sendId)
|
||||
|
||||
@ -61,6 +61,7 @@ object VaultManagerModule {
|
||||
@Singleton
|
||||
fun provideCipherManager(
|
||||
ciphersService: CiphersService,
|
||||
settingsDiskSource: SettingsDiskSource,
|
||||
vaultDiskSource: VaultDiskSource,
|
||||
vaultSdkSource: VaultSdkSource,
|
||||
authDiskSource: AuthDiskSource,
|
||||
@ -71,6 +72,7 @@ object VaultManagerModule {
|
||||
pushManager: PushManager,
|
||||
): CipherManager = CipherManagerImpl(
|
||||
fileManager = fileManager,
|
||||
settingsDiskSource = settingsDiskSource,
|
||||
authDiskSource = authDiskSource,
|
||||
ciphersService = ciphersService,
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
@ -85,6 +87,7 @@ object VaultManagerModule {
|
||||
@Singleton
|
||||
fun provideFolderManager(
|
||||
folderService: FolderService,
|
||||
settingsDiskSource: SettingsDiskSource,
|
||||
vaultDiskSource: VaultDiskSource,
|
||||
vaultSdkSource: VaultSdkSource,
|
||||
authDiskSource: AuthDiskSource,
|
||||
@ -92,6 +95,7 @@ object VaultManagerModule {
|
||||
pushManager: PushManager,
|
||||
): FolderManager = FolderManagerImpl(
|
||||
authDiskSource = authDiskSource,
|
||||
settingsDiskSource = settingsDiskSource,
|
||||
folderService = folderService,
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
@ -106,6 +110,7 @@ object VaultManagerModule {
|
||||
vaultDiskSource: VaultDiskSource,
|
||||
vaultSdkSource: VaultSdkSource,
|
||||
authDiskSource: AuthDiskSource,
|
||||
settingsDiskSource: SettingsDiskSource,
|
||||
fileManager: FileManager,
|
||||
reviewPromptManager: ReviewPromptManager,
|
||||
pushManager: PushManager,
|
||||
@ -113,6 +118,7 @@ object VaultManagerModule {
|
||||
): SendManager = SendManagerImpl(
|
||||
fileManager = fileManager,
|
||||
authDiskSource = authDiskSource,
|
||||
settingsDiskSource = settingsDiskSource,
|
||||
sendsService = sendsService,
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
|
||||
@ -260,6 +260,7 @@ class PushManagerTest {
|
||||
pushManager.onMessageReceived(SYNC_CIPHER_CREATE_NOTIFICATION_MAP)
|
||||
assertEquals(
|
||||
SyncCipherUpsertData(
|
||||
userId = "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
cipherId = "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321",
|
||||
organizationId = "6a41d965-ed95-4eae-98c3-5f1ec609c2c1",
|
||||
collectionIds = listOf(),
|
||||
@ -293,6 +294,7 @@ class PushManagerTest {
|
||||
pushManager.onMessageReceived(SYNC_CIPHER_UPDATE_NOTIFICATION_MAP)
|
||||
assertEquals(
|
||||
SyncCipherUpsertData(
|
||||
userId = "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
cipherId = "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321",
|
||||
organizationId = "6a41d965-ed95-4eae-98c3-5f1ec609c2c1",
|
||||
collectionIds = listOf(),
|
||||
@ -311,6 +313,7 @@ class PushManagerTest {
|
||||
pushManager.onMessageReceived(SYNC_FOLDER_CREATE_NOTIFICATION_MAP)
|
||||
assertEquals(
|
||||
SyncFolderUpsertData(
|
||||
userId = "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
folderId = "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321",
|
||||
revisionDate = ZonedDateTime.parse("2023-10-27T12:00:00.000Z"),
|
||||
isUpdate = false,
|
||||
@ -342,6 +345,7 @@ class PushManagerTest {
|
||||
pushManager.onMessageReceived(SYNC_FOLDER_UPDATE_NOTIFICATION_MAP)
|
||||
assertEquals(
|
||||
SyncFolderUpsertData(
|
||||
userId = "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
folderId = "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321",
|
||||
revisionDate = ZonedDateTime.parse("2023-10-27T12:00:00.000Z"),
|
||||
isUpdate = true,
|
||||
@ -372,6 +376,7 @@ class PushManagerTest {
|
||||
pushManager.onMessageReceived(SYNC_SEND_CREATE_NOTIFICATION_MAP)
|
||||
assertEquals(
|
||||
SyncSendUpsertData(
|
||||
userId = "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
sendId = "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321",
|
||||
revisionDate = ZonedDateTime.parse("2023-10-27T12:00:00.000Z"),
|
||||
isUpdate = false,
|
||||
@ -401,6 +406,7 @@ class PushManagerTest {
|
||||
pushManager.onMessageReceived(SYNC_SEND_UPDATE_NOTIFICATION_MAP)
|
||||
assertEquals(
|
||||
SyncSendUpsertData(
|
||||
userId = "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
sendId = "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321",
|
||||
revisionDate = ZonedDateTime.parse("2023-10-27T12:00:00.000Z"),
|
||||
isUpdate = true,
|
||||
@ -437,7 +443,8 @@ class PushManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with sync cipher create does nothing`() = runTest {
|
||||
fun `onMessageReceived with sync cipher create emits to syncCipherUpsertFlow`() =
|
||||
runTest {
|
||||
pushManager.syncCipherUpsertFlow.test {
|
||||
pushManager.onMessageReceived(SYNC_CIPHER_CREATE_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
@ -459,7 +466,8 @@ class PushManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with sync cipher update does nothing`() = runTest {
|
||||
fun `onMessageReceived with sync cipher update emits to syncCipherUpsertFlow`() =
|
||||
runTest {
|
||||
pushManager.syncCipherUpsertFlow.test {
|
||||
pushManager.onMessageReceived(SYNC_CIPHER_UPDATE_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
@ -543,62 +551,6 @@ class PushManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class NullUserState {
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
authDiskSource.userState = null
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with logout does nothing`() = runTest {
|
||||
pushManager.logoutFlow.test {
|
||||
pushManager.onMessageReceived(LOGOUT_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with logout with kdf reason does nothing`() = runTest {
|
||||
pushManager.logoutFlow.test {
|
||||
pushManager.onMessageReceived(LOGOUT_KDF_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with sync ciphers does nothing`() = runTest {
|
||||
pushManager.fullSyncFlow.test {
|
||||
pushManager.onMessageReceived(SYNC_CIPHERS_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with sync org keys does nothing`() = runTest {
|
||||
pushManager.fullSyncFlow.test {
|
||||
pushManager.onMessageReceived(SYNC_ORG_KEYS_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with sync settings does nothing`() = runTest {
|
||||
pushManager.fullSyncFlow.test {
|
||||
pushManager.onMessageReceived(SYNC_SETTINGS_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with sync vault does nothing`() = runTest {
|
||||
pushManager.fullSyncFlow.test {
|
||||
pushManager.onMessageReceived(SYNC_VAULT_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class NonNullUserState {
|
||||
@BeforeEach
|
||||
|
||||
@ -29,6 +29,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.util.FakeSettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
|
||||
import com.x8bit.bitwarden.data.platform.manager.PushManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.ReviewPromptManager
|
||||
@ -91,6 +92,7 @@ class CipherManagerTest {
|
||||
coEvery { delete(*anyVararg()) } just runs
|
||||
}
|
||||
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
||||
private val fakeSettingsDiskSource = FakeSettingsDiskSource()
|
||||
private val ciphersService: CiphersService = mockk()
|
||||
private val vaultDiskSource: VaultDiskSource = mockk()
|
||||
private val vaultSdkSource: VaultSdkSource = mockk()
|
||||
@ -106,6 +108,7 @@ class CipherManagerTest {
|
||||
|
||||
private val cipherManager: CipherManager = CipherManagerImpl(
|
||||
ciphersService = ciphersService,
|
||||
settingsDiskSource = fakeSettingsDiskSource,
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
@ -2403,6 +2406,7 @@ class CipherManagerTest {
|
||||
|
||||
mutableSyncCipherUpsertFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = userId,
|
||||
cipherId = cipherId,
|
||||
revisionDate = ZonedDateTime.now(clock),
|
||||
isUpdate = false,
|
||||
@ -2450,6 +2454,7 @@ class CipherManagerTest {
|
||||
|
||||
mutableSyncCipherUpsertFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = userId,
|
||||
cipherId = cipherId,
|
||||
revisionDate = ZonedDateTime.now(clock),
|
||||
isUpdate = false,
|
||||
@ -2492,6 +2497,7 @@ class CipherManagerTest {
|
||||
|
||||
mutableSyncCipherUpsertFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = userId,
|
||||
cipherId = cipherId,
|
||||
revisionDate = ZonedDateTime.now(clock),
|
||||
isUpdate = true,
|
||||
@ -2518,6 +2524,7 @@ class CipherManagerTest {
|
||||
|
||||
mutableSyncCipherUpsertFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = userId,
|
||||
cipherId = cipherId,
|
||||
revisionDate = ZonedDateTime.now(clock),
|
||||
isUpdate = true,
|
||||
@ -2552,6 +2559,7 @@ class CipherManagerTest {
|
||||
|
||||
mutableSyncCipherUpsertFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = userId,
|
||||
cipherId = cipherId,
|
||||
revisionDate = ZonedDateTime.now(clock).minus(5, ChronoUnit.MINUTES),
|
||||
isUpdate = true,
|
||||
@ -2589,6 +2597,7 @@ class CipherManagerTest {
|
||||
|
||||
mutableSyncCipherUpsertFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = userId,
|
||||
cipherId = cipherId,
|
||||
revisionDate = ZonedDateTime.now(clock),
|
||||
isUpdate = true,
|
||||
@ -2620,6 +2629,7 @@ class CipherManagerTest {
|
||||
|
||||
mutableSyncCipherUpsertFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = userId,
|
||||
cipherId = cipherId,
|
||||
revisionDate = ZonedDateTime.now(clock),
|
||||
isUpdate = false,
|
||||
@ -2659,6 +2669,7 @@ class CipherManagerTest {
|
||||
|
||||
mutableSyncCipherUpsertFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = userId,
|
||||
cipherId = cipherId,
|
||||
revisionDate = ZonedDateTime.now(clock),
|
||||
isUpdate = false,
|
||||
@ -2697,6 +2708,7 @@ class CipherManagerTest {
|
||||
|
||||
mutableSyncCipherUpsertFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = userId,
|
||||
cipherId = cipherId,
|
||||
revisionDate = ZonedDateTime.now(clock),
|
||||
isUpdate = true,
|
||||
@ -2712,6 +2724,43 @@ class CipherManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `syncCipherUpsertFlow with inactive userId should clear the last sync time`() = runTest {
|
||||
val number = 1
|
||||
val userId = "nonActiveUserId"
|
||||
val cipherId = "mockId-$number"
|
||||
val originalCipher = mockk<SyncResponseJson.Cipher> {
|
||||
every { revisionDate } returns ZonedDateTime.now(clock).minus(5, ChronoUnit.MINUTES)
|
||||
}
|
||||
val lastSyncTime = clock.instant()
|
||||
|
||||
fakeSettingsDiskSource.storeLastSyncTime(userId = userId, lastSyncTime = lastSyncTime)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
coEvery {
|
||||
vaultDiskSource.getCipher(userId = userId, cipherId = cipherId)
|
||||
} returns originalCipher
|
||||
|
||||
mutableSyncCipherUpsertFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
userId = userId,
|
||||
cipherId = cipherId,
|
||||
revisionDate = ZonedDateTime.now(clock),
|
||||
isUpdate = true,
|
||||
collectionIds = null,
|
||||
organizationId = null,
|
||||
),
|
||||
)
|
||||
|
||||
fakeSettingsDiskSource.assertLastSyncTime(userId = userId, expected = null)
|
||||
coVerify(exactly = 1) {
|
||||
vaultDiskSource.getCipher(userId = userId, cipherId = cipherId)
|
||||
}
|
||||
coVerify(exactly = 0) {
|
||||
ciphersService.getCipher(cipherId)
|
||||
vaultDiskSource.saveCipher(userId = userId, cipher = any())
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupMockUri(
|
||||
url: String,
|
||||
queryParams: Map<String, String> = emptyMap(),
|
||||
|
||||
@ -16,6 +16,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.util.FakeSettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
|
||||
import com.x8bit.bitwarden.data.platform.manager.PushManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncFolderDeleteData
|
||||
@ -49,6 +50,7 @@ import java.time.temporal.ChronoUnit
|
||||
|
||||
class FolderManagerTest {
|
||||
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
||||
private val fakeSettingsDiskSource = FakeSettingsDiskSource()
|
||||
private val folderService = mockk<FolderService>()
|
||||
private val vaultDiskSource = mockk<VaultDiskSource>()
|
||||
private val vaultSdkSource = mockk<VaultSdkSource>()
|
||||
@ -61,6 +63,7 @@ class FolderManagerTest {
|
||||
|
||||
private val folderManager: FolderManager = FolderManagerImpl(
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
settingsDiskSource = fakeSettingsDiskSource,
|
||||
folderService = folderService,
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
@ -435,6 +438,7 @@ class FolderManagerTest {
|
||||
|
||||
mutableSyncFolderUpsertFlow.tryEmit(
|
||||
SyncFolderUpsertData(
|
||||
userId = userId,
|
||||
folderId = folderId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = false,
|
||||
@ -460,6 +464,7 @@ class FolderManagerTest {
|
||||
|
||||
mutableSyncFolderUpsertFlow.tryEmit(
|
||||
SyncFolderUpsertData(
|
||||
userId = userId,
|
||||
folderId = folderId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = true,
|
||||
@ -486,6 +491,7 @@ class FolderManagerTest {
|
||||
|
||||
mutableSyncFolderUpsertFlow.tryEmit(
|
||||
SyncFolderUpsertData(
|
||||
userId = userId,
|
||||
folderId = folderId,
|
||||
revisionDate = ZonedDateTime.ofInstant(
|
||||
Instant.ofEpochSecond(0), ZoneId.of("UTC"),
|
||||
@ -518,6 +524,7 @@ class FolderManagerTest {
|
||||
|
||||
mutableSyncFolderUpsertFlow.tryEmit(
|
||||
SyncFolderUpsertData(
|
||||
userId = userId,
|
||||
folderId = folderId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = false,
|
||||
@ -552,6 +559,7 @@ class FolderManagerTest {
|
||||
|
||||
mutableSyncFolderUpsertFlow.tryEmit(
|
||||
SyncFolderUpsertData(
|
||||
userId = userId,
|
||||
folderId = folderId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = true,
|
||||
@ -563,6 +571,42 @@ class FolderManagerTest {
|
||||
vaultDiskSource.saveFolder(userId = userId, folder = folder)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `syncFolderUpsertFlow with inactive userId should clear the last sync time`() = runTest {
|
||||
val number = 1
|
||||
val userId = "nonActiveUserId"
|
||||
val folderId = "mockId-$number"
|
||||
val lastSyncTime = FIXED_CLOCK.instant()
|
||||
|
||||
fakeSettingsDiskSource.storeLastSyncTime(userId = userId, lastSyncTime = lastSyncTime)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val folderView = createMockFolder(
|
||||
number = number,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK).minus(5, ChronoUnit.MINUTES),
|
||||
)
|
||||
coEvery {
|
||||
vaultDiskSource.getFolders(userId = userId)
|
||||
} returns MutableStateFlow(listOf(folderView))
|
||||
|
||||
mutableSyncFolderUpsertFlow.tryEmit(
|
||||
SyncFolderUpsertData(
|
||||
userId = userId,
|
||||
folderId = folderId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = true,
|
||||
),
|
||||
)
|
||||
|
||||
fakeSettingsDiskSource.assertLastSyncTime(userId = userId, expected = null)
|
||||
coVerify(exactly = 1) {
|
||||
vaultDiskSource.getFolders(userId = userId)
|
||||
}
|
||||
coVerify(exactly = 0) {
|
||||
folderService.getFolder(folderId = folderId)
|
||||
vaultDiskSource.saveFolder(userId = userId, folder = any())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val FIXED_CLOCK: Clock = Clock.fixed(
|
||||
|
||||
@ -20,6 +20,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.util.FakeSettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
|
||||
import com.x8bit.bitwarden.data.platform.manager.PushManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.ReviewPromptManager
|
||||
@ -64,6 +65,7 @@ class SendManagerTest {
|
||||
coEvery { delete(files = anyVararg()) } just runs
|
||||
}
|
||||
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
||||
private val fakeSettingsDiskSource = FakeSettingsDiskSource()
|
||||
private val sendsService = mockk<SendsService>()
|
||||
private val vaultDiskSource = mockk<VaultDiskSource>()
|
||||
private val vaultSdkSource = mockk<VaultSdkSource>()
|
||||
@ -82,6 +84,7 @@ class SendManagerTest {
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
settingsDiskSource = fakeSettingsDiskSource,
|
||||
fileManager = fileManager,
|
||||
reviewPromptManager = reviewPromptManager,
|
||||
pushManager = pushManager,
|
||||
@ -129,6 +132,7 @@ class SendManagerTest {
|
||||
|
||||
mutableSyncSendUpsertFlow.tryEmit(
|
||||
SyncSendUpsertData(
|
||||
userId = userId,
|
||||
sendId = sendId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = false,
|
||||
@ -152,6 +156,7 @@ class SendManagerTest {
|
||||
|
||||
mutableSyncSendUpsertFlow.tryEmit(
|
||||
SyncSendUpsertData(
|
||||
userId = userId,
|
||||
sendId = sendId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = true,
|
||||
@ -182,6 +187,7 @@ class SendManagerTest {
|
||||
|
||||
mutableSyncSendUpsertFlow.tryEmit(
|
||||
SyncSendUpsertData(
|
||||
userId = userId,
|
||||
sendId = sendId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK).minus(5, ChronoUnit.MINUTES),
|
||||
isUpdate = true,
|
||||
@ -221,6 +227,7 @@ class SendManagerTest {
|
||||
|
||||
mutableSyncSendUpsertFlow.tryEmit(
|
||||
SyncSendUpsertData(
|
||||
userId = userId,
|
||||
sendId = sendId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = true,
|
||||
@ -251,6 +258,7 @@ class SendManagerTest {
|
||||
|
||||
mutableSyncSendUpsertFlow.tryEmit(
|
||||
SyncSendUpsertData(
|
||||
userId = userId,
|
||||
sendId = sendId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = false,
|
||||
@ -283,6 +291,7 @@ class SendManagerTest {
|
||||
|
||||
mutableSyncSendUpsertFlow.tryEmit(
|
||||
SyncSendUpsertData(
|
||||
userId = userId,
|
||||
sendId = sendId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = false,
|
||||
@ -318,6 +327,7 @@ class SendManagerTest {
|
||||
|
||||
mutableSyncSendUpsertFlow.tryEmit(
|
||||
SyncSendUpsertData(
|
||||
userId = userId,
|
||||
sendId = sendId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = true,
|
||||
@ -330,6 +340,42 @@ class SendManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `syncSendUpsertFlow with inactive userId should clear the last sync time`() = runTest {
|
||||
val number = 1
|
||||
val userId = "nonActiveUserId"
|
||||
val sendId = "mockId-$number"
|
||||
val lastSyncTime = FIXED_CLOCK.instant()
|
||||
|
||||
fakeSettingsDiskSource.storeLastSyncTime(userId = userId, lastSyncTime = lastSyncTime)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val sendView = createMockSend(
|
||||
number = number,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK).minus(5, ChronoUnit.MINUTES),
|
||||
)
|
||||
coEvery {
|
||||
vaultDiskSource.getSends(userId = userId)
|
||||
} returns MutableStateFlow(listOf(sendView))
|
||||
|
||||
mutableSyncSendUpsertFlow.tryEmit(
|
||||
SyncSendUpsertData(
|
||||
userId = userId,
|
||||
sendId = sendId,
|
||||
revisionDate = ZonedDateTime.now(FIXED_CLOCK),
|
||||
isUpdate = true,
|
||||
),
|
||||
)
|
||||
|
||||
fakeSettingsDiskSource.assertLastSyncTime(userId = userId, expected = null)
|
||||
coVerify(exactly = 1) {
|
||||
vaultDiskSource.getSends(userId = userId)
|
||||
}
|
||||
coVerify(exactly = 0) {
|
||||
sendsService.getSend(sendId = sendId)
|
||||
vaultDiskSource.saveSend(userId = userId, send = any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createSend with no active user should return CreateSendResult Error`() =
|
||||
runTest {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user