Add notification troubleshoot test about blocked users.

This commit is contained in:
Benoit Marty 2025-09-22 20:12:26 +02:00
parent 648583c68a
commit fa14e4c106
31 changed files with 338 additions and 30 deletions

View file

@ -0,0 +1,69 @@
/*
* Copyright 2025 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.push.impl.troubleshoot
import dev.zacsweers.metro.ContributesIntoSet
import dev.zacsweers.metro.Inject
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.impl.R
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.NotificationTroubleshootTestDelegate
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
import io.element.android.services.toolbox.api.strings.StringProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.StateFlow
@ContributesIntoSet(SessionScope::class)
@Inject
class IgnoredUsersTest(
private val matrixClient: MatrixClient,
private val stringProvider: StringProvider,
) : NotificationTroubleshootTest {
override val order = 80
private val delegate = NotificationTroubleshootTestDelegate(
defaultName = stringProvider.getString(R.string.troubleshoot_notifications_test_blocked_users_title),
defaultDescription = stringProvider.getString(R.string.troubleshoot_notifications_test_blocked_users_description),
fakeDelay = NotificationTroubleshootTestDelegate.SHORT_DELAY,
)
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
override suspend fun run(coroutineScope: CoroutineScope) {
delegate.start()
val ignorerUsers = matrixClient.ignoredUsersFlow.value
if (ignorerUsers.isEmpty()) {
delegate.updateState(
description = stringProvider.getString(R.string.troubleshoot_notifications_test_blocked_users_result_none),
status = NotificationTroubleshootTestState.Status.Success,
)
} else {
delegate.updateState(
description = stringProvider.getQuantityString(
R.plurals.troubleshoot_notifications_test_blocked_users_result_some,
ignorerUsers.size,
ignorerUsers.size
),
status = NotificationTroubleshootTestState.Status.Failure(
hasQuickFix = true,
isCritical = false,
quickFixButtonString = stringProvider.getString(R.string.troubleshoot_notifications_test_blocked_users_quick_fix),
),
)
}
}
override suspend fun quickFix(
coroutineScope: CoroutineScope,
navigator: NotificationTroubleshootNavigator,
) {
navigator.openIgnoredUsers()
}
override suspend fun reset() = delegate.reset()
}

View file

@ -13,6 +13,7 @@ import dev.zacsweers.metro.Inject
import io.element.android.libraries.push.api.PushService
import io.element.android.libraries.push.api.gateway.PushGatewayFailure
import io.element.android.libraries.push.impl.R
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.NotificationTroubleshootTestDelegate
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
@ -99,7 +100,10 @@ class PushLoopbackTest(
)
}
override suspend fun quickFix(coroutineScope: CoroutineScope) {
override suspend fun quickFix(
coroutineScope: CoroutineScope,
navigator: NotificationTroubleshootNavigator,
) {
delegate.start()
pushService.getCurrentPushProvider()?.rotateToken()
run(coroutineScope)

View file

@ -54,6 +54,14 @@
<string name="push_distributor_background_sync_android">"Background synchronization"</string>
<string name="push_distributor_firebase_android">"Google Services"</string>
<string name="push_no_valid_google_play_services_apk_android">"No valid Google Play Services found. Notifications may not work properly."</string>
<string name="troubleshoot_notifications_test_blocked_users_description">"Checking blocked users"</string>
<string name="troubleshoot_notifications_test_blocked_users_quick_fix">"View blocked users"</string>
<string name="troubleshoot_notifications_test_blocked_users_result_none">"No users are blocked."</string>
<plurals name="troubleshoot_notifications_test_blocked_users_result_some">
<item quantity="one">"You blocked %1$d user. You will not receive notifications for this user."</item>
<item quantity="other">"You blocked %1$d users. You will not receive notifications for these users."</item>
</plurals>
<string name="troubleshoot_notifications_test_blocked_users_title">"Blocked users"</string>
<string name="troubleshoot_notifications_test_current_push_provider_description">"Get the name of the current provider."</string>
<string name="troubleshoot_notifications_test_current_push_provider_failure">"No push providers selected."</string>
<string name="troubleshoot_notifications_test_current_push_provider_success">"Current push provider: %1$s."</string>

View file

@ -0,0 +1,93 @@
/*
* Copyright 2025 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.push.impl.troubleshoot
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_ID_2
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
import io.element.android.services.toolbox.test.strings.FakeStringProvider
import io.element.android.tests.testutils.lambda.lambdaRecorder
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Test
class IgnoredUsersTestTest {
@Test
fun `test IgnoredUsersTest order`() = runTest {
val sut = IgnoredUsersTest(
matrixClient = FakeMatrixClient(),
stringProvider = FakeStringProvider(),
)
assertThat(sut.order).isEqualTo(80)
}
@Test
fun `test IgnoredUsersTest quick fix`() = runTest {
val sut = IgnoredUsersTest(
matrixClient = FakeMatrixClient(),
stringProvider = FakeStringProvider(),
)
val openIgnoredUsersResult = lambdaRecorder<Unit> {}
val navigator = object : NotificationTroubleshootNavigator {
override fun openIgnoredUsers() = openIgnoredUsersResult()
}
sut.quickFix(
coroutineScope = backgroundScope,
navigator = navigator,
)
openIgnoredUsersResult.assertions().isCalledOnce()
}
@Test
fun `test IgnoredUsersTest with no blocked users`() = runTest {
val sut = IgnoredUsersTest(
matrixClient = FakeMatrixClient(
ignoredUsersFlow = MutableStateFlow(persistentListOf())
),
stringProvider = FakeStringProvider(),
)
backgroundScope.launch {
sut.run(this)
}
sut.state.test {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
val lastItem = awaitItem()
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
}
}
@Test
fun `test IgnoredUsersTest with blocked users`() = runTest {
val sut = IgnoredUsersTest(
matrixClient = FakeMatrixClient(
ignoredUsersFlow = MutableStateFlow(persistentListOf(A_USER_ID, A_USER_ID_2))
),
stringProvider = FakeStringProvider(),
)
backgroundScope.launch {
sut.run(this)
}
sut.state.test {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
val lastItem = awaitItem()
val lastStatus = lastItem.status as NotificationTroubleshootTestState.Status.Failure
assertThat(lastStatus.hasQuickFix).isTrue()
assertThat(lastStatus.isCritical).isFalse()
assertThat(lastStatus.quickFixButtonString).isNotNull()
assertThat(lastItem.description).contains("2")
}
}
}

View file

@ -15,6 +15,7 @@ import io.element.android.libraries.push.api.gateway.PushGatewayFailure
import io.element.android.libraries.push.test.FakePushService
import io.element.android.libraries.pushproviders.test.FakePushProvider
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
import io.element.android.libraries.troubleshoot.test.FakeNotificationTroubleshootNavigator
import io.element.android.services.toolbox.test.strings.FakeStringProvider
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
import io.element.android.tests.testutils.lambda.lambdaRecorder
@ -97,7 +98,7 @@ class PushLoopbackTestTest {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
val lastItem = awaitItem()
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true))
sut.quickFix(this)
sut.quickFix(this, FakeNotificationTroubleshootNavigator())
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true))
rotateTokenLambda.assertions().isCalledOnce()