Merge pull request #5596 from element-hq/feature/bma/removeNavigationState

Remove application navigation state usage in the push module
This commit is contained in:
Benoit Marty 2025-10-23 17:35:31 +02:00 committed by GitHub
commit 2871a7a9e6
35 changed files with 190 additions and 231 deletions

View file

@ -134,7 +134,7 @@ class LoggedInPresenter(
private suspend fun ensurePusherIsRegistered(pusherRegistrationState: MutableState<AsyncData<Unit>>) { private suspend fun ensurePusherIsRegistered(pusherRegistrationState: MutableState<AsyncData<Unit>>) {
Timber.tag(pusherTag.value).d("Ensure pusher is registered") Timber.tag(pusherTag.value).d("Ensure pusher is registered")
val currentPushProvider = pushService.getCurrentPushProvider() val currentPushProvider = pushService.getCurrentPushProvider(matrixClient.sessionId)
val result = if (currentPushProvider == null) { val result = if (currentPushProvider == null) {
Timber.tag(pusherTag.value).d("Register with the first available push provider with at least one distributor") Timber.tag(pusherTag.value).d("Register with the first available push provider with at least one distributor")
val pushProvider = pushService.getAvailablePushProviders() val pushProvider = pushService.getAvailablePushProviders()

View file

@ -487,7 +487,7 @@ class LoggedInPresenterTest {
Result.success(Unit) Result.success(Unit)
}, },
selectPushProviderLambda: (SessionId, PushProvider) -> Unit = { _, _ -> lambdaError() }, selectPushProviderLambda: (SessionId, PushProvider) -> Unit = { _, _ -> lambdaError() },
currentPushProvider: () -> PushProvider? = { null }, currentPushProvider: (SessionId) -> PushProvider? = { null },
setIgnoreRegistrationErrorLambda: (SessionId, Boolean) -> Unit = { _, _ -> lambdaError() }, setIgnoreRegistrationErrorLambda: (SessionId, Boolean) -> Unit = { _, _ -> lambdaError() },
): PushService { ): PushService {
return FakePushService( return FakePushService(

View file

@ -94,7 +94,7 @@ class NotificationSettingsPresenter(
var refreshPushProvider by remember { mutableIntStateOf(0) } var refreshPushProvider by remember { mutableIntStateOf(0) }
LaunchedEffect(refreshPushProvider) { LaunchedEffect(refreshPushProvider) {
val p = pushService.getCurrentPushProvider() val p = pushService.getCurrentPushProvider(matrixClient.sessionId)
val distributor = p?.getCurrentDistributor(matrixClient.sessionId) val distributor = p?.getCurrentDistributor(matrixClient.sessionId)
currentDistributor = if (distributor != null) { currentDistributor = if (distributor != null) {
AsyncData.Success(distributor) AsyncData.Success(distributor)

View file

@ -7,6 +7,8 @@
package io.element.android.libraries.push.api package io.element.android.libraries.push.api
import io.element.android.libraries.matrix.api.core.SessionId
interface GetCurrentPushProvider { interface GetCurrentPushProvider {
suspend fun getCurrentPushProvider(): String? suspend fun getCurrentPushProvider(sessionId: SessionId): String?
} }

View file

@ -18,7 +18,7 @@ interface PushService {
/** /**
* Return the current push provider, or null if none. * Return the current push provider, or null if none.
*/ */
suspend fun getCurrentPushProvider(): PushProvider? suspend fun getCurrentPushProvider(sessionId: SessionId): PushProvider?
/** /**
* Return the list of push providers, available at compile time, sorted by index. * Return the list of push providers, available at compile time, sorted by index.
@ -51,7 +51,7 @@ interface PushService {
/** /**
* Return false in case of early error. * Return false in case of early error.
*/ */
suspend fun testPush(): Boolean suspend fun testPush(sessionId: SessionId): Boolean
/** /**
* Get a flow of total number of received Push. * Get a flow of total number of received Push.

View file

@ -9,23 +9,15 @@ package io.element.android.libraries.push.impl
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.push.api.GetCurrentPushProvider import io.element.android.libraries.push.api.GetCurrentPushProvider
import io.element.android.libraries.pushstore.api.UserPushStoreFactory import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.services.appnavstate.api.AppNavigationStateService
import io.element.android.services.appnavstate.api.currentSessionId
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultGetCurrentPushProvider( class DefaultGetCurrentPushProvider(
private val pushStoreFactory: UserPushStoreFactory, private val pushStoreFactory: UserPushStoreFactory,
private val appNavigationStateService: AppNavigationStateService,
) : GetCurrentPushProvider { ) : GetCurrentPushProvider {
override suspend fun getCurrentPushProvider(): String? { override suspend fun getCurrentPushProvider(sessionId: SessionId): String? {
return appNavigationStateService return pushStoreFactory.getOrCreate(sessionId).getPushProviderName()
.appNavigationState
.value
.navigationState
.currentSessionId()
?.let { pushStoreFactory.getOrCreate(it) }
?.getPushProviderName()
} }
} }

View file

@ -44,8 +44,8 @@ class DefaultPushService(
observeSessions() observeSessions()
} }
override suspend fun getCurrentPushProvider(): PushProvider? { override suspend fun getCurrentPushProvider(sessionId: SessionId): PushProvider? {
val currentPushProvider = getCurrentPushProvider.getCurrentPushProvider() val currentPushProvider = getCurrentPushProvider.getCurrentPushProvider(sessionId)
return pushProviders.find { it.name == currentPushProvider } return pushProviders.find { it.name == currentPushProvider }
} }
@ -97,9 +97,9 @@ class DefaultPushService(
userPushStoreFactory.getOrCreate(sessionId).setIgnoreRegistrationError(ignore) userPushStoreFactory.getOrCreate(sessionId).setIgnoreRegistrationError(ignore)
} }
override suspend fun testPush(): Boolean { override suspend fun testPush(sessionId: SessionId): Boolean {
val pushProvider = getCurrentPushProvider() ?: return false val pushProvider = getCurrentPushProvider(sessionId) ?: return false
val config = pushProvider.getCurrentUserPushConfig() ?: return false val config = pushProvider.getPushConfig(sessionId) ?: return false
testPush.execute(config) testPush.execute(config)
return true return true
} }

View file

@ -13,17 +13,17 @@ import io.element.android.appconfig.PushConfig
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Config
interface TestPush { interface TestPush {
suspend fun execute(config: CurrentUserPushConfig) suspend fun execute(config: Config)
} }
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultTestPush( class DefaultTestPush(
private val pushGatewayNotifyRequest: PushGatewayNotifyRequest, private val pushGatewayNotifyRequest: PushGatewayNotifyRequest,
) : TestPush { ) : TestPush {
override suspend fun execute(config: CurrentUserPushConfig) { override suspend fun execute(config: Config) {
pushGatewayNotifyRequest.execute( pushGatewayNotifyRequest.execute(
PushGatewayNotifyRequest.Params( PushGatewayNotifyRequest.Params(
url = config.url, url = config.url,

View file

@ -37,7 +37,7 @@ class CurrentPushProviderTest(
override suspend fun run(coroutineScope: CoroutineScope) { override suspend fun run(coroutineScope: CoroutineScope) {
delegate.start() delegate.start()
val pushProvider = pushService.getCurrentPushProvider() val pushProvider = pushService.getCurrentPushProvider(sessionId)
if (pushProvider == null) { if (pushProvider == null) {
delegate.updateState( delegate.updateState(
description = stringProvider.getString(R.string.troubleshoot_notifications_test_current_push_provider_failure), description = stringProvider.getString(R.string.troubleshoot_notifications_test_current_push_provider_failure),

View file

@ -7,9 +7,10 @@
package io.element.android.libraries.push.impl.troubleshoot package io.element.android.libraries.push.impl.troubleshoot
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoSet import dev.zacsweers.metro.ContributesIntoSet
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.push.api.PushService import io.element.android.libraries.push.api.PushService
import io.element.android.libraries.push.api.gateway.PushGatewayFailure import io.element.android.libraries.push.api.gateway.PushGatewayFailure
import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.R
@ -28,9 +29,10 @@ import kotlinx.coroutines.withTimeout
import timber.log.Timber import timber.log.Timber
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@ContributesIntoSet(AppScope::class) @ContributesIntoSet(SessionScope::class)
@Inject @Inject
class PushLoopbackTest( class PushLoopbackTest(
private val sessionId: SessionId,
private val pushService: PushService, private val pushService: PushService,
private val diagnosticPushHandler: DiagnosticPushHandler, private val diagnosticPushHandler: DiagnosticPushHandler,
private val clock: SystemClock, private val clock: SystemClock,
@ -52,9 +54,9 @@ class PushLoopbackTest(
completable.complete(clock.epochMillis() - startTime) completable.complete(clock.epochMillis() - startTime)
} }
val testPushResult = try { val testPushResult = try {
pushService.testPush() pushService.testPush(sessionId)
} catch (pusherRejected: PushGatewayFailure.PusherRejected) { } catch (pusherRejected: PushGatewayFailure.PusherRejected) {
val hasQuickFix = pushService.getCurrentPushProvider()?.canRotateToken() == true val hasQuickFix = pushService.getCurrentPushProvider(sessionId)?.canRotateToken() == true
delegate.updateState( delegate.updateState(
description = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_failure_1), description = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_failure_1),
status = NotificationTroubleshootTestState.Status.Failure(hasQuickFix = hasQuickFix) status = NotificationTroubleshootTestState.Status.Failure(hasQuickFix = hasQuickFix)
@ -105,7 +107,7 @@ class PushLoopbackTest(
navigator: NotificationTroubleshootNavigator, navigator: NotificationTroubleshootNavigator,
) { ) {
delegate.start() delegate.start()
pushService.getCurrentPushProvider()?.rotateToken() pushService.getCurrentPushProvider(sessionId)?.rotateToken()
run(coroutineScope) run(coroutineScope)
} }

View file

@ -25,11 +25,11 @@ import io.element.android.libraries.push.impl.store.PushDataStore
import io.element.android.libraries.push.impl.test.FakeTestPush import io.element.android.libraries.push.impl.test.FakeTestPush
import io.element.android.libraries.push.impl.test.TestPush import io.element.android.libraries.push.impl.test.TestPush
import io.element.android.libraries.push.test.FakeGetCurrentPushProvider import io.element.android.libraries.push.test.FakeGetCurrentPushProvider
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Config
import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider import io.element.android.libraries.pushproviders.api.PushProvider
import io.element.android.libraries.pushproviders.test.FakePushProvider import io.element.android.libraries.pushproviders.test.FakePushProvider
import io.element.android.libraries.pushproviders.test.aCurrentUserPushConfig import io.element.android.libraries.pushproviders.test.aSessionPushConfig
import io.element.android.libraries.pushstore.api.UserPushStoreFactory import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretStore import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretStore
import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStore import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStore
@ -47,7 +47,7 @@ class DefaultPushServiceTest {
@Test @Test
fun `test push no push provider`() = runTest { fun `test push no push provider`() = runTest {
val defaultPushService = createDefaultPushService() val defaultPushService = createDefaultPushService()
assertThat(defaultPushService.testPush()).isFalse() assertThat(defaultPushService.testPush(A_SESSION_ID)).isFalse()
} }
@Test @Test
@ -57,22 +57,22 @@ class DefaultPushServiceTest {
pushProviders = setOf(aPushProvider), pushProviders = setOf(aPushProvider),
getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aPushProvider.name), getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aPushProvider.name),
) )
assertThat(defaultPushService.testPush()).isFalse() assertThat(defaultPushService.testPush(A_SESSION_ID)).isFalse()
} }
@Test @Test
fun `test push ok`() = runTest { fun `test push ok`() = runTest {
val aConfig = aCurrentUserPushConfig() val aConfig = aSessionPushConfig()
val testPushResult = lambdaRecorder<CurrentUserPushConfig, Unit> { } val testPushResult = lambdaRecorder<Config, Unit> { }
val aPushProvider = FakePushProvider( val aPushProvider = FakePushProvider(
currentUserPushConfig = aConfig config = aConfig
) )
val defaultPushService = createDefaultPushService( val defaultPushService = createDefaultPushService(
pushProviders = setOf(aPushProvider), pushProviders = setOf(aPushProvider),
getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aPushProvider.name), getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aPushProvider.name),
testPush = FakeTestPush(executeResult = testPushResult), testPush = FakeTestPush(executeResult = testPushResult),
) )
assertThat(defaultPushService.testPush()).isTrue() assertThat(defaultPushService.testPush(A_SESSION_ID)).isTrue()
testPushResult.assertions() testPushResult.assertions()
.isCalledOnce() .isCalledOnce()
.with(value(aConfig)) .with(value(aConfig))
@ -81,7 +81,7 @@ class DefaultPushServiceTest {
@Test @Test
fun `getCurrentPushProvider null`() = runTest { fun `getCurrentPushProvider null`() = runTest {
val defaultPushService = createDefaultPushService() val defaultPushService = createDefaultPushService()
val result = defaultPushService.getCurrentPushProvider() val result = defaultPushService.getCurrentPushProvider(A_SESSION_ID)
assertThat(result).isNull() assertThat(result).isNull()
} }
@ -92,7 +92,7 @@ class DefaultPushServiceTest {
pushProviders = setOf(aPushProvider), pushProviders = setOf(aPushProvider),
getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aPushProvider.name), getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aPushProvider.name),
) )
val result = defaultPushService.getCurrentPushProvider() val result = defaultPushService.getCurrentPushProvider(A_SESSION_ID)
assertThat(result).isEqualTo(aPushProvider) assertThat(result).isEqualTo(aPushProvider)
} }

View file

@ -9,7 +9,7 @@ package io.element.android.libraries.push.impl.test
import io.element.android.appconfig.PushConfig import io.element.android.appconfig.PushConfig
import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest
import io.element.android.libraries.pushproviders.test.aCurrentUserPushConfig import io.element.android.libraries.pushproviders.test.aSessionPushConfig
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.lambda.value
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@ -24,7 +24,7 @@ class DefaultTestPushTest {
executeResult = executeResult, executeResult = executeResult,
) )
) )
val aConfig = aCurrentUserPushConfig() val aConfig = aSessionPushConfig()
defaultTestPush.execute(aConfig) defaultTestPush.execute(aConfig)
executeResult.assertions() executeResult.assertions()
.isCalledOnce() .isCalledOnce()

View file

@ -7,13 +7,13 @@
package io.element.android.libraries.push.impl.test package io.element.android.libraries.push.impl.test
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Config
import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaError
class FakeTestPush( class FakeTestPush(
private val executeResult: (CurrentUserPushConfig) -> Unit = { lambdaError() } private val executeResult: (Config) -> Unit = { lambdaError() }
) : TestPush { ) : TestPush {
override suspend fun execute(config: CurrentUserPushConfig) { override suspend fun execute(config: Config) {
executeResult(config) executeResult(config)
} }
} }

View file

@ -8,14 +8,19 @@
package io.element.android.libraries.push.impl.troubleshoot package io.element.android.libraries.push.impl.troubleshoot
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_FAILURE_REASON import io.element.android.libraries.matrix.test.A_FAILURE_REASON
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.push.api.PushService
import io.element.android.libraries.push.api.gateway.PushGatewayFailure import io.element.android.libraries.push.api.gateway.PushGatewayFailure
import io.element.android.libraries.push.test.FakePushService import io.element.android.libraries.push.test.FakePushService
import io.element.android.libraries.pushproviders.test.FakePushProvider import io.element.android.libraries.pushproviders.test.FakePushProvider
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
import io.element.android.libraries.troubleshoot.test.FakeNotificationTroubleshootNavigator import io.element.android.libraries.troubleshoot.test.FakeNotificationTroubleshootNavigator
import io.element.android.libraries.troubleshoot.test.runAndTestState import io.element.android.libraries.troubleshoot.test.runAndTestState
import io.element.android.services.toolbox.api.strings.StringProvider
import io.element.android.services.toolbox.api.systemclock.SystemClock
import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.services.toolbox.test.strings.FakeStringProvider
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
@ -25,13 +30,7 @@ import org.junit.Test
class PushLoopbackTestTest { class PushLoopbackTestTest {
@Test @Test
fun `test PushLoopbackTest timeout - push is not received`() = runTest { fun `test PushLoopbackTest timeout - push is not received`() = runTest {
val diagnosticPushHandler = DiagnosticPushHandler() val sut = createPushLoopbackTest()
val sut = PushLoopbackTest(
pushService = FakePushService(),
diagnosticPushHandler = diagnosticPushHandler,
clock = FakeSystemClock(),
stringProvider = FakeStringProvider(),
)
sut.runAndTestState { sut.runAndTestState {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress) assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
@ -42,16 +41,12 @@ class PushLoopbackTestTest {
@Test @Test
fun `test PushLoopbackTest PusherRejected error`() = runTest { fun `test PushLoopbackTest PusherRejected error`() = runTest {
val diagnosticPushHandler = DiagnosticPushHandler() val sut = createPushLoopbackTest(
val sut = PushLoopbackTest(
pushService = FakePushService( pushService = FakePushService(
testPushBlock = { testPushBlock = {
throw PushGatewayFailure.PusherRejected() throw PushGatewayFailure.PusherRejected()
} }
), ),
diagnosticPushHandler = diagnosticPushHandler,
clock = FakeSystemClock(),
stringProvider = FakeStringProvider(),
) )
sut.runAndTestState { sut.runAndTestState {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
@ -65,9 +60,8 @@ class PushLoopbackTestTest {
@Test @Test
fun `test PushLoopbackTest PusherRejected error with quick fix`() = runTest { fun `test PushLoopbackTest PusherRejected error with quick fix`() = runTest {
val diagnosticPushHandler = DiagnosticPushHandler()
val rotateTokenLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) } val rotateTokenLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
val sut = PushLoopbackTest( val sut = createPushLoopbackTest(
pushService = FakePushService( pushService = FakePushService(
testPushBlock = { testPushBlock = {
throw PushGatewayFailure.PusherRejected() throw PushGatewayFailure.PusherRejected()
@ -79,9 +73,6 @@ class PushLoopbackTestTest {
) )
} }
), ),
diagnosticPushHandler = diagnosticPushHandler,
clock = FakeSystemClock(),
stringProvider = FakeStringProvider(),
) )
sut.runAndTestState { sut.runAndTestState {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
@ -97,14 +88,10 @@ class PushLoopbackTestTest {
@Test @Test
fun `test PushLoopbackTest setup error`() = runTest { fun `test PushLoopbackTest setup error`() = runTest {
val diagnosticPushHandler = DiagnosticPushHandler() val sut = createPushLoopbackTest(
val sut = PushLoopbackTest(
pushService = FakePushService( pushService = FakePushService(
testPushBlock = { false } testPushBlock = { false }
), ),
diagnosticPushHandler = diagnosticPushHandler,
clock = FakeSystemClock(),
stringProvider = FakeStringProvider(),
) )
sut.runAndTestState { sut.runAndTestState {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
@ -116,16 +103,12 @@ class PushLoopbackTestTest {
@Test @Test
fun `test PushLoopbackTest other error`() = runTest { fun `test PushLoopbackTest other error`() = runTest {
val diagnosticPushHandler = DiagnosticPushHandler() val sut = createPushLoopbackTest(
val sut = PushLoopbackTest(
pushService = FakePushService( pushService = FakePushService(
testPushBlock = { testPushBlock = {
throw AN_EXCEPTION throw AN_EXCEPTION
} }
), ),
diagnosticPushHandler = diagnosticPushHandler,
clock = FakeSystemClock(),
stringProvider = FakeStringProvider(),
) )
sut.runAndTestState { sut.runAndTestState {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
@ -139,14 +122,12 @@ class PushLoopbackTestTest {
@Test @Test
fun `test PushLoopbackTest push is received`() = runTest { fun `test PushLoopbackTest push is received`() = runTest {
val diagnosticPushHandler = DiagnosticPushHandler() val diagnosticPushHandler = DiagnosticPushHandler()
val sut = PushLoopbackTest( val sut = createPushLoopbackTest(
pushService = FakePushService(testPushBlock = { pushService = FakePushService(testPushBlock = {
diagnosticPushHandler.handlePush() diagnosticPushHandler.handlePush()
true true
}), }),
diagnosticPushHandler = diagnosticPushHandler, diagnosticPushHandler = diagnosticPushHandler,
clock = FakeSystemClock(),
stringProvider = FakeStringProvider(),
) )
sut.runAndTestState { sut.runAndTestState {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true)) assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
@ -156,3 +137,17 @@ class PushLoopbackTestTest {
} }
} }
} }
private fun createPushLoopbackTest(
sessionId: SessionId = A_SESSION_ID,
pushService: PushService = FakePushService(),
diagnosticPushHandler: DiagnosticPushHandler = DiagnosticPushHandler(),
clock: SystemClock = FakeSystemClock(),
stringProvider: StringProvider = FakeStringProvider(),
) = PushLoopbackTest(
sessionId = sessionId,
pushService = pushService,
diagnosticPushHandler = diagnosticPushHandler,
clock = clock,
stringProvider = stringProvider
)

View file

@ -7,10 +7,11 @@
package io.element.android.libraries.push.test package io.element.android.libraries.push.test
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.push.api.GetCurrentPushProvider import io.element.android.libraries.push.api.GetCurrentPushProvider
class FakeGetCurrentPushProvider( class FakeGetCurrentPushProvider(
private val currentPushProvider: String? private val currentPushProvider: String?
) : GetCurrentPushProvider { ) : GetCurrentPushProvider {
override suspend fun getCurrentPushProvider(): String? = currentPushProvider override suspend fun getCurrentPushProvider(sessionId: SessionId): String? = currentPushProvider
} }

View file

@ -19,19 +19,19 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
class FakePushService( class FakePushService(
private val testPushBlock: suspend () -> Boolean = { true }, private val testPushBlock: suspend (SessionId) -> Boolean = { true },
private val availablePushProviders: List<PushProvider> = emptyList(), private val availablePushProviders: List<PushProvider> = emptyList(),
private val registerWithLambda: suspend (MatrixClient, PushProvider, Distributor) -> Result<Unit> = { _, _, _ -> private val registerWithLambda: suspend (MatrixClient, PushProvider, Distributor) -> Result<Unit> = { _, _, _ ->
Result.success(Unit) Result.success(Unit)
}, },
private val currentPushProvider: () -> PushProvider? = { availablePushProviders.firstOrNull() }, private val currentPushProvider: (SessionId) -> PushProvider? = { availablePushProviders.firstOrNull() },
private val selectPushProviderLambda: suspend (SessionId, PushProvider) -> Unit = { _, _ -> lambdaError() }, private val selectPushProviderLambda: suspend (SessionId, PushProvider) -> Unit = { _, _ -> lambdaError() },
private val setIgnoreRegistrationErrorLambda: (SessionId, Boolean) -> Unit = { _, _ -> lambdaError() }, private val setIgnoreRegistrationErrorLambda: (SessionId, Boolean) -> Unit = { _, _ -> lambdaError() },
private val resetPushHistoryResult: () -> Unit = { lambdaError() }, private val resetPushHistoryResult: () -> Unit = { lambdaError() },
private val resetBatteryOptimizationStateResult: () -> Unit = { lambdaError() }, private val resetBatteryOptimizationStateResult: () -> Unit = { lambdaError() },
) : PushService { ) : PushService {
override suspend fun getCurrentPushProvider(): PushProvider? { override suspend fun getCurrentPushProvider(sessionId: SessionId): PushProvider? {
return registeredPushProvider ?: currentPushProvider() return registeredPushProvider ?: currentPushProvider(sessionId)
} }
override fun getAvailablePushProviders(): List<PushProvider> { override fun getAvailablePushProviders(): List<PushProvider> {
@ -68,8 +68,8 @@ class FakePushService(
setIgnoreRegistrationErrorLambda(sessionId, ignore) setIgnoreRegistrationErrorLambda(sessionId, ignore)
} }
override suspend fun testPush(): Boolean = simulateLongTask { override suspend fun testPush(sessionId: SessionId): Boolean = simulateLongTask {
testPushBlock() testPushBlock(sessionId)
} }
private val pushHistoryItemsFlow = MutableStateFlow<List<PushHistoryItem>>(emptyList()) private val pushHistoryItemsFlow = MutableStateFlow<List<PushHistoryItem>>(emptyList())

View file

@ -7,7 +7,7 @@
package io.element.android.libraries.pushproviders.api package io.element.android.libraries.pushproviders.api
data class CurrentUserPushConfig( data class Config(
val url: String, val url: String,
val pushKey: String, val pushKey: String,
) )

View file

@ -59,7 +59,7 @@ interface PushProvider {
*/ */
suspend fun onSessionDeleted(sessionId: SessionId) suspend fun onSessionDeleted(sessionId: SessionId)
suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? suspend fun getPushConfig(sessionId: SessionId): Config?
fun canRotateToken(): Boolean fun canRotateToken(): Boolean

View file

@ -13,7 +13,7 @@ import dev.zacsweers.metro.Inject
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.matrix.api.MatrixClient 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.SessionId
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Config
import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider import io.element.android.libraries.pushproviders.api.PushProvider
import io.element.android.libraries.pushproviders.api.PusherSubscriber import io.element.android.libraries.pushproviders.api.PusherSubscriber
@ -74,9 +74,9 @@ class FirebasePushProvider(
*/ */
override suspend fun onSessionDeleted(sessionId: SessionId) = Unit override suspend fun onSessionDeleted(sessionId: SessionId) = Unit
override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? { override suspend fun getPushConfig(sessionId: SessionId): Config? {
return firebaseStore.getFcmToken()?.let { fcmToken -> return firebaseStore.getFcmToken()?.let { fcmToken ->
CurrentUserPushConfig( Config(
url = firebaseGatewayProvider.getFirebaseGateway(), url = firebaseGatewayProvider.getFirebaseGateway(),
pushKey = fcmToken pushKey = fcmToken
) )

View file

@ -13,7 +13,7 @@ import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_SESSION_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.FakeMatrixClient
import io.element.android.libraries.push.test.FakePusherSubscriber import io.element.android.libraries.push.test.FakePusherSubscriber
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Config
import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PusherSubscriber import io.element.android.libraries.pushproviders.api.PusherSubscriber
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
@ -152,7 +152,7 @@ class FirebasePushProviderTest {
token = null token = null
) )
) )
val result = firebasePushProvider.getCurrentUserPushConfig() val result = firebasePushProvider.getPushConfig(A_SESSION_ID)
assertThat(result).isNull() assertThat(result).isNull()
} }
@ -163,8 +163,8 @@ class FirebasePushProviderTest {
token = "aToken" token = "aToken"
), ),
) )
val result = firebasePushProvider.getCurrentUserPushConfig() val result = firebasePushProvider.getPushConfig(A_SESSION_ID)
assertThat(result).isEqualTo(CurrentUserPushConfig(A_FIREBASE_GATEWAY, "aToken")) assertThat(result).isEqualTo(Config(A_FIREBASE_GATEWAY, "aToken"))
} }
@Test @Test

View file

@ -9,7 +9,7 @@ package io.element.android.libraries.pushproviders.test
import io.element.android.libraries.matrix.api.MatrixClient 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.SessionId
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Config
import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider import io.element.android.libraries.pushproviders.api.PushProvider
import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaError
@ -21,7 +21,7 @@ class FakePushProvider(
private val distributors: List<Distributor> = listOf(Distributor("aDistributorValue", "aDistributorName")), private val distributors: List<Distributor> = listOf(Distributor("aDistributorValue", "aDistributorName")),
private val currentDistributorValue: () -> String? = { lambdaError() }, private val currentDistributorValue: () -> String? = { lambdaError() },
private val currentDistributor: () -> Distributor? = { distributors.firstOrNull() }, private val currentDistributor: () -> Distributor? = { distributors.firstOrNull() },
private val currentUserPushConfig: CurrentUserPushConfig? = null, private val config: Config? = null,
private val registerWithResult: (MatrixClient, Distributor) -> Result<Unit> = { _, _ -> lambdaError() }, private val registerWithResult: (MatrixClient, Distributor) -> Result<Unit> = { _, _ -> lambdaError() },
private val unregisterWithResult: (MatrixClient) -> Result<Unit> = { lambdaError() }, private val unregisterWithResult: (MatrixClient) -> Result<Unit> = { lambdaError() },
private val onSessionDeletedLambda: (SessionId) -> Unit = { lambdaError() }, private val onSessionDeletedLambda: (SessionId) -> Unit = { lambdaError() },
@ -50,8 +50,8 @@ class FakePushProvider(
onSessionDeletedLambda(sessionId) onSessionDeletedLambda(sessionId)
} }
override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? { override suspend fun getPushConfig(sessionId: SessionId): Config? {
return currentUserPushConfig return config
} }
override fun canRotateToken(): Boolean { override fun canRotateToken(): Boolean {

View file

@ -7,12 +7,12 @@
package io.element.android.libraries.pushproviders.test package io.element.android.libraries.pushproviders.test
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Config
fun aCurrentUserPushConfig( fun aSessionPushConfig(
url: String = "aUrl", url: String = "aUrl",
pushKey: String = "aPushKey", pushKey: String = "aPushKey",
) = CurrentUserPushConfig( ) = Config(
url = url, url = url,
pushKey = pushKey, pushKey = pushKey,
) )

View file

@ -30,7 +30,6 @@ dependencies {
implementation(projects.libraries.pushproviders.api) implementation(projects.libraries.pushproviders.api)
implementation(projects.libraries.architecture) implementation(projects.libraries.architecture)
implementation(projects.libraries.core) implementation(projects.libraries.core)
implementation(projects.services.appnavstate.api)
implementation(projects.services.toolbox.api) implementation(projects.services.toolbox.api)
implementation(projects.libraries.network) implementation(projects.libraries.network)
@ -53,5 +52,4 @@ dependencies {
testImplementation(projects.libraries.pushstore.test) testImplementation(projects.libraries.pushstore.test)
testImplementation(projects.libraries.troubleshoot.test) testImplementation(projects.libraries.troubleshoot.test)
testImplementation(projects.services.toolbox.test) testImplementation(projects.services.toolbox.test)
testImplementation(projects.services.appnavstate.test)
} }

View file

@ -1,37 +0,0 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.unifiedpush
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import io.element.android.services.appnavstate.api.AppNavigationStateService
import io.element.android.services.appnavstate.api.currentSessionId
interface UnifiedPushCurrentUserPushConfigProvider {
suspend fun provide(): CurrentUserPushConfig?
}
@ContributesBinding(AppScope::class)
class DefaultUnifiedPushCurrentUserPushConfigProvider(
private val pushClientSecret: PushClientSecret,
private val unifiedPushStore: UnifiedPushStore,
private val appNavigationStateService: AppNavigationStateService,
) : UnifiedPushCurrentUserPushConfigProvider {
override suspend fun provide(): CurrentUserPushConfig? {
val currentSession = appNavigationStateService.appNavigationState.value.navigationState.currentSessionId() ?: return null
val clientSecret = pushClientSecret.getSecretForUser(currentSession)
val url = unifiedPushStore.getPushGateway(clientSecret) ?: return null
val pushKey = unifiedPushStore.getEndpoint(clientSecret) ?: return null
return CurrentUserPushConfig(
url = url,
pushKey = pushKey,
)
}
}

View file

@ -12,7 +12,7 @@ import dev.zacsweers.metro.ContributesIntoSet
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
import io.element.android.libraries.matrix.api.MatrixClient 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.SessionId
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Config
import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider import io.element.android.libraries.pushproviders.api.PushProvider
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
@ -25,7 +25,7 @@ class UnifiedPushProvider(
private val unRegisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, private val unRegisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase,
private val pushClientSecret: PushClientSecret, private val pushClientSecret: PushClientSecret,
private val unifiedPushStore: UnifiedPushStore, private val unifiedPushStore: UnifiedPushStore,
private val unifiedPushCurrentUserPushConfigProvider: UnifiedPushCurrentUserPushConfigProvider, private val unifiedPushSessionPushConfigProvider: UnifiedPushSessionPushConfigProvider,
) : PushProvider { ) : PushProvider {
override val index = UnifiedPushConfig.INDEX override val index = UnifiedPushConfig.INDEX
override val name = UnifiedPushConfig.NAME override val name = UnifiedPushConfig.NAME
@ -62,8 +62,8 @@ class UnifiedPushProvider(
unRegisterUnifiedPushUseCase.cleanup(clientSecret) unRegisterUnifiedPushUseCase.cleanup(clientSecret)
} }
override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? { override suspend fun getPushConfig(sessionId: SessionId): Config? {
return unifiedPushCurrentUserPushConfigProvider.provide() return unifiedPushSessionPushConfigProvider.provide(sessionId)
} }
override fun canRotateToken(): Boolean = false override fun canRotateToken(): Boolean = false

View file

@ -0,0 +1,34 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.unifiedpush
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.pushproviders.api.Config
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
interface UnifiedPushSessionPushConfigProvider {
suspend fun provide(sessionId: SessionId): Config?
}
@ContributesBinding(AppScope::class)
class DefaultUnifiedPushPushConfigProvider(
private val pushClientSecret: PushClientSecret,
private val unifiedPushStore: UnifiedPushStore,
) : UnifiedPushSessionPushConfigProvider {
override suspend fun provide(sessionId: SessionId): Config? {
val clientSecret = pushClientSecret.getSecretForUser(sessionId)
val url = unifiedPushStore.getPushGateway(clientSecret) ?: return null
val pushKey = unifiedPushStore.getEndpoint(clientSecret) ?: return null
return Config(
url = url,
pushKey = pushKey,
)
}
}

View file

@ -7,13 +7,14 @@
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoSet import dev.zacsweers.metro.ContributesIntoSet
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushApiFactory import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushApiFactory
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushCurrentUserPushConfigProvider import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushSessionPushConfigProvider
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
@ -22,12 +23,13 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ContributesIntoSet(AppScope::class) @ContributesIntoSet(SessionScope::class)
@Inject @Inject
class UnifiedPushMatrixGatewayTest( class UnifiedPushMatrixGatewayTest(
private val sessionId: SessionId,
private val unifiedPushApiFactory: UnifiedPushApiFactory, private val unifiedPushApiFactory: UnifiedPushApiFactory,
private val coroutineDispatchers: CoroutineDispatchers, private val coroutineDispatchers: CoroutineDispatchers,
private val unifiedPushCurrentUserPushConfigProvider: UnifiedPushCurrentUserPushConfigProvider, private val unifiedPushSessionPushConfigProvider: UnifiedPushSessionPushConfigProvider,
) : NotificationTroubleshootTest { ) : NotificationTroubleshootTest {
override val order = 450 override val order = 450
private val delegate = NotificationTroubleshootTestDelegate( private val delegate = NotificationTroubleshootTestDelegate(
@ -44,7 +46,7 @@ class UnifiedPushMatrixGatewayTest(
override suspend fun run(coroutineScope: CoroutineScope) { override suspend fun run(coroutineScope: CoroutineScope) {
delegate.start() delegate.start()
val config = unifiedPushCurrentUserPushConfigProvider.provide() val config = unifiedPushSessionPushConfigProvider.provide(sessionId)
if (config == null) { if (config == null) {
delegate.updateState( delegate.updateState(
description = "No current push provider", description = "No current push provider",

View file

@ -10,36 +10,16 @@ package io.element.android.libraries.pushproviders.unifiedpush
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.A_SECRET import io.element.android.libraries.matrix.test.A_SECRET
import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Config
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import io.element.android.libraries.pushstore.test.userpushstore.clientsecret.FakePushClientSecret import io.element.android.libraries.pushstore.test.userpushstore.clientsecret.FakePushClientSecret
import io.element.android.services.appnavstate.api.AppNavigationState
import io.element.android.services.appnavstate.api.AppNavigationStateService
import io.element.android.services.appnavstate.api.NavigationState
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
class DefaultUnifiedPushCurrentUserPushConfigProviderTest { class DefaultUnifiedPushCurrentUserPushConfigProviderTest {
@Test
fun `getCurrentUserPushConfig no session`() = runTest {
val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider()
val result = sut.provide()
assertThat(result).isNull()
}
@Test @Test
fun `getCurrentUserPushConfig no push gateway`() = runTest { fun `getCurrentUserPushConfig no push gateway`() = runTest {
val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider( val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider(
appNavigationStateService = FakeAppNavigationStateService(
appNavigationState = MutableStateFlow(
AppNavigationState(
navigationState = NavigationState.Session(owner = "owner", sessionId = A_SESSION_ID),
isInForeground = true
)
)
),
pushClientSecret = FakePushClientSecret( pushClientSecret = FakePushClientSecret(
getSecretForUserResult = { A_SECRET } getSecretForUserResult = { A_SECRET }
), ),
@ -47,21 +27,13 @@ class DefaultUnifiedPushCurrentUserPushConfigProviderTest {
getPushGatewayResult = { null } getPushGatewayResult = { null }
), ),
) )
val result = sut.provide() val result = sut.provide(A_SESSION_ID)
assertThat(result).isNull() assertThat(result).isNull()
} }
@Test @Test
fun `getCurrentUserPushConfig no push key`() = runTest { fun `getCurrentUserPushConfig no push key`() = runTest {
val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider( val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider(
appNavigationStateService = FakeAppNavigationStateService(
appNavigationState = MutableStateFlow(
AppNavigationState(
navigationState = NavigationState.Session(owner = "owner", sessionId = A_SESSION_ID),
isInForeground = true
)
)
),
pushClientSecret = FakePushClientSecret( pushClientSecret = FakePushClientSecret(
getSecretForUserResult = { A_SECRET } getSecretForUserResult = { A_SECRET }
), ),
@ -70,21 +42,13 @@ class DefaultUnifiedPushCurrentUserPushConfigProviderTest {
getEndpointResult = { null } getEndpointResult = { null }
), ),
) )
val result = sut.provide() val result = sut.provide(A_SESSION_ID)
assertThat(result).isNull() assertThat(result).isNull()
} }
@Test @Test
fun `getCurrentUserPushConfig ok`() = runTest { fun `getCurrentUserPushConfig ok`() = runTest {
val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider( val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider(
appNavigationStateService = FakeAppNavigationStateService(
appNavigationState = MutableStateFlow(
AppNavigationState(
navigationState = NavigationState.Session(owner = "owner", sessionId = A_SESSION_ID),
isInForeground = true
)
)
),
pushClientSecret = FakePushClientSecret( pushClientSecret = FakePushClientSecret(
getSecretForUserResult = { A_SECRET } getSecretForUserResult = { A_SECRET }
), ),
@ -93,19 +57,17 @@ class DefaultUnifiedPushCurrentUserPushConfigProviderTest {
getEndpointResult = { "aEndpoint" } getEndpointResult = { "aEndpoint" }
), ),
) )
val result = sut.provide() val result = sut.provide(A_SESSION_ID)
assertThat(result).isEqualTo(CurrentUserPushConfig("aPushGateway", "aEndpoint")) assertThat(result).isEqualTo(Config("aPushGateway", "aEndpoint"))
} }
private fun createDefaultUnifiedPushCurrentUserPushConfigProvider( private fun createDefaultUnifiedPushCurrentUserPushConfigProvider(
pushClientSecret: PushClientSecret = FakePushClientSecret(), pushClientSecret: PushClientSecret = FakePushClientSecret(),
unifiedPushStore: UnifiedPushStore = FakeUnifiedPushStore(), unifiedPushStore: UnifiedPushStore = FakeUnifiedPushStore(),
appNavigationStateService: AppNavigationStateService = FakeAppNavigationStateService(), ): DefaultUnifiedPushPushConfigProvider {
): DefaultUnifiedPushCurrentUserPushConfigProvider { return DefaultUnifiedPushPushConfigProvider(
return DefaultUnifiedPushCurrentUserPushConfigProvider(
pushClientSecret = pushClientSecret, pushClientSecret = pushClientSecret,
unifiedPushStore = unifiedPushStore, unifiedPushStore = unifiedPushStore,
appNavigationStateService = appNavigationStateService,
) )
} }
} }

View file

@ -16,9 +16,9 @@ import io.element.android.libraries.matrix.test.A_SECRET
import io.element.android.libraries.matrix.test.A_SESSION_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.FakeMatrixClient
import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.test.aCurrentUserPushConfig import io.element.android.libraries.pushproviders.test.aSessionPushConfig
import io.element.android.libraries.pushproviders.unifiedpush.troubleshoot.FakeUnifiedPushCurrentUserPushConfigProvider
import io.element.android.libraries.pushproviders.unifiedpush.troubleshoot.FakeUnifiedPushDistributorProvider import io.element.android.libraries.pushproviders.unifiedpush.troubleshoot.FakeUnifiedPushDistributorProvider
import io.element.android.libraries.pushproviders.unifiedpush.troubleshoot.FakeUnifiedPushSessionPushConfigProvider
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import io.element.android.libraries.pushstore.test.userpushstore.clientsecret.FakePushClientSecret import io.element.android.libraries.pushstore.test.userpushstore.clientsecret.FakePushClientSecret
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
@ -211,13 +211,13 @@ class UnifiedPushProviderTest {
@Test @Test
fun `getCurrentUserPushConfig invokes the provider methods`() = runTest { fun `getCurrentUserPushConfig invokes the provider methods`() = runTest {
val currentUserPushConfig = aCurrentUserPushConfig() val currentUserPushConfig = aSessionPushConfig()
val unifiedPushProvider = createUnifiedPushProvider( val unifiedPushProvider = createUnifiedPushProvider(
unifiedPushCurrentUserPushConfigProvider = FakeUnifiedPushCurrentUserPushConfigProvider( unifiedPushSessionPushConfigProvider = FakeUnifiedPushSessionPushConfigProvider(
currentUserPushConfig = { currentUserPushConfig } config = { currentUserPushConfig }
) )
) )
val result = unifiedPushProvider.getCurrentUserPushConfig() val result = unifiedPushProvider.getPushConfig(A_SESSION_ID)
assertThat(result).isEqualTo(currentUserPushConfig) assertThat(result).isEqualTo(currentUserPushConfig)
} }
@ -248,7 +248,7 @@ class UnifiedPushProviderTest {
unRegisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase = FakeUnregisterUnifiedPushUseCase(), unRegisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase = FakeUnregisterUnifiedPushUseCase(),
pushClientSecret: PushClientSecret = FakePushClientSecret(), pushClientSecret: PushClientSecret = FakePushClientSecret(),
unifiedPushStore: UnifiedPushStore = FakeUnifiedPushStore(), unifiedPushStore: UnifiedPushStore = FakeUnifiedPushStore(),
unifiedPushCurrentUserPushConfigProvider: UnifiedPushCurrentUserPushConfigProvider = FakeUnifiedPushCurrentUserPushConfigProvider(), unifiedPushSessionPushConfigProvider: UnifiedPushSessionPushConfigProvider = FakeUnifiedPushSessionPushConfigProvider(),
): UnifiedPushProvider { ): UnifiedPushProvider {
return UnifiedPushProvider( return UnifiedPushProvider(
unifiedPushDistributorProvider = unifiedPushDistributorProvider, unifiedPushDistributorProvider = unifiedPushDistributorProvider,
@ -256,7 +256,7 @@ class UnifiedPushProviderTest {
unRegisterUnifiedPushUseCase = unRegisterUnifiedPushUseCase, unRegisterUnifiedPushUseCase = unRegisterUnifiedPushUseCase,
pushClientSecret = pushClientSecret, pushClientSecret = pushClientSecret,
unifiedPushStore = unifiedPushStore, unifiedPushStore = unifiedPushStore,
unifiedPushCurrentUserPushConfigProvider = unifiedPushCurrentUserPushConfigProvider, unifiedPushSessionPushConfigProvider = unifiedPushSessionPushConfigProvider,
) )
} }
} }

View file

@ -1,20 +0,0 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushCurrentUserPushConfigProvider
import io.element.android.tests.testutils.lambda.lambdaError
class FakeUnifiedPushCurrentUserPushConfigProvider(
private val currentUserPushConfig: () -> CurrentUserPushConfig? = { lambdaError() },
) : UnifiedPushCurrentUserPushConfigProvider {
override suspend fun provide(): CurrentUserPushConfig? {
return currentUserPushConfig()
}
}

View file

@ -0,0 +1,21 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.pushproviders.api.Config
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushSessionPushConfigProvider
import io.element.android.tests.testutils.lambda.lambdaError
class FakeUnifiedPushSessionPushConfigProvider(
private val config: (SessionId) -> Config? = { lambdaError() },
) : UnifiedPushSessionPushConfigProvider {
override suspend fun provide(sessionId: SessionId): Config? {
return config(sessionId)
}
}

View file

@ -8,8 +8,10 @@
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.pushproviders.test.aCurrentUserPushConfig import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.pushproviders.api.Config
import io.element.android.libraries.pushproviders.test.aSessionPushConfig
import io.element.android.libraries.pushproviders.unifiedpush.FakeUnifiedPushApiFactory import io.element.android.libraries.pushproviders.unifiedpush.FakeUnifiedPushApiFactory
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig
import io.element.android.libraries.pushproviders.unifiedpush.invalidDiscoveryResponse import io.element.android.libraries.pushproviders.unifiedpush.invalidDiscoveryResponse
@ -27,7 +29,7 @@ class UnifiedPushMatrixGatewayTestTest {
@Test @Test
fun `test UnifiedPushMatrixGatewayTest success`() = runTest { fun `test UnifiedPushMatrixGatewayTest success`() = runTest {
val sut = createUnifiedPushMatrixGatewayTest( val sut = createUnifiedPushMatrixGatewayTest(
currentUserPushConfig = aCurrentUserPushConfig(), config = aSessionPushConfig(),
discoveryResponse = matrixDiscoveryResponse, discoveryResponse = matrixDiscoveryResponse,
) )
sut.runAndTestState { sut.runAndTestState {
@ -41,7 +43,7 @@ class UnifiedPushMatrixGatewayTestTest {
@Test @Test
fun `test UnifiedPushMatrixGatewayTest no config found`() = runTest { fun `test UnifiedPushMatrixGatewayTest no config found`() = runTest {
val sut = createUnifiedPushMatrixGatewayTest( val sut = createUnifiedPushMatrixGatewayTest(
currentUserPushConfig = null, config = null,
discoveryResponse = matrixDiscoveryResponse, discoveryResponse = matrixDiscoveryResponse,
) )
sut.runAndTestState { sut.runAndTestState {
@ -55,7 +57,7 @@ class UnifiedPushMatrixGatewayTestTest {
@Test @Test
fun `test UnifiedPushMatrixGatewayTest not valid gateway`() = runTest { fun `test UnifiedPushMatrixGatewayTest not valid gateway`() = runTest {
val sut = createUnifiedPushMatrixGatewayTest( val sut = createUnifiedPushMatrixGatewayTest(
currentUserPushConfig = aCurrentUserPushConfig(), config = aSessionPushConfig(),
discoveryResponse = invalidDiscoveryResponse, discoveryResponse = invalidDiscoveryResponse,
) )
sut.runAndTestState { sut.runAndTestState {
@ -72,7 +74,7 @@ class UnifiedPushMatrixGatewayTestTest {
@Test @Test
fun `test UnifiedPushMatrixGatewayTest network error`() = runTest { fun `test UnifiedPushMatrixGatewayTest network error`() = runTest {
val sut = createUnifiedPushMatrixGatewayTest( val sut = createUnifiedPushMatrixGatewayTest(
currentUserPushConfig = aCurrentUserPushConfig(), config = aSessionPushConfig(),
discoveryResponse = { error("Network error") }, discoveryResponse = { error("Network error") },
) )
sut.runAndTestState { sut.runAndTestState {
@ -91,14 +93,16 @@ class UnifiedPushMatrixGatewayTestTest {
} }
private fun TestScope.createUnifiedPushMatrixGatewayTest( private fun TestScope.createUnifiedPushMatrixGatewayTest(
currentUserPushConfig: CurrentUserPushConfig? = null, sessionId: SessionId = A_SESSION_ID,
config: Config? = null,
discoveryResponse: () -> DiscoveryResponse = matrixDiscoveryResponse, discoveryResponse: () -> DiscoveryResponse = matrixDiscoveryResponse,
): UnifiedPushMatrixGatewayTest { ): UnifiedPushMatrixGatewayTest {
return UnifiedPushMatrixGatewayTest( return UnifiedPushMatrixGatewayTest(
sessionId = sessionId,
unifiedPushApiFactory = FakeUnifiedPushApiFactory(discoveryResponse), unifiedPushApiFactory = FakeUnifiedPushApiFactory(discoveryResponse),
coroutineDispatchers = testCoroutineDispatchers(), coroutineDispatchers = testCoroutineDispatchers(),
unifiedPushCurrentUserPushConfigProvider = FakeUnifiedPushCurrentUserPushConfigProvider( unifiedPushSessionPushConfigProvider = FakeUnifiedPushSessionPushConfigProvider(
currentUserPushConfig = { currentUserPushConfig } config = { config }
), ),
) )
} }

View file

@ -35,7 +35,6 @@ dependencies {
testCommonDependencies(libs) testCommonDependencies(libs)
testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.preferences.test)
testImplementation(projects.services.appnavstate.test)
testImplementation(projects.libraries.pushstore.test) testImplementation(projects.libraries.pushstore.test)
androidTestImplementation(libs.coroutines.test) androidTestImplementation(libs.coroutines.test)

View file

@ -10,6 +10,7 @@ package io.element.android.libraries.troubleshoot.impl
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
import im.vector.app.features.analytics.plan.NotificationTroubleshoot import im.vector.app.features.analytics.plan.NotificationTroubleshoot
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.push.api.GetCurrentPushProvider import io.element.android.libraries.push.api.GetCurrentPushProvider
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
@ -25,6 +26,7 @@ import kotlinx.coroutines.flow.onEach
@Inject @Inject
class TroubleshootTestSuite( class TroubleshootTestSuite(
private val sessionId: SessionId,
private val notificationTroubleshootTests: Set<@JvmSuppressWildcards NotificationTroubleshootTest>, private val notificationTroubleshootTests: Set<@JvmSuppressWildcards NotificationTroubleshootTest>,
private val getCurrentPushProvider: GetCurrentPushProvider, private val getCurrentPushProvider: GetCurrentPushProvider,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
@ -41,7 +43,7 @@ class TroubleshootTestSuite(
suspend fun start(coroutineScope: CoroutineScope) { suspend fun start(coroutineScope: CoroutineScope) {
val testFilterData = TestFilterData( val testFilterData = TestFilterData(
currentPushProviderName = getCurrentPushProvider.getCurrentPushProvider() currentPushProviderName = getCurrentPushProvider.getCurrentPushProvider(sessionId)
) )
tests = notificationTroubleshootTests tests = notificationTroubleshootTests
.filter { it.isRelevant(testFilterData) } .filter { it.isRelevant(testFilterData) }

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.troubleshoot.impl
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.push.test.FakeGetCurrentPushProvider import io.element.android.libraries.push.test.FakeGetCurrentPushProvider
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
@ -170,6 +171,7 @@ private fun createTroubleshootTestSuite(
currentPushProvider: String? = null, currentPushProvider: String? = null,
): TroubleshootTestSuite { ): TroubleshootTestSuite {
return TroubleshootTestSuite( return TroubleshootTestSuite(
sessionId = A_SESSION_ID,
notificationTroubleshootTests = tests, notificationTroubleshootTests = tests,
getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider), getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider),
analyticsService = FakeAnalyticsService(), analyticsService = FakeAnalyticsService(),