Skip to content

Commit

Permalink
fix: Ghost incoming call appearing on call start (#2101)
Browse files Browse the repository at this point in the history
  • Loading branch information
ohassine authored Sep 28, 2023
1 parent d842f28 commit 718a7e1
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,13 @@ class CallsScope internal constructor(
callRepository = callRepository,
)

val startCall: StartCallUseCase get() = StartCallUseCase(callManager, syncManager, kaliumConfigs)
val startCall: StartCallUseCase get() = StartCallUseCase(
callManager = callManager,
syncManager = syncManager,
callRepository = callRepository,
answerCall = answerCall,
kaliumConfigs = kaliumConfigs
)

val answerCall: AnswerCallUseCase
get() = AnswerCallUseCaseImpl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package com.wire.kalium.logic.feature.call.usecase

import com.wire.kalium.logic.data.call.CallRepository
import com.wire.kalium.logic.data.call.CallType
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.feature.call.CallManager
Expand All @@ -27,6 +28,7 @@ import com.wire.kalium.logic.functional.fold
import com.wire.kalium.logic.sync.SyncManager
import com.wire.kalium.util.KaliumDispatcher
import com.wire.kalium.util.KaliumDispatcherImpl
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext

/**
Expand All @@ -38,6 +40,8 @@ class StartCallUseCase internal constructor(
private val callManager: Lazy<CallManager>,
private val syncManager: SyncManager,
private val kaliumConfigs: KaliumConfigs,
private val callRepository: CallRepository,
private val answerCall: AnswerCallUseCase,
private val dispatchers: KaliumDispatcher = KaliumDispatcherImpl
) {

Expand All @@ -46,14 +50,22 @@ class StartCallUseCase internal constructor(
callType: CallType = CallType.AUDIO,
) = withContext(dispatchers.default) {
syncManager.waitUntilLiveOrFailure().fold({
Result.SyncFailure
return@withContext Result.SyncFailure
}, {
callRepository.incomingCallsFlow().first().run {
find {
it.conversationId == conversationId
}?.apply {
answerCall(conversationId)
return@withContext Result.Success
}
}
callManager.value.startCall(
conversationId = conversationId,
callType = callType,
isAudioCbr = kaliumConfigs.forceConstantBitrateCalls
)
Result.Success
return@withContext Result.Success
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ package com.wire.kalium.logic.feature.call.usecase

import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.NetworkFailure
import com.wire.kalium.logic.data.call.CallRepository
import com.wire.kalium.logic.data.call.CallType
import com.wire.kalium.logic.feature.call.CallManager
import com.wire.kalium.logic.featureFlags.KaliumConfigs
import com.wire.kalium.logic.framework.TestCall
import com.wire.kalium.logic.framework.TestConversation
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.sync.SyncManager
Expand All @@ -35,18 +37,43 @@ import io.mockative.given
import io.mockative.mock
import io.mockative.once
import io.mockative.verify
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertIs

class StartCallUseCaseTest {

@Test
fun givenAnIncomingCall_whenStartingANewCall_thenAnswerCallUseCaseShouldBeInvokedOnce() =
runTest {
val conversationId = TestConversation.ID

val (arrangement, startCall) = Arrangement()
.withWaitingForSyncSucceeding()
.withAnIncomingCall()
.arrange()

startCall.invoke(conversationId)

verify(arrangement.answerCall)
.suspendFunction(arrangement.answerCall::invoke)
.with(eq(conversationId))
.wasInvoked(once)

verify(arrangement.callManager)
.suspendFunction(arrangement.callManager::startCall)
.with(any(), any(), any())
.wasNotInvoked()
}

@Test
fun givenCallingParamsAndSyncSucceeds_whenRunningUseCase_thenInvokeStartCallOnce() = runTest {
val conversationId = TestConversation.ID

val (arrangement, startCall) = Arrangement()
.withWaitingForSyncSucceeding()
.withNoIncomingCall()
.arrange()

startCall.invoke(conversationId, CallType.AUDIO)
Expand All @@ -55,19 +82,29 @@ class StartCallUseCaseTest {
.suspendFunction(arrangement.callManager::startCall)
.with(eq(conversationId), eq(CallType.AUDIO), eq(false))
.wasInvoked(once)

verify(arrangement.answerCall)
.suspendFunction(arrangement.answerCall::invoke)
.with(any())
.wasNotInvoked()
}

@Test
fun givenCallingParamsAndSyncSucceeds_whenRunningUseCase_thenReturnSuccess() = runTest {
val conversationId = TestConversation.ID

val (_, startCall) = Arrangement()
val (arrangement, startCall) = Arrangement()
.withWaitingForSyncSucceeding()
.withNoIncomingCall()
.arrange()

val result = startCall.invoke(conversationId, CallType.AUDIO)

assertIs<StartCallUseCase.Result.Success>(result)
verify(arrangement.answerCall)
.suspendFunction(arrangement.answerCall::invoke)
.with(any())
.wasNotInvoked()
}

@Test
Expand Down Expand Up @@ -105,6 +142,7 @@ class StartCallUseCaseTest {

val (arrangement, startCall) = Arrangement()
.withWaitingForSyncSucceeding()
.withNoIncomingCall()
.arrangeWithCBR()

startCall.invoke(conversationId, CallType.AUDIO)
Expand All @@ -113,6 +151,10 @@ class StartCallUseCaseTest {
.suspendFunction(arrangement.callManager::startCall)
.with(eq(conversationId), eq(CallType.AUDIO), eq(true))
.wasInvoked(once)
verify(arrangement.answerCall)
.suspendFunction(arrangement.answerCall::invoke)
.with(any())
.wasNotInvoked()
}

private class Arrangement {
Expand All @@ -123,19 +165,45 @@ class StartCallUseCaseTest {
@Mock
val syncManager = mock(classOf<SyncManager>())

private val kaliumConfigs = KaliumConfigs()
@Mock
val answerCall = mock(classOf<AnswerCallUseCase>())

@Mock
val callRepository = mock(classOf<CallRepository>())

private val kaliumConfigs = KaliumConfigs()

private val startCallUseCase = StartCallUseCase(
lazy { callManager }, syncManager, kaliumConfigs
lazy { callManager }, syncManager, kaliumConfigs, callRepository, answerCall
)

private val startCallUseCaseWithCBR = StartCallUseCase(
lazy { callManager }, syncManager, KaliumConfigs(forceConstantBitrateCalls = true)
lazy { callManager },
syncManager,
KaliumConfigs(forceConstantBitrateCalls = true),
callRepository,
answerCall
)

fun withWaitingForSyncSucceeding() = withSyncReturning(Either.Right(Unit))
fun withAnIncomingCall() = apply {
given(callRepository)
.suspendFunction(callRepository::incomingCallsFlow)
.whenInvoked()
.then {
flowOf(listOf(TestCall.groupIncomingCall(TestConversation.ID)))
}
}

fun withNoIncomingCall() = apply {
given(callRepository)
.suspendFunction(callRepository::incomingCallsFlow)
.whenInvoked()
.then { flowOf(listOf()) }
}

fun withWaitingForSyncFailing() = withSyncReturning(Either.Left(NetworkFailure.NoNetworkConnection(null)))
fun withWaitingForSyncFailing() =
withSyncReturning(Either.Left(NetworkFailure.NoNetworkConnection(null)))

private fun withSyncReturning(result: Either<CoreFailure, Unit>) = apply {
given(syncManager)
Expand Down

0 comments on commit 718a7e1

Please sign in to comment.