diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/sync/SlowSyncRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/sync/SlowSyncRepository.kt index 20d60cfcbc6..15b434b2661 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/sync/SlowSyncRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/sync/SlowSyncRepository.kt @@ -101,10 +101,10 @@ internal class SlowSyncRepositoryImpl(private val metadataDao: MetadataDAO) : Sl return metadataDao.valueByKey(key = SLOW_SYNC_VERSION_KEY)?.toIntOrNull() ?: 0 } - private companion object { + companion object { const val LAST_SLOW_SYNC_INSTANT_KEY = "lastSlowSyncInstant" - const val SLOW_SYNC_VERSION_KEY = "slowSyncVersion" - const val MLS_NEEDS_RECOVERY_KEY = "mlsNeedsRecovery" - const val NEEDS_TO_PERSIST_HISTORY_LOST_MESSAGES_KEY = "needsToPersistHistoryLostMessages" + private const val SLOW_SYNC_VERSION_KEY = "slowSyncVersion" + private const val MLS_NEEDS_RECOVERY_KEY = "mlsNeedsRecovery" + private const val NEEDS_TO_PERSIST_HISTORY_LOST_MESSAGES_KEY = "needsToPersistHistoryLostMessages" } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/ConversationEventReceiver.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/ConversationEventReceiver.kt index da6e4019301..3773fc88d50 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/ConversationEventReceiver.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/ConversationEventReceiver.kt @@ -104,10 +104,7 @@ internal class ConversationEventReceiverImpl( Either.Right(Unit) } - is Event.Conversation.ConversationMessageTimer -> { - conversationMessageTimerEventHandler.handle(event) - Either.Right(Unit) - } + is Event.Conversation.ConversationMessageTimer -> conversationMessageTimerEventHandler.handle(event) } } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/sync/IncrementalSyncRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/sync/IncrementalSyncRepositoryTest.kt index 961e50cfb01..2880efb1d71 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/sync/IncrementalSyncRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/sync/IncrementalSyncRepositoryTest.kt @@ -27,7 +27,6 @@ import io.mockative.classOf import io.mockative.given import io.mockative.mock import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf @@ -204,7 +203,6 @@ class IncrementalSyncRepositoryTest { assertEquals(initialValue, state) } - @OptIn(ExperimentalCoroutinesApi::class) @Test fun givenASlowStateCollector_whenStateIsUpdatedManyTimes_thenUpdateEmissionShouldNotBeBlockedByOverflownBuffer() = runTest { val updateCount = 10_000 @@ -231,7 +229,6 @@ class IncrementalSyncRepositoryTest { } } - @OptIn(ExperimentalCoroutinesApi::class) @Test fun givenASlowPolicyCollector_whenPolicyIsUpdatedManyTimes_thenUpdateEmissionShouldNotBeBlockedByOverflownBuffer() = runTest { val updateCount = 10_000 diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/sync/SlowSyncRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/sync/SlowSyncRepositoryTest.kt index ba3757a1700..d97312530ed 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/sync/SlowSyncRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/sync/SlowSyncRepositoryTest.kt @@ -30,6 +30,7 @@ import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull +import kotlin.test.assertTrue import kotlin.time.Duration.Companion.seconds class SlowSyncRepositoryTest { @@ -43,6 +44,15 @@ class SlowSyncRepositoryTest { slowSyncRepository = SlowSyncRepositoryImpl(database.builder.metadataDAO) } + @Test + fun givenLastInstantWasNeverSet_whenGettingLastInstant_thenTheStateIsNull() = runTest(testDispatcher) { + // Empty Given + + val lastSyncInstant = slowSyncRepository.observeLastSlowSyncCompletionInstant().first() + + assertNull(lastSyncInstant) + } + @Test fun givenInstantIsUpdated_whenGettingTheLastSlowSyncInstant_thenShouldReturnTheNewState() = runTest(testDispatcher) { val instant = DateTimeUtil.currentInstant() @@ -59,19 +69,6 @@ class SlowSyncRepositoryTest { assertEquals(version, slowSyncRepository.getSlowSyncVersion()) } - @IgnoreIOS // TODO investigate why test is failing - @Test - fun givenLastInstantWasNeverSet_whenGettingLastInstant_thenTheStateIsNull() = runTest(testDispatcher) { - // Empty Given - - val lastSyncInstant = slowSyncRepository.observeLastSlowSyncCompletionInstant().first() - - assertNull(lastSyncInstant) - } - - // TODO: Re-enable once we can update Turbine to 0.11.0+ (Requires Kotlin 1.6.21+) - // @Ignore - @IgnoreIOS // TODO investigate why test is failing @Test fun givenAnInstantIsUpdated_whenObservingTheLastSlowSyncInstant_thenTheNewStateIsPropagatedForObservers() = runTest(testDispatcher) { val firstInstant = DateTimeUtil.currentInstant() @@ -132,4 +129,13 @@ class SlowSyncRepositoryTest { assertEquals(newStatus, currentState) } + + @Test + fun givenMetaDataDao_whenClearLastSlowSyncCompletionInstantIsCalled_thenInvokeDeleteValueOnce() = runTest(testDispatcher) { + slowSyncRepository.setNeedsToPersistHistoryLostMessage(true) + + val result = slowSyncRepository.needsToPersistHistoryLostMessage() + + assertTrue { result } + } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestEvent.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestEvent.kt index 3eea9359a49..80381c993a3 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestEvent.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestEvent.kt @@ -27,8 +27,6 @@ import com.wire.kalium.logic.data.event.Event import com.wire.kalium.logic.data.user.Connection import com.wire.kalium.logic.data.user.ConnectionState import com.wire.kalium.logic.data.user.UserId -import com.wire.kalium.network.api.base.authenticated.client.ClientTypeDTO -import com.wire.kalium.network.api.base.authenticated.client.DeviceTypeDTO import com.wire.kalium.util.DateTimeUtil.toIsoDateTimeString import kotlinx.datetime.Instant @@ -43,6 +41,15 @@ object TestEvent { "2022-03-30T15:36:00.000Z" ) + fun memberLeave(eventId: String = "eventId", members: List = listOf()) = Event.Conversation.MemberLeave( + eventId, + TestConversation.ID, + false, + TestUser.USER_ID, + listOf(), + "2022-03-30T15:36:00.000Z" + ) + fun memberChange(eventId: String = "eventId", member: Member) = Event.Conversation.MemberChanged.MemberChangedRole( eventId, TestConversation.ID, @@ -188,4 +195,30 @@ object TestEvent { timestamp.toIsoDateTimeString(), "content", ) + + fun newConversationEvent() = Event.Conversation.NewConversation( + id = "eventId", + conversationId = TestConversation.ID, + transient = false, + timestampIso = "timestamp", + conversation = TestConversation.CONVERSATION_RESPONSE, + senderUserId = TestUser.SELF.id + ) + + fun newMLSWelcomeEvent() = Event.Conversation.MLSWelcome( + "eventId", + TestConversation.ID, + false, + TestUser.USER_ID, + "dummy-message", + timestampIso = "2022-03-30T15:36:00.000Z" + ) + + fun newAccessUpdateEvent() = Event.Conversation.AccessUpdate( + id = "eventId", + conversationId = TestConversation.ID, + data = TestConversation.CONVERSATION_RESPONSE, + qualifiedFrom = TestUser.USER_ID, + transient = false + ) } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/MissingMetadataUpdateManagerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/MissingMetadataUpdateManagerTest.kt index c103953b56b..8859ed8a013 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/MissingMetadataUpdateManagerTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/MissingMetadataUpdateManagerTest.kt @@ -32,12 +32,10 @@ import io.mockative.given import io.mockative.mock import io.mockative.once import io.mockative.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.coroutines.yield import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class MissingMetadataUpdateManagerTest { @Test diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/ObserveSyncStateUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/ObserveSyncStateUseCaseTest.kt index f0099e77dd6..fb2e3998653 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/ObserveSyncStateUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/ObserveSyncStateUseCaseTest.kt @@ -18,34 +18,194 @@ package com.wire.kalium.logic.sync -import com.wire.kalium.logic.data.session.SessionRepository -import com.wire.kalium.logic.data.sync.InMemoryIncrementalSyncRepository -import com.wire.kalium.logic.data.sync.SlowSyncRepositoryImpl +import app.cash.turbine.test +import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.data.sync.IncrementalSyncRepository +import com.wire.kalium.logic.data.sync.IncrementalSyncStatus import com.wire.kalium.logic.data.sync.SlowSyncRepository -import com.wire.kalium.persistence.TestUserDatabase -import com.wire.kalium.persistence.dao.UserIDEntity +import com.wire.kalium.logic.data.sync.SlowSyncStatus +import com.wire.kalium.logic.data.sync.SlowSyncStep +import com.wire.kalium.logic.data.sync.SyncState +import com.wire.kalium.logic.test_util.TestKaliumDispatcher import io.mockative.Mock -import io.mockative.classOf +import io.mockative.given import io.mockative.mock -import kotlin.test.BeforeTest +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import kotlin.test.Test +import kotlin.test.assertEquals class ObserveSyncStateUseCaseTest { - private lateinit var slowSyncRepository: SlowSyncRepository - private lateinit var incrementalSyncRepository: IncrementalSyncRepository - private lateinit var observeSyncState: ObserveSyncStateUseCase + @Test + fun givenSlowSyncStatusEmitsFailedState_whenRunningUseCase_thenEmitFailedState() = runTest(TestKaliumDispatcher.default) { + val (_, useCase) = Arrangement() + .withSlowSyncFailureState() + .withIncrementalSyncLiveState() + .arrange() - @Mock - val sessionRepository = mock(classOf()) + useCase().test { + val item = awaitItem() + assertEquals(SyncState.Failed(coreFailure), item) + } + } + + @Test + fun givenSlowSyncStatusEmitsOngoingState_whenRunningUseCase_thenEmitSlowSyncState() = runTest(TestKaliumDispatcher.default) { + val (_, useCase) = Arrangement() + .withSlowSyncOngoingState() + .withIncrementalSyncLiveState() + .arrange() + + useCase().test { + val item = awaitItem() + assertEquals(SyncState.SlowSync, item) + } + } + + @Test + fun givenSlowSyncStatusEmitsPendingState_whenRunningUseCase_thenEmitWaitingState() = runTest(TestKaliumDispatcher.default) { + val (_, useCase) = Arrangement() + .withSlowSyncPendingState() + .withIncrementalSyncLiveState() + .arrange() + + useCase().test { + val item = awaitItem() + assertEquals(SyncState.Waiting, item) + } + } + + @Test + fun givenIncrementalSyncStateEmitsLiveState_whenRunningUseCase_thenEmitLiveState() = runTest(TestKaliumDispatcher.default) { + val (_, useCase) = Arrangement() + .withSlowSyncCompletedState() + .withIncrementalSyncLiveState() + .arrange() + + useCase().test { + val item = awaitItem() + assertEquals(SyncState.Live, item) + } + } + + @Test + fun givenIncrementalSyncStateEmitsFailedState_whenRunningUseCase_thenEmitFailedState() = + runTest(TestKaliumDispatcher.default) { + val (_, useCase) = Arrangement() + .withSlowSyncCompletedState() + .withIncrementalSyncFailedState() + .arrange() + + useCase().test { + val item = awaitItem() + assertEquals(SyncState.Failed(coreFailure), item) + } + } + + @Test + fun givenIncrementalSyncStateEmitsFetchingPendingEventsState_whenRunningUseCase_thenEmitGatheringPendingEventsState() = + runTest(TestKaliumDispatcher.default) { + val (_, useCase) = Arrangement() + .withSlowSyncCompletedState() + .withIncrementalSyncFetchingPendingEventsState() + .arrange() + + useCase().test { + val item = awaitItem() + assertEquals(SyncState.GatheringPendingEvents, item) + } + } - @BeforeTest - fun setup() { - val database = TestUserDatabase(UserIDEntity("SELF_USER", "DOMAIN")) - slowSyncRepository = SlowSyncRepositoryImpl(database.builder.metadataDAO) - incrementalSyncRepository = InMemoryIncrementalSyncRepository() - observeSyncState = ObserveSyncStateUseCase(slowSyncRepository, incrementalSyncRepository) + @Test + fun givenIncrementalSyncStateEmitsPendingState_whenRunningUseCase_thenEmitGatheringPendingEventsState() = + runTest(TestKaliumDispatcher.default) { + val (_, useCase) = Arrangement() + .withSlowSyncCompletedState() + .withIncrementalSyncPendingState() + .arrange() + + useCase().test { + val item = awaitItem() + assertEquals(SyncState.GatheringPendingEvents, item) + } + } + + private class Arrangement { + + @Mock + val slowSyncRepository: SlowSyncRepository = mock(SlowSyncRepository::class) + + @Mock + val incrementalSyncRepository: IncrementalSyncRepository = mock(IncrementalSyncRepository::class) + + fun arrange() = this to ObserveSyncStateUseCase( + slowSyncRepository = slowSyncRepository, + incrementalSyncRepository = incrementalSyncRepository + ) + + fun withSlowSyncFailureState() = apply { + given(slowSyncRepository) + .getter(slowSyncRepository::slowSyncStatus) + .whenInvoked() + .thenReturn(slowSyncFailureFlow) + } + + fun withSlowSyncOngoingState() = apply { + given(slowSyncRepository) + .getter(slowSyncRepository::slowSyncStatus) + .whenInvoked() + .thenReturn(MutableStateFlow(SlowSyncStatus.Ongoing(SlowSyncStep.CONTACTS)).asStateFlow()) + } + + fun withSlowSyncPendingState() = apply { + given(slowSyncRepository) + .getter(slowSyncRepository::slowSyncStatus) + .whenInvoked() + .thenReturn(MutableStateFlow(SlowSyncStatus.Pending).asStateFlow()) + } + + fun withSlowSyncCompletedState() = apply { + given(slowSyncRepository) + .getter(slowSyncRepository::slowSyncStatus) + .whenInvoked() + .thenReturn(MutableStateFlow(SlowSyncStatus.Complete).asStateFlow()) + } + + fun withIncrementalSyncLiveState() = apply { + given(incrementalSyncRepository) + .getter(incrementalSyncRepository::incrementalSyncState) + .whenInvoked() + .thenReturn(flowOf(IncrementalSyncStatus.Live)) + } + + fun withIncrementalSyncFailedState() = apply { + given(incrementalSyncRepository) + .getter(incrementalSyncRepository::incrementalSyncState) + .whenInvoked() + .thenReturn(incrementalSyncFailureFlow) + } + + fun withIncrementalSyncFetchingPendingEventsState() = apply { + given(incrementalSyncRepository) + .getter(incrementalSyncRepository::incrementalSyncState) + .whenInvoked() + .thenReturn(flowOf(IncrementalSyncStatus.FetchingPendingEvents)) + } + + fun withIncrementalSyncPendingState() = apply { + given(incrementalSyncRepository) + .getter(incrementalSyncRepository::incrementalSyncState) + .whenInvoked() + .thenReturn(flowOf(IncrementalSyncStatus.Pending)) + } } - // TODO(test): Add tests + companion object { + val coreFailure = CoreFailure.Unknown(null) + val slowSyncFailureFlow = MutableStateFlow(SlowSyncStatus.Failed(coreFailure)).asStateFlow() + val incrementalSyncFailureFlow = flowOf(IncrementalSyncStatus.Failed(coreFailure)) + } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/PendingMessagesSenderWorkerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/PendingMessagesSenderWorkerTest.kt index 6405d955917..55e8192b304 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/PendingMessagesSenderWorkerTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/PendingMessagesSenderWorkerTest.kt @@ -32,12 +32,10 @@ import io.mockative.given import io.mockative.mock import io.mockative.once import io.mockative.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlin.test.BeforeTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class PendingMessagesSenderWorkerTest { @Mock diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/SyncManagerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/SyncManagerTest.kt index 3aead52f235..48dfc761542 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/SyncManagerTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/SyncManagerTest.kt @@ -39,6 +39,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import kotlin.test.Test +import kotlin.test.assertFalse import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) @@ -152,6 +153,76 @@ class SyncManagerTest { result.await().shouldFail() } + @Test + fun givenSlowSyncFailed_whenWaitingUntilStartedOrFailure_thenShouldReturnFailure() = runTest { + val (arrangement, syncManager) = Arrangement().arrange() + arrangement.slowSyncRepository.updateSlowSyncStatus(SlowSyncStatus.Failed(CoreFailure.MissingClientRegistration)) + + val result = syncManager.waitUntilStartedOrFailure() + + result.shouldFail() + } + + @Test + fun givenSlowSyncOngoing_whenWaitingUntilStartedOrFailure_thenShouldReturnSuccess() = runTest { + val (arrangement, syncManager) = Arrangement().arrange() + arrangement.slowSyncRepository.updateSlowSyncStatus(SlowSyncStatus.Ongoing(SlowSyncStep.CONNECTIONS)) + + val result = syncManager.waitUntilStartedOrFailure() + + result.shouldSucceed() + } + + @Test + fun givenSlowSyncComplete_whenWaitingUntilStartedOrFailure_thenShouldReturnSuccess() = runTest { + val (arrangement, syncManager) = Arrangement().arrange() + arrangement.slowSyncRepository.updateSlowSyncStatus(SlowSyncStatus.Complete) + + val result = syncManager.waitUntilStartedOrFailure() + + result.shouldSucceed() + } + + @Test + fun givenSlowSyncRepositoryReturnsOngoingState_whenCallingIsSlowSyncOngoing_thenReturnTrue() = runTest { + val (arrangement, syncManager) = Arrangement().arrange() + arrangement.slowSyncRepository.updateSlowSyncStatus(SlowSyncStatus.Ongoing(SlowSyncStep.CONNECTIONS)) + + val result = syncManager.isSlowSyncOngoing() + + assertTrue { result } + } + + @Test + fun givenSlowSyncRepositoryReturnsDifferentStateThanOngoing_whenCallingIsSlowSyncOngoing_thenReturnFalse() = runTest { + val (arrangement, syncManager) = Arrangement().arrange() + arrangement.slowSyncRepository.updateSlowSyncStatus(SlowSyncStatus.Pending) + + val result = syncManager.isSlowSyncOngoing() + + assertFalse { result } + } + + @Test + fun givenSlowSyncRepositoryReturnsCompleteState_whenCallingIsSlowSyncCompleted_thenReturnTrue() = runTest { + val (arrangement, syncManager) = Arrangement().arrange() + arrangement.slowSyncRepository.updateSlowSyncStatus(SlowSyncStatus.Complete) + + val result = syncManager.isSlowSyncCompleted() + + assertTrue { result } + } + + @Test + fun givenSlowSyncRepositoryReturnsDifferentStateThanComplete_whenCallingIsSlowSyncCompleted_thenReturnFalse() = runTest { + val (arrangement, syncManager) = Arrangement().arrange() + arrangement.slowSyncRepository.updateSlowSyncStatus(SlowSyncStatus.Pending) + + val result = syncManager.isSlowSyncCompleted() + + assertFalse { result } + } + @Suppress("unused") private class Arrangement { val database = TestUserDatabase(UserIDEntity("SELF_USER", "DOMAIN")) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/ConversationEventReceiverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/ConversationEventReceiverTest.kt new file mode 100644 index 00000000000..ddb2c4a96d1 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/ConversationEventReceiverTest.kt @@ -0,0 +1,311 @@ +/* + * Wire + * Copyright (C) 2023 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.sync.receiver + +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.data.conversation.Conversation +import com.wire.kalium.logic.framework.TestEvent +import com.wire.kalium.logic.framework.TestUser +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.sync.receiver.conversation.ConversationMessageTimerEventHandler +import com.wire.kalium.logic.sync.receiver.conversation.DeletedConversationEventHandler +import com.wire.kalium.logic.sync.receiver.conversation.MLSWelcomeEventHandler +import com.wire.kalium.logic.sync.receiver.conversation.MemberChangeEventHandler +import com.wire.kalium.logic.sync.receiver.conversation.MemberJoinEventHandler +import com.wire.kalium.logic.sync.receiver.conversation.MemberLeaveEventHandler +import com.wire.kalium.logic.sync.receiver.conversation.NewConversationEventHandler +import com.wire.kalium.logic.sync.receiver.conversation.ReceiptModeUpdateEventHandler +import com.wire.kalium.logic.sync.receiver.conversation.RenamedConversationEventHandler +import com.wire.kalium.logic.sync.receiver.conversation.message.NewMessageEventHandler +import com.wire.kalium.logic.util.shouldFail +import com.wire.kalium.logic.util.shouldSucceed +import io.mockative.Mock +import io.mockative.any +import io.mockative.classOf +import io.mockative.eq +import io.mockative.given +import io.mockative.mock +import io.mockative.once +import io.mockative.verify +import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Instant +import kotlin.test.Test + +class ConversationEventReceiverTest { + @Test + fun givenNewMessageEvent_whenOnEventInvoked_thenNewMessageEventHandlerShouldBeCalled() = runTest { + val newMessageEvent = TestEvent.newMessageEvent("some-dummy-encrypted-content") + + val (arrangement, featureConfigEventReceiver) = Arrangement().arrange() + + val result = featureConfigEventReceiver.onEvent(newMessageEvent) + + verify(arrangement.newMessageEventHandler) + .suspendFunction(arrangement.newMessageEventHandler::handleNewProteusMessage) + .with(eq(newMessageEvent)) + .wasInvoked(once) + + result.shouldSucceed() + } + + @Test + fun givenNewMLSMessageEvent_whenOnEventInvoked_thenNewMLSMessageEventHandlerShouldBeCalled() = runTest { + val newMLSMessageEvent = TestEvent.newMLSMessageEvent(Instant.DISTANT_FUTURE) + + val (arrangement, featureConfigEventReceiver) = Arrangement().arrange() + + val result = featureConfigEventReceiver.onEvent(newMLSMessageEvent) + + verify(arrangement.newMessageEventHandler) + .suspendFunction(arrangement.newMessageEventHandler::handleNewMLSMessage) + .with(eq(newMLSMessageEvent)) + .wasInvoked(once) + + result.shouldSucceed() + } + + @Test + fun givenNewConversationEvent_whenOnEventInvoked_thenNewConversationHandlerShouldBeCalled() = runTest { + val newConversationEvent = TestEvent.newConversationEvent() + + val (arrangement, featureConfigEventReceiver) = Arrangement().arrange() + + val result = featureConfigEventReceiver.onEvent(newConversationEvent) + + verify(arrangement.newConversationEventHandler) + .suspendFunction(arrangement.newConversationEventHandler::handle) + .with(eq(newConversationEvent)) + .wasInvoked(once) + + result.shouldSucceed() + } + + @Test + fun givenDeletedConversationEvent_whenOnEventInvoked_thenDeletedConversationHandlerShouldBeCalled() = runTest { + val deletedConversationEvent = TestEvent.deletedConversation() + + val (arrangement, featureConfigEventReceiver) = Arrangement().arrange() + + val result = featureConfigEventReceiver.onEvent(deletedConversationEvent) + + verify(arrangement.deletedConversationEventHandler) + .suspendFunction(arrangement.deletedConversationEventHandler::handle) + .with(eq(deletedConversationEvent)) + .wasInvoked(once) + + result.shouldSucceed() + } + + @Test + fun givenMemberJoinEvent_whenOnEventInvoked_thenMemberJoinHandlerShouldBeCalled() = runTest { + val memberJoinEvent = TestEvent.memberJoin() + + val (arrangement, featureConfigEventReceiver) = Arrangement() + .withMemberJoinSucceeded() + .arrange() + + val result = featureConfigEventReceiver.onEvent(memberJoinEvent) + + verify(arrangement.memberJoinEventHandler) + .suspendFunction(arrangement.memberJoinEventHandler::handle) + .with(eq(memberJoinEvent)) + .wasInvoked(once) + + result.shouldSucceed() + } + + @Test + fun givenMemberLeaveEvent_whenOnEventInvoked_thenPropagateMemberLeaveHandlerResult() = runTest { + val memberLeaveEvent = TestEvent.memberLeave() + + val (arrangement, featureConfigEventReceiver) = Arrangement() + .withMemberLeaveSucceeded() + .arrange() + + val result = featureConfigEventReceiver.onEvent(memberLeaveEvent) + + verify(arrangement.memberLeaveEventHandler) + .suspendFunction(arrangement.memberLeaveEventHandler::handle) + .with(eq(memberLeaveEvent)) + .wasInvoked(once) + + result.shouldSucceed() + } + + @Test + fun givenMemberChangeEvent_whenOnEventInvoked_thenMemberChangeHandlerShouldBeCalled() = runTest { + val memberChangeEvent = TestEvent.memberChange(member = Conversation.Member(TestUser.USER_ID, Conversation.Member.Role.Admin)) + + val (arrangement, featureConfigEventReceiver) = Arrangement().arrange() + + val result = featureConfigEventReceiver.onEvent(memberChangeEvent) + + verify(arrangement.memberChangeEventHandler) + .suspendFunction(arrangement.memberChangeEventHandler::handle) + .with(eq(memberChangeEvent)) + .wasInvoked(once) + result.shouldSucceed() + } + + @Test + fun givenMLSWelcomeEvent_whenOnEventInvoked_thenMlsWelcomeHandlerShouldBeCalled() = runTest { + val mlsWelcomeEvent = TestEvent.newMLSWelcomeEvent() + + val (arrangement, featureConfigEventReceiver) = Arrangement().arrange() + + val result = featureConfigEventReceiver.onEvent(mlsWelcomeEvent) + + verify(arrangement.mLSWelcomeEventHandler) + .suspendFunction(arrangement.mLSWelcomeEventHandler::handle) + .with(eq(mlsWelcomeEvent)) + .wasInvoked(once) + result.shouldSucceed() + } + + @Test + fun givenRenamedConversationEvent_whenOnEventInvoked_thenRenamedConversationHandlerShouldBeCalled() = runTest { + val renamedConversationEvent = TestEvent.renamedConversation() + + val (arrangement, featureConfigEventReceiver) = Arrangement().arrange() + + val result = featureConfigEventReceiver.onEvent(renamedConversationEvent) + + verify(arrangement.renamedConversationEventHandler) + .suspendFunction(arrangement.renamedConversationEventHandler::handle) + .with(eq(renamedConversationEvent)) + .wasInvoked(once) + result.shouldSucceed() + } + + @Test + fun givenConversationReceiptModeEvent_whenOnEventInvoked_thenReceiptModeUpdateEventHandlerShouldBeCalled() = runTest { + val receiptModeUpdateEvent = TestEvent.receiptModeUpdate() + + val (arrangement, featureConfigEventReceiver) = Arrangement().arrange() + + val result = featureConfigEventReceiver.onEvent(receiptModeUpdateEvent) + + verify(arrangement.receiptModeUpdateEventHandler) + .suspendFunction(arrangement.receiptModeUpdateEventHandler::handle) + .with(eq(receiptModeUpdateEvent)) + .wasInvoked(once) + result.shouldSucceed() + } + + @Test + fun givenAccessUpdateEvent_whenOnEventInvoked_thenReturnSuccess() = runTest { + val accessUpdateEvent = TestEvent.newAccessUpdateEvent() + + val (_, featureConfigEventReceiver) = Arrangement().arrange() + + val result = featureConfigEventReceiver.onEvent(accessUpdateEvent) + + result.shouldSucceed() + } + + @Test + fun givenConversationMessageTimerEvent_whenOnEventInvoked_thenPropagateConversationMessageTimerEventHandlerResult() = runTest { + val conversationMessageTimerEvent = TestEvent.timerChanged() + + val (arrangement, featureConfigEventReceiver) = Arrangement() + .withConversationMessageTimerFailed() + .arrange() + + val result = featureConfigEventReceiver.onEvent(conversationMessageTimerEvent) + + verify(arrangement.conversationMessageTimerEventHandler) + .suspendFunction(arrangement.conversationMessageTimerEventHandler::handle) + .with(eq(conversationMessageTimerEvent)) + .wasInvoked(once) + + result.shouldFail() + } + + private class Arrangement { + + @Mock + val conversationMessageTimerEventHandler = mock(classOf()) + + @Mock + val receiptModeUpdateEventHandler = mock(classOf()) + + @Mock + val renamedConversationEventHandler = mock(classOf()) + + @Mock + val mLSWelcomeEventHandler = mock(classOf()) + + @Mock + val memberChangeEventHandler = mock(classOf()) + + @Mock + val memberLeaveEventHandler = mock(classOf()) + + @Mock + val memberJoinEventHandler = mock(classOf()) + + @Mock + val newMessageEventHandler = mock(classOf()) + + @Mock + val newConversationEventHandler = mock(classOf()) + + @Mock + val deletedConversationEventHandler = mock(classOf()) + + private val featureConfigEventReceiver: ConversationEventReceiver = ConversationEventReceiverImpl( + newMessageHandler = newMessageEventHandler, + newConversationHandler = newConversationEventHandler, + deletedConversationHandler = deletedConversationEventHandler, + memberJoinHandler = memberJoinEventHandler, + memberLeaveHandler = memberLeaveEventHandler, + memberChangeHandler = memberChangeEventHandler, + mlsWelcomeHandler = mLSWelcomeEventHandler, + renamedConversationHandler = renamedConversationEventHandler, + receiptModeUpdateEventHandler = receiptModeUpdateEventHandler, + conversationMessageTimerEventHandler = conversationMessageTimerEventHandler + ) + + fun arrange() = this to featureConfigEventReceiver + + fun withMemberLeaveSucceeded() = apply { + given(memberLeaveEventHandler) + .suspendFunction(memberLeaveEventHandler::handle) + .whenInvokedWith(any()) + .thenReturn(Either.Right(Unit)) + } + + fun withMemberJoinSucceeded() = apply { + given(memberJoinEventHandler) + .suspendFunction(memberJoinEventHandler::handle) + .whenInvokedWith(any()) + .thenReturn(Either.Right(Unit)) + } + + fun withConversationMessageTimerFailed() = apply { + given(conversationMessageTimerEventHandler) + .suspendFunction(conversationMessageTimerEventHandler::handle) + .whenInvokedWith(any()) + .thenReturn(Either.Left(failure)) + } + } + + companion object { + val failure = CoreFailure.MissingClientRegistration + } +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt index d9eb22ce7d5..42ab6e48a6a 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt @@ -42,13 +42,11 @@ import io.mockative.matching import io.mockative.mock import io.mockative.once import io.mockative.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.time.DurationUnit import kotlin.time.toDuration -@OptIn(ExperimentalCoroutinesApi::class) class FeatureConfigEventReceiverTest { @Test diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/TeamEventReceiverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/TeamEventReceiverTest.kt index 0d51dc1d55e..fb581038b76 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/TeamEventReceiverTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/TeamEventReceiverTest.kt @@ -35,12 +35,10 @@ import io.mockative.given import io.mockative.mock import io.mockative.once import io.mockative.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class TeamEventReceiverTest { @Test diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiverTest.kt index be00ba75bc5..47a8b9fca4a 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiverTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiverTest.kt @@ -39,11 +39,9 @@ import io.mockative.given import io.mockative.mock import io.mockative.once import io.mockative.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class UserEventReceiverTest { @Test diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserPropertiesEventReceiverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserPropertiesEventReceiverTest.kt index 92801003c32..2e26c7f9239 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserPropertiesEventReceiverTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserPropertiesEventReceiverTest.kt @@ -28,11 +28,9 @@ import io.mockative.given import io.mockative.mock import io.mockative.once import io.mockative.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class UserPropertiesEventReceiverTest { @Test diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/conversation/MemberChangeEventHandlerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/conversation/MemberChangeEventHandlerTest.kt index 3250c05e67a..44a48609b1e 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/conversation/MemberChangeEventHandlerTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/conversation/MemberChangeEventHandlerTest.kt @@ -35,11 +35,9 @@ import io.mockative.given import io.mockative.mock import io.mockative.once import io.mockative.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlin.test.Test -@OptIn(ExperimentalCoroutinesApi::class) class MemberChangeEventHandlerTest { @Test