Sync notifications using WorkManager (#5545)

* Initial implementation of notification sync using `WorkManager`

* Use custom `MetroWorkerFactory` to allow assisted injection in WorkManager Workers

* Add tests for `FetchNotificationWorker`. Create `FakeNotificationResolverQueue` to help testing.

* Add more tests, fix Konsist checks

* Add tests for `SyncNotificationWorkManagerRequest`

* Simplify `FakeNotificationResolverQueue`
This commit is contained in:
Jorge Martin Espinosa 2025-10-17 11:51:27 +02:00 committed by GitHub
parent f3d75ee85c
commit ebe94f873e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 968 additions and 98 deletions

View file

@ -34,10 +34,12 @@ dependencies {
implementation(projects.libraries.testtags)
implementation(projects.libraries.uiStrings)
implementation(projects.libraries.dateformatter.api)
implementation(projects.libraries.workmanager.api)
api(projects.features.logout.api)
testCommonDependencies(libs, true)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.libraries.sessionStorage.test)
testImplementation(projects.libraries.workmanager.test)
}

View file

@ -26,6 +26,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.encryption.BackupState
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -34,6 +35,7 @@ import kotlinx.coroutines.launch
class LogoutPresenter(
private val matrixClient: MatrixClient,
private val encryptionService: EncryptionService,
private val workManagerScheduler: WorkManagerScheduler,
) : Presenter<LogoutState> {
@Composable
override fun present(): LogoutState {
@ -109,6 +111,9 @@ class LogoutPresenter(
ignoreSdkError: Boolean,
) = launch {
suspend {
// Cancel any pending work (e.g. notification sync)
workManagerScheduler.cancel(matrixClient.sessionId)
matrixClient.logout(userInitiated = true, ignoreSdkError)
}.runCatchingUpdatingState(logoutAction)
}

View file

@ -14,6 +14,7 @@ import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.encryption.BackupState
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
import io.element.android.libraries.matrix.api.encryption.EncryptionService
@ -21,7 +22,9 @@ import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.lambda.lambdaRecorder
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.test.runTest
@ -145,7 +148,9 @@ class LogoutPresenterTest {
@Test
fun `present - logout then confirm`() = runTest {
val presenter = createLogoutPresenter()
val cancelWorkManagerJobsLambda = lambdaRecorder<SessionId, Unit> {}
val workManagerScheduler = FakeWorkManagerScheduler(cancelLambda = cancelWorkManagerJobsLambda)
val presenter = createLogoutPresenter(workManagerScheduler = workManagerScheduler)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@ -158,6 +163,8 @@ class LogoutPresenterTest {
assertThat(loadingState.logoutAction).isInstanceOf(AsyncAction.Loading::class.java)
val successState = awaitItem()
assertThat(successState.logoutAction).isInstanceOf(AsyncAction.Success::class.java)
cancelWorkManagerJobsLambda.assertions().isCalledOnce()
}
}
@ -230,7 +237,9 @@ class LogoutPresenterTest {
internal fun createLogoutPresenter(
matrixClient: MatrixClient = FakeMatrixClient(),
encryptionService: EncryptionService = FakeEncryptionService(),
workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(cancelLambda = {}),
): LogoutPresenter = LogoutPresenter(
matrixClient = matrixClient,
encryptionService = encryptionService,
workManagerScheduler = workManagerScheduler,
)

View file

@ -163,7 +163,7 @@ class DefaultBugReporterTest {
assertThat(foundValues["file"]).contains(fakePushRules)
// device_key now added given they are not null
// so is the push_rules value
// so is the file value for the included push_rules
assertThat(progressValues.size).isEqualTo(EXPECTED_NUMBER_OF_PROGRESS_VALUE + 2)
server.shutdown()