diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt index 9a2ed21755..184766e323 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt @@ -36,7 +36,7 @@ import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.push.api.PushService -import io.element.android.libraries.pushproviders.api.RegistrationFailure +import io.element.android.libraries.push.api.PusherRegistrationFailure import io.element.android.services.analytics.api.AnalyticsService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine @@ -71,7 +71,17 @@ class LoggedInPresenter( when (sessionVerifiedStatus) { SessionVerifiedStatus.Unknown -> Unit SessionVerifiedStatus.Verified -> { - ensurePusherIsRegistered(pusherRegistrationState) + Timber.tag(pusherTag.value).d("Ensure pusher is registered") + pushService.ensurePusherIsRegistered(matrixClient).fold( + onSuccess = { + Timber.tag(pusherTag.value).d("Pusher registered") + pusherRegistrationState.value = AsyncData.Success(Unit) + }, + onFailure = { + Timber.tag(pusherTag.value).e(it, "Failed to register pusher") + pusherRegistrationState.value = AsyncData.Failure(it) + }, + ) } SessionVerifiedStatus.NotVerified -> { pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.AccountNotVerified()) @@ -133,59 +143,6 @@ class LoggedInPresenter( currentSlidingSyncVersion == SlidingSyncVersion.Proxy } - private suspend fun ensurePusherIsRegistered(pusherRegistrationState: MutableState>) { - Timber.tag(pusherTag.value).d("Ensure pusher is registered") - val currentPushProvider = pushService.getCurrentPushProvider(matrixClient.sessionId) - val result = if (currentPushProvider == null) { - Timber.tag(pusherTag.value).d("Register with the first available push provider with at least one distributor") - val pushProvider = pushService.getAvailablePushProviders() - .firstOrNull { it.getDistributors().isNotEmpty() } - // Else fallback to the first available push provider (the list should never be empty) - ?: pushService.getAvailablePushProviders().firstOrNull() - ?: return Unit - .also { Timber.tag(pusherTag.value).w("No push providers available") } - .also { pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.NoProvidersAvailable()) } - val distributor = pushProvider.getDistributors().firstOrNull() - ?: return Unit - .also { Timber.tag(pusherTag.value).w("No distributors available") } - .also { - // In this case, consider the push provider is chosen. - pushService.selectPushProvider(matrixClient.sessionId, pushProvider) - } - .also { pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable()) } - pushService.registerWith(matrixClient, pushProvider, distributor) - } else { - val currentPushDistributor = currentPushProvider.getCurrentDistributor(matrixClient.sessionId) - if (currentPushDistributor == null) { - Timber.tag(pusherTag.value).d("Register with the first available distributor") - val distributor = currentPushProvider.getDistributors().firstOrNull() - ?: return Unit - .also { Timber.tag(pusherTag.value).w("No distributors available") } - .also { pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable()) } - pushService.registerWith(matrixClient, currentPushProvider, distributor) - } else { - Timber.tag(pusherTag.value).d("Re-register with the current distributor") - pushService.registerWith(matrixClient, currentPushProvider, currentPushDistributor) - } - } - result.fold( - onSuccess = { - Timber.tag(pusherTag.value).d("Pusher registered") - pusherRegistrationState.value = AsyncData.Success(Unit) - }, - onFailure = { - Timber.tag(pusherTag.value).e(it, "Failed to register pusher") - if (it is RegistrationFailure) { - pusherRegistrationState.value = AsyncData.Failure( - PusherRegistrationFailure.RegistrationFailure(it.clientException, it.isRegisteringAgain) - ) - } else { - pusherRegistrationState.value = AsyncData.Failure(it) - } - } - ) - } - private fun reportCryptoStatusToAnalytics(verificationState: SessionVerifiedStatus, recoveryState: RecoveryState) { // Update first the user property, to store the current status for that posthog user val userVerificationState = verificationState.toAnalyticsUserPropertyValue() diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt index 9f92a19506..b2f5407519 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt @@ -10,6 +10,7 @@ package io.element.android.appnav.loggedin import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.push.api.PusherRegistrationFailure open class LoggedInStateProvider : PreviewParameterProvider { override val values: Sequence diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index bb908ac0fd..62d8de8c29 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -25,6 +25,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.matrix.api.exception.isNetworkError +import io.element.android.libraries.push.api.PusherRegistrationFailure import io.element.android.libraries.ui.strings.CommonStrings @Composable diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt index aca9545ae7..849dfa85b2 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt @@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import io.element.android.libraries.push.api.PushService +import io.element.android.libraries.push.api.PusherRegistrationFailure import io.element.android.libraries.push.test.FakePushService import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider @@ -42,7 +43,6 @@ import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.consumeItemsUntilPredicate -import io.element.android.tests.testutils.lambda.any import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -115,7 +115,9 @@ class LoggedInPresenterTest { encryptionService = encryptionService, ), syncService = FakeSyncService(initialSyncState = SyncState.Running), - pushService = FakePushService(), + pushService = FakePushService( + ensurePusherIsRegisteredResult = { Result.success(Unit) }, + ), sessionVerificationService = verificationService, analyticsService = analyticsService, encryptionService = encryptionService, @@ -139,10 +141,10 @@ class LoggedInPresenterTest { @Test fun `present - ensure default pusher is not registered if session is not verified`() = runTest { - val lambda = lambdaRecorder> { _, _, _ -> + val lambda = lambdaRecorder> { Result.success(Unit) } - val pushService = createFakePushService(registerWithLambda = lambda) + val pushService = createFakePushService(ensurePusherIsRegisteredResult = lambda) val verificationService = FakeSessionVerificationService( initialSessionVerifiedStatus = SessionVerifiedStatus.NotVerified ) @@ -153,21 +155,18 @@ class LoggedInPresenterTest { val finalState = awaitFirstItem() assertThat(finalState.pusherRegistrationState.errorOrNull()) .isInstanceOf(PusherRegistrationFailure.AccountNotVerified::class.java) - lambda.assertions() - .isNeverCalled() + lambda.assertions().isNeverCalled() } } @Test fun `present - ensure default pusher is registered with default provider`() = runTest { - val lambda = lambdaRecorder> { _, _, _ -> - Result.success(Unit) - } + val lambda = lambdaRecorder> { Result.success(Unit) } val sessionVerificationService = FakeSessionVerificationService( initialSessionVerifiedStatus = SessionVerifiedStatus.Verified ) val pushService = createFakePushService( - registerWithLambda = lambda, + ensurePusherIsRegisteredResult = lambda, ) createLoggedInPresenter( pushService = pushService, @@ -180,27 +179,17 @@ class LoggedInPresenterTest { assertThat(finalState.pusherRegistrationState.isSuccess()).isTrue() lambda.assertions() .isCalledOnce() - .with( - // MatrixClient - any(), - // PushProvider with highest priority (lower index) - value(pushService.getAvailablePushProviders()[0]), - // First distributor - value(pushService.getAvailablePushProviders()[0].getDistributors()[0]), - ) } } @Test fun `present - ensure default pusher is registered with default provider - fail to register`() = runTest { - val lambda = lambdaRecorder> { _, _, _ -> - Result.failure(AN_EXCEPTION) - } + val lambda = lambdaRecorder> { Result.failure(AN_EXCEPTION) } val sessionVerificationService = FakeSessionVerificationService( initialSessionVerifiedStatus = SessionVerifiedStatus.Verified ) val pushService = createFakePushService( - registerWithLambda = lambda, + ensurePusherIsRegisteredResult = lambda, ) createLoggedInPresenter( pushService = pushService, @@ -213,158 +202,36 @@ class LoggedInPresenterTest { assertThat(finalState.pusherRegistrationState.isFailure()).isTrue() lambda.assertions() .isCalledOnce() - .with( - // MatrixClient - any(), - // PushProvider with highest priority (lower index) - value(pushService.getAvailablePushProviders()[0]), - // First distributor - value(pushService.getAvailablePushProviders()[0].getDistributors()[0]), - ) + // Reset the error and do not show again + finalState.eventSink(LoggedInEvents.CloseErrorDialog(doNotShowAgain = false)) + val lastState = awaitItem() + assertThat(lastState.pusherRegistrationState.isUninitialized()).isTrue() + assertThat(lastState.ignoreRegistrationError).isFalse() } } @Test - fun `present - ensure current provider is registered with current distributor`() = runTest { - val lambda = lambdaRecorder> { _, _, _ -> - Result.success(Unit) - } - val sessionVerificationService = FakeSessionVerificationService( - initialSessionVerifiedStatus = SessionVerifiedStatus.Verified - ) - val distributor = Distributor("aDistributorValue1", "aDistributorName1") - val pushProvider = FakePushProvider( - index = 0, - name = "aFakePushProvider0", - distributors = listOf( - Distributor("aDistributorValue0", "aDistributorName0"), - distributor, - ), - currentDistributor = { distributor }, - ) - val pushService = createFakePushService( - pushProvider1 = pushProvider, - currentPushProvider = { pushProvider }, - registerWithLambda = lambda, - ) - createLoggedInPresenter( - pushService = pushService, - sessionVerificationService = sessionVerificationService, - matrixClient = FakeMatrixClient( - accountManagementUrlResult = { Result.success(null) }, - ), - ).test { - val finalState = awaitFirstItem() - assertThat(finalState.pusherRegistrationState.isSuccess()).isTrue() - lambda.assertions() - .isCalledOnce() - .with( - // MatrixClient - any(), - // Current push provider - value(pushProvider), - // Current distributor - value(distributor), - ) - } - } - - @Test - fun `present - if current push provider does not have current distributor, the first one is used`() = runTest { - val lambda = lambdaRecorder> { _, _, _ -> - Result.success(Unit) - } - val sessionVerificationService = FakeSessionVerificationService( - initialSessionVerifiedStatus = SessionVerifiedStatus.Verified - ) - val pushProvider = FakePushProvider( - index = 0, - name = "aFakePushProvider0", - distributors = listOf( - Distributor("aDistributorValue0", "aDistributorName0"), - Distributor("aDistributorValue1", "aDistributorName1"), - ), - currentDistributor = { null }, - ) - val pushService = createFakePushService( - pushProvider0 = pushProvider, - currentPushProvider = { pushProvider }, - registerWithLambda = lambda, - ) - createLoggedInPresenter( - pushService = pushService, - sessionVerificationService = sessionVerificationService, - matrixClient = FakeMatrixClient( - accountManagementUrlResult = { Result.success(null) }, - ), - ).test { - val finalState = awaitFirstItem() - assertThat(finalState.pusherRegistrationState.isSuccess()).isTrue() - lambda.assertions() - .isCalledOnce() - .with( - // MatrixClient - any(), - // PushProvider with highest priority (lower index) - value(pushService.getAvailablePushProviders()[0]), - // First distributor - value(pushService.getAvailablePushProviders()[0].getDistributors()[0]), - ) - } - } - - @Test - fun `present - if current push provider does not have distributors, nothing happen`() = runTest { - val lambda = lambdaRecorder> { _, _, _ -> - Result.success(Unit) - } - val sessionVerificationService = FakeSessionVerificationService( - initialSessionVerifiedStatus = SessionVerifiedStatus.Verified - ) - val pushProvider = FakePushProvider( - index = 0, - name = "aFakePushProvider0", - distributors = emptyList(), - ) - val pushService = createFakePushService( - pushProvider0 = pushProvider, - currentPushProvider = { pushProvider }, - registerWithLambda = lambda, - ) - createLoggedInPresenter( - pushService = pushService, - sessionVerificationService = sessionVerificationService, - ).test { - val finalState = awaitFirstItem() - assertThat(finalState.pusherRegistrationState.errorOrNull()) - .isInstanceOf(PusherRegistrationFailure.NoDistributorsAvailable::class.java) - lambda.assertions() - .isNeverCalled() - } - } - - @Test - fun `present - case no push provider available provider`() = runTest { - val lambda = lambdaRecorder> { _, _, _ -> - Result.success(Unit) - } - val sessionVerificationService = FakeSessionVerificationService(SessionVerifiedStatus.Verified) + fun `present - ensure default pusher is registered with default provider - fail to register - do not show again`() = runTest { + val lambda = lambdaRecorder> { Result.failure(AN_EXCEPTION) } val setIgnoreRegistrationErrorLambda = lambdaRecorder { _, _ -> } + val sessionVerificationService = FakeSessionVerificationService( + initialSessionVerifiedStatus = SessionVerifiedStatus.Verified + ) val pushService = createFakePushService( - pushProvider0 = null, - pushProvider1 = null, - registerWithLambda = lambda, + ensurePusherIsRegisteredResult = lambda, setIgnoreRegistrationErrorLambda = setIgnoreRegistrationErrorLambda, ) createLoggedInPresenter( pushService = pushService, sessionVerificationService = sessionVerificationService, + matrixClient = FakeMatrixClient( + accountManagementUrlResult = { Result.success(null) }, + ), ).test { val finalState = awaitFirstItem() - assertThat(finalState.pusherRegistrationState.errorOrNull()) - .isInstanceOf(PusherRegistrationFailure.NoProvidersAvailable::class.java) + assertThat(finalState.pusherRegistrationState.isFailure()).isTrue() lambda.assertions() - .isNeverCalled() + .isCalledOnce() // Reset the error and do not show again finalState.eventSink(LoggedInEvents.CloseErrorDialog(doNotShowAgain = true)) skipItems(1) @@ -382,95 +249,6 @@ class LoggedInPresenterTest { } } - @Test - fun `present - case one push provider but no distributor available`() = runTest { - val lambda = lambdaRecorder> { _, _, _ -> - Result.success(Unit) - } - val selectPushProviderLambda = lambdaRecorder { _, _ -> } - val sessionVerificationService = FakeSessionVerificationService( - initialSessionVerifiedStatus = SessionVerifiedStatus.Verified - ) - val pushProvider = FakePushProvider( - index = 0, - name = "aFakePushProvider", - distributors = emptyList(), - ) - val pushService = createFakePushService( - pushProvider0 = pushProvider, - pushProvider1 = null, - registerWithLambda = lambda, - selectPushProviderLambda = selectPushProviderLambda, - ) - createLoggedInPresenter( - pushService = pushService, - sessionVerificationService = sessionVerificationService, - ).test { - val finalState = awaitFirstItem() - assertThat(finalState.pusherRegistrationState.errorOrNull()) - .isInstanceOf(PusherRegistrationFailure.NoDistributorsAvailable::class.java) - lambda.assertions() - .isNeverCalled() - selectPushProviderLambda.assertions() - .isCalledOnce() - .with( - // SessionId - value(A_SESSION_ID), - // PushProvider - value(pushProvider), - ) - // Reset the error - finalState.eventSink(LoggedInEvents.CloseErrorDialog(doNotShowAgain = false)) - val lastState = awaitItem() - assertThat(lastState.pusherRegistrationState.isUninitialized()).isTrue() - } - } - - @Test - fun `present - case two push providers but first one does not have distributor - second one will be used`() = runTest { - val lambda = lambdaRecorder> { _, _, _ -> - Result.success(Unit) - } - val sessionVerificationService = FakeSessionVerificationService( - initialSessionVerifiedStatus = SessionVerifiedStatus.Verified - ) - val pushProvider0 = FakePushProvider( - index = 0, - name = "aFakePushProvider0", - distributors = emptyList(), - ) - val distributor = Distributor("aDistributorValue1", "aDistributorName1") - val pushProvider1 = FakePushProvider( - index = 1, - name = "aFakePushProvider1", - distributors = listOf(distributor), - ) - val pushService = createFakePushService( - pushProvider0 = pushProvider0, - pushProvider1 = pushProvider1, - registerWithLambda = lambda, - ) - createLoggedInPresenter( - pushService = pushService, - sessionVerificationService = sessionVerificationService, - matrixClient = FakeMatrixClient( - accountManagementUrlResult = { Result.success(null) }, - ), - ).test { - val finalState = awaitFirstItem() - assertThat(finalState.pusherRegistrationState.isSuccess()).isTrue() - lambda.assertions().isCalledOnce() - .with( - // MatrixClient - any(), - // PushProvider with the distributor - value(pushProvider1), - // First distributor of second push provider - value(distributor), - ) - } - } - private fun createFakePushService( pushProvider0: PushProvider? = FakePushProvider( index = 0, @@ -484,7 +262,7 @@ class LoggedInPresenterTest { distributors = listOf(Distributor("aDistributorValue1", "aDistributorName1")), currentDistributor = { null }, ), - registerWithLambda: (MatrixClient, PushProvider, Distributor) -> Result = { _, _, _ -> + ensurePusherIsRegisteredResult: () -> Result = { Result.success(Unit) }, selectPushProviderLambda: (SessionId, PushProvider) -> Unit = { _, _ -> lambdaError() }, @@ -493,7 +271,7 @@ class LoggedInPresenterTest { ): PushService { return FakePushService( availablePushProviders = listOfNotNull(pushProvider0, pushProvider1), - registerWithLambda = registerWithLambda, + ensurePusherIsRegisteredResult = ensurePusherIsRegisteredResult, currentPushProvider = currentPushProvider, selectPushProviderLambda = selectPushProviderLambda, setIgnoreRegistrationErrorLambda = setIgnoreRegistrationErrorLambda, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt index 24c569cc4b..9d9e80b3f3 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt @@ -25,6 +25,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runUpdatingStateNoSuccess import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService @@ -51,6 +52,8 @@ class NotificationSettingsPresenter( private val pushService: PushService, private val systemNotificationsEnabledProvider: SystemNotificationsEnabledProvider, private val fullScreenIntentPermissionsPresenter: Presenter, + @SessionCoroutineScope + private val sessionCoroutineScope: CoroutineScope, ) : Presenter { @Composable override fun present(): NotificationSettingsState { @@ -141,7 +144,7 @@ class NotificationSettingsPresenter( is NotificationSettingsEvents.SetInviteForMeNotificationsEnabled -> { localCoroutineScope.setInviteForMeNotificationsEnabled(event.enabled, changeNotificationSettingAction) } - is NotificationSettingsEvents.SetNotificationsEnabled -> localCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled) + is NotificationSettingsEvents.SetNotificationsEnabled -> sessionCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled) NotificationSettingsEvents.ClearConfigurationMismatchError -> { matrixSettings.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false) } @@ -262,5 +265,10 @@ class NotificationSettingsPresenter( private fun CoroutineScope.setNotificationsEnabled(userPushStore: UserPushStore, enabled: Boolean) = launch { userPushStore.setNotificationEnabledForDevice(enabled) + if (enabled) { + pushService.ensurePusherIsRegistered(matrixClient) + } else { + pushService.getCurrentPushProvider(matrixClient.sessionId)?.unregister(matrixClient) + } } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt index 27cca104b1..5821d141cc 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt @@ -8,9 +8,6 @@ package io.element.android.features.preferences.impl.notifications -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState @@ -28,6 +25,9 @@ import io.element.android.libraries.pushproviders.test.FakePushProvider import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.consumeItemsUntilPredicate +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.test +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test import kotlin.time.Duration.Companion.milliseconds @@ -36,9 +36,7 @@ class NotificationSettingsPresenterTest { @Test fun `present - ensures initial state is correct`() = runTest { val presenter = createNotificationSettingsPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.appSettings.appNotificationsEnabled).isFalse() assertThat(initialState.appSettings.systemNotificationsEnabled).isTrue() @@ -62,9 +60,7 @@ class NotificationSettingsPresenterTest { fun `present - default group notification mode changed`() = runTest { val notificationSettingsService = FakeNotificationSettingsService() val presenter = createNotificationSettingsPresenter(notificationSettingsService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES) notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES) val updatedState = consumeItemsUntilPredicate { @@ -80,9 +76,7 @@ class NotificationSettingsPresenterTest { fun `present - notification settings mismatched`() = runTest { val notificationSettingsService = FakeNotificationSettingsService() val presenter = createNotificationSettingsPresenter(notificationSettingsService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { notificationSettingsService.setDefaultRoomNotificationMode( isEncrypted = true, isOneToOne = false, @@ -110,9 +104,7 @@ class NotificationSettingsPresenterTest { initialOneToOneDefaultMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY ) val presenter = createNotificationSettingsPresenter(notificationSettingsService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(NotificationSettingsEvents.FixConfigurationMismatch) val fixedState = consumeItemsUntilPredicate(timeout = 2000.milliseconds) { @@ -125,10 +117,19 @@ class NotificationSettingsPresenterTest { @Test fun `present - set notifications enabled`() = runTest { - val presenter = createNotificationSettingsPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + val unregisterWithResult = lambdaRecorder> { Result.success(Unit) } + val ensurePusherIsRegisteredResult = lambdaRecorder> { Result.success(Unit) } + val presenter = createNotificationSettingsPresenter( + pushService = FakePushService( + currentPushProvider = { + FakePushProvider( + unregisterWithResult = unregisterWithResult, + ) + }, + ensurePusherIsRegisteredResult = ensurePusherIsRegisteredResult, + ) + ) + presenter.test { val loadedState = consumeItemsUntilPredicate { it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid }.last() @@ -138,16 +139,21 @@ class NotificationSettingsPresenterTest { !it.appSettings.appNotificationsEnabled }.last() assertThat(updatedState.appSettings.appNotificationsEnabled).isFalse() - cancelAndIgnoreRemainingEvents() + unregisterWithResult.assertions().isCalledOnce() + // Enable notification again + loadedState.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(true)) + val updatedState2 = consumeItemsUntilPredicate { + it.appSettings.appNotificationsEnabled + }.last() + assertThat(updatedState2.appSettings.appNotificationsEnabled).isTrue() + ensurePusherIsRegisteredResult.assertions().isCalledOnce() } } @Test fun `present - set call notifications enabled`() = runTest { val presenter = createNotificationSettingsPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val loadedState = consumeItemsUntilPredicate { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == false }.last() @@ -166,9 +172,7 @@ class NotificationSettingsPresenterTest { @Test fun `present - set invite for me notifications enabled`() = runTest { val presenter = createNotificationSettingsPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val loadedState = consumeItemsUntilPredicate { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.inviteForMeNotificationsEnabled == false }.last() @@ -187,9 +191,7 @@ class NotificationSettingsPresenterTest { @Test fun `present - set atRoom notifications enabled`() = runTest { val presenter = createNotificationSettingsPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val loadedState = consumeItemsUntilPredicate { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == false }.last() @@ -210,9 +212,7 @@ class NotificationSettingsPresenterTest { val notificationSettingsService = FakeNotificationSettingsService() val presenter = createNotificationSettingsPresenter(notificationSettingsService) notificationSettingsService.givenSetAtRoomError(AN_EXCEPTION) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val loadedState = consumeItemsUntilPredicate { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == false }.last() @@ -237,9 +237,7 @@ class NotificationSettingsPresenterTest { val presenter = createNotificationSettingsPresenter( pushService = createFakePushService(), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitLastSequentialItem() assertThat(initialState.currentPushDistributor).isEqualTo(AsyncData.Success(Distributor(value = "aDistributorValue0", name = "aDistributorName0"))) assertThat(initialState.availablePushDistributors).containsExactly( @@ -271,9 +269,7 @@ class NotificationSettingsPresenterTest { val presenter = createNotificationSettingsPresenter( pushService = createFakePushService(), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitLastSequentialItem() assertThat(initialState.currentPushDistributor).isEqualTo(AsyncData.Success(Distributor(value = "aDistributorValue0", name = "aDistributorName0"))) assertThat(initialState.availablePushDistributors).containsExactly( @@ -298,9 +294,7 @@ class NotificationSettingsPresenterTest { pushService = createFakePushService(), fullScreenIntentPermissionsStateLambda = fullScreenIntentPermissionsStateLambda, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitLastSequentialItem() assertThat(initialState.fullScreenIntentPermissionsState.permissionGranted).isFalse() @@ -324,11 +318,7 @@ class NotificationSettingsPresenterTest { }, ), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitLastSequentialItem() - initialState.eventSink.invoke(NotificationSettingsEvents.ChangePushProvider) + presenter.test { val withDialog = awaitItem() assertThat(withDialog.showChangePushProviderDialog).isTrue() withDialog.eventSink(NotificationSettingsEvents.SetPushProvider(1)) @@ -361,7 +351,7 @@ class NotificationSettingsPresenterTest { ) } - private fun createNotificationSettingsPresenter( + private fun TestScope.createNotificationSettingsPresenter( notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), pushService: PushService = FakePushService(), fullScreenIntentPermissionsStateLambda: () -> FullScreenIntentPermissionsState = { aFullScreenIntentPermissionsState() }, @@ -374,6 +364,7 @@ class NotificationSettingsPresenterTest { pushService = pushService, systemNotificationsEnabledProvider = FakeSystemNotificationsEnabledProvider(), fullScreenIntentPermissionsPresenter = { fullScreenIntentPermissionsStateLambda() }, + sessionCoroutineScope = backgroundScope, ) } } diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PushService.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PushService.kt index 2096a04656..a43ee36403 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PushService.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PushService.kt @@ -38,6 +38,15 @@ interface PushService { distributor: Distributor, ): Result + /** + * Ensure that the pusher with the current push provider and distributor is registered. + * If there is no current config, the default push provider with the default distributor will be used. + * Error can be [PusherRegistrationFailure]. + */ + suspend fun ensurePusherIsRegistered( + matrixClient: MatrixClient, + ): Result + /** * Store the given push provider as the current one, but do not register. * To be used when there is no distributor available. diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/PusherRegistrationFailure.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PusherRegistrationFailure.kt similarity index 95% rename from appnav/src/main/kotlin/io/element/android/appnav/loggedin/PusherRegistrationFailure.kt rename to libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PusherRegistrationFailure.kt index 4b386cd46a..b8ae677aab 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/PusherRegistrationFailure.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/PusherRegistrationFailure.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.appnav.loggedin +package io.element.android.libraries.push.api import io.element.android.libraries.matrix.api.exception.ClientException diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt index 5c77438293..3f3a8d05b4 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt @@ -15,8 +15,10 @@ import dev.zacsweers.metro.binding import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.push.api.GetCurrentPushProvider import io.element.android.libraries.push.api.PushService +import io.element.android.libraries.push.api.PusherRegistrationFailure import io.element.android.libraries.push.api.history.PushHistoryItem import io.element.android.libraries.push.impl.push.MutableBatteryOptimizationStore import io.element.android.libraries.push.impl.store.PushDataStore @@ -24,11 +26,13 @@ import io.element.android.libraries.push.impl.test.TestPush import io.element.android.libraries.push.impl.unregistration.ServiceUnregisteredHandler import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider +import io.element.android.libraries.pushproviders.api.RegistrationFailure import io.element.android.libraries.pushstore.api.UserPushStoreFactory import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretStore import io.element.android.libraries.sessionstorage.api.observer.SessionListener import io.element.android.libraries.sessionstorage.api.observer.SessionObserver import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first import timber.log.Timber @ContributesBinding(AppScope::class, binding = binding()) @@ -84,6 +88,59 @@ class DefaultPushService( return pushProvider.registerWith(matrixClient, distributor) } + override suspend fun ensurePusherIsRegistered(matrixClient: MatrixClient): Result { + val verificationStatus = matrixClient.sessionVerificationService.sessionVerifiedStatus.first() + if (verificationStatus != SessionVerifiedStatus.Verified) { + return Result.failure(PusherRegistrationFailure.AccountNotVerified()) + .also { Timber.w("Account is not verified") } + } + Timber.d("Ensure pusher is registered") + val currentPushProvider = getCurrentPushProvider(matrixClient.sessionId) + val result = if (currentPushProvider == null) { + Timber.d("Register with the first available push provider with at least one distributor") + val pushProvider = getAvailablePushProviders() + .firstOrNull { it.getDistributors().isNotEmpty() } + // Else fallback to the first available push provider (the list should never be empty) + ?: getAvailablePushProviders().firstOrNull() + ?: return Result.failure(PusherRegistrationFailure.NoProvidersAvailable()) + .also { Timber.w("No push providers available") } + val distributor = pushProvider.getDistributors().firstOrNull() + ?: return Result.failure(PusherRegistrationFailure.NoDistributorsAvailable()) + .also { Timber.w("No distributors available") } + .also { + // In this case, consider the push provider is chosen. + selectPushProvider(matrixClient.sessionId, pushProvider) + } + registerWith(matrixClient, pushProvider, distributor) + } else { + val currentPushDistributor = currentPushProvider.getCurrentDistributor(matrixClient.sessionId) + if (currentPushDistributor == null) { + Timber.d("Register with the first available distributor") + val distributor = currentPushProvider.getDistributors().firstOrNull() + ?: return Result.failure(PusherRegistrationFailure.NoDistributorsAvailable()) + .also { Timber.w("No distributors available") } + registerWith(matrixClient, currentPushProvider, distributor) + } else { + Timber.d("Re-register with the current distributor") + registerWith(matrixClient, currentPushProvider, currentPushDistributor) + } + } + return result.fold( + onSuccess = { + Timber.d("Pusher registered") + Result.success(Unit) + }, + onFailure = { + Timber.e(it, "Failed to register pusher") + if (it is RegistrationFailure) { + Result.failure(PusherRegistrationFailure.RegistrationFailure(it.clientException, it.isRegisteringAgain)) + } else { + Result.failure(it) + } + } + ) + } + override suspend fun selectPushProvider( sessionId: SessionId, pushProvider: PushProvider, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt index 7ddbc93fb1..f48b7ee612 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt @@ -12,12 +12,15 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import io.element.android.libraries.push.api.GetCurrentPushProvider +import io.element.android.libraries.push.api.PusherRegistrationFailure import io.element.android.libraries.push.api.history.PushHistoryItem import io.element.android.libraries.push.impl.push.FakeMutableBatteryOptimizationStore import io.element.android.libraries.push.impl.push.MutableBatteryOptimizationStore @@ -40,6 +43,7 @@ import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushSto import io.element.android.libraries.pushstore.test.userpushstore.clientsecret.InMemoryPushClientSecretStore import io.element.android.libraries.sessionstorage.api.observer.SessionObserver import io.element.android.libraries.sessionstorage.test.observer.NoOpSessionObserver +import io.element.android.tests.testutils.lambda.any import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import kotlinx.coroutines.flow.first @@ -339,6 +343,281 @@ class DefaultPushServiceTest { } } + @Test + fun `ensurePusher - error when account is not verified`() = runTest { + val sessionVerificationService = FakeSessionVerificationService( + initialSessionVerifiedStatus = SessionVerifiedStatus.NotVerified + ) + val pushService = createDefaultPushService() + val result = pushService.ensurePusherIsRegistered( + FakeMatrixClient( + sessionVerificationService = sessionVerificationService, + ) + ) + assertThat(result.exceptionOrNull()!!).isInstanceOf(PusherRegistrationFailure.AccountNotVerified::class.java) + } + + @Test + fun `ensurePusher - case two push providers but first one does not have distributor - second one will be used`() = runTest { + val lambda = lambdaRecorder> { _, _ -> + Result.success(Unit) + } + val sessionVerificationService = FakeSessionVerificationService( + initialSessionVerifiedStatus = SessionVerifiedStatus.Verified + ) + val pushProvider0 = FakePushProvider( + index = 0, + name = "aFakePushProvider0", + distributors = emptyList(), + ) + val distributor = Distributor("aDistributorValue1", "aDistributorName1") + val pushProvider1 = FakePushProvider( + index = 1, + name = "aFakePushProvider1", + distributors = listOf(distributor), + registerWithResult = lambda, + ) + val pushService = createDefaultPushService( + pushProviders = setOf( + pushProvider0, + pushProvider1, + ), + ) + val result = pushService.ensurePusherIsRegistered( + FakeMatrixClient( + sessionVerificationService = sessionVerificationService, + ) + ) + assertThat(result.isSuccess).isTrue() + lambda.assertions().isCalledOnce() + .with( + // MatrixClient + any(), + // First distributor of second push provider + value(distributor), + ) + } + + @Test + fun `ensurePusher - case one push provider but no distributor available`() = runTest { + val lambda = lambdaRecorder> { _, _ -> + Result.success(Unit) + } + val sessionVerificationService = FakeSessionVerificationService( + initialSessionVerifiedStatus = SessionVerifiedStatus.Verified + ) + val pushProvider = FakePushProvider( + index = 0, + name = "aFakePushProvider", + distributors = emptyList(), + registerWithResult = lambda, + ) + val pushService = createDefaultPushService( + pushProviders = setOf(pushProvider), + ) + val result = pushService.ensurePusherIsRegistered( + FakeMatrixClient( + sessionVerificationService = sessionVerificationService, + ) + ) + assertThat(result.exceptionOrNull()).isInstanceOf(PusherRegistrationFailure.NoDistributorsAvailable::class.java) + lambda.assertions().isNeverCalled() + } + + @Test + fun `ensurePusher - ensure default pusher is registered with default provider`() = runTest { + val lambda = lambdaRecorder> { _, _ -> + Result.success(Unit) + } + val sessionVerificationService = FakeSessionVerificationService( + initialSessionVerifiedStatus = SessionVerifiedStatus.Verified + ) + val pushService = createDefaultPushService( + pushProviders = setOf( + FakePushProvider( + index = 0, + name = "aFakePushProvider", + distributors = listOf(Distributor("aDistributorValue0", "aDistributorName0")), + registerWithResult = lambda, + ) + ), + ) + val result = pushService.ensurePusherIsRegistered( + FakeMatrixClient( + sessionVerificationService = sessionVerificationService, + ) + ) + assertThat(result.isSuccess).isTrue() + lambda.assertions() + .isCalledOnce() + .with( + // MatrixClient + any(), + // First distributor + value(pushService.getAvailablePushProviders()[0].getDistributors()[0]), + ) + } + + @Test + fun `ensurePusher - ensure default pusher is registered with default provider - fail to register`() = runTest { + val lambda = lambdaRecorder> { _, _ -> + Result.failure(AN_EXCEPTION) + } + val sessionVerificationService = FakeSessionVerificationService( + initialSessionVerifiedStatus = SessionVerifiedStatus.Verified + ) + val pushService = createDefaultPushService( + pushProviders = setOf( + FakePushProvider( + index = 0, + name = "aFakePushProvider", + distributors = listOf(Distributor("aDistributorValue0", "aDistributorName0")), + registerWithResult = lambda, + ) + ), + ) + val result = pushService.ensurePusherIsRegistered( + FakeMatrixClient( + sessionVerificationService = sessionVerificationService, + ) + ) + assertThat(result.isFailure).isTrue() + lambda.assertions() + .isCalledOnce() + .with( + // MatrixClient + any(), + // First distributor + value(pushService.getAvailablePushProviders()[0].getDistributors()[0]), + ) + } + + @Test + fun `ensurePusher - if current push provider does not have distributors, nothing happen`() = runTest { + val lambda = lambdaRecorder> { _, _ -> + Result.success(Unit) + } + val sessionVerificationService = FakeSessionVerificationService( + initialSessionVerifiedStatus = SessionVerifiedStatus.Verified + ) + val pushProvider = FakePushProvider( + index = 0, + name = "aFakePushProvider0", + distributors = emptyList(), + registerWithResult = lambda, + ) + val pushService = createDefaultPushService( + pushProviders = setOf(pushProvider), + getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = pushProvider.name), + ) + val result = pushService.ensurePusherIsRegistered( + FakeMatrixClient( + sessionVerificationService = sessionVerificationService, + ) + ) + assertThat(result.exceptionOrNull()) + .isInstanceOf(PusherRegistrationFailure.NoDistributorsAvailable::class.java) + lambda.assertions() + .isNeverCalled() + } + + @Test + fun `ensurePusher - ensure current provider is registered with current distributor`() = runTest { + val lambda = lambdaRecorder> { _, _ -> + Result.success(Unit) + } + val sessionVerificationService = FakeSessionVerificationService( + initialSessionVerifiedStatus = SessionVerifiedStatus.Verified + ) + val distributor = Distributor("aDistributorValue1", "aDistributorName1") + val pushProvider = FakePushProvider( + index = 0, + name = "aFakePushProvider0", + distributors = listOf( + Distributor("aDistributorValue0", "aDistributorName0"), + distributor, + ), + currentDistributor = { distributor }, + registerWithResult = lambda, + ) + val pushService = createDefaultPushService( + pushProviders = setOf(pushProvider), + getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = pushProvider.name), + ) + val result = pushService.ensurePusherIsRegistered( + FakeMatrixClient( + sessionVerificationService = sessionVerificationService, + ) + ) + assertThat(result.isSuccess).isTrue() + lambda.assertions() + .isCalledOnce() + .with( + // MatrixClient + any(), + // Current distributor + value(distributor), + ) + } + + @Test + fun `ensurePusher - case no push provider available provider`() = runTest { + val lambda = lambdaRecorder> { _, _ -> + Result.success(Unit) + } + val sessionVerificationService = FakeSessionVerificationService(SessionVerifiedStatus.Verified) + val pushService = createDefaultPushService( + pushProviders = emptySet(), + ) + val result = pushService.ensurePusherIsRegistered( + FakeMatrixClient( + sessionVerificationService = sessionVerificationService, + ) + ) + assertThat(result.exceptionOrNull()) + .isInstanceOf(PusherRegistrationFailure.NoProvidersAvailable::class.java) + lambda.assertions() + .isNeverCalled() + } + + @Test + fun `ensurePusher - if current push provider does not have current distributor, the first one is used`() = runTest { + val lambda = lambdaRecorder> { _, _ -> + Result.success(Unit) + } + val sessionVerificationService = FakeSessionVerificationService( + initialSessionVerifiedStatus = SessionVerifiedStatus.Verified + ) + val pushProvider = FakePushProvider( + index = 0, + name = "aFakePushProvider0", + distributors = listOf( + Distributor("aDistributorValue0", "aDistributorName0"), + Distributor("aDistributorValue1", "aDistributorName1"), + ), + currentDistributor = { null }, + registerWithResult = lambda, + ) + val pushService = createDefaultPushService( + pushProviders = setOf(pushProvider), + getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = pushProvider.name), + ) + val result = pushService.ensurePusherIsRegistered( + FakeMatrixClient( + sessionVerificationService = sessionVerificationService, + ) + ) + assertThat(result.isSuccess).isTrue() + lambda.assertions() + .isCalledOnce() + .with( + // MatrixClient + any(), + // First distributor + value(pushService.getAvailablePushProviders()[0].getDistributors()[0]), + ) + } + private fun createDefaultPushService( testPush: TestPush = FakeTestPush(), userPushStoreFactory: UserPushStoreFactory = FakeUserPushStoreFactory(), diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePushService.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePushService.kt index ee99ef1a20..d39c5eb274 100644 --- a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePushService.kt +++ b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePushService.kt @@ -32,6 +32,7 @@ class FakePushService( private val resetPushHistoryResult: () -> Unit = { lambdaError() }, private val resetBatteryOptimizationStateResult: () -> Unit = { lambdaError() }, private val onServiceUnregisteredResult: (UserId) -> Unit = { lambdaError() }, + private val ensurePusherIsRegisteredResult: () -> Result = { lambdaError() }, ) : PushService { override suspend fun getCurrentPushProvider(sessionId: SessionId): PushProvider? { return registeredPushProvider ?: currentPushProvider(sessionId) @@ -56,6 +57,10 @@ class FakePushService( } } + override suspend fun ensurePusherIsRegistered(matrixClient: MatrixClient): Result { + return ensurePusherIsRegisteredResult() + } + override suspend fun selectPushProvider(sessionId: SessionId, pushProvider: PushProvider) { selectPushProviderLambda(sessionId, pushProvider) }