From 99051407b3727aa3b7cabadf710d66ddff6bd196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Saleniuk?= <30429749+saleniuk@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:17:42 +0200 Subject: [PATCH] fix: update legalhold status when fetching user data (#2708) * fix: update legalhold status when fetching user data * changes after review * fix: do not override degraded state * detekt * fixes after resolving conflicts --- .../kalium/logic/data/user/UserRepository.kt | 27 ++- .../kalium/logic/feature/UserSessionScope.kt | 50 +++-- .../handler/legalhold/LegalHoldHandler.kt | 35 +++- .../logic/data/user/UserRepositoryTest.kt | 12 +- .../handler/legalhold/LegalHoldHandlerTest.kt | 178 ++++++++++++++++++ 5 files changed, 261 insertions(+), 41 deletions(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt index e35f8f9445a..6e7ddea4493 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt @@ -54,6 +54,7 @@ import com.wire.kalium.logic.functional.mapRight import com.wire.kalium.logic.functional.onFailure import com.wire.kalium.logic.functional.onSuccess import com.wire.kalium.logic.kaliumLogger +import com.wire.kalium.logic.sync.receiver.handler.legalhold.LegalHoldHandler import com.wire.kalium.logic.wrapApiRequest import com.wire.kalium.logic.wrapStorageRequest import com.wire.kalium.network.api.base.authenticated.TeamsApi @@ -167,6 +168,7 @@ internal class UserDataSource internal constructor( private val sessionRepository: SessionRepository, private val selfUserId: UserId, private val selfTeamIdProvider: SelfTeamIdProvider, + private val legalHoldHandler: LegalHoldHandler, private val idMapper: IdMapper = MapperProvider.idMapper(), private val userMapper: UserMapper = MapperProvider.userMapper(), private val teamMapper: TeamMapper = MapperProvider.teamMapper(), @@ -341,7 +343,7 @@ internal class UserDataSource internal constructor( private suspend fun persistUsers( listUserProfileDTO: List, listTeamMemberDTO: List, - ) = wrapStorageRequest { + ): Either { val mapTeamMemberDTO = listTeamMemberDTO.associateBy { it.nonQualifiedUserId } val selfUserTeamId = selfTeamIdProvider().getOrNull()?.value val teamMembers = listUserProfileDTO @@ -370,13 +372,22 @@ internal class UserDataSource internal constructor( ) ) } - if (teamMembers.isNotEmpty()) { - userDAO.upsertUsers(teamMembers) - userDAO.upsertConnectionStatuses(teamMembers.associate { it.id to it.connectionStatus }) - } - if (otherUsers.isNotEmpty()) { - userDAO.upsertUsers(otherUsers) - } + return listUserProfileDTO + .map { + legalHoldHandler.handleUserFetch(it.id.toModel(), it.legalHoldStatus == LegalHoldStatusDTO.ENABLED) + } + .foldToEitherWhileRight(Unit) { value, _ -> value } + .flatMap { + wrapStorageRequest { + if (teamMembers.isNotEmpty()) { + userDAO.upsertUsers(teamMembers) + userDAO.upsertConnectionStatuses(teamMembers.associate { it.id to it.connectionStatus }) + } + if (otherUsers.isNotEmpty()) { + userDAO.upsertUsers(otherUsers) + } + } + } } override suspend fun fetchUsersIfUnknownByIds(ids: Set): Either = wrapStorageRequest { diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt index 869eee15e60..c3c35cf16d8 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt @@ -741,6 +741,28 @@ class UserSessionScope internal constructor( messageDraftDAO = userStorage.database.messageDraftDAO, ) + private val slowSyncRepository: SlowSyncRepository by lazy { SlowSyncRepositoryImpl(userStorage.database.metadataDAO) } + private val incrementalSyncRepository: IncrementalSyncRepository by lazy { InMemoryIncrementalSyncRepository() } + + private val legalHoldSystemMessagesHandler = LegalHoldSystemMessagesHandlerImpl( + selfUserId = userId, + persistMessage = persistMessage, + conversationRepository = conversationRepository, + messageRepository = messageRepository + ) + + private val legalHoldHandler = LegalHoldHandlerImpl( + selfUserId = userId, + fetchUsersClientsFromRemote = fetchUsersClientsFromRemote, + fetchSelfClientsFromRemote = fetchSelfClientsFromRemote, + observeLegalHoldStateForUser = observeLegalHoldStateForUser, + membersHavingLegalHoldClient = membersHavingLegalHoldClient, + userConfigRepository = userConfigRepository, + conversationRepository = conversationRepository, + observeSyncState = observeSyncState, + legalHoldSystemMessagesHandler = legalHoldSystemMessagesHandler, + ) + private val userRepository: UserRepository = UserDataSource( userStorage.database.userDAO, userStorage.database.metadataDAO, @@ -750,7 +772,8 @@ class UserSessionScope internal constructor( authenticatedNetworkContainer.teamsApi, globalScope.sessionRepository, userId, - selfTeamId + selfTeamId, + legalHoldHandler ) private val accountRepository: AccountRepository @@ -876,12 +899,6 @@ class UserSessionScope internal constructor( kaliumFileSystem = kaliumFileSystem ) - private val incrementalSyncRepository: IncrementalSyncRepository by lazy { - InMemoryIncrementalSyncRepository() - } - - private val slowSyncRepository: SlowSyncRepository by lazy { SlowSyncRepositoryImpl(userStorage.database.metadataDAO) } - private val eventGatherer: EventGatherer get() = EventGathererImpl(eventRepository, incrementalSyncRepository) private val eventProcessor: EventProcessor by lazy { @@ -1464,13 +1481,6 @@ class UserSessionScope internal constructor( val membersHavingLegalHoldClient: MembersHavingLegalHoldClientUseCase get() = MembersHavingLegalHoldClientUseCaseImpl(clientRepository) - private val legalHoldSystemMessagesHandler = LegalHoldSystemMessagesHandlerImpl( - selfUserId = userId, - persistMessage = persistMessage, - conversationRepository = conversationRepository, - messageRepository = messageRepository - ) - private val updateSelfClientCapabilityToLegalHoldConsent: UpdateSelfClientCapabilityToLegalHoldConsentUseCase get() = UpdateSelfClientCapabilityToLegalHoldConsentUseCaseImpl( clientRemoteRepository = clientRemoteRepository, @@ -1480,18 +1490,6 @@ class UserSessionScope internal constructor( kaliumLogger = userScopedLogger, ) - private val legalHoldHandler = LegalHoldHandlerImpl( - selfUserId = userId, - fetchUsersClientsFromRemote = fetchUsersClientsFromRemote, - fetchSelfClientsFromRemote = fetchSelfClientsFromRemote, - observeLegalHoldStateForUser = observeLegalHoldStateForUser, - membersHavingLegalHoldClient = membersHavingLegalHoldClient, - userConfigRepository = userConfigRepository, - conversationRepository = conversationRepository, - observeSyncState = observeSyncState, - legalHoldSystemMessagesHandler = legalHoldSystemMessagesHandler, - ) - private val fetchLegalHoldForSelfUserFromRemoteUseCase: FetchLegalHoldForSelfUserFromRemoteUseCase get() = FetchLegalHoldForSelfUserFromRemoteUseCaseImpl( teamRepository = teamRepository, diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt index 41481190cc9..9d9c580a870 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt @@ -35,6 +35,7 @@ import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.functional.flatMap import com.wire.kalium.logic.functional.foldToEitherWhileRight import com.wire.kalium.logic.functional.getOrElse +import com.wire.kalium.logic.functional.getOrNull import com.wire.kalium.logic.functional.map import com.wire.kalium.logic.kaliumLogger import com.wire.kalium.logic.sync.ObserveSyncStateUseCase @@ -61,6 +62,7 @@ internal interface LegalHoldHandler { handleFailure: suspend () -> Either ): Either suspend fun handleConversationMembersChanged(conversationId: ConversationId): Either + suspend fun handleUserFetch(userId: UserId, userIsUnderLegalHold: Boolean): Either } @Suppress("LongParameterList") @@ -197,19 +199,40 @@ internal class LegalHoldHandlerImpl internal constructor( } } + override suspend fun handleUserFetch(userId: UserId, userIsUnderLegalHold: Boolean): Either { + val userHasBeenUnderLegalHold = isUserUnderLegalHold(userId) + if (userHasBeenUnderLegalHold != userIsUnderLegalHold) { + processEvent(selfUserId, userId) // fetch and persist current clients for the given user + handleConversationsForUser(userId) + if (userIsUnderLegalHold) { + legalHoldSystemMessagesHandler.handleEnabledForUser(userId, DateTimeUtil.currentIsoDateTimeString()) + } else { + legalHoldSystemMessagesHandler.handleDisabledForUser(userId, DateTimeUtil.currentIsoDateTimeString()) + } + } + + return Either.Right(Unit) + } + private suspend fun isUserUnderLegalHold(userId: UserId): Boolean = observeLegalHoldStateForUser(userId).firstOrNull() == LegalHoldState.Enabled + @Suppress("NestedBlockDepth") private suspend fun handleForConversation( conversationId: ConversationId, newStatus: Conversation.LegalHoldStatus, systemMessageTimestampIso: String = DateTimeUtil.currentIsoDateTimeString(), ): Boolean = if (newStatus != Conversation.LegalHoldStatus.UNKNOWN) { - conversationRepository.updateLegalHoldStatus(conversationId, newStatus) - .getOrElse(false) - .also { isChanged -> // if conversation legal hold status has changed, create system message for it - if (isChanged) { + conversationRepository.observeLegalHoldStatus(conversationId).firstOrNull()?.getOrNull().let { currentStatus -> + if (currentStatus != newStatus) { + val isChanged = when { + currentStatus == Conversation.LegalHoldStatus.DEGRADED && newStatus == Conversation.LegalHoldStatus.ENABLED -> { + true // in case it's already degraded we want to keep it, not change it to enabled but create system messages + } + else -> conversationRepository.updateLegalHoldStatus(conversationId, newStatus).getOrElse(false) + } + if (isChanged) { // if conversation legal hold status has changed, create system message for it when (newStatus) { Conversation.LegalHoldStatus.DISABLED -> legalHoldSystemMessagesHandler.handleDisabledForConversation(conversationId, systemMessageTimestampIso) @@ -218,7 +241,9 @@ internal class LegalHoldHandlerImpl internal constructor( else -> { /* do nothing */ } } } - } + isChanged + } else false + } } else false private suspend fun handleConversationsForUser(userId: UserId) { diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/user/UserRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/user/UserRepositoryTest.kt index 15d9b7d38ed..9b8fac225ec 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/user/UserRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/user/UserRepositoryTest.kt @@ -36,6 +36,7 @@ import com.wire.kalium.logic.framework.TestUser import com.wire.kalium.logic.framework.TestUser.LIST_USERS_DTO import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.functional.getOrNull +import com.wire.kalium.logic.sync.receiver.handler.legalhold.LegalHoldHandler import com.wire.kalium.logic.test_util.TestNetworkException import com.wire.kalium.logic.test_util.TestNetworkException.federationNotEnabled import com.wire.kalium.logic.test_util.TestNetworkException.generic @@ -64,9 +65,9 @@ import com.wire.kalium.persistence.dao.client.ClientDAO import io.ktor.http.HttpStatusCode import io.mockative.Mock import io.mockative.any -import io.mockative.eq import io.mockative.coEvery import io.mockative.coVerify +import io.mockative.eq import io.mockative.every import io.mockative.matches import io.mockative.mock @@ -821,6 +822,9 @@ class UserRepositoryTest { @Mock val selfTeamIdProvider: SelfTeamIdProvider = mock(SelfTeamIdProvider::class) + @Mock + val legalHoldHandler: LegalHoldHandler = mock(LegalHoldHandler::class) + val selfUserId = TestUser.SELF.id val userRepository: UserRepository by lazy { @@ -833,7 +837,8 @@ class UserRepositoryTest { teamsApi, sessionRepository, selfUserId, - selfTeamIdProvider + selfTeamIdProvider, + legalHoldHandler ) } @@ -1033,6 +1038,9 @@ class UserRepositoryTest { sessionRepository.updateSsoIdAndScimInfo(any(), any(), any()) }.returns(Either.Right(Unit)) withGetTeamMemberSuccess(TestTeam.memberDTO(selfUserId.value)) + coEvery { + legalHoldHandler.handleUserFetch(any(), any()) + }.returns(Either.Right(Unit)) apply(block) return this to userRepository } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt index 62f83f13870..371777810b9 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt @@ -273,6 +273,7 @@ class LegalHoldHandlerTest { val (arrangement, handler) = Arrangement() .withObserveLegalHoldStateForUserSuccess(LegalHoldState.Enabled) .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.ENABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.ENABLED) .arrange() // when handler.handleDisable(legalHoldEventDisabled) @@ -315,6 +316,7 @@ class LegalHoldHandlerTest { // given val (arrangement, handler) = Arrangement() .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.DISABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) .withMembersHavingLegalHoldClientSuccess(listOf(TestUser.OTHER_USER_ID)) .arrange() // when @@ -330,6 +332,7 @@ class LegalHoldHandlerTest { // given val (arrangement, handler) = Arrangement() .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.ENABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.ENABLED) .withUpdateLegalHoldStatusSuccess(false) .withMembersHavingLegalHoldClientSuccess(listOf(TestUser.OTHER_USER_ID)) .arrange() @@ -346,6 +349,7 @@ class LegalHoldHandlerTest { // given val (arrangement, handler) = Arrangement() .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.ENABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.ENABLED) .arrange() // when handler.handleEnable(legalHoldEventEnabled.copy(userId = TestUser.OTHER_USER_ID)) @@ -360,6 +364,7 @@ class LegalHoldHandlerTest { // given val (arrangement, handler) = Arrangement() .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.DISABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) .withUpdateLegalHoldStatusSuccess(false) .arrange() // when @@ -375,6 +380,7 @@ class LegalHoldHandlerTest { // given val (arrangement, handler) = Arrangement() .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.DISABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) .arrange() // when handler.handleNewMessage(applicationMessage(Conversation.LegalHoldStatus.ENABLED), false) @@ -389,6 +395,7 @@ class LegalHoldHandlerTest { // given val (arrangement, handler) = Arrangement() .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.DISABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) .withUpdateLegalHoldStatusSuccess(false) .arrange() // when @@ -404,6 +411,7 @@ class LegalHoldHandlerTest { // given val (arrangement, handler) = Arrangement() .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.ENABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.ENABLED) .arrange() // when handler.handleNewMessage(applicationMessage(Conversation.LegalHoldStatus.DISABLED), false) @@ -418,6 +426,7 @@ class LegalHoldHandlerTest { // given val (arrangement, handler) = Arrangement() .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.DISABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) .arrange() val message = applicationMessage(Conversation.LegalHoldStatus.ENABLED) // when @@ -438,6 +447,7 @@ class LegalHoldHandlerTest { val syncStatesFlow = MutableStateFlow(SyncState.GatheringPendingEvents) val (arrangement, handler) = Arrangement() .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.DISABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) .withGetConversationMembersSuccess(listOf(TestUser.OTHER_USER_ID)) .withMembersHavingLegalHoldClientSuccess(emptyList()) // checked before legal hold state change so empty .withObserveLegalHoldStateForUserSuccess(LegalHoldState.Enabled) // checked after legal hold state change, that's why enabled @@ -464,6 +474,7 @@ class LegalHoldHandlerTest { // given val (arrangement, handler) = Arrangement() .withGetConversationsByUserIdSuccess(listOf(conversation(legalHoldStatus = Conversation.LegalHoldStatus.DISABLED))) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) .withGetConversationMembersSuccess(listOf(TestUser.OTHER_USER_ID)) .withMembersHavingLegalHoldClientSuccess(emptyList()) // checked before legal hold state change so empty .withObserveLegalHoldStateForUserSuccess(LegalHoldState.Enabled) // checked after legal hold state change, that's why enabled @@ -506,6 +517,7 @@ class LegalHoldHandlerTest { val membersHavingLegalHoldClientAfter = listOf(TestUser.OTHER_USER_ID) val (arrangement, handler) = Arrangement() .withMembersHavingLegalHoldClientSuccess(membersHavingLegalHoldClientBefore, membersHavingLegalHoldClientAfter) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) .withUpdateLegalHoldStatusSuccess(true) .arrange() // when @@ -529,6 +541,7 @@ class LegalHoldHandlerTest { val membersHavingLegalHoldClientAfter = emptyList() val (arrangement, handler) = Arrangement() .withMembersHavingLegalHoldClientSuccess(membersHavingLegalHoldClientBefore, membersHavingLegalHoldClientAfter) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.ENABLED) .withUpdateLegalHoldStatusSuccess(true) .arrange() // when @@ -552,6 +565,7 @@ class LegalHoldHandlerTest { val membersHavingLegalHoldClientAfter = listOf(TestUser.OTHER_USER_ID) val (arrangement, handler) = Arrangement() .withMembersHavingLegalHoldClientSuccess(membersHavingLegalHoldClientBefore, membersHavingLegalHoldClientAfter) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) .withUpdateLegalHoldStatusSuccess(true) .arrange() // when @@ -575,6 +589,7 @@ class LegalHoldHandlerTest { val membersHavingLegalHoldClientAfter = listOf(TestUser.OTHER_USER_ID) val (arrangement, handler) = Arrangement() .withMembersHavingLegalHoldClientSuccess(membersHavingLegalHoldClientBefore, membersHavingLegalHoldClientAfter) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) .withUpdateLegalHoldStatusSuccess(false) .arrange() // when @@ -601,6 +616,7 @@ class LegalHoldHandlerTest { val membersHavingLegalHoldClientAfter = listOf(TestUser.OTHER_USER_ID_2) val (arrangement, handler) = Arrangement() .withMembersHavingLegalHoldClientSuccess(membersHavingLegalHoldClientBefore, membersHavingLegalHoldClientAfter) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.ENABLED) .withUpdateLegalHoldStatusSuccess() .arrange() // when @@ -625,8 +641,13 @@ class LegalHoldHandlerTest { val conversationId = TestConversation.CONVERSATION.id val userId = TestUser.OTHER_USER_ID val membersHavingLegalHoldClient = if (thereAreMembersWithLegalHoldEnabledAfterChange) listOf(userId) else emptyList() + val conversationLegalHoldStatusBefore = when { + thereAreMembersWithLegalHoldEnabledAfterChange != legalHoldStatusForConversationChanged -> Conversation.LegalHoldStatus.ENABLED + else -> Conversation.LegalHoldStatus.DISABLED + } val (arrangement, handler) = Arrangement() .withMembersHavingLegalHoldClientSuccess(membersHavingLegalHoldClient) + .withObserveConversationLegalHoldStatus(conversationLegalHoldStatusBefore) .withUpdateLegalHoldStatusSuccess(isChanged = legalHoldStatusForConversationChanged) .arrange() // when @@ -726,6 +747,157 @@ class LegalHoldHandlerTest { expectedConversationLegalHoldStatus = Conversation.LegalHoldStatus.DISABLED, ) + @Test + fun givenUserHasNotBeenButNowIsUnderLegalHold_whenHandlingUserFetch_thenChangeConversationStatusesToEnabled() = runTest { + // given + val userId = TestUser.OTHER_USER_ID + val conversation = conversation(legalHoldStatus = Conversation.LegalHoldStatus.DISABLED) + val (arrangement, handler) = Arrangement() + .withGetConversationsByUserIdSuccess(listOf(conversation)) + .withObserveLegalHoldStateForUserSuccess(LegalHoldState.Disabled) // used before legal hold state change + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DISABLED) // used before legal hold state change + .withMembersHavingLegalHoldClientSuccess(listOf(userId)) // used after legal hold state change + .withUpdateLegalHoldStatusSuccess(isChanged = true) + .arrange() + // when + handler.handleUserFetch(userId, true) + // then + coVerify { + arrangement.fetchUsersClientsFromRemote.invoke(any()) + }.wasInvoked() + coVerify { + arrangement.legalHoldSystemMessagesHandler.handleEnabledForUser(eq(userId), any()) + }.wasInvoked() + coVerify { + arrangement.legalHoldSystemMessagesHandler.handleDisabledForUser(eq(userId), any()) + }.wasNotInvoked() + coVerify { + arrangement.conversationRepository.updateLegalHoldStatus( + eq(conversation.id), + eq(Conversation.LegalHoldStatus.ENABLED) + ) + }.wasInvoked() + } + + @Test + fun givenUserHasBeenButNowIsNotUnderLegalHold_whenHandlingUserFetch_thenChangeConversationStatusesToDisabled() = runTest { + // given + val userId = TestUser.OTHER_USER_ID + val conversation = conversation(legalHoldStatus = Conversation.LegalHoldStatus.ENABLED) + val (arrangement, handler) = Arrangement() + .withGetConversationsByUserIdSuccess(listOf(conversation)) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.ENABLED) + .withObserveLegalHoldStateForUserSuccess(LegalHoldState.Enabled) // used before legal hold state change + .withMembersHavingLegalHoldClientSuccess(listOf()) // used after legal hold state change + .withUpdateLegalHoldStatusSuccess(isChanged = true) + .arrange() + // when + handler.handleUserFetch(userId, false) + // then + coVerify { + arrangement.fetchUsersClientsFromRemote.invoke(eq(listOf(userId))) + }.wasInvoked() + coVerify { + arrangement.legalHoldSystemMessagesHandler.handleEnabledForUser(eq(userId), any()) + }.wasNotInvoked() + coVerify { + arrangement.legalHoldSystemMessagesHandler.handleDisabledForUser(eq(userId), any()) + }.wasInvoked() + coVerify { + arrangement.conversationRepository.updateLegalHoldStatus( + eq(conversation.id), + eq(Conversation.LegalHoldStatus.DISABLED) + ) + }.wasInvoked() + } + + @Test + fun givenUserIsStillNotUnderLegalHold_whenHandlingUserFetch_thenDoNotChangeStatuses() = runTest { + // given + val userId = TestUser.OTHER_USER_ID + val conversation = conversation(legalHoldStatus = Conversation.LegalHoldStatus.DISABLED) + val (arrangement, handler) = Arrangement() + .withGetConversationsByUserIdSuccess(listOf(conversation)) + .withObserveLegalHoldStateForUserSuccess(LegalHoldState.Disabled) // used before legal hold state change + .withMembersHavingLegalHoldClientSuccess(listOf()) // used after legal hold state change + .withUpdateLegalHoldStatusSuccess(isChanged = false) + .arrange() + // when + handler.handleUserFetch(userId, false) + // then + coVerify { + arrangement.fetchUsersClientsFromRemote.invoke(eq(listOf(userId))) + }.wasNotInvoked() + coVerify { + arrangement.legalHoldSystemMessagesHandler.handleEnabledForUser(eq(userId), any()) + }.wasNotInvoked() + coVerify { + arrangement.legalHoldSystemMessagesHandler.handleDisabledForUser(eq(userId), any()) + }.wasNotInvoked() + coVerify { + arrangement.conversationRepository.updateLegalHoldStatus( + eq(conversation.id), + any() + ) + }.wasNotInvoked() + } + + @Test + fun givenUserIsStillUnderLegalHold_whenHandlingUserFetch_thenDoNotChangeStatuses() = runTest { + // given + val userId = TestUser.OTHER_USER_ID + val conversation = conversation(legalHoldStatus = Conversation.LegalHoldStatus.ENABLED) + val (arrangement, handler) = Arrangement() + .withGetConversationsByUserIdSuccess(listOf(conversation)) + .withObserveLegalHoldStateForUserSuccess(LegalHoldState.Enabled) // used before legal hold state change + .withMembersHavingLegalHoldClientSuccess(listOf(userId)) // used after legal hold state change + .withUpdateLegalHoldStatusSuccess(isChanged = false) + .arrange() + // when + handler.handleUserFetch(userId, true) + // then + coVerify { + arrangement.fetchUsersClientsFromRemote.invoke(eq(listOf(userId))) + }.wasNotInvoked() + coVerify { + arrangement.legalHoldSystemMessagesHandler.handleEnabledForUser(eq(userId), any()) + }.wasNotInvoked() + coVerify { + arrangement.legalHoldSystemMessagesHandler.handleDisabledForUser(eq(userId), any()) + }.wasNotInvoked() + coVerify { + arrangement.conversationRepository.updateLegalHoldStatus( + eq(conversation.id), + any() + ) + }.wasNotInvoked() + } + + @Test + fun givenUserLegalHoldDisabledButConversationDegraded_whenHandlingEnable_thenDoNotChangeConversationStatusButCreateSystemMessages() = + runTest { + // given + val conversation = conversation(legalHoldStatus = Conversation.LegalHoldStatus.DEGRADED) + val (arrangement, handler) = Arrangement() + .withObserveLegalHoldStateForUserSuccess(LegalHoldState.Disabled) + .withGetConversationsByUserIdSuccess(listOf(conversation)) + .withObserveConversationLegalHoldStatus(Conversation.LegalHoldStatus.DEGRADED) + .withSetLegalHoldChangeNotifiedSuccess() + .arrange() + // when + handler.handleEnable(legalHoldEventEnabled) + // then + coVerify { + arrangement.conversationRepository.updateLegalHoldStatus( + eq(conversation.id), + eq(Conversation.LegalHoldStatus.ENABLED) + ) + }.wasNotInvoked() + coVerify { + arrangement.legalHoldSystemMessagesHandler.handleEnabledForUser(any(), any()) + }.wasInvoked() + } + private class Arrangement { @Mock @@ -827,6 +999,12 @@ class LegalHoldHandlerTest { }.returns(Either.Right(isChanged)) } + suspend fun withObserveConversationLegalHoldStatus(status: Conversation.LegalHoldStatus) = apply { + coEvery { + conversationRepository.observeLegalHoldStatus(any()) + }.returns(flowOf(Either.Right(status))) + } + suspend fun withGetConversationsByUserIdSuccess(conversations: List = emptyList()) = apply { coEvery { conversationRepository.getConversationsByUserId(any())