From e56f613c0c23b19e016d9083cb1c38eb36b2fbf4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 18:37:35 +0000 Subject: [PATCH 001/252] Update core to v1.13.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5933c701da..73cb5c2f89 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ ksp = "1.9.23-1.0.20" firebaseAppDistribution = "4.2.0" # AndroidX -core = "1.13.0" +core = "1.13.1" # Warning: there is an issue with 1.1.0, that I cannot repro on unit test. # To repro with the application: # Clear the cache of the application and run the app. Nearly each time, there is an infinite loading From 383d3e698e9acc66b5085a22f61bcf7949407e15 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 May 2024 00:24:52 +0000 Subject: [PATCH 002/252] Update datastore to v1.1.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5933c701da..51ac1dda5a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ core = "1.13.0" # Clear the cache of the application and run the app. Nearly each time, there is an infinite loading # Due to the DefaultMigrationStore not bahaving as expected. # Stick to 1.0.0 for now, and ensure that this scenario cannot be reproduced when upgrading the version. -datastore = "1.0.0" +datastore = "1.1.1" constraintlayout = "2.1.4" constraintlayout_compose = "1.0.1" lifecycle = "2.7.0" From 72b1a829352db81a433983cb8f5e5bec8653e055 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 14:53:51 +0000 Subject: [PATCH 003/252] Update dependency org.maplibre.gl:android-sdk to v11 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 27dfaa42f1..0e6aa375c8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -173,7 +173,7 @@ vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0" telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil", version.ref = "telephoto" } telephoto_flick = { module = "me.saket.telephoto:flick-android", version.ref = "telephoto" } statemachine = "com.freeletics.flowredux:compose:1.2.1" -maplibre = "org.maplibre.gl:android-sdk:10.3.1" +maplibre = "org.maplibre.gl:android-sdk:11.0.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.2" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.2" opusencoder = "io.element.android:opusencoder:1.1.0" From 12b51e381eeabcc9115a8587782c624b3ba45976 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 12:55:39 +0000 Subject: [PATCH 004/252] Update dependency io.nlopez.compose.rules:detekt to v0.3.13 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index a0eedc8db7..0e5ec1c674 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -60,7 +60,7 @@ allprojects { config.from(files("$rootDir/tools/detekt/detekt.yml")) } dependencies { - detektPlugins("io.nlopez.compose.rules:detekt:0.3.12") + detektPlugins("io.nlopez.compose.rules:detekt:0.3.13") } // KtLint From 000bcb448cff00c6afee6b6cc1d61ab30136d8bc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 3 Apr 2024 11:34:49 +0200 Subject: [PATCH 005/252] Rework Modifier.applyIf. It was using `Modifier.composed` which is not good for performance and detekt is warning about this. --- .../impl/search/RoomListSearchView.kt | 11 ++++--- .../designsystem/modifiers/ApplyIf.kt | 32 ++++++++----------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt index a18dd6607f..8190c8078e 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/search/RoomListSearchView.kt @@ -87,10 +87,13 @@ internal fun RoomListSearchView( ) { Column( modifier = modifier - .applyIf(state.isSearchActive, ifTrue = { - // Disable input interaction to underlying views - pointerInput(Unit) {} - }) + .applyIf( + condition = state.isSearchActive, + ifTrue = { + // Disable input interaction to underlying views + pointerInput(Unit) {} + } + ) ) { if (state.isSearchActive) { RoomListSearchContent( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ApplyIf.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ApplyIf.kt index a18d0ef3ed..3a4a433821 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ApplyIf.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/ApplyIf.kt @@ -16,30 +16,26 @@ package io.element.android.libraries.designsystem.modifiers -import android.annotation.SuppressLint -import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.composed import androidx.compose.ui.platform.debugInspectorInfo +import androidx.compose.ui.platform.inspectable /** * Applies the [ifTrue] modifier when the [condition] is true, [ifFalse] otherwise. */ -@SuppressLint("UnnecessaryComposedModifier") // It's actually necessary due to the `@Composable` lambdas fun Modifier.applyIf( condition: Boolean, - ifTrue: @Composable Modifier.() -> Modifier, - ifFalse: @Composable (Modifier.() -> Modifier)? = null -): Modifier = - composed( - inspectorInfo = debugInspectorInfo { - name = "applyIf" - value = condition - } - ) { - when { - condition -> then(ifTrue(Modifier)) - ifFalse != null -> then(ifFalse(Modifier)) - else -> this - } + ifTrue: Modifier.() -> Modifier, + ifFalse: (Modifier.() -> Modifier)? = null +): Modifier = this then inspectable( + inspectorInfo = debugInspectorInfo { + name = "applyIf" + value = condition } +) { + this then when { + condition -> ifTrue(Modifier) + ifFalse != null -> ifFalse(Modifier) + else -> Modifier + } +} From 07ca064ac48976a1a473ec1b1f50caefaaf96e21 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 3 Apr 2024 12:00:21 +0200 Subject: [PATCH 006/252] Supress warning `ModifierComposed` for autofill, there is a low risk of performance issue here. --- .../loginpassword/LoginPasswordView.kt | 28 +++++++++++-------- .../theme/components/TextField.kt | 1 + 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index 51ed3fcf6f..926601bcee 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -141,7 +141,7 @@ fun LoginPasswordView( // Submit Box( modifier = Modifier - .padding(horizontal = 16.dp) + .padding(horizontal = 16.dp) ) { ButtonColumnMolecule { Button( @@ -201,11 +201,14 @@ private fun LoginForm( .fillMaxWidth() .onTabOrEnterKeyFocusNext(focusManager) .testTag(TestTags.loginEmailUsername) - .autofill(autofillTypes = listOf(AutofillType.Username), onFill = { - val sanitized = it.sanitize() - loginFieldState = sanitized - eventSink(LoginPasswordEvents.SetLogin(sanitized)) - }), + .autofill( + autofillTypes = listOf(AutofillType.Username), + onFill = { + val sanitized = it.sanitize() + loginFieldState = sanitized + eventSink(LoginPasswordEvents.SetLogin(sanitized)) + } + ), placeholder = { Text(text = stringResource(CommonStrings.common_username)) }, @@ -247,11 +250,14 @@ private fun LoginForm( .fillMaxWidth() .onTabOrEnterKeyFocusNext(focusManager) .testTag(TestTags.loginPassword) - .autofill(autofillTypes = listOf(AutofillType.Password), onFill = { - val sanitized = it.sanitize() - passwordFieldState = sanitized - eventSink(LoginPasswordEvents.SetPassword(sanitized)) - }), + .autofill( + autofillTypes = listOf(AutofillType.Password), + onFill = { + val sanitized = it.sanitize() + passwordFieldState = sanitized + eventSink(LoginPasswordEvents.SetPassword(sanitized)) + } + ), onValueChange = { val sanitized = it.sanitize() passwordFieldState = sanitized diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt index f2c004d2ca..10fb4a2906 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt @@ -213,6 +213,7 @@ private fun TextFieldValueContentToPreview() { } } +@Suppress("ModifierComposed") @OptIn(ExperimentalComposeUiApi::class) fun Modifier.autofill(autofillTypes: List, onFill: (String) -> Unit) = composed { val autofillNode = AutofillNode(autofillTypes, onFill = onFill) From 25b46ee23028b0c08779ffaa3a812089343e939a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 May 2024 16:57:08 +0200 Subject: [PATCH 007/252] Suppress warning for ModifierComposed (detekt) --- .../element/android/libraries/designsystem/components/Bloom.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt index 8558780a22..f8121ec500 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt @@ -168,6 +168,7 @@ data class BloomLayer( * @param bottomSoftEdgeAlpha The alpha value to apply to the bottom soft edge. * @param alpha The alpha value to apply to the bloom effect. */ +@SuppressWarnings("ModifierComposed") fun Modifier.bloom( hash: String?, background: Color, @@ -312,6 +313,7 @@ fun Modifier.bloom( * @param bottomSoftEdgeAlpha The alpha value to apply to the bottom soft edge. * @param alpha The alpha value to apply to the bloom effect. */ +@SuppressWarnings("ModifierComposed") fun Modifier.avatarBloom( avatarData: AvatarData, background: Color, From a2e88383bd013d9a8a20f62b39044ed556ac5233 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 21 May 2024 16:12:19 +0200 Subject: [PATCH 008/252] maplibre : remove all mapbox references and update plugin to be compatible. --- .../location/impl/common/MapDefaults.kt | 4 ++-- .../location/impl/send/SendLocationView.kt | 6 ++--- .../location/impl/show/ShowLocationView.kt | 8 +++---- .../impl/show/ShowLocationViewTest.kt | 2 +- gradle/libs.versions.toml | 2 +- .../libraries/maplibre/compose/CameraMode.kt | 2 +- .../compose/CameraMoveStartedReason.kt | 12 +++++----- .../maplibre/compose/CameraPositionState.kt | 18 +++++++------- .../libraries/maplibre/compose/IconAnchor.kt | 2 +- .../libraries/maplibre/compose/MapApplier.kt | 8 +++---- ...Composable.kt => MapLibreMapComposable.kt} | 8 +++---- .../libraries/maplibre/compose/MapUpdater.kt | 14 +++++------ .../libraries/maplibre/compose/MapboxMap.kt | 24 +++++++++---------- .../libraries/maplibre/compose/Symbol.kt | 10 ++++---- 14 files changed, 60 insertions(+), 60 deletions(-) rename libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/{MapboxMapComposable.kt => MapLibreMapComposable.kt} (83%) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/MapDefaults.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/MapDefaults.kt index f5f0bbe078..6896172363 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/MapDefaults.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/MapDefaults.kt @@ -21,12 +21,12 @@ import android.view.Gravity import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.graphics.Color -import com.mapbox.mapboxsdk.camera.CameraPosition -import com.mapbox.mapboxsdk.geometry.LatLng import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.maplibre.compose.MapLocationSettings import io.element.android.libraries.maplibre.compose.MapSymbolManagerSettings import io.element.android.libraries.maplibre.compose.MapUiSettings +import org.maplibre.android.camera.CameraPosition +import org.maplibre.android.geometry.LatLng /** * Common configuration values for the map. diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt index a396cc120f..40aac154fa 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt @@ -37,7 +37,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import com.mapbox.mapboxsdk.camera.CameraPosition import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.location.api.Location @@ -61,9 +60,10 @@ import io.element.android.libraries.designsystem.theme.components.bottomsheet.re import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.maplibre.compose.CameraMode import io.element.android.libraries.maplibre.compose.CameraMoveStartedReason -import io.element.android.libraries.maplibre.compose.MapboxMap +import io.element.android.libraries.maplibre.compose.MapLibreMap import io.element.android.libraries.maplibre.compose.rememberCameraPositionState import io.element.android.libraries.ui.strings.CommonStrings +import org.maplibre.android.camera.CameraPosition @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -189,7 +189,7 @@ fun SendLocationView( .consumeWindowInsets(it), contentAlignment = Alignment.Center ) { - MapboxMap( + MapLibreMap( styleUri = rememberTileStyleUrl(), modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 2f352d50d2..3370dbc751 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -33,8 +33,6 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import com.mapbox.mapboxsdk.camera.CameraPosition -import com.mapbox.mapboxsdk.geometry.LatLng import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.TypographyTokens @@ -56,12 +54,14 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.maplibre.compose.CameraMode import io.element.android.libraries.maplibre.compose.CameraMoveStartedReason import io.element.android.libraries.maplibre.compose.IconAnchor -import io.element.android.libraries.maplibre.compose.MapboxMap +import io.element.android.libraries.maplibre.compose.MapLibreMap import io.element.android.libraries.maplibre.compose.Symbol import io.element.android.libraries.maplibre.compose.rememberCameraPositionState import io.element.android.libraries.maplibre.compose.rememberSymbolState import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.toImmutableMap +import org.maplibre.android.camera.CameraPosition +import org.maplibre.android.geometry.LatLng @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -166,7 +166,7 @@ fun ShowLocationView( ) } - MapboxMap( + MapLibreMap( styleUri = rememberTileStyleUrl(), modifier = Modifier.fillMaxSize(), images = mapOf(PIN_ID to CommonDrawables.pin).toImmutableMap(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationViewTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationViewTest.kt index 05160dca56..22377c3879 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationViewTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationViewTest.kt @@ -144,7 +144,7 @@ private fun AndroidComposeTestRule.setShowL onBackPressed: () -> Unit = EnsureNeverCalled(), ) { setContent { - // Simulate a LocalInspectionMode for MapboxMap + // Simulate a LocalInspectionMode for MapLibreMap CompositionLocalProvider(LocalInspectionMode provides true) { ShowLocationView( state = state, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0e6aa375c8..e917045d33 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -175,7 +175,7 @@ telephoto_flick = { module = "me.saket.telephoto:flick-android", version.ref = " statemachine = "com.freeletics.flowredux:compose:1.2.1" maplibre = "org.maplibre.gl:android-sdk:11.0.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.2" -maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.2" +maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.0" opusencoder = "io.element.android:opusencoder:1.1.0" kotlinpoet = "com.squareup:kotlinpoet:1.16.0" diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraMode.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraMode.kt index 0c85d3dfb3..a527201b31 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraMode.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraMode.kt @@ -19,7 +19,7 @@ package io.element.android.libraries.maplibre.compose import androidx.compose.runtime.Immutable -import com.mapbox.mapboxsdk.location.modes.CameraMode as InternalCameraMode +import org.maplibre.android.location.modes.CameraMode as InternalCameraMode @Immutable public enum class CameraMode { diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraMoveStartedReason.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraMoveStartedReason.kt index 10c9d8b69a..697bbed8ed 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraMoveStartedReason.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraMoveStartedReason.kt @@ -19,14 +19,14 @@ package io.element.android.libraries.maplibre.compose import androidx.compose.runtime.Immutable -import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION -import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE -import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_DEVELOPER_ANIMATION +import org.maplibre.android.maps.MapLibreMap.OnCameraMoveStartedListener.REASON_API_ANIMATION +import org.maplibre.android.maps.MapLibreMap.OnCameraMoveStartedListener.REASON_API_GESTURE +import org.maplibre.android.maps.MapLibreMap.OnCameraMoveStartedListener.REASON_DEVELOPER_ANIMATION /** * Enumerates the different reasons why the map camera started to move. * - * Based on enum values from https://docs.maptiler.com/maplibre-gl-native-android/com.mapbox.mapboxsdk.maps/#oncameramovestartedlistener. + * Based on enum values from https://docs.maptiler.com/maplibre-gl-native-android/org.maplibre.android.maps/#oncameramovestartedlistener. * * [NO_MOVEMENT_YET] is used as the initial state before any map movement has been observed. * @@ -44,11 +44,11 @@ public enum class CameraMoveStartedReason(public val value: Int) { public companion object { /** - * Converts from the Maps SDK [com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener] + * Converts from the Maps SDK [org.maplibre.android.maps.MapLibreMap.OnCameraMoveStartedListener] * constants to [CameraMoveStartedReason], or returns [UNKNOWN] if there is no such * [CameraMoveStartedReason] for the given [value]. * - * See https://docs.maptiler.com/maplibre-gl-native-android/com.mapbox.mapboxsdk.maps/#oncameramovestartedlistener. + * See https://docs.maptiler.com/maplibre-gl-native-android/org.maplibre.android.maps/#oncameramovestartedlistener. */ public fun fromInt(value: Int): CameraMoveStartedReason { return values().firstOrNull { it.value == value } ?: return UNKNOWN diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraPositionState.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraPositionState.kt index 114e6acc02..a71ece9732 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraPositionState.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/CameraPositionState.kt @@ -28,11 +28,11 @@ import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.runtime.staticCompositionLocalOf -import com.mapbox.mapboxsdk.camera.CameraPosition -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory -import com.mapbox.mapboxsdk.maps.MapboxMap -import com.mapbox.mapboxsdk.maps.Projection import kotlinx.parcelize.Parcelize +import org.maplibre.android.camera.CameraPosition +import org.maplibre.android.camera.CameraUpdateFactory +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.Projection /** * Create and [rememberSaveable] a [CameraPositionState] using [CameraPositionState.Saver]. @@ -49,7 +49,7 @@ public inline fun rememberCameraPositionState( /** * A state object that can be hoisted to control and observe the map's camera state. - * A [CameraPositionState] may only be used by a single [MapboxMap] composable at a time + * A [CameraPositionState] may only be used by a single [MapLibreMap] composable at a time * as it reflects instance state for a single view of a map. * * @param position the initial camera position @@ -143,15 +143,15 @@ public class CameraPositionState( // The map currently associated with this CameraPositionState. // Guarded by `lock`. - private var map: MapboxMap? by mutableStateOf(null) + private var map: MapLibreMap? by mutableStateOf(null) // The current map is set and cleared by side effect. // There can be only one associated at a time. - internal fun setMap(map: MapboxMap?) { + internal fun setMap(map: MapLibreMap?) { synchronized(lock) { if (this.map == null && map == null) return if (this.map != null && map != null) { - error("CameraPositionState may only be associated with one MapboxMap at a time") + error("CameraPositionState may only be associated with one MapLibreMap at a time") } this.map = map if (map == null) { @@ -179,7 +179,7 @@ internal val LocalCameraPositionState = staticCompositionLocalOf { CameraPositio /** The current [CameraPositionState] used by the map. */ public val currentCameraPositionState: CameraPositionState - @[MapboxMapComposable ReadOnlyComposable Composable] + @[MapLibreMapComposable ReadOnlyComposable Composable] get() = LocalCameraPositionState.current @Parcelize diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/IconAnchor.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/IconAnchor.kt index 25f6f38c66..cb64f63a44 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/IconAnchor.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/IconAnchor.kt @@ -19,7 +19,7 @@ package io.element.android.libraries.maplibre.compose import androidx.compose.runtime.Immutable -import com.mapbox.mapboxsdk.style.layers.Property +import org.maplibre.android.style.layers.Property @Immutable public enum class IconAnchor { diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapApplier.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapApplier.kt index 650e9d27ef..9b03a1e952 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapApplier.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapApplier.kt @@ -19,9 +19,9 @@ package io.element.android.libraries.maplibre.compose import androidx.compose.runtime.AbstractApplier -import com.mapbox.mapboxsdk.maps.MapboxMap -import com.mapbox.mapboxsdk.maps.Style -import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.Style +import org.maplibre.android.plugins.annotation.SymbolManager internal interface MapNode { fun onAttached() {} @@ -32,7 +32,7 @@ internal interface MapNode { private object MapNodeRoot : MapNode internal class MapApplier( - val map: MapboxMap, + val map: MapLibreMap, val style: Style, val symbolManager: SymbolManager, ) : AbstractApplier(MapNodeRoot) { diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMapComposable.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLibreMapComposable.kt similarity index 83% rename from libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMapComposable.kt rename to libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLibreMapComposable.kt index 15876b0033..19f5864815 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMapComposable.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLibreMapComposable.kt @@ -22,10 +22,10 @@ import androidx.compose.runtime.ComposableTargetMarker /** * An annotation that can be used to mark a composable function as being expected to be use in a - * composable function that is also marked or inferred to be marked as a [MapboxMapComposable]. + * composable function that is also marked or inferred to be marked as a [MapLibreMapComposable]. * - * This will produce build warnings when [MapboxMapComposable] composable functions are used outside - * of a [MapboxMapComposable] content lambda, and vice versa. + * This will produce build warnings when [MapLibreMapComposable] composable functions are used outside + * of a [MapLibreMapComposable] content lambda, and vice versa. */ @Retention(AnnotationRetention.BINARY) @ComposableTargetMarker(description = "MapLibre Map Composable") @@ -36,4 +36,4 @@ import androidx.compose.runtime.ComposableTargetMarker AnnotationTarget.TYPE, AnnotationTarget.TYPE_PARAMETER, ) -public annotation class MapboxMapComposable +public annotation class MapLibreMapComposable diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt index 759061ac3d..7a41c9f7b9 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt @@ -26,17 +26,17 @@ import androidx.compose.runtime.ComposeNode import androidx.compose.runtime.currentComposer import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext -import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions -import com.mapbox.mapboxsdk.location.LocationComponentOptions -import com.mapbox.mapboxsdk.location.OnCameraTrackingChangedListener -import com.mapbox.mapboxsdk.location.engine.LocationEngineRequest -import com.mapbox.mapboxsdk.maps.MapboxMap -import com.mapbox.mapboxsdk.maps.Style +import org.maplibre.android.location.LocationComponentActivationOptions +import org.maplibre.android.location.LocationComponentOptions +import org.maplibre.android.location.OnCameraTrackingChangedListener +import org.maplibre.android.location.engine.LocationEngineRequest +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.Style private const val LOCATION_REQUEST_INTERVAL = 750L internal class MapPropertiesNode( - val map: MapboxMap, + val map: MapLibreMap, style: Style, context: Context, cameraPositionState: CameraPositionState, diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt index 4fab4b506f..352585cc1a 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt @@ -46,14 +46,14 @@ import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver -import com.mapbox.mapboxsdk.Mapbox -import com.mapbox.mapboxsdk.maps.MapView -import com.mapbox.mapboxsdk.maps.MapboxMap -import com.mapbox.mapboxsdk.maps.Style -import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.persistentMapOf import kotlinx.coroutines.awaitCancellation +import org.maplibre.android.MapLibre +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.Style +import org.maplibre.android.plugins.annotation.SymbolManager import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -63,7 +63,7 @@ import kotlin.coroutines.suspendCoroutine * Heavily inspired by https://github.com/googlemaps/android-maps-compose * * @param styleUri a URI where to asynchronously fetch a style for the map - * @param modifier Modifier to be applied to the MapboxMap + * @param modifier Modifier to be applied to the MapLibreMap * @param images images added to the map's style to be later used with [Symbol] * @param cameraPositionState the [CameraPositionState] to be used to control or observe the map's * camera state @@ -73,7 +73,7 @@ import kotlin.coroutines.suspendCoroutine * @param content the content of the map */ @Composable -public fun MapboxMap( +public fun MapLibreMap( styleUri: String, modifier: Modifier = Modifier, images: ImmutableMap = persistentMapOf(), @@ -82,7 +82,7 @@ public fun MapboxMap( symbolManagerSettings: MapSymbolManagerSettings = DefaultMapSymbolManagerSettings, locationSettings: MapLocationSettings = DefaultMapLocationSettings, content: ( - @Composable @MapboxMapComposable + @Composable @MapLibreMapComposable () -> Unit )? = null, ) { @@ -99,7 +99,7 @@ public fun MapboxMap( val context = LocalContext.current val mapView = remember { - Mapbox.getInstance(context) + MapLibre.getInstance(context) MapView(context) } @@ -168,13 +168,13 @@ private suspend inline fun CompositionContext.newComposition( } } -private suspend inline fun MapView.awaitMap(): MapboxMap = suspendCoroutine { continuation -> +private suspend inline fun MapView.awaitMap(): MapLibreMap = suspendCoroutine { continuation -> getMapAsync { map -> continuation.resume(map) } } -private suspend inline fun MapboxMap.awaitStyle( +private suspend inline fun MapLibreMap.awaitStyle( context: Context, styleUri: String, images: ImmutableMap, @@ -227,7 +227,7 @@ private fun MapView.lifecycleObserver(previousState: MutableState { // Skip calling mapView.onCreate if the lifecycle did not go through onDestroy - in - // this case the MapboxMap composable also doesn't leave the composition. So, + // this case the MapLibreMap composable also doesn't leave the composition. So, // recreating the map does not restore state properly which must be avoided. if (previousState.value != Lifecycle.Event.ON_STOP) { this.onCreate(Bundle()) diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/Symbol.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/Symbol.kt index bb40c7dfa9..18d942ec80 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/Symbol.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/Symbol.kt @@ -26,10 +26,10 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import com.mapbox.mapboxsdk.geometry.LatLng -import com.mapbox.mapboxsdk.plugins.annotation.Symbol -import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager -import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions +import org.maplibre.android.geometry.LatLng +import org.maplibre.android.plugins.annotation.Symbol +import org.maplibre.android.plugins.annotation.SymbolManager +import org.maplibre.android.plugins.annotation.SymbolOptions internal class SymbolNode( val symbolManager: SymbolManager, @@ -85,7 +85,7 @@ public fun rememberSymbolState( * @param iconAnchor the anchor for the symbol image */ @Composable -@MapboxMapComposable +@MapLibreMapComposable public fun Symbol( iconId: String, state: SymbolState = rememberSymbolState(), From 181658ab449d83dd067824644a43dd7979b8a08b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 May 2024 16:57:38 +0000 Subject: [PATCH 009/252] Update dependency com.posthog:posthog-android to v3.2.2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0a25a41045..fc2eefec11 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -180,7 +180,7 @@ opusencoder = "io.element.android:opusencoder:1.1.0" kotlinpoet = "com.squareup:kotlinpoet:1.16.0" # Analytics -posthog = "com.posthog:posthog-android:3.2.1" +posthog = "com.posthog:posthog-android:3.2.2" sentry = "io.sentry:sentry-android:7.9.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.21.0" From eb97bce6c68b0713ab29fa569061bdf0760bffb4 Mon Sep 17 00:00:00 2001 From: Joe Groocock Date: Tue, 7 May 2024 01:10:03 +0100 Subject: [PATCH 010/252] Ensure selected/deselected filters stay on top This looks a little more "sane", and more closely matches what iOS does with it's filter chips. This has to manually track which filters were "just-deselected" and move those even higher up the z stack to ensure they appear above even when sliding right. This is because the order is determined by the position left-to-right of the _final_ destination of the chip. In this case we want anything that's either currently selected, or was selected and is still fading out to appear on top. Signed-off-by: Joe Groocock --- changelog.d/2809.bugfix | 1 + .../impl/filters/RoomListFiltersView.kt | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 changelog.d/2809.bugfix diff --git a/changelog.d/2809.bugfix b/changelog.d/2809.bugfix new file mode 100644 index 0000000000..70e3079686 --- /dev/null +++ b/changelog.d/2809.bugfix @@ -0,0 +1 @@ +Render selected/deselected room list filters on top diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt index fcdc260c6e..be7448c4a0 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt @@ -32,12 +32,15 @@ import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChipDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.preview.ElementPreview @@ -62,6 +65,7 @@ fun RoomListFiltersView( } val lazyListState = rememberLazyListState() + val previousFilters = remember { mutableStateOf(listOf()) } LazyRow( contentPadding = PaddingValues(start = 8.dp, end = 16.dp), modifier = modifier.fillMaxWidth(), @@ -75,17 +79,26 @@ fun RoomListFiltersView( modifier = Modifier .padding(start = 8.dp) .testTag(TestTags.homeScreenClearFilters), - onClick = ::onClearFiltersClicked + onClick = { + previousFilters.value = state.selectedFilters() + onClearFiltersClicked() + } ) } } - for (filterWithSelection in state.filterSelectionStates) { + state.filterSelectionStates.forEachIndexed { i, filterWithSelection -> item(filterWithSelection.filter) { + val zIndex = (if (previousFilters.value.contains(filterWithSelection.filter)) state.filterSelectionStates.size else 0) - i.toFloat() RoomListFilterView( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier + .animateItemPlacement() + .zIndex(zIndex), roomListFilter = filterWithSelection.filter, selected = filterWithSelection.isSelected, - onClick = ::onToggleFilter, + onClick = { + previousFilters.value = state.selectedFilters() + onToggleFilter(it) + }, ) } } From 7aa9d856b853c64ccc6e6cbbd8efe3c0229188cd Mon Sep 17 00:00:00 2001 From: Joe Groocock Date: Wed, 8 May 2024 09:54:48 +0100 Subject: [PATCH 011/252] Animate room filter colours This is much closer to how iOS looks, and is much nicer on the eyes. Signed-off-by: Joe Groocock --- .../impl/filters/RoomListFiltersView.kt | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt index be7448c4a0..2ebad2646e 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt @@ -16,6 +16,9 @@ package io.element.android.features.roomlist.impl.filters +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -139,16 +142,27 @@ private fun RoomListFilterView( onClick: (RoomListFilter) -> Unit, modifier: Modifier = Modifier ) { + val background = animateColorAsState( + targetValue = if (selected) ElementTheme.colors.bgActionPrimaryRest else ElementTheme.colors.bgCanvasDefault, + animationSpec = spring(stiffness = Spring.StiffnessMediumLow), + label = "chip background colour", + ) + val textColour = animateColorAsState( + targetValue = if (selected) ElementTheme.colors.textOnSolidPrimary else ElementTheme.colors.textPrimary, + animationSpec = spring(stiffness = Spring.StiffnessMediumLow), + label = "chip text colour", + ) + FilterChip( selected = selected, onClick = { onClick(roomListFilter) }, modifier = modifier.height(36.dp), shape = CircleShape, colors = FilterChipDefaults.filterChipColors( - containerColor = ElementTheme.colors.bgCanvasDefault, - selectedContainerColor = ElementTheme.colors.bgActionPrimaryRest, - labelColor = ElementTheme.colors.textPrimary, - selectedLabelColor = ElementTheme.colors.textOnSolidPrimary, + containerColor = background.value, + selectedContainerColor = background.value, + labelColor = textColour.value, + selectedLabelColor = textColour.value ), label = { Text(text = stringResource(id = roomListFilter.stringResource)) From b852578ffc0139304c04aaca7794900a4acbc4b1 Mon Sep 17 00:00:00 2001 From: Joe Groocock Date: Tue, 21 May 2024 22:17:18 +0000 Subject: [PATCH 012/252] Animate room list filters when clearing They animate cleanly back to their original locations now, and correctly overlap each other when doing so. The only thing that is mildly jarring is the reappearing chips, but there's no way to animate appearance in the current version of jetpack compose, so that'll have to be fixed later. Signed-off-by: Joe Groocock --- .../features/roomlist/impl/filters/RoomListFiltersView.kt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt index 2ebad2646e..7a1cb2039a 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersView.kt @@ -34,7 +34,6 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChipDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -106,13 +105,6 @@ fun RoomListFiltersView( } } } - LaunchedEffect(state.filterSelectionStates) { - // Checking for canScrollBackward is necessary for the itemPlacementAnimation to work correctly. - // We don't want the itemPlacementAnimation to be triggered when clearing the filters. - if (!state.hasAnyFilterSelected || lazyListState.canScrollBackward) { - lazyListState.animateScrollToItem(0) - } - } } @Composable From 50004e37cf74bfcf671f8fd55a77b56be2947f2a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 09:23:24 +0200 Subject: [PATCH 013/252] Add test on UnifiedPushGatewayResolver --- .../unifiedpush/UnifiedPushApiFactory.kt | 37 +++++ .../unifiedpush/UnifiedPushGatewayResolver.kt | 17 +-- .../unifiedpush/FakeUnifiedPushApiFactory.kt | 40 +++++ .../UnifiedPushGatewayResolverTest.kt | 143 ++++++++++++++++++ 4 files changed, 227 insertions(+), 10 deletions(-) create mode 100644 libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushApiFactory.kt create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushApiFactory.kt create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolverTest.kt diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushApiFactory.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushApiFactory.kt new file mode 100644 index 0000000000..84a923df44 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushApiFactory.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.network.RetrofitFactory +import io.element.android.libraries.pushproviders.unifiedpush.network.UnifiedPushApi +import javax.inject.Inject + +interface UnifiedPushApiFactory { + fun create(baseUrl: String): UnifiedPushApi +} + +@ContributesBinding(AppScope::class) +class DefaultUnifiedPushApiFactory @Inject constructor( + private val retrofitFactory: RetrofitFactory, +) : UnifiedPushApiFactory { + override fun create(baseUrl: String): UnifiedPushApi { + return retrofitFactory.create(baseUrl) + .create(UnifiedPushApi::class.java) + } +} diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt index 54b80a5110..74ae4cd5aa 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt @@ -17,28 +17,25 @@ package io.element.android.libraries.pushproviders.unifiedpush import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.network.RetrofitFactory -import io.element.android.libraries.pushproviders.unifiedpush.network.UnifiedPushApi import kotlinx.coroutines.withContext import timber.log.Timber import java.net.URL import javax.inject.Inject class UnifiedPushGatewayResolver @Inject constructor( - private val retrofitFactory: RetrofitFactory, + private val unifiedPushApiFactory: UnifiedPushApiFactory, private val coroutineDispatchers: CoroutineDispatchers, ) { suspend fun getGateway(endpoint: String): String? { val gateway = UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL - val url = URL(endpoint) - val port = if (url.port != -1) ":${url.port}" else "" - val customBase = "${url.protocol}://${url.host}$port" - val customUrl = "$customBase/_matrix/push/v1/notify" - Timber.i("Testing $customUrl") try { + val url = URL(endpoint) + val port = if (url.port != -1) ":${url.port}" else "" + val customBase = "${url.protocol}://${url.host}$port" + val customUrl = "$customBase/_matrix/push/v1/notify" + Timber.i("Testing $customUrl") return withContext(coroutineDispatchers.io) { - val api = retrofitFactory.create(customBase) - .create(UnifiedPushApi::class.java) + val api = unifiedPushApiFactory.create(customBase) try { val discoveryResponse = api.discover() if (discoveryResponse.unifiedpush.gateway == "matrix") { diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushApiFactory.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushApiFactory.kt new file mode 100644 index 0000000000..e0d7808505 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushApiFactory.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +import io.element.android.libraries.pushproviders.unifiedpush.network.DiscoveryResponse +import io.element.android.libraries.pushproviders.unifiedpush.network.UnifiedPushApi + +class FakeUnifiedPushApiFactory( + private val discoveryResponse: () -> DiscoveryResponse +) : UnifiedPushApiFactory { + var baseUrlParameter: String? = null + private set + + override fun create(baseUrl: String): UnifiedPushApi { + baseUrlParameter = baseUrl + return FakeUnifiedPushApi(discoveryResponse) + } +} + +class FakeUnifiedPushApi( + private val discoveryResponse: () -> DiscoveryResponse +) : UnifiedPushApi { + override suspend fun discover(): DiscoveryResponse { + return discoveryResponse() + } +} diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolverTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolverTest.kt new file mode 100644 index 0000000000..51d2a086e4 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolverTest.kt @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.pushproviders.unifiedpush.network.DiscoveryResponse +import io.element.android.libraries.pushproviders.unifiedpush.network.DiscoveryUnifiedPush +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class UnifiedPushGatewayResolverTest { + private val matrixDiscoveryResponse = { + DiscoveryResponse( + unifiedpush = DiscoveryUnifiedPush( + gateway = "matrix" + ) + ) + } + + private val invalidDiscoveryResponse = { + DiscoveryResponse( + unifiedpush = DiscoveryUnifiedPush( + gateway = "" + ) + ) + } + + @Test + fun `when a custom url provide a correct matrix gateway, the custom url is returned`() = runTest { + val unifiedPushApiFactory = FakeUnifiedPushApiFactory( + discoveryResponse = matrixDiscoveryResponse + ) + val sut = createUnifiedPushGatewayResolver( + unifiedPushApiFactory = unifiedPushApiFactory + ) + val result = sut.getGateway("https://custom.url") + assertThat(unifiedPushApiFactory.baseUrlParameter).isEqualTo("https://custom.url") + assertThat(result).isEqualTo("https://custom.url/_matrix/push/v1/notify") + } + + @Test + fun `when a custom url with port provides a correct matrix gateway, the custom url is returned`() = runTest { + val unifiedPushApiFactory = FakeUnifiedPushApiFactory( + discoveryResponse = matrixDiscoveryResponse + ) + val sut = createUnifiedPushGatewayResolver( + unifiedPushApiFactory = unifiedPushApiFactory + ) + val result = sut.getGateway("https://custom.url:123") + assertThat(unifiedPushApiFactory.baseUrlParameter).isEqualTo("https://custom.url:123") + assertThat(result).isEqualTo("https://custom.url:123/_matrix/push/v1/notify") + } + + @Test + fun `when a custom url with port and path provides a correct matrix gateway, the custom url is returned`() = runTest { + val unifiedPushApiFactory = FakeUnifiedPushApiFactory( + discoveryResponse = matrixDiscoveryResponse + ) + val sut = createUnifiedPushGatewayResolver( + unifiedPushApiFactory = unifiedPushApiFactory + ) + val result = sut.getGateway("https://custom.url:123/some/path") + assertThat(unifiedPushApiFactory.baseUrlParameter).isEqualTo("https://custom.url:123") + assertThat(result).isEqualTo("https://custom.url:123/_matrix/push/v1/notify") + } + + @Test + fun `when a custom url with http scheme provides a correct matrix gateway, the custom url is returned`() = runTest { + val unifiedPushApiFactory = FakeUnifiedPushApiFactory( + discoveryResponse = matrixDiscoveryResponse + ) + val sut = createUnifiedPushGatewayResolver( + unifiedPushApiFactory = unifiedPushApiFactory + ) + val result = sut.getGateway("http://custom.url:123/some/path") + assertThat(unifiedPushApiFactory.baseUrlParameter).isEqualTo("http://custom.url:123") + assertThat(result).isEqualTo("http://custom.url:123/_matrix/push/v1/notify") + } + + @Test + fun `when a custom url is not reachable, the default url is returned`() = runTest { + val unifiedPushApiFactory = FakeUnifiedPushApiFactory( + discoveryResponse = { throw Exception() } + ) + val sut = createUnifiedPushGatewayResolver( + unifiedPushApiFactory = unifiedPushApiFactory + ) + val result = sut.getGateway("http://custom.url") + assertThat(unifiedPushApiFactory.baseUrlParameter).isEqualTo("http://custom.url") + assertThat(result).isEqualTo(UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL) + } + + @Test + fun `when a custom url is invalid, the default url is returned`() = runTest { + val unifiedPushApiFactory = FakeUnifiedPushApiFactory( + discoveryResponse = matrixDiscoveryResponse + ) + val sut = createUnifiedPushGatewayResolver( + unifiedPushApiFactory = unifiedPushApiFactory + ) + val result = sut.getGateway("invalid") + assertThat(unifiedPushApiFactory.baseUrlParameter).isNull() + assertThat(result).isEqualTo(UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL) + } + + @Test + fun `when a custom url provides a invalid matrix gateway, the default url is returned`() = runTest { + val unifiedPushApiFactory = FakeUnifiedPushApiFactory( + discoveryResponse = invalidDiscoveryResponse + ) + val sut = createUnifiedPushGatewayResolver( + unifiedPushApiFactory = unifiedPushApiFactory + ) + val result = sut.getGateway("https://custom.url") + assertThat(unifiedPushApiFactory.baseUrlParameter).isEqualTo("https://custom.url") + assertThat(result).isEqualTo(UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL) + } + + private fun TestScope.createUnifiedPushGatewayResolver( + unifiedPushApiFactory: UnifiedPushApiFactory = FakeUnifiedPushApiFactory( + discoveryResponse = { DiscoveryResponse() } + ) + ) = UnifiedPushGatewayResolver( + unifiedPushApiFactory = unifiedPushApiFactory, + coroutineDispatchers = testCoroutineDispatchers() + ) +} From 86eceb3cbc5f28fdfc95d9a91e76a75215ec7ed0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 09:25:57 +0200 Subject: [PATCH 014/252] UnifiedPushGatewayResolver.getGateway cannot return null. --- .../unifiedpush/UnifiedPushGatewayResolver.kt | 2 +- .../VectorUnifiedPushMessagingReceiver.kt | 34 +++++++------------ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt index 74ae4cd5aa..1077bf970e 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt @@ -26,7 +26,7 @@ class UnifiedPushGatewayResolver @Inject constructor( private val unifiedPushApiFactory: UnifiedPushApiFactory, private val coroutineDispatchers: CoroutineDispatchers, ) { - suspend fun getGateway(endpoint: String): String? { + suspend fun getGateway(endpoint: String): String { val gateway = UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL try { val url = URL(endpoint) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt index c47aabae37..8aa41bc250 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt @@ -76,29 +76,19 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() { coroutineScope.launch { val gateway = unifiedPushGatewayResolver.getGateway(endpoint) unifiedPushStore.storePushGateway(gateway, instance) - if (gateway == null) { - Timber.tag(loggerTag.value).w("No gateway found for endpoint $endpoint") - endpointRegistrationHandler.registrationDone( - RegistrationResult( - clientSecret = instance, - result = Result.failure(IllegalStateException("No gateway found for endpoint $endpoint")), - ) + val result = newGatewayHandler.handle(endpoint, gateway, instance) + .onFailure { + Timber.tag(loggerTag.value).e(it, "Failed to handle new gateway") + } + .onSuccess { + unifiedPushStore.storeUpEndpoint(endpoint, instance) + } + endpointRegistrationHandler.registrationDone( + RegistrationResult( + clientSecret = instance, + result = result, ) - } else { - val result = newGatewayHandler.handle(endpoint, gateway, instance) - .onFailure { - Timber.tag(loggerTag.value).e(it, "Failed to handle new gateway") - } - .onSuccess { - unifiedPushStore.storeUpEndpoint(endpoint, instance) - } - endpointRegistrationHandler.registrationDone( - RegistrationResult( - clientSecret = instance, - result = result, - ) - ) - } + ) } guardServiceStarter.stop() } From 1bf38e96ae6f20960e7f79e895f2b4e00868f3d8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 10:31:08 +0200 Subject: [PATCH 015/252] Add test on `UnifiedPushProvider` --- .../android/libraries/matrix/test/TestData.kt | 1 + .../unifiedpush/build.gradle.kts | 1 + .../unifiedpush/RegisterUnifiedPushUseCase.kt | 13 +- .../unifiedpush/UnifiedPushStore.kt | 28 +- .../UnregisterUnifiedPushUseCase.kt | 13 +- .../unifiedpush/FakePushClientSecret.kt | 33 ++ .../FakeRegisterUnifiedPushUseCase.kt | 27 ++ .../unifiedpush/FakeUnifiedPushStore.kt | 52 +++ .../FakeUnregisterUnifiedPushUseCase.kt | 27 ++ .../unifiedpush/UnifiedPushProviderTest.kt | 321 ++++++++++++++++++ 10 files changed, 502 insertions(+), 14 deletions(-) create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakePushClientSecret.kt create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnregisterUnifiedPushUseCase.kt create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index e507e34033..d10d1ad0d2 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationSettings const val A_USER_NAME = "alice" const val A_PASSWORD = "password" +const val A_SECRET = "secret" val A_USER_ID = UserId("@alice:server.org") val A_USER_ID_2 = UserId("@bob:server.org") diff --git a/libraries/pushproviders/unifiedpush/build.gradle.kts b/libraries/pushproviders/unifiedpush/build.gradle.kts index d5dcc9727d..d2ea2ccc23 100644 --- a/libraries/pushproviders/unifiedpush/build.gradle.kts +++ b/libraries/pushproviders/unifiedpush/build.gradle.kts @@ -60,4 +60,5 @@ dependencies { testImplementation(projects.libraries.matrix.test) testImplementation(projects.tests.testutils) testImplementation(projects.services.toolbox.test) + testImplementation(projects.services.appnavstate.test) } diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt index 24f93b88dc..1839bb4d18 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt @@ -17,6 +17,8 @@ package io.element.android.libraries.pushproviders.unifiedpush import android.content.Context +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler @@ -30,12 +32,17 @@ import org.unifiedpush.android.connector.UnifiedPush import javax.inject.Inject import kotlin.time.Duration.Companion.seconds -class RegisterUnifiedPushUseCase @Inject constructor( +interface RegisterUnifiedPushUseCase { + suspend fun execute(distributor: Distributor, clientSecret: String): Result +} + +@ContributesBinding(AppScope::class) +class DefaultRegisterUnifiedPushUseCase @Inject constructor( @ApplicationContext private val context: Context, private val endpointRegistrationHandler: EndpointRegistrationHandler, private val coroutineScope: CoroutineScope, -) { - suspend fun execute(distributor: Distributor, clientSecret: String): Result { +) : RegisterUnifiedPushUseCase { + override suspend fun execute(distributor: Distributor, clientSecret: String): Result { UnifiedPush.saveDistributor(context, distributor.value) val completable = CompletableDeferred>() val job = coroutineScope.launch { diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushStore.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushStore.kt index 7b6ee5ef37..397ca381eb 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushStore.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushStore.kt @@ -19,22 +19,34 @@ package io.element.android.libraries.pushproviders.unifiedpush import android.content.Context import android.content.SharedPreferences import androidx.core.content.edit +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.DefaultPreferences import io.element.android.libraries.matrix.api.core.UserId import javax.inject.Inject -class UnifiedPushStore @Inject constructor( +interface UnifiedPushStore { + fun getEndpoint(clientSecret: String): String? + fun storeUpEndpoint(endpoint: String?, clientSecret: String) + fun getPushGateway(clientSecret: String): String? + fun storePushGateway(gateway: String?, clientSecret: String) + fun getDistributorValue(userId: UserId): String? + fun setDistributorValue(userId: UserId, value: String) +} + +@ContributesBinding(AppScope::class) +class DefaultUnifiedPushStore @Inject constructor( @ApplicationContext val context: Context, @DefaultPreferences private val defaultPrefs: SharedPreferences, -) { +) : UnifiedPushStore { /** * Retrieves the UnifiedPush Endpoint. * * @param clientSecret the client secret, to identify the session * @return the UnifiedPush Endpoint or null if not received */ - fun getEndpoint(clientSecret: String): String? { + override fun getEndpoint(clientSecret: String): String? { return defaultPrefs.getString(PREFS_ENDPOINT_OR_TOKEN + clientSecret, null) } @@ -44,7 +56,7 @@ class UnifiedPushStore @Inject constructor( * @param endpoint the endpoint to store * @param clientSecret the client secret, to identify the session */ - fun storeUpEndpoint(endpoint: String?, clientSecret: String) { + override fun storeUpEndpoint(endpoint: String?, clientSecret: String) { defaultPrefs.edit { putString(PREFS_ENDPOINT_OR_TOKEN + clientSecret, endpoint) } @@ -56,7 +68,7 @@ class UnifiedPushStore @Inject constructor( * @param clientSecret the client secret, to identify the session * @return the Push Gateway or null if not defined */ - fun getPushGateway(clientSecret: String): String? { + override fun getPushGateway(clientSecret: String): String? { return defaultPrefs.getString(PREFS_PUSH_GATEWAY + clientSecret, null) } @@ -66,17 +78,17 @@ class UnifiedPushStore @Inject constructor( * @param gateway the push gateway to store * @param clientSecret the client secret, to identify the session */ - fun storePushGateway(gateway: String?, clientSecret: String) { + override fun storePushGateway(gateway: String?, clientSecret: String) { defaultPrefs.edit { putString(PREFS_PUSH_GATEWAY + clientSecret, gateway) } } - fun getDistributorValue(userId: UserId): String? { + override fun getDistributorValue(userId: UserId): String? { return defaultPrefs.getString(PREFS_DISTRIBUTOR + userId, null) } - fun setDistributorValue(userId: UserId, value: String) { + override fun setDistributorValue(userId: UserId, value: String) { defaultPrefs.edit { putString(PREFS_DISTRIBUTOR + userId, value) } diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt index b2dd33c252..1eb9876ae4 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt @@ -17,18 +17,25 @@ package io.element.android.libraries.pushproviders.unifiedpush import android.content.Context +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.pushproviders.api.PusherSubscriber import org.unifiedpush.android.connector.UnifiedPush import javax.inject.Inject -class UnregisterUnifiedPushUseCase @Inject constructor( +interface UnregisterUnifiedPushUseCase { + suspend fun execute(matrixClient: MatrixClient, clientSecret: String): Result +} + +@ContributesBinding(AppScope::class) +class DefaultUnregisterUnifiedPushUseCase @Inject constructor( @ApplicationContext private val context: Context, private val unifiedPushStore: UnifiedPushStore, private val pusherSubscriber: PusherSubscriber, -) { - suspend fun execute(matrixClient: MatrixClient, clientSecret: String): Result { +) : UnregisterUnifiedPushUseCase { + override suspend fun execute(matrixClient: MatrixClient, clientSecret: String): Result { val endpoint = unifiedPushStore.getEndpoint(clientSecret) val gateway = unifiedPushStore.getPushGateway(clientSecret) if (endpoint == null || gateway == null) { diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakePushClientSecret.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakePushClientSecret.kt new file mode 100644 index 0000000000..1ab65696aa --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakePushClientSecret.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret + +class FakePushClientSecret( + private val getSecretForUserResult: (SessionId) -> String = { TODO() }, + private val getUserIdFromSecretResult: (String) -> SessionId? = { TODO() } +) : PushClientSecret { + override suspend fun getSecretForUser(userId: SessionId): String { + return getSecretForUserResult(userId) + } + + override suspend fun getUserIdFromSecret(clientSecret: String): SessionId? { + return getUserIdFromSecretResult(clientSecret) + } +} diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt new file mode 100644 index 0000000000..b1995af699 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +import io.element.android.libraries.pushproviders.api.Distributor + +class FakeRegisterUnifiedPushUseCase( + private val result: (Distributor, String) -> Result = { _, _ -> TODO("Not yet implemented") } +) : RegisterUnifiedPushUseCase { + override suspend fun execute(distributor: Distributor, clientSecret: String): Result { + return result(distributor, clientSecret) + } +} diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt new file mode 100644 index 0000000000..2fcdb367a9 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +import io.element.android.libraries.matrix.api.core.UserId + +class FakeUnifiedPushStore( + private val getEndpointResult: (String) -> String? = { TODO() }, + private val storeUpEndpointResult: (String?, String) -> Unit = { _, _ -> TODO() }, + private val getPushGatewayResult: (String) -> String? = { TODO() }, + private val storePushGatewayResult: (String?, String) -> Unit = { _, _ -> TODO() }, + private val getDistributorValueResult: (UserId) -> String? = { TODO() }, + private val setDistributorValueResult: (UserId, String) -> Unit = { _, _ -> TODO() }, +) : UnifiedPushStore { + override fun getEndpoint(clientSecret: String): String? { + return getEndpointResult(clientSecret) + } + + override fun storeUpEndpoint(endpoint: String?, clientSecret: String) { + storeUpEndpointResult(endpoint, clientSecret) + } + + override fun getPushGateway(clientSecret: String): String? { + return getPushGatewayResult(clientSecret) + } + + override fun storePushGateway(gateway: String?, clientSecret: String) { + storePushGatewayResult(gateway, clientSecret) + } + + override fun getDistributorValue(userId: UserId): String? { + return getDistributorValueResult(userId) + } + + override fun setDistributorValue(userId: UserId, value: String) { + setDistributorValueResult(userId, value) + } +} diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnregisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnregisterUnifiedPushUseCase.kt new file mode 100644 index 0000000000..7aca65fe0d --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnregisterUnifiedPushUseCase.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +import io.element.android.libraries.matrix.api.MatrixClient + +class FakeUnregisterUnifiedPushUseCase( + private val result: (MatrixClient, String) -> Result = { _, _ -> TODO("Not yet implemented") } +) : UnregisterUnifiedPushUseCase { + override suspend fun execute(matrixClient: MatrixClient, clientSecret: String): Result { + return result(matrixClient, clientSecret) + } +} diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt new file mode 100644 index 0000000000..3ab0678ab5 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +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.core.UserId +import io.element.android.libraries.matrix.test.AN_EXCEPTION +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.FakeMatrixClient +import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig +import io.element.android.libraries.pushproviders.api.Distributor +import io.element.android.libraries.pushproviders.unifiedpush.troubleshoot.FakeUnifiedPushDistributorProvider +import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret +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 io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class UnifiedPushProviderTest { + @Test + fun `test index and name`() { + val unifiedPushProvider = createUnifiedPushProvider() + assertThat(unifiedPushProvider.name).isEqualTo(UnifiedPushConfig.NAME) + assertThat(unifiedPushProvider.index).isEqualTo(UnifiedPushConfig.INDEX) + } + + @Test + fun `getDistributors return the available distributors`() { + val unifiedPushProvider = createUnifiedPushProvider( + unifiedPushDistributorProvider = FakeUnifiedPushDistributorProvider( + getDistributorsResult = listOf( + Distributor("value", "Name"), + ) + ) + ) + val result = unifiedPushProvider.getDistributors() + assertThat(result).containsExactly(Distributor("value", "Name")) + assertThat(unifiedPushProvider.isAvailable()).isTrue() + } + + @Test + fun `getDistributors return empty`() { + val unifiedPushProvider = createUnifiedPushProvider( + unifiedPushDistributorProvider = FakeUnifiedPushDistributorProvider( + getDistributorsResult = emptyList() + ) + ) + val result = unifiedPushProvider.getDistributors() + assertThat(result).isEmpty() + assertThat(unifiedPushProvider.isAvailable()).isFalse() + } + + @Test + fun `register ok`() = runTest { + val getSecretForUserResultLambda = lambdaRecorder { A_SECRET } + val executeLambda = lambdaRecorder> { _, _ -> Result.success(Unit) } + val setDistributorValueResultLambda = lambdaRecorder { _, _ -> } + val unifiedPushProvider = createUnifiedPushProvider( + pushClientSecret = FakePushClientSecret( + getSecretForUserResult = getSecretForUserResultLambda, + ), + registerUnifiedPushUseCase = FakeRegisterUnifiedPushUseCase( + result = executeLambda, + ), + unifiedPushStore = FakeUnifiedPushStore( + setDistributorValueResult = setDistributorValueResultLambda, + ), + ) + val result = unifiedPushProvider.registerWith(FakeMatrixClient(), Distributor("value", "Name")) + assertThat(result).isEqualTo(Result.success(Unit)) + getSecretForUserResultLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(A_SESSION_ID))) + executeLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(Distributor("value", "Name")), value(A_SECRET))) + setDistributorValueResultLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(A_SESSION_ID), value("value"))) + } + + @Test + fun `register ko`() = runTest { + val getSecretForUserResultLambda = lambdaRecorder { A_SECRET } + val executeLambda = lambdaRecorder> { _, _ -> Result.failure(AN_EXCEPTION) } + val setDistributorValueResultLambda = lambdaRecorder(ensureNeverCalled = true) { _, _ -> } + val unifiedPushProvider = createUnifiedPushProvider( + pushClientSecret = FakePushClientSecret( + getSecretForUserResult = getSecretForUserResultLambda, + ), + registerUnifiedPushUseCase = FakeRegisterUnifiedPushUseCase( + result = executeLambda, + ), + unifiedPushStore = FakeUnifiedPushStore( + setDistributorValueResult = setDistributorValueResultLambda, + ), + ) + val result = unifiedPushProvider.registerWith(FakeMatrixClient(), Distributor("value", "Name")) + assertThat(result).isEqualTo(Result.failure(AN_EXCEPTION)) + getSecretForUserResultLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(A_SESSION_ID))) + executeLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(Distributor("value", "Name")), value(A_SECRET))) + } + + @Test + fun `unregister ok`() = runTest { + val matrixClient = FakeMatrixClient() + val getSecretForUserResultLambda = lambdaRecorder { A_SECRET } + val executeLambda = lambdaRecorder> { _, _ -> Result.success(Unit) } + val unifiedPushProvider = createUnifiedPushProvider( + pushClientSecret = FakePushClientSecret( + getSecretForUserResult = getSecretForUserResultLambda, + ), + unRegisterUnifiedPushUseCase = FakeUnregisterUnifiedPushUseCase( + result = executeLambda, + ), + ) + val result = unifiedPushProvider.unregister(matrixClient) + assertThat(result).isEqualTo(Result.success(Unit)) + getSecretForUserResultLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(A_SESSION_ID))) + executeLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(matrixClient), value(A_SECRET))) + } + + @Test + fun `unregister ko`() = runTest { + val matrixClient = FakeMatrixClient() + val getSecretForUserResultLambda = lambdaRecorder { A_SECRET } + val executeLambda = lambdaRecorder> { _, _ -> Result.failure(AN_EXCEPTION) } + val unifiedPushProvider = createUnifiedPushProvider( + pushClientSecret = FakePushClientSecret( + getSecretForUserResult = getSecretForUserResultLambda, + ), + unRegisterUnifiedPushUseCase = FakeUnregisterUnifiedPushUseCase( + result = executeLambda, + ), + ) + val result = unifiedPushProvider.unregister(matrixClient) + assertThat(result).isEqualTo(Result.failure(AN_EXCEPTION)) + getSecretForUserResultLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(A_SESSION_ID))) + executeLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(matrixClient), value(A_SECRET))) + } + + @Test + fun `getCurrentDistributor ok`() = runTest { + val distributor = Distributor("value", "Name") + val matrixClient = FakeMatrixClient() + val unifiedPushProvider = createUnifiedPushProvider( + unifiedPushStore = FakeUnifiedPushStore( + getDistributorValueResult = { distributor.value } + ), + unifiedPushDistributorProvider = FakeUnifiedPushDistributorProvider( + getDistributorsResult = listOf( + Distributor("value2", "Name2"), + distributor, + ) + ) + ) + val result = unifiedPushProvider.getCurrentDistributor(matrixClient) + assertThat(result).isEqualTo(distributor) + } + + @Test + fun `getCurrentDistributor not know`() = runTest { + val distributor = Distributor("value", "Name") + val matrixClient = FakeMatrixClient() + val unifiedPushProvider = createUnifiedPushProvider( + unifiedPushStore = FakeUnifiedPushStore( + getDistributorValueResult = { "unknown" } + ), + unifiedPushDistributorProvider = FakeUnifiedPushDistributorProvider( + getDistributorsResult = listOf( + distributor, + ) + ) + ) + val result = unifiedPushProvider.getCurrentDistributor(matrixClient) + assertThat(result).isNull() + } + + @Test + fun `getCurrentDistributor not found`() = runTest { + val distributor = Distributor("value", "Name") + val matrixClient = FakeMatrixClient() + val unifiedPushProvider = createUnifiedPushProvider( + unifiedPushStore = FakeUnifiedPushStore( + getDistributorValueResult = { distributor.value } + ), + unifiedPushDistributorProvider = FakeUnifiedPushDistributorProvider( + getDistributorsResult = emptyList() + ) + ) + val result = unifiedPushProvider.getCurrentDistributor(matrixClient) + assertThat(result).isNull() + } + + @Test + fun `getCurrentUserPushConfig no session`() = runTest { + val unifiedPushProvider = createUnifiedPushProvider() + val result = unifiedPushProvider.getCurrentUserPushConfig() + assertThat(result).isNull() + } + + @Test + fun `getCurrentUserPushConfig no push gateway`() = runTest { + val unifiedPushProvider = createUnifiedPushProvider( + appNavigationStateService = FakeAppNavigationStateService( + appNavigationState = MutableStateFlow( + AppNavigationState( + navigationState = NavigationState.Session(owner = "owner", sessionId = A_SESSION_ID), + isInForeground = true + ) + ) + ), + pushClientSecret = FakePushClientSecret( + getSecretForUserResult = { A_SECRET } + ), + unifiedPushStore = FakeUnifiedPushStore( + getPushGatewayResult = { null } + ), + ) + val result = unifiedPushProvider.getCurrentUserPushConfig() + assertThat(result).isNull() + } + + @Test + fun `getCurrentUserPushConfig no push key`() = runTest { + val unifiedPushProvider = createUnifiedPushProvider( + appNavigationStateService = FakeAppNavigationStateService( + appNavigationState = MutableStateFlow( + AppNavigationState( + navigationState = NavigationState.Session(owner = "owner", sessionId = A_SESSION_ID), + isInForeground = true + ) + ) + ), + pushClientSecret = FakePushClientSecret( + getSecretForUserResult = { A_SECRET } + ), + unifiedPushStore = FakeUnifiedPushStore( + getPushGatewayResult = { "aPushGateway" }, + getEndpointResult = { null } + ), + ) + val result = unifiedPushProvider.getCurrentUserPushConfig() + assertThat(result).isNull() + } + + @Test + fun `getCurrentUserPushConfig ok`() = runTest { + val unifiedPushProvider = createUnifiedPushProvider( + appNavigationStateService = FakeAppNavigationStateService( + appNavigationState = MutableStateFlow( + AppNavigationState( + navigationState = NavigationState.Session(owner = "owner", sessionId = A_SESSION_ID), + isInForeground = true + ) + ) + ), + pushClientSecret = FakePushClientSecret( + getSecretForUserResult = { A_SECRET } + ), + unifiedPushStore = FakeUnifiedPushStore( + getPushGatewayResult = { "aPushGateway" }, + getEndpointResult = { "aEndpoint" } + ), + ) + val result = unifiedPushProvider.getCurrentUserPushConfig() + assertThat(result).isEqualTo(CurrentUserPushConfig("aPushGateway", "aEndpoint")) + } + + private fun createUnifiedPushProvider( + unifiedPushDistributorProvider: UnifiedPushDistributorProvider = FakeUnifiedPushDistributorProvider(), + registerUnifiedPushUseCase: RegisterUnifiedPushUseCase = FakeRegisterUnifiedPushUseCase(), + unRegisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase = FakeUnregisterUnifiedPushUseCase(), + pushClientSecret: PushClientSecret = FakePushClientSecret(), + unifiedPushStore: UnifiedPushStore = FakeUnifiedPushStore(), + appNavigationStateService: AppNavigationStateService = FakeAppNavigationStateService(), + ): UnifiedPushProvider { + return UnifiedPushProvider( + unifiedPushDistributorProvider = unifiedPushDistributorProvider, + registerUnifiedPushUseCase = registerUnifiedPushUseCase, + unRegisterUnifiedPushUseCase = unRegisterUnifiedPushUseCase, + pushClientSecret = pushClientSecret, + unifiedPushStore = unifiedPushStore, + appNavigationStateService = appNavigationStateService + ) + } +} From 538c2b05a3d81c95c1326d60a44f823a73a25501 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 11:11:14 +0200 Subject: [PATCH 016/252] Create FakeIsPlayServiceAvailable --- .../firebase/FakeIsPlayServiceAvailable.kt | 23 +++++++++++++++++++ .../FirebaseAvailabilityTestTest.kt | 13 +++-------- 2 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeIsPlayServiceAvailable.kt diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeIsPlayServiceAvailable.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeIsPlayServiceAvailable.kt new file mode 100644 index 0000000000..6994e6140e --- /dev/null +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeIsPlayServiceAvailable.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.firebase + +class FakeIsPlayServiceAvailable( + private val isAvailable: Boolean, +) : IsPlayServiceAvailable { + override fun isAvailable() = isAvailable +} diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt index 6f1a3da7cb..afba0b4642 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.pushproviders.firebase.troubleshoot import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.pushproviders.firebase.FakeIsPlayServiceAvailable import io.element.android.libraries.pushproviders.firebase.IsPlayServiceAvailable import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState import io.element.android.services.toolbox.test.strings.FakeStringProvider @@ -29,11 +30,7 @@ class FirebaseAvailabilityTestTest { @Test fun `test FirebaseAvailabilityTest success`() = runTest { val sut = FirebaseAvailabilityTest( - isPlayServiceAvailable = object : IsPlayServiceAvailable { - override fun isAvailable(): Boolean { - return true - } - }, + isPlayServiceAvailable = FakeIsPlayServiceAvailable(true), stringProvider = FakeStringProvider(), ) launch { @@ -50,11 +47,7 @@ class FirebaseAvailabilityTestTest { @Test fun `test FirebaseAvailabilityTest failure`() = runTest { val sut = FirebaseAvailabilityTest( - isPlayServiceAvailable = object : IsPlayServiceAvailable { - override fun isAvailable(): Boolean { - return false - } - }, + isPlayServiceAvailable = FakeIsPlayServiceAvailable(false), stringProvider = FakeStringProvider(), ) launch { From a2f66814923e5a10cba35649b2046a9b30b9ce2f Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 22 May 2024 11:16:26 +0200 Subject: [PATCH 017/252] version++ --- plugins/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 75461d217f..9561332c74 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -56,7 +56,7 @@ private const val versionMinor = 4 // Note: even values are reserved for regular release, odd values for hotfix release. // When creating a hotfix, you should decrease the value, since the current value // is the value for the next regular release. -private const val versionPatch = 13 +private const val versionPatch = 14 object Versions { val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch From 008797285f7f307b9aa511171174f7aa46e18ee6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 11:18:13 +0200 Subject: [PATCH 018/252] Add test for FirebasePushProvider --- .../push/test/FakePusherSubscriber.kt | 33 +++ .../pushproviders/firebase/build.gradle.kts | 1 + .../firebase/FirebasePushProviderTest.kt | 197 ++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100644 libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePusherSubscriber.kt create mode 100644 libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePusherSubscriber.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePusherSubscriber.kt new file mode 100644 index 0000000000..bf45ccabbb --- /dev/null +++ b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePusherSubscriber.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.test + +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.pushproviders.api.PusherSubscriber + +class FakePusherSubscriber( + private val registerPusherResult: (MatrixClient, String, String) -> Result = { _, _, _ -> TODO() }, + private val unregisterPusherResult: (MatrixClient, String, String) -> Result = { _, _, _ -> TODO() }, +) : PusherSubscriber { + override suspend fun registerPusher(matrixClient: MatrixClient, pushKey: String, gateway: String): Result { + return registerPusherResult(matrixClient, pushKey, gateway) + } + + override suspend fun unregisterPusher(matrixClient: MatrixClient, pushKey: String, gateway: String): Result { + return unregisterPusherResult(matrixClient, pushKey, gateway) + } +} diff --git a/libraries/pushproviders/firebase/build.gradle.kts b/libraries/pushproviders/firebase/build.gradle.kts index 6e36b92a09..d6be60a397 100644 --- a/libraries/pushproviders/firebase/build.gradle.kts +++ b/libraries/pushproviders/firebase/build.gradle.kts @@ -59,6 +59,7 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.push.test) testImplementation(projects.tests.testutils) testImplementation(projects.services.toolbox.test) } diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt new file mode 100644 index 0000000000..4243db2716 --- /dev/null +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.firebase + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.push.test.FakePusherSubscriber +import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig +import io.element.android.libraries.pushproviders.api.Distributor +import io.element.android.libraries.pushproviders.api.PusherSubscriber +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class FirebasePushProviderTest { + @Test + fun `test index and name`() { + val firebasePushProvider = createFirebasePushProvider() + assertThat(firebasePushProvider.name).isEqualTo(FirebaseConfig.NAME) + assertThat(firebasePushProvider.index).isEqualTo(FirebaseConfig.INDEX) + } + + @Test + fun `getDistributors return the unique distributor`() { + val firebasePushProvider = createFirebasePushProvider() + val result = firebasePushProvider.getDistributors() + assertThat(result).containsExactly(Distributor("Firebase", "Firebase")) + } + + @Test + fun `getCurrentDistributor always return the unique distributor`() = runTest { + val firebasePushProvider = createFirebasePushProvider() + val result = firebasePushProvider.getCurrentDistributor(FakeMatrixClient()) + assertThat(result).isEqualTo(Distributor("Firebase", "Firebase")) + } + + @Test + fun `isAvailable true`() { + val firebasePushProvider = createFirebasePushProvider( + isPlayServiceAvailable = FakeIsPlayServiceAvailable(isAvailable = true) + ) + assertThat(firebasePushProvider.isAvailable()).isTrue() + } + + @Test + fun `isAvailable false`() { + val firebasePushProvider = createFirebasePushProvider( + isPlayServiceAvailable = FakeIsPlayServiceAvailable(isAvailable = false) + ) + assertThat(firebasePushProvider.isAvailable()).isFalse() + } + + @Test + fun `register ok`() = runTest { + val matrixClient = FakeMatrixClient() + val registerPusherResultLambda = lambdaRecorder> { _, _, _ -> Result.success(Unit) } + val firebasePushProvider = createFirebasePushProvider( + firebaseStore = InMemoryFirebaseStore( + token = "aToken" + ), + pusherSubscriber = FakePusherSubscriber( + registerPusherResult = registerPusherResultLambda + ) + ) + val result = firebasePushProvider.registerWith(matrixClient, Distributor("value", "Name")) + assertThat(result).isEqualTo(Result.success(Unit)) + registerPusherResultLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(matrixClient), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL))) + } + + @Test + fun `register ko no token`() = runTest { + val firebasePushProvider = createFirebasePushProvider( + firebaseStore = InMemoryFirebaseStore( + token = null + ), + pusherSubscriber = FakePusherSubscriber( + registerPusherResult = { _, _, _ -> Result.success(Unit) } + ) + ) + val result = firebasePushProvider.registerWith(FakeMatrixClient(), Distributor("value", "Name")) + assertThat(result.isFailure).isTrue() + } + + @Test + fun `register ko error`() = runTest { + val firebasePushProvider = createFirebasePushProvider( + firebaseStore = InMemoryFirebaseStore( + token = "aToken" + ), + pusherSubscriber = FakePusherSubscriber( + registerPusherResult = { _, _, _ -> Result.failure(AN_EXCEPTION) } + ) + ) + val result = firebasePushProvider.registerWith(FakeMatrixClient(), Distributor("value", "Name")) + assertThat(result.isFailure).isTrue() + } + + @Test + fun `unregister ok`() = runTest { + val matrixClient = FakeMatrixClient() + val unregisterPusherResultLambda = lambdaRecorder> { _, _, _ -> Result.success(Unit) } + val firebasePushProvider = createFirebasePushProvider( + firebaseStore = InMemoryFirebaseStore( + token = "aToken" + ), + pusherSubscriber = FakePusherSubscriber( + unregisterPusherResult = unregisterPusherResultLambda + ) + ) + val result = firebasePushProvider.unregister(matrixClient) + assertThat(result).isEqualTo(Result.success(Unit)) + unregisterPusherResultLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(matrixClient), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL))) + } + + @Test + fun `unregister ko no token`() = runTest { + val firebasePushProvider = createFirebasePushProvider( + firebaseStore = InMemoryFirebaseStore( + token = null + ), + pusherSubscriber = FakePusherSubscriber( + unregisterPusherResult = { _, _, _ -> Result.success(Unit) } + ) + ) + val result = firebasePushProvider.unregister(FakeMatrixClient()) + assertThat(result.isFailure).isTrue() + } + + @Test + fun `unregister ko error`() = runTest { + val firebasePushProvider = createFirebasePushProvider( + firebaseStore = InMemoryFirebaseStore( + token = "aToken" + ), + pusherSubscriber = FakePusherSubscriber( + unregisterPusherResult = { _, _, _ -> Result.failure(AN_EXCEPTION) } + ) + ) + val result = firebasePushProvider.unregister(FakeMatrixClient()) + assertThat(result.isFailure).isTrue() + } + + @Test + fun `getCurrentUserPushConfig no push ket`() = runTest { + val firebasePushProvider = createFirebasePushProvider( + firebaseStore = InMemoryFirebaseStore( + token = null + ) + ) + val result = firebasePushProvider.getCurrentUserPushConfig() + assertThat(result).isNull() + } + + @Test + fun `getCurrentUserPushConfig ok`() = runTest { + val firebasePushProvider = createFirebasePushProvider( + firebaseStore = InMemoryFirebaseStore( + token = "aToken" + ), + ) + val result = firebasePushProvider.getCurrentUserPushConfig() + assertThat(result).isEqualTo(CurrentUserPushConfig(FirebaseConfig.PUSHER_HTTP_URL, "aToken")) + } + + private fun createFirebasePushProvider( + firebaseStore: FirebaseStore = InMemoryFirebaseStore(), + pusherSubscriber: PusherSubscriber = FakePusherSubscriber(), + isPlayServiceAvailable: IsPlayServiceAvailable = FakeIsPlayServiceAvailable(false), + ): FirebasePushProvider { + return FirebasePushProvider( + firebaseStore = firebaseStore, + pusherSubscriber = pusherSubscriber, + isPlayServiceAvailable = isPlayServiceAvailable, + ) + } +} From b70c5915f73c920356663df998bb802ea82420ee Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 11:31:56 +0200 Subject: [PATCH 019/252] Remove unused code notificationStyleChanged() We may properly add it again later if necessary. --- .../android/libraries/push/api/PushService.kt | 3 --- .../libraries/push/impl/DefaultPushService.kt | 6 ------ .../DefaultNotificationDrawerManager.kt | 12 ------------ .../DefaultNotificationDrawerManagerTest.kt | 1 - .../android/libraries/push/test/FakePushService.kt | 3 --- 5 files changed, 25 deletions(-) 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 607213953d..ce27acb7b3 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 @@ -21,9 +21,6 @@ import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider interface PushService { - // TODO Move away - fun notificationStyleChanged() - /** * Return the current push provider, or null if none. */ 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 4396337883..e51a5bd128 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 @@ -21,7 +21,6 @@ import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.push.api.GetCurrentPushProvider import io.element.android.libraries.push.api.PushService -import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider import io.element.android.libraries.pushstore.api.UserPushStoreFactory @@ -30,16 +29,11 @@ import javax.inject.Inject @ContributesBinding(AppScope::class) class DefaultPushService @Inject constructor( - private val defaultNotificationDrawerManager: DefaultNotificationDrawerManager, private val pushersManager: PushersManager, private val userPushStoreFactory: UserPushStoreFactory, private val pushProviders: Set<@JvmSuppressWildcards PushProvider>, private val getCurrentPushProvider: GetCurrentPushProvider, ) : PushService { - override fun notificationStyleChanged() { - defaultNotificationDrawerManager.notificationStyleChanged() - } - override suspend fun getCurrentPushProvider(): PushProvider? { val currentPushProvider = getCurrentPushProvider.getCurrentPushProvider() return pushProviders.find { it.name == currentPushProvider } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index 7c9a60c279..f4cd1bab39 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -221,18 +221,6 @@ class DefaultNotificationDrawerManager @Inject constructor( } } - // TODO EAx Must be per account - fun notificationStyleChanged() { - updateEvents(doRender = true) { - val newSettings = true // pushDataStore.useCompleteNotificationFormat() - if (newSettings != useCompleteNotificationFormat) { - // Settings has changed, remove all current notifications - notificationRenderer.cancelAllNotifications() - useCompleteNotificationFormat = newSettings - } - } - } - private fun updateEvents( doRender: Boolean, action: (NotificationEventQueue) -> Unit, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index b84e7b4be3..e3220f8dfa 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -59,7 +59,6 @@ class DefaultNotificationDrawerManagerTest { fun `cover all APIs`() = runTest { // For now just call all the API. Later, add more valuable tests. val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager() - defaultNotificationDrawerManager.notificationStyleChanged() defaultNotificationDrawerManager.clearAllMessagesEvents(A_SESSION_ID, doRender = true) defaultNotificationDrawerManager.clearAllMessagesEvents(A_SESSION_ID, doRender = false) defaultNotificationDrawerManager.clearEvent(A_SESSION_ID, AN_EVENT_ID, doRender = true) 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 a148e5f6a2..584e93d8e1 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 @@ -29,9 +29,6 @@ class FakePushService( Result.success(Unit) }, ) : PushService { - override fun notificationStyleChanged() { - } - override suspend fun getCurrentPushProvider(): PushProvider? { return availablePushProviders.firstOrNull() } From 06d5376adc5bf60ea095c54f5df62177bdd36e93 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 22 May 2024 11:46:42 +0200 Subject: [PATCH 020/252] CI : fix release workflow - concurrency group --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3998bc0246..8feecd0db9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: name: Create App Bundle (Gplay) runs-on: ubuntu-latest concurrency: - group: ${{ github.ref == 'refs/head/main' && format('build-release-main-{0}', github.sha) }} + group: ${{ github.ref == 'refs/head/main' && format('build-release-main-gplay-{0}', github.sha) }} cancel-in-progress: true steps: - uses: actions/checkout@v4 @@ -43,7 +43,7 @@ jobs: name: Create APKs (FDroid) runs-on: ubuntu-latest concurrency: - group: ${{ github.ref == 'refs/head/main' && format('build-release-main-{0}', github.sha) }} + group: ${{ github.ref == 'refs/head/main' && format('build-release-main-fdroid-{0}', github.sha) }} cancel-in-progress: true steps: - uses: actions/checkout@v4 From 20880b01da5738d9f44b5e8b81cd87580a33033e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 11:55:10 +0200 Subject: [PATCH 021/252] Extract testPush to its own class and rename PushersManager to DefaultPusherSubscriber --- .../libraries/push/impl/DefaultPushService.kt | 5 +- ...sManager.kt => DefaultPusherSubscriber.kt} | 28 +--------- .../push/impl/push/DefaultPushHandler.kt | 6 ++- .../libraries/push/impl/test/TestPush.kt | 52 +++++++++++++++++++ 4 files changed, 61 insertions(+), 30 deletions(-) rename libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/{PushersManager.kt => DefaultPusherSubscriber.kt} (79%) create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/test/TestPush.kt 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 e51a5bd128..4c235d9844 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 @@ -21,6 +21,7 @@ import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.push.api.GetCurrentPushProvider import io.element.android.libraries.push.api.PushService +import io.element.android.libraries.push.impl.test.TestPush import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider import io.element.android.libraries.pushstore.api.UserPushStoreFactory @@ -29,7 +30,7 @@ import javax.inject.Inject @ContributesBinding(AppScope::class) class DefaultPushService @Inject constructor( - private val pushersManager: PushersManager, + private val testPush: TestPush, private val userPushStoreFactory: UserPushStoreFactory, private val pushProviders: Set<@JvmSuppressWildcards PushProvider>, private val getCurrentPushProvider: GetCurrentPushProvider, @@ -74,7 +75,7 @@ class DefaultPushService @Inject constructor( override suspend fun testPush(): Boolean { val pushProvider = getCurrentPushProvider() ?: return false val config = pushProvider.getCurrentUserPushConfig() ?: return false - pushersManager.testPush(config) + testPush.execute(config) return true } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt similarity index 79% rename from libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt rename to libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt index 7dba9f5678..481081de1f 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt @@ -22,13 +22,9 @@ import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.MatrixClient -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.SessionId import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData import io.element.android.libraries.matrix.api.pusher.UnsetHttpPusherData -import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest -import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.PusherSubscriber import io.element.android.libraries.pushstore.api.UserPushStoreFactory import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret @@ -37,29 +33,14 @@ import javax.inject.Inject internal const val DEFAULT_PUSHER_FILE_TAG = "mobile" -private val loggerTag = LoggerTag("PushersManager", LoggerTag.PushLoggerTag) +private val loggerTag = LoggerTag("DefaultPusherSubscriber", LoggerTag.PushLoggerTag) @ContributesBinding(AppScope::class) -class PushersManager @Inject constructor( - // private val localeProvider: LocaleProvider, +class DefaultPusherSubscriber @Inject constructor( private val buildMeta: BuildMeta, - // private val getDeviceInfoUseCase: GetDeviceInfoUseCase, - private val pushGatewayNotifyRequest: PushGatewayNotifyRequest, private val pushClientSecret: PushClientSecret, private val userPushStoreFactory: UserPushStoreFactory, ) : PusherSubscriber { - suspend fun testPush(config: CurrentUserPushConfig) { - pushGatewayNotifyRequest.execute( - PushGatewayNotifyRequest.Params( - url = config.url, - appId = PushConfig.PUSHER_APP_ID, - pushKey = config.pushKey, - eventId = TEST_EVENT_ID, - roomId = TEST_ROOM_ID, - ) - ) - } - /** * Register a pusher to the server if not done yet. */ @@ -131,9 +112,4 @@ class PushersManager @Inject constructor( Timber.tag(loggerTag.value).e(throwable, "Unable to unregister the pusher") } } - - companion object { - val TEST_EVENT_ID = EventId("\$THIS_IS_A_FAKE_EVENT_ID") - val TEST_ROOM_ID = RoomId("!room:domain") - } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt index 2c9abb77b3..bdc10dfc62 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt @@ -23,10 +23,12 @@ import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService -import io.element.android.libraries.push.impl.PushersManager +import io.element.android.libraries.push.impl.DefaultPusherSubscriber import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver import io.element.android.libraries.push.impl.store.DefaultPushDataStore +import io.element.android.libraries.push.impl.test.DefaultTestPush +import io.element.android.libraries.push.impl.test.TestPush import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler import io.element.android.libraries.pushproviders.api.PushData import io.element.android.libraries.pushproviders.api.PushHandler @@ -76,7 +78,7 @@ class DefaultPushHandler @Inject constructor( defaultPushDataStore.incrementPushCounter() // Diagnostic Push - if (pushData.eventId == PushersManager.TEST_EVENT_ID) { + if (pushData.eventId == DefaultTestPush.TEST_EVENT_ID) { diagnosticPushHandler.handlePush() return } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/test/TestPush.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/test/TestPush.kt new file mode 100644 index 0000000000..667918941e --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/test/TestPush.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.test + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.appconfig.PushConfig +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest +import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig +import javax.inject.Inject + +interface TestPush { + suspend fun execute(config: CurrentUserPushConfig) +} + +@ContributesBinding(AppScope::class) +class DefaultTestPush @Inject constructor( + private val pushGatewayNotifyRequest: PushGatewayNotifyRequest, +) : TestPush { + override suspend fun execute(config: CurrentUserPushConfig) { + pushGatewayNotifyRequest.execute( + PushGatewayNotifyRequest.Params( + url = config.url, + appId = PushConfig.PUSHER_APP_ID, + pushKey = config.pushKey, + eventId = TEST_EVENT_ID, + roomId = TEST_ROOM_ID, + ) + ) + } + + companion object { + val TEST_EVENT_ID = EventId("\$THIS_IS_A_FAKE_EVENT_ID") + val TEST_ROOM_ID = RoomId("!room:domain") + } +} From a41d323c938ac3c23c679290abce62a3934f5892 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 22 May 2024 11:56:06 +0200 Subject: [PATCH 022/252] MapLibre : rename file MapBoxMap to match composable. --- .../libraries/maplibre/compose/{MapboxMap.kt => MapLibreMap.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/{MapboxMap.kt => MapLibreMap.kt} (100%) diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLibreMap.kt similarity index 100% rename from libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt rename to libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLibreMap.kt From dc6e62a324bca70848e074c50350cc4aa66693db Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 12:21:28 +0200 Subject: [PATCH 023/252] Add test on PushGatewayNotifyRequest --- .../push/impl/pushgateway/PushGatewayAPI.kt | 2 +- .../impl/pushgateway/PushGatewayApiFactory.kt | 36 +++++++ .../impl/pushgateway/PushGatewayDevice.kt | 2 +- .../pushgateway/PushGatewayNotification.kt | 2 +- .../impl/pushgateway/PushGatewayNotifyBody.kt | 2 +- .../pushgateway/PushGatewayNotifyRequest.kt | 9 +- .../pushgateway/PushGatewayNotifyResponse.kt | 2 +- .../pushgateway/FakePushGatewayApiFactory.kt | 37 +++++++ .../PushGatewayNotifyRequestTest.kt | 102 ++++++++++++++++++ 9 files changed, 183 insertions(+), 11 deletions(-) create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayApiFactory.kt create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/FakePushGatewayApiFactory.kt create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequestTest.kt diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayAPI.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayAPI.kt index d8de5429ef..4df39587f1 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayAPI.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayAPI.kt @@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.pushgateway import retrofit2.http.Body import retrofit2.http.POST -internal interface PushGatewayAPI { +interface PushGatewayAPI { /** * Ask the Push Gateway to send a push to the current device. * diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayApiFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayApiFactory.kt new file mode 100644 index 0000000000..1ea4c72fbb --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayApiFactory.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.pushgateway + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.network.RetrofitFactory +import javax.inject.Inject + +interface PushGatewayApiFactory { + fun create(baseUrl: String): PushGatewayAPI +} + +@ContributesBinding(AppScope::class) +class DefaultPushGatewayApiFactory @Inject constructor( + private val retrofitFactory: RetrofitFactory, +) : PushGatewayApiFactory { + override fun create(baseUrl: String): PushGatewayAPI { + return retrofitFactory.create(baseUrl) + .create(PushGatewayAPI::class.java) + } +} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayDevice.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayDevice.kt index 7adedfcfd2..ad5a264168 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayDevice.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayDevice.kt @@ -20,7 +20,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -internal data class PushGatewayDevice( +data class PushGatewayDevice( /** * Required. The app_id given when the pusher was created. */ diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotification.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotification.kt index 5e341e3286..28ad04a078 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotification.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotification.kt @@ -20,7 +20,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -internal data class PushGatewayNotification( +data class PushGatewayNotification( @SerialName("event_id") val eventId: String, @SerialName("room_id") diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyBody.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyBody.kt index ce41d2d83e..14727cab2f 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyBody.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyBody.kt @@ -20,7 +20,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -internal data class PushGatewayNotifyBody( +data class PushGatewayNotifyBody( /** * Required. Information about the push notification */ diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt index e8c01493ab..85d8637a2a 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt @@ -17,12 +17,11 @@ package io.element.android.libraries.push.impl.pushgateway import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.network.RetrofitFactory import io.element.android.libraries.push.api.gateway.PushGatewayFailure import javax.inject.Inject class PushGatewayNotifyRequest @Inject constructor( - private val retrofitFactory: RetrofitFactory, + private val pushGatewayApiFactory: PushGatewayApiFactory, ) { data class Params( val url: String, @@ -33,12 +32,10 @@ class PushGatewayNotifyRequest @Inject constructor( ) suspend fun execute(params: Params) { - val sygnalApi = retrofitFactory.create( + val pushGatewayApi = pushGatewayApiFactory.create( params.url.substringBefore(PushGatewayConfig.URI_PUSH_GATEWAY_PREFIX_PATH) ) - .create(PushGatewayAPI::class.java) - - val response = sygnalApi.notify( + val response = pushGatewayApi.notify( PushGatewayNotifyBody( PushGatewayNotification( eventId = params.eventId.value, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyResponse.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyResponse.kt index 13d9cbad1d..75b5e52111 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyResponse.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyResponse.kt @@ -20,7 +20,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -internal data class PushGatewayNotifyResponse( +data class PushGatewayNotifyResponse( @SerialName("rejected") val rejectedPushKeys: List ) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/FakePushGatewayApiFactory.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/FakePushGatewayApiFactory.kt new file mode 100644 index 0000000000..0b9730843e --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/FakePushGatewayApiFactory.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.pushgateway + +class FakePushGatewayApiFactory( + private val notifyResponse: () -> PushGatewayNotifyResponse +) : PushGatewayApiFactory { + var baseUrlParameter: String? = null + private set + + override fun create(baseUrl: String): PushGatewayAPI { + baseUrlParameter = baseUrl + return FakePushGatewayAPI(notifyResponse) + } +} + +class FakePushGatewayAPI( + private val notifyResponse: () -> PushGatewayNotifyResponse +) : PushGatewayAPI { + override suspend fun notify(body: PushGatewayNotifyBody): PushGatewayNotifyResponse { + return notifyResponse() + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequestTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequestTest.kt new file mode 100644 index 0000000000..d70025327d --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequestTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.pushgateway + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.push.api.gateway.PushGatewayFailure +import io.element.android.libraries.push.impl.test.DefaultTestPush +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertThrows +import org.junit.Test + +class PushGatewayNotifyRequestTest { + @Test + fun `notify success`() = runTest { + val factory = FakePushGatewayApiFactory( + notifyResponse = { + PushGatewayNotifyResponse( + rejectedPushKeys = emptyList() + ) + } + ) + val pushGatewayNotifyRequest = PushGatewayNotifyRequest( + pushGatewayApiFactory = factory, + ) + pushGatewayNotifyRequest.execute( + PushGatewayNotifyRequest.Params( + url = "aUrl", + appId = "anAppId", + pushKey = "aPushKey", + eventId = DefaultTestPush.TEST_EVENT_ID, + roomId = DefaultTestPush.TEST_ROOM_ID, + ) + ) + assertThat(factory.baseUrlParameter).isEqualTo("aUrl") + } + + @Test + fun `notify success, url is stripped`() = runTest { + val factory = FakePushGatewayApiFactory( + notifyResponse = { + PushGatewayNotifyResponse( + rejectedPushKeys = emptyList() + ) + } + ) + val pushGatewayNotifyRequest = PushGatewayNotifyRequest( + pushGatewayApiFactory = factory, + ) + pushGatewayNotifyRequest.execute( + PushGatewayNotifyRequest.Params( + url = "aUrl" + PushGatewayConfig.URI_PUSH_GATEWAY_PREFIX_PATH, + appId = "anAppId", + pushKey = "aPushKey", + eventId = DefaultTestPush.TEST_EVENT_ID, + roomId = DefaultTestPush.TEST_ROOM_ID, + ) + ) + assertThat(factory.baseUrlParameter).isEqualTo("aUrl") + } + + @Test + fun `notify with rejected push key should throw expected Exception`() { + val factory = FakePushGatewayApiFactory( + notifyResponse = { + PushGatewayNotifyResponse( + rejectedPushKeys = listOf("aPushKey") + ) + } + ) + val pushGatewayNotifyRequest = PushGatewayNotifyRequest( + pushGatewayApiFactory = factory, + ) + assertThrows(PushGatewayFailure.PusherRejected::class.java) { + runTest { + pushGatewayNotifyRequest.execute( + PushGatewayNotifyRequest.Params( + url = "aUrl", + appId = "anAppId", + pushKey = "aPushKey", + eventId = DefaultTestPush.TEST_EVENT_ID, + roomId = DefaultTestPush.TEST_ROOM_ID, + ) + ) + } + } + assertThat(factory.baseUrlParameter).isEqualTo("aUrl") + } +} From 505f6d4daee2c56132df273ee78dbff693716f34 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 12:34:47 +0200 Subject: [PATCH 024/252] Add test on DefaultPushService --- libraries/push/impl/build.gradle.kts | 1 + .../libraries/push/impl/DefaultPushService.kt | 3 - .../push/impl/DefaultPushServiceTest.kt | 221 ++++++++++++++++++ .../libraries/push/impl/test/FakeTestPush.kt | 27 +++ .../pushproviders/test/FakePushProvider.kt | 9 +- .../userpushstore/FakeUserPushStoreFactory.kt | 6 +- 6 files changed, 259 insertions(+), 8 deletions(-) create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakeTestPush.kt diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index ee528a4ae7..05e249798f 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -72,6 +72,7 @@ dependencies { testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushproviders.test) + testImplementation(projects.libraries.pushstore.test) testImplementation(projects.tests.testutils) testImplementation(projects.services.appnavstate.test) testImplementation(projects.services.toolbox.impl) 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 4c235d9844..bf2b1ed542 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 @@ -46,9 +46,6 @@ class DefaultPushService @Inject constructor( .sortedBy { it.index } } - /** - * Get current push provider, compare with provided one, then unregister and register if different, and store change. - */ override suspend fun registerWith( matrixClient: MatrixClient, 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 new file mode 100644 index 0000000000..d6bc267af5 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl + +import com.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStore +import com.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.push.api.GetCurrentPushProvider +import io.element.android.libraries.push.impl.test.FakeTestPush +import io.element.android.libraries.push.impl.test.TestPush +import io.element.android.libraries.push.test.FakeGetCurrentPushProvider +import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig +import io.element.android.libraries.pushproviders.api.Distributor +import io.element.android.libraries.pushproviders.api.PushProvider +import io.element.android.libraries.pushproviders.test.FakePushProvider +import io.element.android.libraries.pushstore.api.UserPushStoreFactory +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultPushServiceTest { + @Test + fun `test push no push provider`() = runTest { + val defaultPushService = createDefaultPushService() + assertThat(defaultPushService.testPush()).isFalse() + } + + @Test + fun `test push no config`() = runTest { + val aPushProvider = FakePushProvider() + val defaultPushService = createDefaultPushService( + pushProviders = setOf(aPushProvider), + getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aPushProvider.name), + ) + assertThat(defaultPushService.testPush()).isFalse() + } + + @Test + fun `test push ok`() = runTest { + val aConfig = CurrentUserPushConfig( + url = "aUrl", + pushKey = "aPushKey", + ) + val testPushResult = lambdaRecorder { } + val aPushProvider = FakePushProvider( + currentUserPushConfig = aConfig + ) + val defaultPushService = createDefaultPushService( + pushProviders = setOf(aPushProvider), + getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aPushProvider.name), + testPush = FakeTestPush(executeResult = testPushResult), + ) + assertThat(defaultPushService.testPush()).isTrue() + testPushResult.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(aConfig))) + } + + @Test + fun `getCurrentPushProvider null`() = runTest { + val defaultPushService = createDefaultPushService() + val result = defaultPushService.getCurrentPushProvider() + assertThat(result).isNull() + } + + @Test + fun `getCurrentPushProvider ok`() = runTest { + val aPushProvider = FakePushProvider() + val defaultPushService = createDefaultPushService( + pushProviders = setOf(aPushProvider), + getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aPushProvider.name), + ) + val result = defaultPushService.getCurrentPushProvider() + assertThat(result).isEqualTo(aPushProvider) + } + + @Test + fun `getAvailablePushProviders empty`() = runTest { + val defaultPushService = createDefaultPushService() + val result = defaultPushService.getAvailablePushProviders() + assertThat(result).isEmpty() + } + + @Test + fun `registerWith ok`() = runTest { + val client = FakeMatrixClient() + val aPushProvider = FakePushProvider( + registerWithResult = { _, _ -> Result.success(Unit) }, + ) + val aDistributor = Distributor("aValue", "aName") + val defaultPushService = createDefaultPushService() + val result = defaultPushService.registerWith(client, aPushProvider, aDistributor) + assertThat(result).isEqualTo(Result.success(Unit)) + } + + @Test + fun `registerWith fail to register`() = runTest { + val client = FakeMatrixClient() + val aPushProvider = FakePushProvider( + registerWithResult = { _, _ -> Result.failure(AN_EXCEPTION) }, + ) + val aDistributor = Distributor("aValue", "aName") + val defaultPushService = createDefaultPushService() + val result = defaultPushService.registerWith(client, aPushProvider, aDistributor) + assertThat(result.isFailure).isTrue() + } + + @Test + fun `registerWith fail to unregister previous push provider`() = runTest { + val client = FakeMatrixClient() + val aCurrentPushProvider = FakePushProvider( + unregisterWithResult = { Result.failure(AN_EXCEPTION) }, + name = "aCurrentPushProvider", + ) + val aPushProvider = FakePushProvider( + name = "aPushProvider", + ) + val userPushStore = FakeUserPushStore().apply { + setPushProviderName(aCurrentPushProvider.name) + } + val aDistributor = Distributor("aValue", "aName") + val defaultPushService = createDefaultPushService( + pushProviders = setOf(aCurrentPushProvider, aPushProvider), + getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aCurrentPushProvider.name), + userPushStoreFactory = FakeUserPushStoreFactory( + userPushStore = userPushStore, + ), + ) + val result = defaultPushService.registerWith(client, aPushProvider, aDistributor) + assertThat(result.isFailure).isTrue() + assertThat(userPushStore.getPushProviderName()).isEqualTo(aCurrentPushProvider.name) + } + + @Test + fun `registerWith unregister previous push provider and register new OK`() = runTest { + val client = FakeMatrixClient() + val unregisterLambda = lambdaRecorder> { Result.success(Unit) } + val registerLambda = lambdaRecorder> { _, _ -> Result.success(Unit) } + val aCurrentPushProvider = FakePushProvider( + unregisterWithResult = unregisterLambda, + name = "aCurrentPushProvider", + ) + val aPushProvider = FakePushProvider( + registerWithResult = registerLambda, + name = "aPushProvider", + ) + val userPushStore = FakeUserPushStore().apply { + setPushProviderName(aCurrentPushProvider.name) + } + val aDistributor = Distributor("aValue", "aName") + val defaultPushService = createDefaultPushService( + pushProviders = setOf(aCurrentPushProvider, aPushProvider), + getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aCurrentPushProvider.name), + userPushStoreFactory = FakeUserPushStoreFactory( + userPushStore = userPushStore, + ), + ) + val result = defaultPushService.registerWith(client, aPushProvider, aDistributor) + assertThat(result.isSuccess).isTrue() + assertThat(userPushStore.getPushProviderName()).isEqualTo(aPushProvider.name) + unregisterLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(client))) + registerLambda.assertions() + .isCalledExactly(1) + .withSequence(listOf(value(client), value(aDistributor))) + } + + @Test + fun `getAvailablePushProviders sorted`() = runTest { + val aPushProvider1 = FakePushProvider( + index = 1, + name = "aPushProvider1", + ) + val aPushProvider2 = FakePushProvider( + index = 2, + name = "aPushProvider2", + ) + val aPushProvider3 = FakePushProvider( + index = 3, + name = "aPushProvider3", + ) + val defaultPushService = createDefaultPushService( + pushProviders = setOf(aPushProvider1, aPushProvider3, aPushProvider2), + ) + val result = defaultPushService.getAvailablePushProviders() + assertThat(result).containsExactly(aPushProvider1, aPushProvider2, aPushProvider3).inOrder() + } + + private fun createDefaultPushService( + testPush: TestPush = FakeTestPush(), + userPushStoreFactory: UserPushStoreFactory = FakeUserPushStoreFactory(), + pushProviders: Set<@JvmSuppressWildcards PushProvider> = emptySet(), + getCurrentPushProvider: GetCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = null), + ): DefaultPushService { + return DefaultPushService( + testPush = testPush, + userPushStoreFactory = userPushStoreFactory, + pushProviders = pushProviders, + getCurrentPushProvider = getCurrentPushProvider, + ) + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakeTestPush.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakeTestPush.kt new file mode 100644 index 0000000000..0f6f1e1862 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakeTestPush.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.test + +import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig + +class FakeTestPush( + private val executeResult: (CurrentUserPushConfig) -> Unit = { TODO() } +) : TestPush { + override suspend fun execute(config: CurrentUserPushConfig) { + executeResult(config) + } +} diff --git a/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt b/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt index 44aa1ca18f..ef938f71c8 100644 --- a/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt +++ b/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt @@ -26,13 +26,16 @@ class FakePushProvider( override val name: String = "aFakePushProvider", private val isAvailable: Boolean = true, private val distributors: List = listOf(Distributor("aDistributorValue", "aDistributorName")), + private val currentUserPushConfig: CurrentUserPushConfig? = null, + private val registerWithResult: (MatrixClient, Distributor) -> Result = { _, _ -> TODO() }, + private val unregisterWithResult: (MatrixClient) -> Result = { TODO() }, ) : PushProvider { override fun isAvailable(): Boolean = isAvailable override fun getDistributors(): List = distributors override suspend fun registerWith(matrixClient: MatrixClient, distributor: Distributor): Result { - return Result.success(Unit) + return registerWithResult(matrixClient, distributor) } override suspend fun getCurrentDistributor(matrixClient: MatrixClient): Distributor? { @@ -40,10 +43,10 @@ class FakePushProvider( } override suspend fun unregister(matrixClient: MatrixClient): Result { - return Result.success(Unit) + return unregisterWithResult(matrixClient) } override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? { - return null + return currentUserPushConfig } } diff --git a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt index 2f4f524cc2..dcd270967e 100644 --- a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt +++ b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt @@ -20,8 +20,10 @@ import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.pushstore.api.UserPushStore import io.element.android.libraries.pushstore.api.UserPushStoreFactory -class FakeUserPushStoreFactory : UserPushStoreFactory { +class FakeUserPushStoreFactory( + val userPushStore: UserPushStore = FakeUserPushStore() +) : UserPushStoreFactory { override fun getOrCreate(userId: SessionId): UserPushStore { - return FakeUserPushStore() + return userPushStore } } From 08f70b9cfd91b7739b05687f5acd38fdbdd919a9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 14:41:14 +0200 Subject: [PATCH 025/252] Move some classes to the test module. --- libraries/pushproviders/unifiedpush/build.gradle.kts | 1 + .../pushproviders/unifiedpush/UnifiedPushProviderTest.kt | 1 + libraries/pushstore/impl/build.gradle.kts | 1 + .../pushstore/impl/clientsecret/PushClientSecretImplTest.kt | 1 + .../test/userpushstore/clientsecret}/FakePushClientSecret.kt | 2 +- .../clientsecret/InMemoryPushClientSecretStore.kt | 2 +- 6 files changed, 6 insertions(+), 2 deletions(-) rename libraries/{pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush => pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret}/FakePushClientSecret.kt (93%) rename libraries/pushstore/{impl/src/test/kotlin/io/element/android/libraries/pushstore/impl => test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore}/clientsecret/InMemoryPushClientSecretStore.kt (94%) diff --git a/libraries/pushproviders/unifiedpush/build.gradle.kts b/libraries/pushproviders/unifiedpush/build.gradle.kts index d2ea2ccc23..42b950d2ca 100644 --- a/libraries/pushproviders/unifiedpush/build.gradle.kts +++ b/libraries/pushproviders/unifiedpush/build.gradle.kts @@ -58,6 +58,7 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.pushstore.test) testImplementation(projects.tests.testutils) testImplementation(projects.services.toolbox.test) testImplementation(projects.services.appnavstate.test) diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt index 3ab0678ab5..38326b2792 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.pushproviders.unifiedpush +import com.element.android.libraries.pushstore.test.userpushstore.FakePushClientSecret import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId diff --git a/libraries/pushstore/impl/build.gradle.kts b/libraries/pushstore/impl/build.gradle.kts index 28e53e011c..69f0f21e55 100644 --- a/libraries/pushstore/impl/build.gradle.kts +++ b/libraries/pushstore/impl/build.gradle.kts @@ -49,6 +49,7 @@ dependencies { testImplementation(libs.coroutines.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.services.appnavstate.test) + testImplementation(projects.libraries.pushstore.test) testImplementation(projects.libraries.sessionStorage.test) androidTestImplementation(libs.coroutines.test) diff --git a/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt b/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt index dc0e5b3651..d7494ed3d6 100644 --- a/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt +++ b/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.pushstore.impl.clientsecret +import com.element.android.libraries.pushstore.test.userpushstore.clientsecret.InMemoryPushClientSecretStore import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.sessionstorage.test.observer.NoOpSessionObserver diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakePushClientSecret.kt b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt similarity index 93% rename from libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakePushClientSecret.kt rename to libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt index 1ab65696aa..dc7cc39596 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakePushClientSecret.kt +++ b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.pushproviders.unifiedpush +package com.element.android.libraries.pushstore.test.userpushstore.clientsecret import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret diff --git a/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/InMemoryPushClientSecretStore.kt b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/InMemoryPushClientSecretStore.kt similarity index 94% rename from libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/InMemoryPushClientSecretStore.kt rename to libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/InMemoryPushClientSecretStore.kt index 8c9b577967..36f1698849 100644 --- a/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/InMemoryPushClientSecretStore.kt +++ b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/InMemoryPushClientSecretStore.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.pushstore.impl.clientsecret +package com.element.android.libraries.pushstore.test.userpushstore.clientsecret import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretStore From 707a530752c930bc8d33177d6270d9d91dc1a7fd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 14:44:35 +0200 Subject: [PATCH 026/252] Fix wrong package name. --- .../impl/notifications/NotificationSettingsPresenterTests.kt | 2 +- .../android/libraries/push/impl/DefaultPushServiceTest.kt | 4 ++-- .../pushproviders/unifiedpush/UnifiedPushProviderTest.kt | 2 +- .../pushstore/impl/clientsecret/PushClientSecretImplTest.kt | 2 +- .../pushstore/test/userpushstore/FakeUserPushStore.kt | 2 +- .../pushstore/test/userpushstore/FakeUserPushStoreFactory.kt | 2 +- .../test/userpushstore/clientsecret/FakePushClientSecret.kt | 2 +- .../clientsecret/InMemoryPushClientSecretStore.kt | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) rename libraries/pushstore/test/src/main/kotlin/{com => io}/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt (96%) rename libraries/pushstore/test/src/main/kotlin/{com => io}/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt (93%) rename libraries/pushstore/test/src/main/kotlin/{com => io}/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt (93%) rename libraries/pushstore/test/src/main/kotlin/{com => io}/element/android/libraries/pushstore/test/userpushstore/clientsecret/InMemoryPushClientSecretStore.kt (94%) diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt index e058d4691b..a6e9c63208 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt @@ -19,12 +19,12 @@ 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.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory import io.element.android.tests.testutils.consumeItemsUntilPredicate import kotlinx.coroutines.test.runTest import org.junit.Test 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 d6bc267af5..4f959a6cfc 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 @@ -16,8 +16,6 @@ package io.element.android.libraries.push.impl -import com.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStore -import com.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.test.AN_EXCEPTION @@ -31,6 +29,8 @@ import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider import io.element.android.libraries.pushproviders.test.FakePushProvider import io.element.android.libraries.pushstore.api.UserPushStoreFactory +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStore +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import kotlinx.coroutines.test.runTest diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt index 38326b2792..7ee626d15b 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt @@ -16,7 +16,6 @@ package io.element.android.libraries.pushproviders.unifiedpush -import com.element.android.libraries.pushstore.test.userpushstore.FakePushClientSecret import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId @@ -29,6 +28,7 @@ import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.unifiedpush.troubleshoot.FakeUnifiedPushDistributorProvider import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret +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 diff --git a/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt b/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt index d7494ed3d6..0277feef45 100644 --- a/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt +++ b/libraries/pushstore/impl/src/test/kotlin/io/element/android/libraries/pushstore/impl/clientsecret/PushClientSecretImplTest.kt @@ -16,9 +16,9 @@ package io.element.android.libraries.pushstore.impl.clientsecret -import com.element.android.libraries.pushstore.test.userpushstore.clientsecret.InMemoryPushClientSecretStore import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.pushstore.test.userpushstore.clientsecret.InMemoryPushClientSecretStore import io.element.android.libraries.sessionstorage.test.observer.NoOpSessionObserver import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt similarity index 96% rename from libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt rename to libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt index 3428fb5d3e..3872faa711 100644 --- a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt +++ b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.element.android.libraries.pushstore.test.userpushstore +package io.element.android.libraries.pushstore.test.userpushstore import io.element.android.libraries.pushstore.api.UserPushStore import kotlinx.coroutines.flow.Flow diff --git a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt similarity index 93% rename from libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt rename to libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt index dcd270967e..b3a983122f 100644 --- a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt +++ b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.element.android.libraries.pushstore.test.userpushstore +package io.element.android.libraries.pushstore.test.userpushstore import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.pushstore.api.UserPushStore diff --git a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt similarity index 93% rename from libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt rename to libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt index dc7cc39596..0853804049 100644 --- a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt +++ b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.element.android.libraries.pushstore.test.userpushstore.clientsecret +package io.element.android.libraries.pushstore.test.userpushstore.clientsecret import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret diff --git a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/InMemoryPushClientSecretStore.kt b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/clientsecret/InMemoryPushClientSecretStore.kt similarity index 94% rename from libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/InMemoryPushClientSecretStore.kt rename to libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/clientsecret/InMemoryPushClientSecretStore.kt index 36f1698849..632014109e 100644 --- a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/clientsecret/InMemoryPushClientSecretStore.kt +++ b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/clientsecret/InMemoryPushClientSecretStore.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.element.android.libraries.pushstore.test.userpushstore.clientsecret +package io.element.android.libraries.pushstore.test.userpushstore.clientsecret import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecretStore From 97530e752ff0d13555e804ed234ac5937c6e33d6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 15:03:18 +0200 Subject: [PATCH 027/252] Add test on DefaultPusherSubscriber --- .../matrix/test/pushers/FakePushersService.kt | 9 +- .../push/impl/DefaultPusherSubscriberTest.kt | 197 ++++++++++++++++++ 2 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/pushers/FakePushersService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/pushers/FakePushersService.kt index 05a40e9995..adbb2f5b25 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/pushers/FakePushersService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/pushers/FakePushersService.kt @@ -20,7 +20,10 @@ import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData import io.element.android.libraries.matrix.api.pusher.UnsetHttpPusherData -class FakePushersService : PushersService { - override suspend fun setHttpPusher(setHttpPusherData: SetHttpPusherData) = Result.success(Unit) - override suspend fun unsetHttpPusher(unsetHttpPusherData: UnsetHttpPusherData): Result = Result.success(Unit) +class FakePushersService( + private val setHttpPusherResult: (SetHttpPusherData) -> Result = { TODO() }, + private val unsetHttpPusherResult: (UnsetHttpPusherData) -> Result = { TODO() }, +) : PushersService { + override suspend fun setHttpPusher(setHttpPusherData: SetHttpPusherData) = setHttpPusherResult(setHttpPusherData) + override suspend fun unsetHttpPusher(unsetHttpPusherData: UnsetHttpPusherData): Result = unsetHttpPusherResult(unsetHttpPusherData) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt new file mode 100644 index 0000000000..e6fd39273e --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl + +import com.google.common.truth.Truth.assertThat +import io.element.android.appconfig.PushConfig +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData +import io.element.android.libraries.matrix.api.pusher.UnsetHttpPusherData +import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.A_SECRET +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.matrix.test.pushers.FakePushersService +import io.element.android.libraries.pushstore.api.UserPushStoreFactory +import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStore +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory +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.value +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultPusherSubscriberTest { + @Test + fun `test register pusher OK`() = runTest { + testRegisterPusher( + currentPushKey = null, + registerResult = Result.success(Unit), + ) + } + + @Test + fun `test re-register pusher OK`() = runTest { + testRegisterPusher( + currentPushKey = "aPushKey", + registerResult = Result.success(Unit), + ) + } + + @Test + fun `test register pusher error`() = runTest { + testRegisterPusher( + currentPushKey = null, + registerResult = Result.failure(AN_EXCEPTION), + ) + } + + @Test + fun `test re-register pusher error`() = runTest { + testRegisterPusher( + currentPushKey = "aPushKey", + registerResult = Result.failure(AN_EXCEPTION), + ) + } + + private suspend fun testRegisterPusher( + currentPushKey: String?, + registerResult: Result, + ) { + val setHttpPusherResult = lambdaRecorder> { registerResult } + val userPushStore = FakeUserPushStore().apply { + setCurrentRegisteredPushKey(currentPushKey) + } + assertThat(userPushStore.getCurrentRegisteredPushKey()).isEqualTo(currentPushKey) + + val matrixClient = FakeMatrixClient( + pushersService = FakePushersService( + setHttpPusherResult = setHttpPusherResult, + ), + ) + val defaultPusherSubscriber = createDefaultPusherSubscriber( + pushClientSecret = FakePushClientSecret( + getSecretForUserResult = { A_SECRET }, + ), + userPushStoreFactory = FakeUserPushStoreFactory( + userPushStore = userPushStore, + ), + ) + val result = defaultPusherSubscriber.registerPusher( + matrixClient = matrixClient, + pushKey = "aPushKey", + gateway = "aGateway", + ) + assertThat(result).isEqualTo(registerResult) + setHttpPusherResult.assertions() + .isCalledExactly(1) + .withSequence( + listOf( + value( + SetHttpPusherData( + pushKey = "aPushKey", + appId = PushConfig.PUSHER_APP_ID, + url = "aGateway", + appDisplayName = "MyApp", + deviceDisplayName = "MyDevice", + profileTag = DEFAULT_PUSHER_FILE_TAG + "_", + lang = "en", + defaultPayload = "{\"cs\":\"$A_SECRET\"}", + ), + ) + ) + ) + assertThat(userPushStore.getCurrentRegisteredPushKey()).isEqualTo( + if (registerResult.isSuccess) "aPushKey" else currentPushKey + ) + } + + @Test + fun `test unregister pusher OK`() = runTest { + testUnregisterPusher( + currentPushKey = "aPushKey", + unregisterResult = Result.success(Unit), + ) + } + + @Test + fun `test unregister pusher error`() = runTest { + testUnregisterPusher( + currentPushKey = "aPushKey", + unregisterResult = Result.failure(AN_EXCEPTION), + ) + } + + private suspend fun testUnregisterPusher( + currentPushKey: String?, + unregisterResult: Result, + ) { + val unsetHttpPusherResult = lambdaRecorder> { unregisterResult } + val userPushStore = FakeUserPushStore().apply { + setCurrentRegisteredPushKey(currentPushKey) + } + assertThat(userPushStore.getCurrentRegisteredPushKey()).isEqualTo(currentPushKey) + + val matrixClient = FakeMatrixClient( + pushersService = FakePushersService( + unsetHttpPusherResult = unsetHttpPusherResult, + ), + ) + val defaultPusherSubscriber = createDefaultPusherSubscriber( + pushClientSecret = FakePushClientSecret( + getSecretForUserResult = { A_SECRET }, + ), + userPushStoreFactory = FakeUserPushStoreFactory( + userPushStore = userPushStore, + ), + ) + val result = defaultPusherSubscriber.unregisterPusher( + matrixClient = matrixClient, + pushKey = "aPushKey", + gateway = "aGateway", + ) + assertThat(result).isEqualTo(unregisterResult) + unsetHttpPusherResult.assertions() + .isCalledExactly(1) + .withSequence( + listOf( + value( + UnsetHttpPusherData( + pushKey = "aPushKey", + appId = PushConfig.PUSHER_APP_ID, + ), + ) + ) + ) + assertThat(userPushStore.getCurrentRegisteredPushKey()).isEqualTo( + if (unregisterResult.isSuccess) null else currentPushKey + ) + } + + private fun createDefaultPusherSubscriber( + buildMeta: BuildMeta = aBuildMeta(applicationName = "MyApp"), + userPushStoreFactory: UserPushStoreFactory = FakeUserPushStoreFactory(), + pushClientSecret: PushClientSecret = FakePushClientSecret(), + ): DefaultPusherSubscriber { + return DefaultPusherSubscriber( + buildMeta = buildMeta, + pushClientSecret = pushClientSecret, + userPushStoreFactory = userPushStoreFactory, + ) + } +} From 3bde744d7609dda7b40f1a57f3a4167d51f3c829 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 15:22:03 +0200 Subject: [PATCH 028/252] Add test on DefaultTestPush --- .../pushgateway/PushGatewayNotifyRequest.kt | 16 ++++-- ...=> DefaultPushGatewayNotifyRequestTest.kt} | 8 +-- .../push/impl/test/DefaultTestPushTest.kt | 55 +++++++++++++++++++ .../impl/test/FakePushGatewayNotifyRequest.kt | 27 +++++++++ 4 files changed, 98 insertions(+), 8 deletions(-) rename libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/{PushGatewayNotifyRequestTest.kt => DefaultPushGatewayNotifyRequestTest.kt} (92%) create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/DefaultTestPushTest.kt create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakePushGatewayNotifyRequest.kt diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt index 85d8637a2a..f6d9e5a39c 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt @@ -15,14 +15,14 @@ */ package io.element.android.libraries.push.impl.pushgateway +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.push.api.gateway.PushGatewayFailure import javax.inject.Inject -class PushGatewayNotifyRequest @Inject constructor( - private val pushGatewayApiFactory: PushGatewayApiFactory, -) { +interface PushGatewayNotifyRequest { data class Params( val url: String, val appId: String, @@ -31,7 +31,15 @@ class PushGatewayNotifyRequest @Inject constructor( val roomId: RoomId, ) - suspend fun execute(params: Params) { + suspend fun execute(params: Params) +} + +@ContributesBinding(AppScope::class) +class DefaultPushGatewayNotifyRequest @Inject constructor( + private val pushGatewayApiFactory: PushGatewayApiFactory, +) : PushGatewayNotifyRequest { + + override suspend fun execute(params: PushGatewayNotifyRequest.Params) { val pushGatewayApi = pushGatewayApiFactory.create( params.url.substringBefore(PushGatewayConfig.URI_PUSH_GATEWAY_PREFIX_PATH) ) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequestTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/DefaultPushGatewayNotifyRequestTest.kt similarity index 92% rename from libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequestTest.kt rename to libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/DefaultPushGatewayNotifyRequestTest.kt index d70025327d..ba432b4c7a 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequestTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/pushgateway/DefaultPushGatewayNotifyRequestTest.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Assert.assertThrows import org.junit.Test -class PushGatewayNotifyRequestTest { +class DefaultPushGatewayNotifyRequestTest { @Test fun `notify success`() = runTest { val factory = FakePushGatewayApiFactory( @@ -33,7 +33,7 @@ class PushGatewayNotifyRequestTest { ) } ) - val pushGatewayNotifyRequest = PushGatewayNotifyRequest( + val pushGatewayNotifyRequest = DefaultPushGatewayNotifyRequest( pushGatewayApiFactory = factory, ) pushGatewayNotifyRequest.execute( @@ -57,7 +57,7 @@ class PushGatewayNotifyRequestTest { ) } ) - val pushGatewayNotifyRequest = PushGatewayNotifyRequest( + val pushGatewayNotifyRequest = DefaultPushGatewayNotifyRequest( pushGatewayApiFactory = factory, ) pushGatewayNotifyRequest.execute( @@ -81,7 +81,7 @@ class PushGatewayNotifyRequestTest { ) } ) - val pushGatewayNotifyRequest = PushGatewayNotifyRequest( + val pushGatewayNotifyRequest = DefaultPushGatewayNotifyRequest( pushGatewayApiFactory = factory, ) assertThrows(PushGatewayFailure.PusherRejected::class.java) { diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/DefaultTestPushTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/DefaultTestPushTest.kt new file mode 100644 index 0000000000..0ccd08df82 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/DefaultTestPushTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.test + +import io.element.android.appconfig.PushConfig +import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest +import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultTestPushTest { + @Test + fun `test DefaultTestPush`() = runTest { + val executeResult = lambdaRecorder { } + val defaultTestPush = DefaultTestPush( + pushGatewayNotifyRequest = FakePushGatewayNotifyRequest( + executeResult = executeResult, + ) + ) + val aConfig = CurrentUserPushConfig( + url = "aUrl", + pushKey = "aPushKey", + ) + defaultTestPush.execute(aConfig) + executeResult.assertions() + .isCalledOnce() + .with( + value( + PushGatewayNotifyRequest.Params( + url = aConfig.url, + appId = PushConfig.PUSHER_APP_ID, + pushKey = aConfig.pushKey, + eventId = DefaultTestPush.TEST_EVENT_ID, + roomId = DefaultTestPush.TEST_ROOM_ID, + ) + ) + ) + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakePushGatewayNotifyRequest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakePushGatewayNotifyRequest.kt new file mode 100644 index 0000000000..6fee8ffbf5 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakePushGatewayNotifyRequest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.test + +import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest + +class FakePushGatewayNotifyRequest( + private val executeResult: (PushGatewayNotifyRequest.Params) -> Unit = { TODO() } +) : PushGatewayNotifyRequest { + override suspend fun execute(params: PushGatewayNotifyRequest.Params) { + executeResult(params) + } +} From fe771a37c2897cf7586a321dc41145ed1657eea6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 15:26:01 +0200 Subject: [PATCH 029/252] isCalledExactly(1) -> isCalledOnce() --- .../AcceptDeclineInvitePresenterTest.kt | 12 ++--- .../impl/bugreport/BugReportPresenterTest.kt | 2 +- .../impl/room/join/DefaultJoinRoomTest.kt | 12 ++--- .../push/impl/DefaultPushServiceTest.kt | 12 ++--- .../push/impl/DefaultPusherSubscriberTest.kt | 44 +++++++++---------- .../firebase/FirebasePushProviderTest.kt | 8 ++-- .../unifiedpush/UnifiedPushProviderTest.kt | 36 +++++++-------- 7 files changed, 61 insertions(+), 65 deletions(-) diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt index 247df956e6..fd92f9259c 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt @@ -192,9 +192,9 @@ class AcceptDeclineInvitePresenterTest { cancelAndConsumeRemainingEvents() } assert(joinRoomFailure) - .isCalledExactly(1) - .withSequence( - listOf(value(A_ROOM_ID), value(emptyList()), value(JoinedRoom.Trigger.Invite)) + .isCalledOnce() + .with( + value(A_ROOM_ID), value(emptyList()), value(JoinedRoom.Trigger.Invite) ) } @@ -221,9 +221,9 @@ class AcceptDeclineInvitePresenterTest { cancelAndConsumeRemainingEvents() } assert(joinRoomSuccess) - .isCalledExactly(1) - .withSequence( - listOf(value(A_ROOM_ID), value(emptyList()), value(JoinedRoom.Trigger.Invite)) + .isCalledOnce() + .with( + value(A_ROOM_ID), value(emptyList()), value(JoinedRoom.Trigger.Invite) ) } diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt index e0c033afc7..69617f30f8 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt @@ -136,7 +136,7 @@ class BugReportPresenterTest { initialState.eventSink.invoke(BugReportEvents.ResetAll) val resetState = awaitItem() assertThat(resetState.hasCrashLogs).isFalse() - logFilesRemoverLambda.assertions().isCalledExactly(1) + logFilesRemoverLambda.assertions().isCalledOnce() // TODO Make it live assertThat(resetState.screenshotUri).isNull() } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt index 8bbdf47e21..b728d21b3d 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt @@ -57,9 +57,9 @@ class DefaultJoinRoomTest { .isNeverCalled() joinRoomLambda .assertions() - .isCalledExactly(1) - .withSequence( - listOf(value(A_ROOM_ID)) + .isCalledOnce() + .with( + value(A_ROOM_ID) ) assertThat(analyticsService.capturedEvents).containsExactly( roomResult.toAnalyticsJoinedRoom(aTrigger) @@ -88,9 +88,9 @@ class DefaultJoinRoomTest { sut.invoke(A_ROOM_ID, A_SERVER_LIST, aTrigger) joinRoomByIdOrAliasLambda .assertions() - .isCalledExactly(1) - .withSequence( - listOf(value(A_ROOM_ID), value(A_SERVER_LIST)) + .isCalledOnce() + .with( + value(A_ROOM_ID), value(A_SERVER_LIST) ) joinRoomLambda .assertions() 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 4f959a6cfc..3268e51a16 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 @@ -70,8 +70,8 @@ class DefaultPushServiceTest { ) assertThat(defaultPushService.testPush()).isTrue() testPushResult.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(aConfig))) + .isCalledOnce() + .with(value(aConfig)) } @Test @@ -177,11 +177,11 @@ class DefaultPushServiceTest { assertThat(result.isSuccess).isTrue() assertThat(userPushStore.getPushProviderName()).isEqualTo(aPushProvider.name) unregisterLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(client))) + .isCalledOnce() + .with(value(client)) registerLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(client), value(aDistributor))) + .isCalledOnce() + .with(value(client), value(aDistributor)) } @Test diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt index e6fd39273e..5cb53071e2 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt @@ -99,21 +99,19 @@ class DefaultPusherSubscriberTest { ) assertThat(result).isEqualTo(registerResult) setHttpPusherResult.assertions() - .isCalledExactly(1) - .withSequence( - listOf( - value( - SetHttpPusherData( - pushKey = "aPushKey", - appId = PushConfig.PUSHER_APP_ID, - url = "aGateway", - appDisplayName = "MyApp", - deviceDisplayName = "MyDevice", - profileTag = DEFAULT_PUSHER_FILE_TAG + "_", - lang = "en", - defaultPayload = "{\"cs\":\"$A_SECRET\"}", - ), - ) + .isCalledOnce() + .with( + value( + SetHttpPusherData( + pushKey = "aPushKey", + appId = PushConfig.PUSHER_APP_ID, + url = "aGateway", + appDisplayName = "MyApp", + deviceDisplayName = "MyDevice", + profileTag = DEFAULT_PUSHER_FILE_TAG + "_", + lang = "en", + defaultPayload = "{\"cs\":\"$A_SECRET\"}", + ), ) ) assertThat(userPushStore.getCurrentRegisteredPushKey()).isEqualTo( @@ -167,15 +165,13 @@ class DefaultPusherSubscriberTest { ) assertThat(result).isEqualTo(unregisterResult) unsetHttpPusherResult.assertions() - .isCalledExactly(1) - .withSequence( - listOf( - value( - UnsetHttpPusherData( - pushKey = "aPushKey", - appId = PushConfig.PUSHER_APP_ID, - ), - ) + .isCalledOnce() + .with( + value( + UnsetHttpPusherData( + pushKey = "aPushKey", + appId = PushConfig.PUSHER_APP_ID, + ), ) ) assertThat(userPushStore.getCurrentRegisteredPushKey()).isEqualTo( diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt index 4243db2716..3c82682767 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt @@ -82,8 +82,8 @@ class FirebasePushProviderTest { val result = firebasePushProvider.registerWith(matrixClient, Distributor("value", "Name")) assertThat(result).isEqualTo(Result.success(Unit)) registerPusherResultLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(matrixClient), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL))) + .isCalledOnce() + .with(value(matrixClient), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL)) } @Test @@ -129,8 +129,8 @@ class FirebasePushProviderTest { val result = firebasePushProvider.unregister(matrixClient) assertThat(result).isEqualTo(Result.success(Unit)) unregisterPusherResultLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(matrixClient), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL))) + .isCalledOnce() + .with(value(matrixClient), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL)) } @Test diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt index 7ee626d15b..826f08a1b0 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushProviderTest.kt @@ -92,14 +92,14 @@ class UnifiedPushProviderTest { val result = unifiedPushProvider.registerWith(FakeMatrixClient(), Distributor("value", "Name")) assertThat(result).isEqualTo(Result.success(Unit)) getSecretForUserResultLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(A_SESSION_ID))) + .isCalledOnce() + .with(value(A_SESSION_ID)) executeLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(Distributor("value", "Name")), value(A_SECRET))) + .isCalledOnce() + .with(value(Distributor("value", "Name")), value(A_SECRET)) setDistributorValueResultLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(A_SESSION_ID), value("value"))) + .isCalledOnce() + .with(value(A_SESSION_ID), value("value")) } @Test @@ -121,11 +121,11 @@ class UnifiedPushProviderTest { val result = unifiedPushProvider.registerWith(FakeMatrixClient(), Distributor("value", "Name")) assertThat(result).isEqualTo(Result.failure(AN_EXCEPTION)) getSecretForUserResultLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(A_SESSION_ID))) + .isCalledOnce() + .with(value(A_SESSION_ID)) executeLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(Distributor("value", "Name")), value(A_SECRET))) + .isCalledOnce() + .with(value(Distributor("value", "Name")), value(A_SECRET)) } @Test @@ -144,11 +144,11 @@ class UnifiedPushProviderTest { val result = unifiedPushProvider.unregister(matrixClient) assertThat(result).isEqualTo(Result.success(Unit)) getSecretForUserResultLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(A_SESSION_ID))) + .isCalledOnce() + .with(value(A_SESSION_ID)) executeLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(matrixClient), value(A_SECRET))) + .isCalledOnce() + .with(value(matrixClient), value(A_SECRET)) } @Test @@ -167,11 +167,11 @@ class UnifiedPushProviderTest { val result = unifiedPushProvider.unregister(matrixClient) assertThat(result).isEqualTo(Result.failure(AN_EXCEPTION)) getSecretForUserResultLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(A_SESSION_ID))) + .isCalledOnce() + .with(value(A_SESSION_ID)) executeLambda.assertions() - .isCalledExactly(1) - .withSequence(listOf(value(matrixClient), value(A_SECRET))) + .isCalledOnce() + .with(value(matrixClient), value(A_SECRET)) } @Test From 980a80bcf5f4cda4d38332d80cef71fdf77cf9a2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 15:29:27 +0200 Subject: [PATCH 030/252] Cleanup --- .../impl/response/AcceptDeclineInvitePresenterTest.kt | 8 ++++++-- .../matrix/impl/room/join/DefaultJoinRoomTest.kt | 3 ++- .../libraries/push/impl/push/DefaultPushHandler.kt | 2 -- .../push/impl/pushgateway/PushGatewayNotifyRequest.kt | 1 - .../firebase/troubleshoot/FirebaseAvailabilityTestTest.kt | 1 - 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt index fd92f9259c..72552b9ed1 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt @@ -194,7 +194,9 @@ class AcceptDeclineInvitePresenterTest { assert(joinRoomFailure) .isCalledOnce() .with( - value(A_ROOM_ID), value(emptyList()), value(JoinedRoom.Trigger.Invite) + value(A_ROOM_ID), + value(emptyList()), + value(JoinedRoom.Trigger.Invite) ) } @@ -223,7 +225,9 @@ class AcceptDeclineInvitePresenterTest { assert(joinRoomSuccess) .isCalledOnce() .with( - value(A_ROOM_ID), value(emptyList()), value(JoinedRoom.Trigger.Invite) + value(A_ROOM_ID), + value(emptyList()), + value(JoinedRoom.Trigger.Invite) ) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt index b728d21b3d..6296f219de 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt @@ -90,7 +90,8 @@ class DefaultJoinRoomTest { .assertions() .isCalledOnce() .with( - value(A_ROOM_ID), value(A_SERVER_LIST) + value(A_ROOM_ID), + value(A_SERVER_LIST) ) joinRoomLambda .assertions() diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt index bdc10dfc62..5e84cad61c 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt @@ -23,12 +23,10 @@ import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService -import io.element.android.libraries.push.impl.DefaultPusherSubscriber import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver import io.element.android.libraries.push.impl.store.DefaultPushDataStore import io.element.android.libraries.push.impl.test.DefaultTestPush -import io.element.android.libraries.push.impl.test.TestPush import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler import io.element.android.libraries.pushproviders.api.PushData import io.element.android.libraries.pushproviders.api.PushHandler diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt index f6d9e5a39c..41c8a05423 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/pushgateway/PushGatewayNotifyRequest.kt @@ -38,7 +38,6 @@ interface PushGatewayNotifyRequest { class DefaultPushGatewayNotifyRequest @Inject constructor( private val pushGatewayApiFactory: PushGatewayApiFactory, ) : PushGatewayNotifyRequest { - override suspend fun execute(params: PushGatewayNotifyRequest.Params) { val pushGatewayApi = pushGatewayApiFactory.create( params.url.substringBefore(PushGatewayConfig.URI_PUSH_GATEWAY_PREFIX_PATH) diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt index afba0b4642..4b85683942 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt @@ -19,7 +19,6 @@ package io.element.android.libraries.pushproviders.firebase.troubleshoot import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.pushproviders.firebase.FakeIsPlayServiceAvailable -import io.element.android.libraries.pushproviders.firebase.IsPlayServiceAvailable import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState import io.element.android.services.toolbox.test.strings.FakeStringProvider import kotlinx.coroutines.launch From 5b074dc0ba5ccaf5c7937eb4161143bf1ecadca6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 15:44:50 +0200 Subject: [PATCH 031/252] Create interface for NotifiableEventResolver --- ...r.kt => DefaultNotifiableEventResolver.kt} | 12 +++-- ... => DefaultNotifiableEventResolverTest.kt} | 54 +++++++++---------- 2 files changed, 35 insertions(+), 31 deletions(-) rename libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/{NotifiableEventResolver.kt => DefaultNotifiableEventResolver.kt} (97%) rename libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/{NotifiableEventResolverTest.kt => DefaultNotifiableEventResolverTest.kt} (94%) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt similarity index 97% rename from libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt rename to libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt index edc773b89b..3ee7fa8744 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt @@ -55,7 +55,7 @@ import io.element.android.services.toolbox.api.systemclock.SystemClock import timber.log.Timber import javax.inject.Inject -private val loggerTag = LoggerTag("NotifiableEventResolver", LoggerTag.NotificationLoggerTag) +private val loggerTag = LoggerTag("DefaultNotifiableEventResolver", LoggerTag.NotificationLoggerTag) /** * The notifiable event resolver is able to create a NotifiableEvent (view model for notifications) from an sdk Event. @@ -63,15 +63,19 @@ private val loggerTag = LoggerTag("NotifiableEventResolver", LoggerTag.Notificat * The NotifiableEventResolver is the only aware of session/store, the NotificationDrawerManager has no knowledge of that, * this pattern allow decoupling between the object responsible of displaying notifications and the matrix sdk. */ -class NotifiableEventResolver @Inject constructor( +interface NotifiableEventResolver { + suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? +} + +class DefaultNotifiableEventResolver @Inject constructor( private val stringProvider: StringProvider, private val clock: SystemClock, private val matrixClientProvider: MatrixClientProvider, private val notificationMediaRepoFactory: NotificationMediaRepo.Factory, @ApplicationContext private val context: Context, private val permalinkParser: PermalinkParser, -) { - suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? { +) : NotifiableEventResolver { + override suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? { // Restore session val client = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: return null val notificationService = client.notificationService() diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolverTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt similarity index 94% rename from libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolverTest.kt rename to libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt index d0cd5a30e5..8eebbbbb5c 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolverTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt @@ -59,17 +59,17 @@ import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) -class NotifiableEventResolverTest { +class DefaultNotifiableEventResolverTest { @Test fun `resolve event no session`() = runTest { - val sut = createNotifiableEventResolver(notificationService = null) + val sut = createDefaultNotifiableEventResolver(notificationService = null) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) assertThat(result).isNull() } @Test fun `resolve event failure`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.failure(AN_EXCEPTION) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) @@ -78,7 +78,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event null`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success(null) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) @@ -87,7 +87,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event message text`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -105,7 +105,7 @@ class NotifiableEventResolverTest { @Test @Config(qualifiers = "en") fun `resolve event message with mention`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -123,7 +123,7 @@ class NotifiableEventResolverTest { @Test fun `resolve HTML formatted event message text takes plain text version`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -146,7 +146,7 @@ class NotifiableEventResolverTest { @Test fun `resolve incorrectly formatted event message text uses fallback`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -169,7 +169,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event message audio`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -186,7 +186,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event message video`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -203,7 +203,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event message voice`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -220,7 +220,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event message image`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -237,7 +237,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event message sticker`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -254,7 +254,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event message file`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -271,7 +271,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event message location`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -288,7 +288,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event message notice`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -305,7 +305,7 @@ class NotifiableEventResolverTest { @Test fun `resolve event message emote`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomMessage( @@ -322,7 +322,7 @@ class NotifiableEventResolverTest { @Test fun `resolve poll`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.Poll( @@ -339,7 +339,7 @@ class NotifiableEventResolverTest { @Test fun `resolve RoomMemberContent invite room`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.StateEvent.RoomMemberContent( @@ -372,7 +372,7 @@ class NotifiableEventResolverTest { @Test fun `resolve RoomMemberContent invite direct`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.StateEvent.RoomMemberContent( @@ -405,7 +405,7 @@ class NotifiableEventResolverTest { @Test fun `resolve RoomMemberContent other`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.StateEvent.RoomMemberContent( @@ -421,7 +421,7 @@ class NotifiableEventResolverTest { @Test fun `resolve RoomEncrypted`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.RoomEncrypted @@ -445,7 +445,7 @@ class NotifiableEventResolverTest { @Test fun `resolve CallInvite`() = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = NotificationContent.MessageLike.CallInvite(A_USER_ID_2) @@ -517,7 +517,7 @@ class NotifiableEventResolverTest { } private fun testNull(content: NotificationContent) = runTest { - val sut = createNotifiableEventResolver( + val sut = createDefaultNotifiableEventResolver( notificationResult = Result.success( createNotificationData( content = content @@ -528,10 +528,10 @@ class NotifiableEventResolverTest { assertThat(result).isNull() } - private fun createNotifiableEventResolver( + private fun createDefaultNotifiableEventResolver( notificationService: FakeNotificationService? = FakeNotificationService(), notificationResult: Result = Result.success(null), - ): NotifiableEventResolver { + ): DefaultNotifiableEventResolver { val context = RuntimeEnvironment.getApplication() as Context notificationService?.givenGetNotificationResult(notificationResult) val matrixClientProvider = FakeMatrixClientProvider(getClient = { @@ -544,7 +544,7 @@ class NotifiableEventResolverTest { val notificationMediaRepoFactory = NotificationMediaRepo.Factory { FakeNotificationMediaRepo() } - return NotifiableEventResolver( + return DefaultNotifiableEventResolver( stringProvider = AndroidStringProvider(context.resources), clock = FakeSystemClock(), matrixClientProvider = matrixClientProvider, From b2a3b9653ef6c789551962b30a3f8831a06582d8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 17:04:54 +0200 Subject: [PATCH 032/252] Add test on DefaultPushHandler --- .../DefaultNotifiableEventResolver.kt | 3 + .../push/impl/push/DefaultPushHandler.kt | 54 +--- .../push/impl/push/IncrementPushDataStore.kt | 35 +++ .../impl/push/OnNotifiableEventReceived.kt | 36 +++ .../FakeNotifiableEventResolver.kt | 30 ++ .../push/impl/push/DefaultPushHandlerTest.kt | 267 ++++++++++++++++++ .../push/FakeOnNotifiableEventReceived.kt | 27 ++ 7 files changed, 411 insertions(+), 41 deletions(-) create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/IncrementPushDataStore.kt create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnNotifiableEventReceived.kt create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/FakeOnNotifiableEventReceived.kt diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt index 3ee7fa8744..0ab01937f4 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt @@ -19,7 +19,9 @@ package io.element.android.libraries.push.impl.notifications import android.content.Context import android.net.Uri import androidx.core.content.FileProvider +import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.core.log.logger.LoggerTag +import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClientProvider @@ -67,6 +69,7 @@ interface NotifiableEventResolver { suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? } +@ContributesBinding(AppScope::class) class DefaultNotifiableEventResolver @Inject constructor( private val stringProvider: StringProvider, private val clock: SystemClock, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt index 5e84cad61c..1d3f349364 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt @@ -16,27 +16,19 @@ package io.element.android.libraries.push.impl.push -import android.os.Handler -import android.os.Looper import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService -import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver -import io.element.android.libraries.push.impl.store.DefaultPushDataStore import io.element.android.libraries.push.impl.test.DefaultTestPush import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler import io.element.android.libraries.pushproviders.api.PushData import io.element.android.libraries.pushproviders.api.PushHandler import io.element.android.libraries.pushstore.api.UserPushStoreFactory import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -44,23 +36,15 @@ private val loggerTag = LoggerTag("PushHandler", LoggerTag.PushLoggerTag) @ContributesBinding(AppScope::class) class DefaultPushHandler @Inject constructor( - private val defaultNotificationDrawerManager: DefaultNotificationDrawerManager, + private val onNotifiableEventReceived: OnNotifiableEventReceived, private val notifiableEventResolver: NotifiableEventResolver, - private val defaultPushDataStore: DefaultPushDataStore, + private val incrementPushDataStore: IncrementPushDataStore, private val userPushStoreFactory: UserPushStoreFactory, private val pushClientSecret: PushClientSecret, - // private val actionIds: NotificationActionIds, private val buildMeta: BuildMeta, private val matrixAuthenticationService: MatrixAuthenticationService, private val diagnosticPushHandler: DiagnosticPushHandler, ) : PushHandler { - private val coroutineScope = CoroutineScope(SupervisorJob()) - - // UI handler - private val uiHandler by lazy { - Handler(Looper.getMainLooper()) - } - /** * Called when message is received. * @@ -68,21 +52,15 @@ class DefaultPushHandler @Inject constructor( */ override suspend fun handle(pushData: PushData) { Timber.tag(loggerTag.value).d("## handling pushData: ${pushData.roomId}/${pushData.eventId}") - if (buildMeta.lowPrivacyLoggingEnabled) { Timber.tag(loggerTag.value).d("## pushData: $pushData") } - - defaultPushDataStore.incrementPushCounter() - + incrementPushDataStore.incrementPushCounter() // Diagnostic Push if (pushData.eventId == DefaultTestPush.TEST_EVENT_ID) { diagnosticPushHandler.handlePush() - return - } - - uiHandler.post { - coroutineScope.launch(Dispatchers.IO) { handleInternal(pushData) } + } else { + handleInternal(pushData) } } @@ -98,7 +76,6 @@ class DefaultPushHandler @Inject constructor( } else { Timber.tag(loggerTag.value).d("## handleInternal()") } - val clientSecret = pushData.clientSecret // clientSecret should not be null. If this happens, restore default session val userId = clientSecret @@ -109,27 +86,22 @@ class DefaultPushHandler @Inject constructor( ?: run { matrixAuthenticationService.getLatestSessionId() } - if (userId == null) { Timber.w("Unable to get a session") return } - - val notifiableEvent = notifiableEventResolver.resolveEvent(userId, pushData.roomId, pushData.eventId) - - if (notifiableEvent == null) { - Timber.w("Unable to get a notification data") - return - } - val userPushStore = userPushStoreFactory.getOrCreate(userId) - if (!userPushStore.getNotificationEnabledForDevice().first()) { + if (userPushStore.getNotificationEnabledForDevice().first()) { + val notifiableEvent = notifiableEventResolver.resolveEvent(userId, pushData.roomId, pushData.eventId) + if (notifiableEvent == null) { + Timber.w("Unable to get a notification data") + return + } + onNotifiableEventReceived.onNotifiableEventReceived(notifiableEvent) + } else { // TODO We need to check if this is an incoming call Timber.tag(loggerTag.value).i("Notification are disabled for this device, ignore push.") - return } - - defaultNotificationDrawerManager.onNotifiableEventReceived(notifiableEvent) } catch (e: Exception) { Timber.tag(loggerTag.value).e(e, "## handleInternal() failed") } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/IncrementPushDataStore.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/IncrementPushDataStore.kt new file mode 100644 index 0000000000..9a7f99176c --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/IncrementPushDataStore.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.push + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.push.impl.store.DefaultPushDataStore +import javax.inject.Inject + +interface IncrementPushDataStore { + suspend fun incrementPushCounter() +} + +@ContributesBinding(AppScope::class) +class DefaultIncrementPushDataStore @Inject constructor( + private val defaultPushDataStore: DefaultPushDataStore +) : IncrementPushDataStore { + override suspend fun incrementPushCounter() { + defaultPushDataStore.incrementPushCounter() + } +} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnNotifiableEventReceived.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnNotifiableEventReceived.kt new file mode 100644 index 0000000000..6d4ad34eb8 --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnNotifiableEventReceived.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.push + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager +import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent +import javax.inject.Inject + +interface OnNotifiableEventReceived { + fun onNotifiableEventReceived(notifiableEvent: NotifiableEvent) +} + +@ContributesBinding(AppScope::class) +class DefaultOnNotifiableEventReceived @Inject constructor( + private val defaultNotificationDrawerManager: DefaultNotificationDrawerManager, +) : OnNotifiableEventReceived { + override fun onNotifiableEventReceived(notifiableEvent: NotifiableEvent) { + defaultNotificationDrawerManager.onNotifiableEventReceived(notifiableEvent) + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt new file mode 100644 index 0000000000..02cdced886 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.notifications + +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.SessionId +import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent + +class FakeNotifiableEventResolver( + private val notifiableEventResult: (SessionId, RoomId, EventId) -> NotifiableEvent? = { _, _, _ -> TODO() } +) : NotifiableEventResolver { + override suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? { + return notifiableEventResult(sessionId, roomId, eventId) + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt new file mode 100644 index 0000000000..f6ec20d22f --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package io.element.android.libraries.push.impl.push + +import app.cash.turbine.test +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +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.SessionId +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SECRET +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService +import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.push.impl.notifications.FakeNotifiableEventResolver +import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent +import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent +import io.element.android.libraries.push.impl.test.DefaultTestPush +import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler +import io.element.android.libraries.pushproviders.api.PushData +import io.element.android.libraries.pushstore.api.UserPushStore +import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStore +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory +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.value +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultPushHandlerTest { + @Test + fun `when classical PushData is received, the notification drawer is informed`() = runTest { + val aNotifiableMessageEvent = aNotifiableMessageEvent() + val notifiableEventResult = + lambdaRecorder { _, _, _ -> aNotifiableMessageEvent } + val onNotifiableEventReceived = lambdaRecorder {} + val incrementPushCounterResult = lambdaRecorder {} + val aPushData = PushData( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + unread = 0, + clientSecret = A_SECRET, + ) + val defaultPushHandler = createDefaultPushHandler( + onNotifiableEventReceived = onNotifiableEventReceived, + notifiableEventResult = notifiableEventResult, + pushClientSecret = FakePushClientSecret( + getUserIdFromSecretResult = { A_USER_ID } + ), + incrementPushCounterResult = incrementPushCounterResult + ) + defaultPushHandler.handle(aPushData) + incrementPushCounterResult.assertions() + .isCalledOnce() + notifiableEventResult.assertions() + .isCalledOnce() + .with(value(A_USER_ID), value(A_ROOM_ID), value(AN_EVENT_ID)) + onNotifiableEventReceived.assertions() + .isCalledOnce() + .with(value(aNotifiableMessageEvent)) + } + + @Test + fun `when classical PushData is received, but notifications are disabled, nothing happen`() = + runTest { + val aNotifiableMessageEvent = aNotifiableMessageEvent() + val notifiableEventResult = + lambdaRecorder { _, _, _ -> aNotifiableMessageEvent } + val onNotifiableEventReceived = lambdaRecorder {} + val incrementPushCounterResult = lambdaRecorder {} + val aPushData = PushData( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + unread = 0, + clientSecret = A_SECRET, + ) + val defaultPushHandler = createDefaultPushHandler( + onNotifiableEventReceived = onNotifiableEventReceived, + notifiableEventResult = notifiableEventResult, + pushClientSecret = FakePushClientSecret( + getUserIdFromSecretResult = { A_USER_ID } + ), + userPushStore = FakeUserPushStore().apply { + setNotificationEnabledForDevice(false) + }, + incrementPushCounterResult = incrementPushCounterResult + ) + defaultPushHandler.handle(aPushData) + incrementPushCounterResult.assertions() + .isCalledOnce() + notifiableEventResult.assertions() + .isNeverCalled() + onNotifiableEventReceived.assertions() + .isNeverCalled() + } + + @Test + fun `when PushData is received, but client secret is not known, fallback the latest session`() = + runTest { + val aNotifiableMessageEvent = aNotifiableMessageEvent() + val notifiableEventResult = + lambdaRecorder { _, _, _ -> aNotifiableMessageEvent } + val onNotifiableEventReceived = lambdaRecorder {} + val incrementPushCounterResult = lambdaRecorder {} + val aPushData = PushData( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + unread = 0, + clientSecret = A_SECRET, + ) + val defaultPushHandler = createDefaultPushHandler( + onNotifiableEventReceived = onNotifiableEventReceived, + notifiableEventResult = notifiableEventResult, + pushClientSecret = FakePushClientSecret( + getUserIdFromSecretResult = { null } + ), + matrixAuthenticationService = FakeAuthenticationService().apply { + getLatestSessionIdLambda = { A_USER_ID } + }, + incrementPushCounterResult = incrementPushCounterResult + ) + defaultPushHandler.handle(aPushData) + incrementPushCounterResult.assertions() + .isCalledOnce() + notifiableEventResult.assertions() + .isCalledOnce() + .with(value(A_USER_ID), value(A_ROOM_ID), value(AN_EVENT_ID)) + onNotifiableEventReceived.assertions() + .isCalledOnce() + .with(value(aNotifiableMessageEvent)) + } + + @Test + fun `when PushData is received, but client secret is not known, and there is no latest session, nothing happen`() = + runTest { + val aNotifiableMessageEvent = aNotifiableMessageEvent() + val notifiableEventResult = + lambdaRecorder { _, _, _ -> aNotifiableMessageEvent } + val onNotifiableEventReceived = lambdaRecorder {} + val incrementPushCounterResult = lambdaRecorder {} + val aPushData = PushData( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + unread = 0, + clientSecret = A_SECRET, + ) + val defaultPushHandler = createDefaultPushHandler( + onNotifiableEventReceived = onNotifiableEventReceived, + notifiableEventResult = notifiableEventResult, + pushClientSecret = FakePushClientSecret( + getUserIdFromSecretResult = { null } + ), + matrixAuthenticationService = FakeAuthenticationService().apply { + getLatestSessionIdLambda = { null } + }, + incrementPushCounterResult = incrementPushCounterResult + ) + defaultPushHandler.handle(aPushData) + incrementPushCounterResult.assertions() + .isCalledOnce() + notifiableEventResult.assertions() + .isNeverCalled() + onNotifiableEventReceived.assertions() + .isNeverCalled() + } + + @Test + fun `when classical PushData is received, but not able to resolve the event, nothing happen`() = + runTest { + val notifiableEventResult = + lambdaRecorder { _, _, _ -> null } + val onNotifiableEventReceived = lambdaRecorder {} + val incrementPushCounterResult = lambdaRecorder {} + val aPushData = PushData( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + unread = 0, + clientSecret = A_SECRET, + ) + val defaultPushHandler = createDefaultPushHandler( + onNotifiableEventReceived = onNotifiableEventReceived, + notifiableEventResult = notifiableEventResult, + buildMeta = aBuildMeta( + // Also test `lowPrivacyLoggingEnabled = false` here + lowPrivacyLoggingEnabled = false + ), + pushClientSecret = FakePushClientSecret( + getUserIdFromSecretResult = { A_USER_ID } + ), + incrementPushCounterResult = incrementPushCounterResult + ) + defaultPushHandler.handle(aPushData) + incrementPushCounterResult.assertions() + .isCalledOnce() + notifiableEventResult.assertions() + .isCalledOnce() + .with(value(A_USER_ID), value(A_ROOM_ID), value(AN_EVENT_ID)) + onNotifiableEventReceived.assertions() + .isNeverCalled() + } + + @Test + fun `when diagnostic PushData is received, the diagnostic push handler is informed `() = + runTest { + val aPushData = PushData( + eventId = DefaultTestPush.TEST_EVENT_ID, + roomId = A_ROOM_ID, + unread = 0, + clientSecret = A_SECRET, + ) + val diagnosticPushHandler = DiagnosticPushHandler() + val defaultPushHandler = createDefaultPushHandler( + diagnosticPushHandler = diagnosticPushHandler, + incrementPushCounterResult = { } + ) + diagnosticPushHandler.state.test { + defaultPushHandler.handle(aPushData) + awaitItem() + } + } + + private fun createDefaultPushHandler( + onNotifiableEventReceived: (NotifiableEvent) -> Unit = { TODO() }, + notifiableEventResult: (SessionId, RoomId, EventId) -> NotifiableEvent? = { _, _, _ -> TODO() }, + incrementPushCounterResult: () -> Unit = { TODO() }, + userPushStore: UserPushStore = FakeUserPushStore(), + pushClientSecret: PushClientSecret = FakePushClientSecret(), + buildMeta: BuildMeta = aBuildMeta(), + matrixAuthenticationService: MatrixAuthenticationService = FakeAuthenticationService(), + diagnosticPushHandler: DiagnosticPushHandler = DiagnosticPushHandler(), + ): DefaultPushHandler { + return DefaultPushHandler( + onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceived), + notifiableEventResolver = FakeNotifiableEventResolver(notifiableEventResult), + incrementPushDataStore = object : IncrementPushDataStore { + override suspend fun incrementPushCounter() { + incrementPushCounterResult() + } + }, + userPushStoreFactory = FakeUserPushStoreFactory(userPushStore), + pushClientSecret = pushClientSecret, + buildMeta = buildMeta, + matrixAuthenticationService = matrixAuthenticationService, + diagnosticPushHandler = diagnosticPushHandler, + ) + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/FakeOnNotifiableEventReceived.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/FakeOnNotifiableEventReceived.kt new file mode 100644 index 0000000000..3c9e025830 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/FakeOnNotifiableEventReceived.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.push + +import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent + +class FakeOnNotifiableEventReceived( + private val onNotifiableEventReceivedResult: (NotifiableEvent) -> Unit, +) : OnNotifiableEventReceived { + override fun onNotifiableEventReceived(notifiableEvent: NotifiableEvent) { + onNotifiableEventReceivedResult(notifiableEvent) + } +} From eafa7139956c19c448062ddfe5ced9c7f16cc9b9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 17:57:35 +0200 Subject: [PATCH 033/252] Add test on VectorFirebaseMessagingService --- .../push/test/test/FakePushHandler.kt | 28 ++++++ .../pushproviders/firebase/build.gradle.kts | 3 + .../firebase/FirebaseNewTokenHandler.kt | 13 ++- .../firebase/FakeFirebaseNewTokenHandler.kt | 25 ++++++ .../VectorFirebaseMessagingServiceTest.kt | 90 +++++++++++++++++++ 5 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/test/FakePushHandler.kt create mode 100644 libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseNewTokenHandler.kt create mode 100644 libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingServiceTest.kt diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/test/FakePushHandler.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/test/FakePushHandler.kt new file mode 100644 index 0000000000..c6933a8867 --- /dev/null +++ b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/test/FakePushHandler.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.test.test + +import io.element.android.libraries.pushproviders.api.PushData +import io.element.android.libraries.pushproviders.api.PushHandler + +class FakePushHandler( + private val handleResult: (PushData) -> Unit = { TODO() } +) : PushHandler { + override suspend fun handle(pushData: PushData) { + handleResult(pushData) + } +} diff --git a/libraries/pushproviders/firebase/build.gradle.kts b/libraries/pushproviders/firebase/build.gradle.kts index d6be60a397..58d3b882d9 100644 --- a/libraries/pushproviders/firebase/build.gradle.kts +++ b/libraries/pushproviders/firebase/build.gradle.kts @@ -58,8 +58,11 @@ dependencies { testImplementation(libs.test.junit) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) + testImplementation(libs.test.robolectric) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.push.test) + testImplementation(projects.libraries.pushstore.test) + testImplementation(projects.libraries.sessionStorage.implMemory) testImplementation(projects.tests.testutils) testImplementation(projects.services.toolbox.test) } diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt index e61ea4bc91..0c11dd8e33 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt @@ -16,8 +16,10 @@ package io.element.android.libraries.pushproviders.firebase +import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.core.extensions.flatMap import io.element.android.libraries.core.log.logger.LoggerTag +import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.pushproviders.api.PusherSubscriber @@ -32,14 +34,19 @@ private val loggerTag = LoggerTag("FirebaseNewTokenHandler", LoggerTag.PushLogge /** * Handle new token receive from Firebase. Will update all the sessions which are using Firebase as a push provider. */ -class FirebaseNewTokenHandler @Inject constructor( +interface FirebaseNewTokenHandler { + suspend fun handle(firebaseToken: String) +} + +@ContributesBinding(AppScope::class) +class DefaultFirebaseNewTokenHandler @Inject constructor( private val pusherSubscriber: PusherSubscriber, private val sessionStore: SessionStore, private val userPushStoreFactory: UserPushStoreFactory, private val matrixAuthenticationService: MatrixAuthenticationService, private val firebaseStore: FirebaseStore, -) { - suspend fun handle(firebaseToken: String) { +) : FirebaseNewTokenHandler { + override suspend fun handle(firebaseToken: String) { firebaseStore.storeFcmToken(firebaseToken) // Register the pusher for all the sessions sessionStore.getAllSessions().toUserList() diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseNewTokenHandler.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseNewTokenHandler.kt new file mode 100644 index 0000000000..3db2b37cfc --- /dev/null +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseNewTokenHandler.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.firebase + +class FakeFirebaseNewTokenHandler( + private val handleResult: (String) -> Unit = { TODO() } +) : FirebaseNewTokenHandler { + override suspend fun handle(firebaseToken: String) { + handleResult(firebaseToken) + } +} diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingServiceTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingServiceTest.kt new file mode 100644 index 0000000000..a8e04cd877 --- /dev/null +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingServiceTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.firebase + +import android.os.Bundle +import com.google.firebase.messaging.RemoteMessage +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SECRET +import io.element.android.libraries.push.test.test.FakePushHandler +import io.element.android.libraries.pushproviders.api.PushData +import io.element.android.libraries.pushproviders.api.PushHandler +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.test.runTest +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class VectorFirebaseMessagingServiceTest { + @Test + fun `test receiving invalid data`() = runTest { + val lambda = lambdaRecorder(ensureNeverCalled = true) { } + val vectorFirebaseMessagingService = createVectorFirebaseMessagingService( + pushHandler = FakePushHandler(handleResult = lambda) + ) + vectorFirebaseMessagingService.onMessageReceived(RemoteMessage(Bundle())) + } + + @Ignore("The test does not wait for the end of the coroutine.") + @Test + fun `test receiving valid data`() = runTest { + val lambda = lambdaRecorder { } + val vectorFirebaseMessagingService = createVectorFirebaseMessagingService( + pushHandler = FakePushHandler(handleResult = lambda) + ) + vectorFirebaseMessagingService.onMessageReceived( + message = RemoteMessage( + Bundle().apply { + putString("event_id", AN_EVENT_ID.value) + putString("room_id", A_ROOM_ID.value) + putString("cs", A_SECRET) + }, + ) + ) + lambda.assertions() + .isCalledOnce() + .with(value(PushData(AN_EVENT_ID, A_ROOM_ID, null, A_SECRET))) + } + + @Ignore("The test does not wait for the end of the coroutine.") + @Test + fun `test new token is forwarded to the handler`() = runTest { + val lambda = lambdaRecorder { } + val vectorFirebaseMessagingService = createVectorFirebaseMessagingService( + firebaseNewTokenHandler = FakeFirebaseNewTokenHandler(handleResult = lambda) + ) + vectorFirebaseMessagingService.onNewToken("aToken") + lambda.assertions() + .isCalledOnce() + .with(value("aToken")) + } + + private fun createVectorFirebaseMessagingService( + firebaseNewTokenHandler: FirebaseNewTokenHandler = FakeFirebaseNewTokenHandler(), + pushHandler: PushHandler = FakePushHandler(), + ): VectorFirebaseMessagingService { + return VectorFirebaseMessagingService().apply { + this.firebaseNewTokenHandler = firebaseNewTokenHandler + this.pushParser = FirebasePushParser() + this.pushHandler = pushHandler + } + } +} From 90a14ce05629612241b668b7481bfeb638358329 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 18:16:53 +0200 Subject: [PATCH 034/252] Change to lambda --- .../android/libraries/push/impl/DefaultPushServiceTest.kt | 4 ++-- .../libraries/push/impl/DefaultPusherSubscriberTest.kt | 4 ++-- .../libraries/push/impl/push/DefaultPushHandlerTest.kt | 2 +- .../pushstore/test/userpushstore/FakeUserPushStoreFactory.kt | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) 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 3268e51a16..546d69b008 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 @@ -141,7 +141,7 @@ class DefaultPushServiceTest { pushProviders = setOf(aCurrentPushProvider, aPushProvider), getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aCurrentPushProvider.name), userPushStoreFactory = FakeUserPushStoreFactory( - userPushStore = userPushStore, + userPushStore = { userPushStore }, ), ) val result = defaultPushService.registerWith(client, aPushProvider, aDistributor) @@ -170,7 +170,7 @@ class DefaultPushServiceTest { pushProviders = setOf(aCurrentPushProvider, aPushProvider), getCurrentPushProvider = FakeGetCurrentPushProvider(currentPushProvider = aCurrentPushProvider.name), userPushStoreFactory = FakeUserPushStoreFactory( - userPushStore = userPushStore, + userPushStore = { userPushStore }, ), ) val result = defaultPushService.registerWith(client, aPushProvider, aDistributor) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt index 5cb53071e2..dd9363de9e 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt @@ -89,7 +89,7 @@ class DefaultPusherSubscriberTest { getSecretForUserResult = { A_SECRET }, ), userPushStoreFactory = FakeUserPushStoreFactory( - userPushStore = userPushStore, + userPushStore = { userPushStore }, ), ) val result = defaultPusherSubscriber.registerPusher( @@ -155,7 +155,7 @@ class DefaultPusherSubscriberTest { getSecretForUserResult = { A_SECRET }, ), userPushStoreFactory = FakeUserPushStoreFactory( - userPushStore = userPushStore, + userPushStore = { userPushStore }, ), ) val result = defaultPusherSubscriber.unregisterPusher( diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt index f6ec20d22f..5eaa879429 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt @@ -257,7 +257,7 @@ class DefaultPushHandlerTest { incrementPushCounterResult() } }, - userPushStoreFactory = FakeUserPushStoreFactory(userPushStore), + userPushStoreFactory = FakeUserPushStoreFactory { userPushStore }, pushClientSecret = pushClientSecret, buildMeta = buildMeta, matrixAuthenticationService = matrixAuthenticationService, diff --git a/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt index b3a983122f..14fd4ce3a6 100644 --- a/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt +++ b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt @@ -21,9 +21,9 @@ import io.element.android.libraries.pushstore.api.UserPushStore import io.element.android.libraries.pushstore.api.UserPushStoreFactory class FakeUserPushStoreFactory( - val userPushStore: UserPushStore = FakeUserPushStore() + val userPushStore: (SessionId) -> UserPushStore = { FakeUserPushStore() } ) : UserPushStoreFactory { override fun getOrCreate(userId: SessionId): UserPushStore { - return userPushStore + return userPushStore(userId) } } From b38c144f4601fb255d822921633088e4ba874b9a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 18:35:02 +0200 Subject: [PATCH 035/252] Add test on DefaultFirebaseNewTokenHandler --- .../test/auth/FakeAuthenticationService.kt | 7 +- .../firebase/FirebaseNewTokenHandler.kt | 17 +- .../DefaultFirebaseNewTokenHandlerTest.kt | 186 ++++++++++++++++++ .../test/userpushstore/FakeUserPushStore.kt | 3 +- .../impl/memory/InMemoryMultiSessionsStore.kt | 44 +++++ 5 files changed, 247 insertions(+), 10 deletions(-) create mode 100644 libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt create mode 100644 libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemoryMultiSessionsStore.kt diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt index c24df7d717..b138c55acb 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt @@ -31,7 +31,9 @@ import kotlinx.coroutines.flow.flowOf val A_OIDC_DATA = OidcDetails(url = "a-url") -class FakeAuthenticationService : MatrixAuthenticationService { +class FakeAuthenticationService( + private val matrixClientResult: ((SessionId) -> Result)? = null +) : MatrixAuthenticationService { private val homeserver = MutableStateFlow(null) private var oidcError: Throwable? = null private var oidcCancelError: Throwable? = null @@ -48,6 +50,9 @@ class FakeAuthenticationService : MatrixAuthenticationService { override suspend fun getLatestSessionId(): SessionId? = getLatestSessionIdLambda() override suspend fun restoreSession(sessionId: SessionId): Result { + if (matrixClientResult != null) { + return matrixClientResult.invoke(sessionId) + } return if (matrixClient != null) { Result.success(matrixClient!!) } else { diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt index 0c11dd8e33..baddab1a0d 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt @@ -60,14 +60,15 @@ class DefaultFirebaseNewTokenHandler @Inject constructor( Timber.tag(loggerTag.value).e(it, "Failed to restore session $sessionId") } .flatMap { client -> - pusherSubscriber.registerPusher( - matrixClient = client, - pushKey = firebaseToken, - gateway = FirebaseConfig.PUSHER_HTTP_URL, - ) - } - .onFailure { - Timber.tag(loggerTag.value).e(it, "Failed to register pusher for session $sessionId") + pusherSubscriber + .registerPusher( + matrixClient = client, + pushKey = firebaseToken, + gateway = FirebaseConfig.PUSHER_HTTP_URL, + ) + .onFailure { + Timber.tag(loggerTag.value).e(it, "Failed to register pusher for session $sessionId") + } } } else { Timber.tag(loggerTag.value).d("This session is not using Firebase pusher") diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt new file mode 100644 index 0000000000..47838ddcdb --- /dev/null +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.firebase + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +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.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_ID_2 +import io.element.android.libraries.matrix.test.A_USER_ID_3 +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService +import io.element.android.libraries.push.test.FakePusherSubscriber +import io.element.android.libraries.pushproviders.api.PusherSubscriber +import io.element.android.libraries.pushstore.api.UserPushStoreFactory +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStore +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory +import io.element.android.libraries.sessionstorage.api.LoginType +import io.element.android.libraries.sessionstorage.api.SessionData +import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.libraries.sessionstorage.impl.memory.InMemoryMultiSessionsStore +import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultFirebaseNewTokenHandlerTest { + @Test + fun `when a new token is received it is stored in the firebase store`() = runTest { + val firebaseStore = InMemoryFirebaseStore() + assertThat(firebaseStore.getFcmToken()).isNull() + val firebaseNewTokenHandler = createDefaultFirebaseNewTokenHandler( + firebaseStore = firebaseStore, + ) + firebaseNewTokenHandler.handle("aToken") + assertThat(firebaseStore.getFcmToken()).isEqualTo("aToken") + } + + @Test + fun `when a new token is received, the handler registers the pusher again to all sessions using Firebase`() = runTest { + val aMatrixClient1 = FakeMatrixClient(A_USER_ID) + val aMatrixClient2 = FakeMatrixClient(A_USER_ID_2) + val aMatrixClient3 = FakeMatrixClient(A_USER_ID_3) + val registerPusherResult = lambdaRecorder> { _, _, _ -> Result.success(Unit) } + val pusherSubscriber = FakePusherSubscriber(registerPusherResult = registerPusherResult) + val firebaseNewTokenHandler = createDefaultFirebaseNewTokenHandler( + sessionStore = InMemoryMultiSessionsStore().apply { + storeData(aSessionData(A_USER_ID)) + storeData(aSessionData(A_USER_ID_2)) + storeData(aSessionData(A_USER_ID_3)) + }, + matrixAuthenticationService = FakeAuthenticationService( + matrixClientResult = { sessionId -> + when (sessionId) { + A_USER_ID -> Result.success(aMatrixClient1) + A_USER_ID_2 -> Result.success(aMatrixClient2) + A_USER_ID_3 -> Result.success(aMatrixClient3) + else -> Result.failure(IllegalStateException()) + } + } + ), + userPushStoreFactory = FakeUserPushStoreFactory( + userPushStore = { sessionId -> + when (sessionId) { + A_USER_ID -> FakeUserPushStore(pushProviderName = FirebaseConfig.NAME) + A_USER_ID_2 -> FakeUserPushStore(pushProviderName = "Other") + A_USER_ID_3 -> FakeUserPushStore(pushProviderName = FirebaseConfig.NAME) + else -> throw IllegalStateException() + } + } + ), + pusherSubscriber = pusherSubscriber, + ) + firebaseNewTokenHandler.handle("aToken") + registerPusherResult.assertions() + .isCalledExactly(2) + .withSequence( + listOf(value(aMatrixClient1), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL)), + listOf(value(aMatrixClient3), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL)), + ) + } + + @Test + fun `when a new token is received, if the session cannot be restore, nothing happen`() = runTest { + val registerPusherResult = lambdaRecorder> { _, _, _ -> Result.success(Unit) } + val pusherSubscriber = FakePusherSubscriber(registerPusherResult = registerPusherResult) + val firebaseNewTokenHandler = createDefaultFirebaseNewTokenHandler( + sessionStore = InMemoryMultiSessionsStore().apply { + storeData(aSessionData(A_USER_ID)) + }, + matrixAuthenticationService = FakeAuthenticationService( + matrixClientResult = { _ -> + Result.failure(IllegalStateException()) + } + ), + userPushStoreFactory = FakeUserPushStoreFactory( + userPushStore = { _ -> + FakeUserPushStore(pushProviderName = FirebaseConfig.NAME) + } + ), + pusherSubscriber = pusherSubscriber, + ) + firebaseNewTokenHandler.handle("aToken") + registerPusherResult.assertions() + .isNeverCalled() + } + + @Test + fun `when a new token is received, error when registering the pusher is ignored`() = runTest { + val aMatrixClient1 = FakeMatrixClient(A_USER_ID) + val registerPusherResult = lambdaRecorder> { _, _, _ -> Result.failure(AN_EXCEPTION) } + val pusherSubscriber = FakePusherSubscriber(registerPusherResult = registerPusherResult) + val firebaseNewTokenHandler = createDefaultFirebaseNewTokenHandler( + sessionStore = InMemoryMultiSessionsStore().apply { + storeData(aSessionData(A_USER_ID)) + }, + matrixAuthenticationService = FakeAuthenticationService( + matrixClientResult = { _ -> + Result.success(aMatrixClient1) + } + ), + userPushStoreFactory = FakeUserPushStoreFactory( + userPushStore = { _ -> + FakeUserPushStore(pushProviderName = FirebaseConfig.NAME) + } + ), + pusherSubscriber = pusherSubscriber, + ) + firebaseNewTokenHandler.handle("aToken") + registerPusherResult.assertions() + registerPusherResult.assertions() + .isCalledOnce() + .with(value(aMatrixClient1), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL)) + } + + private fun createDefaultFirebaseNewTokenHandler( + pusherSubscriber: PusherSubscriber = FakePusherSubscriber(), + sessionStore: SessionStore = InMemorySessionStore(), + userPushStoreFactory: UserPushStoreFactory = FakeUserPushStoreFactory(), + matrixAuthenticationService: MatrixAuthenticationService = FakeAuthenticationService(), + firebaseStore: FirebaseStore = InMemoryFirebaseStore(), + ): FirebaseNewTokenHandler { + return DefaultFirebaseNewTokenHandler( + pusherSubscriber = pusherSubscriber, + sessionStore = sessionStore, + userPushStoreFactory = userPushStoreFactory, + matrixAuthenticationService = matrixAuthenticationService, + firebaseStore = firebaseStore + ) + } + + private fun aSessionData( + sessionId: SessionId, + ): SessionData { + return SessionData( + userId = sessionId.value, + deviceId = "aDeviceId", + accessToken = "anAccessToken", + refreshToken = "aRefreshToken", + homeserverUrl = "aHomeserverUrl", + oidcData = null, + slidingSyncProxy = null, + loginTimestamp = null, + isTokenValid = true, + loginType = LoginType.UNKNOWN, + passphrase = null, + ) + } +} diff --git a/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt index 3872faa711..112a752368 100644 --- a/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt +++ b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt @@ -20,8 +20,9 @@ import io.element.android.libraries.pushstore.api.UserPushStore import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -class FakeUserPushStore : UserPushStore { +class FakeUserPushStore( private var pushProviderName: String? = null +) : UserPushStore { private var currentRegisteredPushKey: String? = null private val notificationEnabledForDevice = MutableStateFlow(true) override suspend fun getPushProviderName(): String? { diff --git a/libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemoryMultiSessionsStore.kt b/libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemoryMultiSessionsStore.kt new file mode 100644 index 0000000000..5331d5755b --- /dev/null +++ b/libraries/session-storage/impl-memory/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/memory/InMemoryMultiSessionsStore.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.sessionstorage.impl.memory + +import io.element.android.libraries.sessionstorage.api.LoggedInState +import io.element.android.libraries.sessionstorage.api.SessionData +import io.element.android.libraries.sessionstorage.api.SessionStore +import kotlinx.coroutines.flow.Flow + +class InMemoryMultiSessionsStore : SessionStore { + private val sessions = mutableListOf() + + override fun isLoggedIn(): Flow = error("Not implemented") + + override fun sessionsFlow(): Flow> = error("Not implemented") + + override suspend fun storeData(sessionData: SessionData) { + sessions.add(sessionData) + } + + override suspend fun updateData(sessionData: SessionData) = error("Not implemented") + + override suspend fun getSession(sessionId: String): SessionData? = error("Not implemented") + + override suspend fun getAllSessions(): List = sessions + + override suspend fun getLatestSession(): SessionData = error("Not implemented") + + override suspend fun removeSession(sessionId: String) = error("Not implemented") +} From 5c68956e6c9986b19d55cc8c6a313855d2a02bac Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 18:44:25 +0200 Subject: [PATCH 036/252] Fix test on `VectorFirebaseMessagingService` --- .../firebase/VectorFirebaseMessagingService.kt | 4 +--- .../firebase/VectorFirebaseMessagingServiceTest.kt | 13 +++++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingService.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingService.kt index 3d251f6e64..a8bc069893 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingService.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingService.kt @@ -22,7 +22,6 @@ import io.element.android.libraries.architecture.bindings import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.pushproviders.api.PushHandler import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -33,8 +32,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { @Inject lateinit var firebaseNewTokenHandler: FirebaseNewTokenHandler @Inject lateinit var pushParser: FirebasePushParser @Inject lateinit var pushHandler: PushHandler - - private val coroutineScope = CoroutineScope(SupervisorJob()) + @Inject lateinit var coroutineScope: CoroutineScope override fun onCreate() { super.onCreate() diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingServiceTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingServiceTest.kt index a8e04cd877..9dfb453919 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingServiceTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/VectorFirebaseMessagingServiceTest.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package io.element.android.libraries.pushproviders.firebase import android.os.Bundle @@ -26,8 +28,10 @@ import io.element.android.libraries.pushproviders.api.PushData import io.element.android.libraries.pushproviders.api.PushHandler import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -43,7 +47,6 @@ class VectorFirebaseMessagingServiceTest { vectorFirebaseMessagingService.onMessageReceived(RemoteMessage(Bundle())) } - @Ignore("The test does not wait for the end of the coroutine.") @Test fun `test receiving valid data`() = runTest { val lambda = lambdaRecorder { } @@ -59,12 +62,12 @@ class VectorFirebaseMessagingServiceTest { }, ) ) + advanceUntilIdle() lambda.assertions() .isCalledOnce() .with(value(PushData(AN_EVENT_ID, A_ROOM_ID, null, A_SECRET))) } - @Ignore("The test does not wait for the end of the coroutine.") @Test fun `test new token is forwarded to the handler`() = runTest { val lambda = lambdaRecorder { } @@ -72,12 +75,13 @@ class VectorFirebaseMessagingServiceTest { firebaseNewTokenHandler = FakeFirebaseNewTokenHandler(handleResult = lambda) ) vectorFirebaseMessagingService.onNewToken("aToken") + advanceUntilIdle() lambda.assertions() .isCalledOnce() .with(value("aToken")) } - private fun createVectorFirebaseMessagingService( + private fun TestScope.createVectorFirebaseMessagingService( firebaseNewTokenHandler: FirebaseNewTokenHandler = FakeFirebaseNewTokenHandler(), pushHandler: PushHandler = FakePushHandler(), ): VectorFirebaseMessagingService { @@ -85,6 +89,7 @@ class VectorFirebaseMessagingServiceTest { this.firebaseNewTokenHandler = firebaseNewTokenHandler this.pushParser = FirebasePushParser() this.pushHandler = pushHandler + this.coroutineScope = this@createVectorFirebaseMessagingService } } } From 738bb831fa0096349dfded0b487efe1ef71e90ba Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 18:59:26 +0200 Subject: [PATCH 037/252] Create interface UnifiedPushGatewayResolver --- .../unifiedpush/UnifiedPushGatewayResolver.kt | 9 ++++++++- ... DefaultUnifiedPushGatewayResolverTest.kt} | 20 +++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) rename libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/{UnifiedPushGatewayResolverTest.kt => DefaultUnifiedPushGatewayResolverTest.kt} (90%) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt index 1077bf970e..707eb3fed4 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt @@ -16,13 +16,20 @@ package io.element.android.libraries.pushproviders.unifiedpush +import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.di.AppScope import kotlinx.coroutines.withContext import timber.log.Timber import java.net.URL import javax.inject.Inject -class UnifiedPushGatewayResolver @Inject constructor( +interface UnifiedPushGatewayResolver { + suspend fun getGateway(endpoint: String): String +} + +@ContributesBinding(AppScope::class) +class DefaultUnifiedPushGatewayResolver @Inject constructor( private val unifiedPushApiFactory: UnifiedPushApiFactory, private val coroutineDispatchers: CoroutineDispatchers, ) { diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolverTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayResolverTest.kt similarity index 90% rename from libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolverTest.kt rename to libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayResolverTest.kt index 51d2a086e4..34086f6f68 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolverTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayResolverTest.kt @@ -24,7 +24,7 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test -class UnifiedPushGatewayResolverTest { +class DefaultUnifiedPushGatewayResolverTest { private val matrixDiscoveryResponse = { DiscoveryResponse( unifiedpush = DiscoveryUnifiedPush( @@ -46,7 +46,7 @@ class UnifiedPushGatewayResolverTest { val unifiedPushApiFactory = FakeUnifiedPushApiFactory( discoveryResponse = matrixDiscoveryResponse ) - val sut = createUnifiedPushGatewayResolver( + val sut = createDefaultUnifiedPushGatewayResolver( unifiedPushApiFactory = unifiedPushApiFactory ) val result = sut.getGateway("https://custom.url") @@ -59,7 +59,7 @@ class UnifiedPushGatewayResolverTest { val unifiedPushApiFactory = FakeUnifiedPushApiFactory( discoveryResponse = matrixDiscoveryResponse ) - val sut = createUnifiedPushGatewayResolver( + val sut = createDefaultUnifiedPushGatewayResolver( unifiedPushApiFactory = unifiedPushApiFactory ) val result = sut.getGateway("https://custom.url:123") @@ -72,7 +72,7 @@ class UnifiedPushGatewayResolverTest { val unifiedPushApiFactory = FakeUnifiedPushApiFactory( discoveryResponse = matrixDiscoveryResponse ) - val sut = createUnifiedPushGatewayResolver( + val sut = createDefaultUnifiedPushGatewayResolver( unifiedPushApiFactory = unifiedPushApiFactory ) val result = sut.getGateway("https://custom.url:123/some/path") @@ -85,7 +85,7 @@ class UnifiedPushGatewayResolverTest { val unifiedPushApiFactory = FakeUnifiedPushApiFactory( discoveryResponse = matrixDiscoveryResponse ) - val sut = createUnifiedPushGatewayResolver( + val sut = createDefaultUnifiedPushGatewayResolver( unifiedPushApiFactory = unifiedPushApiFactory ) val result = sut.getGateway("http://custom.url:123/some/path") @@ -98,7 +98,7 @@ class UnifiedPushGatewayResolverTest { val unifiedPushApiFactory = FakeUnifiedPushApiFactory( discoveryResponse = { throw Exception() } ) - val sut = createUnifiedPushGatewayResolver( + val sut = createDefaultUnifiedPushGatewayResolver( unifiedPushApiFactory = unifiedPushApiFactory ) val result = sut.getGateway("http://custom.url") @@ -111,7 +111,7 @@ class UnifiedPushGatewayResolverTest { val unifiedPushApiFactory = FakeUnifiedPushApiFactory( discoveryResponse = matrixDiscoveryResponse ) - val sut = createUnifiedPushGatewayResolver( + val sut = createDefaultUnifiedPushGatewayResolver( unifiedPushApiFactory = unifiedPushApiFactory ) val result = sut.getGateway("invalid") @@ -124,7 +124,7 @@ class UnifiedPushGatewayResolverTest { val unifiedPushApiFactory = FakeUnifiedPushApiFactory( discoveryResponse = invalidDiscoveryResponse ) - val sut = createUnifiedPushGatewayResolver( + val sut = createDefaultUnifiedPushGatewayResolver( unifiedPushApiFactory = unifiedPushApiFactory ) val result = sut.getGateway("https://custom.url") @@ -132,11 +132,11 @@ class UnifiedPushGatewayResolverTest { assertThat(result).isEqualTo(UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL) } - private fun TestScope.createUnifiedPushGatewayResolver( + private fun TestScope.createDefaultUnifiedPushGatewayResolver( unifiedPushApiFactory: UnifiedPushApiFactory = FakeUnifiedPushApiFactory( discoveryResponse = { DiscoveryResponse() } ) - ) = UnifiedPushGatewayResolver( + ) = DefaultUnifiedPushGatewayResolver( unifiedPushApiFactory = unifiedPushApiFactory, coroutineDispatchers = testCoroutineDispatchers() ) From 09b2b2c476215d258f3dcd42ab29ecb0b065bf8c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 19:00:55 +0200 Subject: [PATCH 038/252] Create interface UnifiedPushNewGatewayHandler --- .../unifiedpush/UnifiedPushNewGatewayHandler.kt | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushNewGatewayHandler.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushNewGatewayHandler.kt index 3ae733d261..f839e6a03e 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushNewGatewayHandler.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushNewGatewayHandler.kt @@ -16,8 +16,10 @@ package io.element.android.libraries.pushproviders.unifiedpush +import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.core.extensions.flatMap import io.element.android.libraries.core.log.logger.LoggerTag +import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.pushproviders.api.PusherSubscriber import io.element.android.libraries.pushstore.api.UserPushStoreFactory @@ -25,18 +27,23 @@ import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret import timber.log.Timber import javax.inject.Inject -private val loggerTag = LoggerTag("UnifiedPushNewGatewayHandler", LoggerTag.PushLoggerTag) +private val loggerTag = LoggerTag("DefaultUnifiedPushNewGatewayHandler", LoggerTag.PushLoggerTag) /** * Handle new endpoint received from UnifiedPush. Will update the session matching the client secret. */ -class UnifiedPushNewGatewayHandler @Inject constructor( +interface UnifiedPushNewGatewayHandler { + suspend fun handle(endpoint: String, pushGateway: String, clientSecret: String): Result +} + +@ContributesBinding(AppScope::class) +class DefaultUnifiedPushNewGatewayHandler @Inject constructor( private val pusherSubscriber: PusherSubscriber, private val userPushStoreFactory: UserPushStoreFactory, private val pushClientSecret: PushClientSecret, private val matrixAuthenticationService: MatrixAuthenticationService, -) { - suspend fun handle(endpoint: String, pushGateway: String, clientSecret: String): Result { +) : UnifiedPushNewGatewayHandler { + override suspend fun handle(endpoint: String, pushGateway: String, clientSecret: String): Result { // Register the pusher for the session with this client secret, if is it using UnifiedPush. val userId = pushClientSecret.getUserIdFromSecret(clientSecret) ?: return Result.failure( IllegalStateException("Unable to retrieve session") From 50b7f2c2c1302d1c8289411e520274190419eda6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 19:18:20 +0200 Subject: [PATCH 039/252] Add test on VectorUnifiedPushMessagingReceiver --- .../unifiedpush/build.gradle.kts | 2 + .../unifiedpush/UnifiedPushGatewayResolver.kt | 4 +- .../unifiedpush/UnifiedPushParser.kt | 1 - .../VectorUnifiedPushMessagingReceiver.kt | 4 +- .../FakeUnifiedPushGatewayResolver.kt | 25 +++ .../FakeUnifiedPushNewGatewayHandler.kt | 25 +++ .../unifiedpush/UnifiedPushParserTest.kt | 3 +- .../VectorUnifiedPushMessagingReceiverTest.kt | 196 ++++++++++++++++++ 8 files changed, 252 insertions(+), 8 deletions(-) create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushGatewayResolver.kt create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushNewGatewayHandler.kt create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiverTest.kt diff --git a/libraries/pushproviders/unifiedpush/build.gradle.kts b/libraries/pushproviders/unifiedpush/build.gradle.kts index 42b950d2ca..d1c8cf5ee1 100644 --- a/libraries/pushproviders/unifiedpush/build.gradle.kts +++ b/libraries/pushproviders/unifiedpush/build.gradle.kts @@ -55,9 +55,11 @@ dependencies { testImplementation(libs.coroutines.test) testImplementation(libs.test.junit) + testImplementation(libs.test.robolectric) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushstore.test) testImplementation(projects.tests.testutils) testImplementation(projects.services.toolbox.test) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt index 707eb3fed4..c39c7ec066 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt @@ -32,8 +32,8 @@ interface UnifiedPushGatewayResolver { class DefaultUnifiedPushGatewayResolver @Inject constructor( private val unifiedPushApiFactory: UnifiedPushApiFactory, private val coroutineDispatchers: CoroutineDispatchers, -) { - suspend fun getGateway(endpoint: String): String { +) : UnifiedPushGatewayResolver { + override suspend fun getGateway(endpoint: String): String { val gateway = UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL try { val url = URL(endpoint) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParser.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParser.kt index f68cd8542b..46c7c0f9bb 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParser.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParser.kt @@ -18,7 +18,6 @@ package io.element.android.libraries.pushproviders.unifiedpush import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.pushproviders.api.PushData -import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import javax.inject.Inject diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt index 8aa41bc250..41e82eab1c 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt @@ -24,7 +24,6 @@ import io.element.android.libraries.pushproviders.api.PushHandler import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import org.unifiedpush.android.connector.MessagingReceiver import timber.log.Timber @@ -40,8 +39,7 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() { @Inject lateinit var unifiedPushGatewayResolver: UnifiedPushGatewayResolver @Inject lateinit var newGatewayHandler: UnifiedPushNewGatewayHandler @Inject lateinit var endpointRegistrationHandler: EndpointRegistrationHandler - - private val coroutineScope = CoroutineScope(SupervisorJob()) + @Inject lateinit var coroutineScope: CoroutineScope override fun onReceive(context: Context, intent: Intent) { context.applicationContext.bindings().inject(this) diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushGatewayResolver.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushGatewayResolver.kt new file mode 100644 index 0000000000..37b70fb438 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushGatewayResolver.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +class FakeUnifiedPushGatewayResolver( + private val getGatewayResult: (String) -> String = { TODO() }, +) : UnifiedPushGatewayResolver { + override suspend fun getGateway(endpoint: String): String { + return getGatewayResult(endpoint) + } +} diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushNewGatewayHandler.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushNewGatewayHandler.kt new file mode 100644 index 0000000000..4e84e5e198 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushNewGatewayHandler.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +class FakeUnifiedPushNewGatewayHandler( + private val handleResult: suspend (String, String, String) -> Result = { _, _, _ -> TODO() }, +) : UnifiedPushNewGatewayHandler { + override suspend fun handle(endpoint: String, pushGateway: String, clientSecret: String): Result { + return handleResult(endpoint, pushGateway, clientSecret) + } +} diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParserTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParserTest.kt index bbccc92581..da710037c4 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParserTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushParserTest.kt @@ -82,9 +82,8 @@ class UnifiedPushParserTest { } companion object { - private val UNIFIED_PUSH_DATA = + val UNIFIED_PUSH_DATA = "{\"notification\":{\"event_id\":\"$AN_EVENT_ID\",\"room_id\":\"$A_ROOM_ID\",\"counts\":{\"unread\":1},\"prio\":\"high\"}}" - // TODO Check client secret format? } } diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiverTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiverTest.kt new file mode 100644 index 0000000000..6c14d1efee --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiverTest.kt @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package io.element.android.libraries.pushproviders.unifiedpush + +import androidx.test.platform.app.InstrumentationRegistry +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +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_SECRET +import io.element.android.libraries.push.test.test.FakePushHandler +import io.element.android.libraries.pushproviders.api.PushData +import io.element.android.libraries.pushproviders.api.PushHandler +import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler +import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class VectorUnifiedPushMessagingReceiverTest { + @Test + fun `onUnregistered does nothing`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver() + vectorUnifiedPushMessagingReceiver.onUnregistered(context, A_SECRET) + } + + @Test + fun `onRegistrationFailed does nothing`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver() + vectorUnifiedPushMessagingReceiver.onRegistrationFailed(context, A_SECRET) + } + + @Test + fun `onMessage valid invoke the push handler`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val pushHandlerResult = lambdaRecorder {} + val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver( + pushHandler = FakePushHandler( + handleResult = pushHandlerResult + ), + ) + vectorUnifiedPushMessagingReceiver.onMessage(context, UnifiedPushParserTest.UNIFIED_PUSH_DATA.toByteArray(), A_SECRET) + advanceUntilIdle() + pushHandlerResult.assertions() + .isCalledOnce() + .with( + value( + PushData( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + unread = 1, + clientSecret = A_SECRET + ) + ) + ) + } + + @Test + fun `onMessage invalid does not invoke the push handler`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val pushHandlerResult = lambdaRecorder {} + val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver( + pushHandler = FakePushHandler( + handleResult = pushHandlerResult + ), + ) + vectorUnifiedPushMessagingReceiver.onMessage(context, "".toByteArray(), A_SECRET) + advanceUntilIdle() + pushHandlerResult.assertions() + .isNeverCalled() + } + + @Test + fun `onNewEndpoint run the expected tasks`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val storePushGatewayResult = lambdaRecorder { _, _ -> } + val storeUpEndpointResult = lambdaRecorder { _, _ -> } + val unifiedPushStore = FakeUnifiedPushStore( + storePushGatewayResult = storePushGatewayResult, + storeUpEndpointResult = storeUpEndpointResult, + ) + val endpointRegistrationHandler = EndpointRegistrationHandler() + val handleResult = lambdaRecorder> { _, _, _ -> Result.success(Unit) } + val unifiedPushNewGatewayHandler = FakeUnifiedPushNewGatewayHandler( + handleResult = handleResult + ) + val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver( + unifiedPushStore = unifiedPushStore, + unifiedPushGatewayResolver = FakeUnifiedPushGatewayResolver( + getGatewayResult = { "aGateway" } + ), + endpointRegistrationHandler = endpointRegistrationHandler, + unifiedPushNewGatewayHandler = unifiedPushNewGatewayHandler, + ) + endpointRegistrationHandler.state.test { + vectorUnifiedPushMessagingReceiver.onNewEndpoint(context, "anEndpoint", A_SECRET) + advanceUntilIdle() + assertThat(awaitItem()).isEqualTo( + RegistrationResult( + clientSecret = A_SECRET, + result = Result.success(Unit) + ) + ) + } + storePushGatewayResult.assertions() + .isCalledOnce() + .with(value("aGateway"), value(A_SECRET)) + storeUpEndpointResult.assertions() + .isCalledOnce() + .with(value("anEndpoint"), value(A_SECRET)) + } + + @Test + fun `onNewEndpoint, if registration fails, the endpoint should not be stored`() = runTest { + val context = InstrumentationRegistry.getInstrumentation().context + val storePushGatewayResult = lambdaRecorder { _, _ -> } + val storeUpEndpointResult = lambdaRecorder { _, _ -> } + val unifiedPushStore = FakeUnifiedPushStore( + storePushGatewayResult = storePushGatewayResult, + storeUpEndpointResult = storeUpEndpointResult, + ) + val endpointRegistrationHandler = EndpointRegistrationHandler() + val handleResult = lambdaRecorder> { _, _, _ -> Result.failure(AN_EXCEPTION) } + val unifiedPushNewGatewayHandler = FakeUnifiedPushNewGatewayHandler( + handleResult = handleResult + ) + val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver( + unifiedPushStore = unifiedPushStore, + unifiedPushGatewayResolver = FakeUnifiedPushGatewayResolver( + getGatewayResult = { "aGateway" } + ), + endpointRegistrationHandler = endpointRegistrationHandler, + unifiedPushNewGatewayHandler = unifiedPushNewGatewayHandler, + ) + endpointRegistrationHandler.state.test { + vectorUnifiedPushMessagingReceiver.onNewEndpoint(context, "anEndpoint", A_SECRET) + advanceUntilIdle() + assertThat(awaitItem()).isEqualTo( + RegistrationResult( + clientSecret = A_SECRET, + result = Result.failure(AN_EXCEPTION) + ) + ) + } + storePushGatewayResult.assertions() + .isCalledOnce() + .with(value("aGateway"), value(A_SECRET)) + storeUpEndpointResult.assertions() + .isNeverCalled() + } + + private fun TestScope.createVectorUnifiedPushMessagingReceiver( + pushHandler: PushHandler = FakePushHandler(), + unifiedPushStore: UnifiedPushStore = FakeUnifiedPushStore(), + unifiedPushGatewayResolver: UnifiedPushGatewayResolver = FakeUnifiedPushGatewayResolver(), + unifiedPushNewGatewayHandler: UnifiedPushNewGatewayHandler = FakeUnifiedPushNewGatewayHandler(), + endpointRegistrationHandler: EndpointRegistrationHandler = EndpointRegistrationHandler(), + ): VectorUnifiedPushMessagingReceiver { + return VectorUnifiedPushMessagingReceiver().apply { + this.pushParser = UnifiedPushParser() + this.pushHandler = pushHandler + this.guardServiceStarter = NoopGuardServiceStarter() + this.unifiedPushStore = unifiedPushStore + this.unifiedPushGatewayResolver = unifiedPushGatewayResolver + this.newGatewayHandler = unifiedPushNewGatewayHandler + this.endpointRegistrationHandler = endpointRegistrationHandler + this.coroutineScope = this@createVectorUnifiedPushMessagingReceiver + } + } +} From f304c1f65ab9910ea8aef7ec06f7f77e9b0cd13e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 22:02:09 +0200 Subject: [PATCH 040/252] Add test on DefaultRegisterUnifiedPushUseCase --- .../DefaultRegisterUnifiedPushUseCaseTest.kt | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt new file mode 100644 index 0000000000..99886be2dd --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +import androidx.test.platform.app.InstrumentationRegistry +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.A_SECRET +import io.element.android.libraries.pushproviders.api.Distributor +import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler +import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class DefaultRegisterUnifiedPushUseCaseTest { + @Test + fun `test registration successful`() = runTest { + val endpointRegistrationHandler = EndpointRegistrationHandler() + val useCase = createDefaultRegisterUnifiedPushUseCase( + endpointRegistrationHandler = endpointRegistrationHandler + ) + val aDistributor = Distributor("aValue", "aName") + launch { + delay(100) + endpointRegistrationHandler.registrationDone(RegistrationResult(A_SECRET, Result.success(Unit))) + } + val result = useCase.execute(aDistributor, A_SECRET) + assertThat(result.isSuccess).isTrue() + } + + @Test + fun `test registration error`() = runTest { + val endpointRegistrationHandler = EndpointRegistrationHandler() + val useCase = createDefaultRegisterUnifiedPushUseCase( + endpointRegistrationHandler = endpointRegistrationHandler + ) + val aDistributor = Distributor("aValue", "aName") + launch { + delay(100) + endpointRegistrationHandler.registrationDone(RegistrationResult(A_SECRET, Result.failure(AN_EXCEPTION))) + } + val result = useCase.execute(aDistributor, A_SECRET) + assertThat(result.isSuccess).isFalse() + } + + @Ignore("Find a solution to test timeout") + @Test + fun `test registration timeout`() = runTest { + val endpointRegistrationHandler = EndpointRegistrationHandler() + val useCase = createDefaultRegisterUnifiedPushUseCase( + endpointRegistrationHandler = endpointRegistrationHandler + ) + val aDistributor = Distributor("aValue", "aName") + val result = useCase.execute(aDistributor, A_SECRET) + assertThat(result.isSuccess).isFalse() + } + + private fun TestScope.createDefaultRegisterUnifiedPushUseCase( + endpointRegistrationHandler: EndpointRegistrationHandler + ): DefaultRegisterUnifiedPushUseCase { + val context = InstrumentationRegistry.getInstrumentation().context + return DefaultRegisterUnifiedPushUseCase( + context = context, + endpointRegistrationHandler = endpointRegistrationHandler, + coroutineScope = this@createDefaultRegisterUnifiedPushUseCase + ) + } +} From f974cdd12a295245207c96d1c491b3cd5756fcaa Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 23:51:10 +0200 Subject: [PATCH 041/252] Add test on DefaultUnifiedPushNewGatewayHandler --- ...DefaultUnifiedPushNewGatewayHandlerTest.kt | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushNewGatewayHandlerTest.kt diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushNewGatewayHandlerTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushNewGatewayHandlerTest.kt new file mode 100644 index 0000000000..3f0b7409cc --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushNewGatewayHandlerTest.kt @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.test.A_SECRET +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService +import io.element.android.libraries.push.test.FakePusherSubscriber +import io.element.android.libraries.pushproviders.api.PusherSubscriber +import io.element.android.libraries.pushstore.api.UserPushStoreFactory +import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStore +import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory +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.value +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultUnifiedPushNewGatewayHandlerTest { + @Test + fun `error when fail to retrieve the session`() = runTest { + val defaultUnifiedPushNewGatewayHandler = createDefaultUnifiedPushNewGatewayHandler( + pushClientSecret = FakePushClientSecret( + getUserIdFromSecretResult = { null } + ) + ) + val result = defaultUnifiedPushNewGatewayHandler.handle( + endpoint = "aEndpoint", + pushGateway = "aPushGateway", + clientSecret = A_SECRET, + ) + assertThat(result.isFailure).isTrue() + assertThat(result.exceptionOrNull()).isInstanceOf(IllegalStateException::class.java) + assertThat(result.exceptionOrNull()?.message).isEqualTo("Unable to retrieve session") + } + + @Test + fun `error when the session is not using UnifiedPush`() = runTest { + val defaultUnifiedPushNewGatewayHandler = createDefaultUnifiedPushNewGatewayHandler( + pushClientSecret = FakePushClientSecret( + getUserIdFromSecretResult = { A_USER_ID } + ), + userPushStoreFactory = FakeUserPushStoreFactory( + userPushStore = { FakeUserPushStore(pushProviderName = "other") } + ) + ) + val result = defaultUnifiedPushNewGatewayHandler.handle( + endpoint = "aEndpoint", + pushGateway = "aPushGateway", + clientSecret = A_SECRET, + ) + assertThat(result.isFailure).isTrue() + assertThat(result.exceptionOrNull()).isInstanceOf(IllegalStateException::class.java) + assertThat(result.exceptionOrNull()?.message).isEqualTo("This session is not using UnifiedPush pusher") + } + + @Test + fun `error when the registration fails`() = runTest { + val aMatrixClient = FakeMatrixClient() + val defaultUnifiedPushNewGatewayHandler = createDefaultUnifiedPushNewGatewayHandler( + pushClientSecret = FakePushClientSecret( + getUserIdFromSecretResult = { A_USER_ID } + ), + userPushStoreFactory = FakeUserPushStoreFactory( + userPushStore = { FakeUserPushStore(pushProviderName = UnifiedPushConfig.NAME) } + ), + pusherSubscriber = FakePusherSubscriber( + registerPusherResult = { _, _, _ -> Result.failure(IllegalStateException("an error")) } + ), + matrixAuthenticationService = FakeAuthenticationService(matrixClientResult = { Result.success(aMatrixClient) }), + ) + val result = defaultUnifiedPushNewGatewayHandler.handle( + endpoint = "aEndpoint", + pushGateway = "aPushGateway", + clientSecret = A_SECRET, + ) + assertThat(result.isFailure).isTrue() + assertThat(result.exceptionOrNull()).isInstanceOf(IllegalStateException::class.java) + assertThat(result.exceptionOrNull()?.message).isEqualTo("an error") + } + + @Test + fun `happy path`() = runTest { + val aMatrixClient = FakeMatrixClient() + val lambda = lambdaRecorder { _: MatrixClient, _: String, _: String -> + Result.success(Unit) + } + val defaultUnifiedPushNewGatewayHandler = createDefaultUnifiedPushNewGatewayHandler( + pushClientSecret = FakePushClientSecret( + getUserIdFromSecretResult = { A_USER_ID } + ), + userPushStoreFactory = FakeUserPushStoreFactory( + userPushStore = { FakeUserPushStore(pushProviderName = UnifiedPushConfig.NAME) } + ), + pusherSubscriber = FakePusherSubscriber( + registerPusherResult = lambda + ), + matrixAuthenticationService = FakeAuthenticationService(matrixClientResult = { Result.success(aMatrixClient) }), + ) + val result = defaultUnifiedPushNewGatewayHandler.handle( + endpoint = "aEndpoint", + pushGateway = "aPushGateway", + clientSecret = A_SECRET, + ) + assertThat(result).isEqualTo(Result.success(Unit)) + lambda.assertions() + .isCalledOnce() + .with(value(aMatrixClient), value("aEndpoint"), value("aPushGateway")) + } + + private fun createDefaultUnifiedPushNewGatewayHandler( + pusherSubscriber: PusherSubscriber = FakePusherSubscriber(), + userPushStoreFactory: UserPushStoreFactory = FakeUserPushStoreFactory(), + pushClientSecret: PushClientSecret = FakePushClientSecret(), + matrixAuthenticationService: MatrixAuthenticationService = FakeAuthenticationService() + ): DefaultUnifiedPushNewGatewayHandler { + return DefaultUnifiedPushNewGatewayHandler( + pusherSubscriber = pusherSubscriber, + userPushStoreFactory = userPushStoreFactory, + pushClientSecret = pushClientSecret, + matrixAuthenticationService = matrixAuthenticationService + ) + } +} From 02fa405b8e51a9f0f04e26ada14ae3c6d6b0bc7e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 May 2024 23:56:25 +0200 Subject: [PATCH 042/252] Add test on UnifiedPushTest.isRelevant --- .../unifiedpush/troubleshoot/UnifiedPushTestTest.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushTestTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushTestTest.kt index 117e8b7457..7fc4d2c0a5 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushTestTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushTestTest.kt @@ -19,7 +19,9 @@ package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.pushproviders.api.Distributor +import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState +import io.element.android.libraries.troubleshoot.api.test.TestFilterData import io.element.android.services.toolbox.test.strings.FakeStringProvider import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest @@ -81,4 +83,15 @@ class UnifiedPushTestTest { assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Success) } } + + @Test + fun `test isRelevant`() = runTest { + val sut = UnifiedPushTest( + unifiedPushDistributorProvider = FakeUnifiedPushDistributorProvider(), + openDistributorWebPageAction = FakeOpenDistributorWebPageAction(), + stringProvider = FakeStringProvider(), + ) + assertThat(sut.isRelevant(TestFilterData(currentPushProviderName = UnifiedPushConfig.NAME))).isTrue() + assertThat(sut.isRelevant(TestFilterData(currentPushProviderName = "other"))).isFalse() + } } From 0a2d87f6b1c6be2624a4bbd8bcc6c7cbac31bc6c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 00:11:50 +0200 Subject: [PATCH 043/252] Add test on DefaultUnregisterUnifiedPushUseCase --- ...DefaultUnregisterUnifiedPushUseCaseTest.kt | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt new file mode 100644 index 0000000000..41090a16cc --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.pushproviders.unifiedpush + +import androidx.test.platform.app.InstrumentationRegistry +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.matrix.test.A_SECRET +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.push.test.FakePusherSubscriber +import io.element.android.libraries.pushproviders.api.PusherSubscriber +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class DefaultUnregisterUnifiedPushUseCaseTest { + @Test + fun `test un registration successful`() = runTest { + val lambda = lambdaRecorder { _: MatrixClient, _: String, _: String -> Result.success(Unit) } + val storeUpEndpointResult = lambdaRecorder { _: String?, _: String -> } + val storePushGatewayResult = lambdaRecorder { _: String?, _: String -> } + val matrixClient = FakeMatrixClient() + val useCase = createDefaultUnregisterUnifiedPushUseCase( + unifiedPushStore = FakeUnifiedPushStore( + getEndpointResult = { "aEndpoint" }, + getPushGatewayResult = { "aGateway" }, + storeUpEndpointResult = storeUpEndpointResult, + storePushGatewayResult = storePushGatewayResult, + ), + pusherSubscriber = FakePusherSubscriber( + unregisterPusherResult = lambda + ) + ) + val result = useCase.execute(matrixClient, A_SECRET) + assertThat(result.isSuccess).isTrue() + lambda.assertions() + .isCalledOnce() + .with(value(matrixClient), value("aEndpoint"), value("aGateway")) + storeUpEndpointResult.assertions() + .isCalledOnce() + .with(value(null), value(A_SECRET)) + storePushGatewayResult.assertions() + .isCalledOnce() + .with(value(null), value(A_SECRET)) + } + + @Test + fun `test un registration error - no endpoint`() = runTest { + val matrixClient = FakeMatrixClient() + val useCase = createDefaultUnregisterUnifiedPushUseCase( + unifiedPushStore = FakeUnifiedPushStore( + getEndpointResult = { null }, + getPushGatewayResult = { "aGateway" }, + ), + ) + val result = useCase.execute(matrixClient, A_SECRET) + assertThat(result.isFailure).isTrue() + } + + @Test + fun `test un registration error - no gateway`() = runTest { + val matrixClient = FakeMatrixClient() + val useCase = createDefaultUnregisterUnifiedPushUseCase( + unifiedPushStore = FakeUnifiedPushStore( + getEndpointResult = { "aEndpoint" }, + getPushGatewayResult = { null }, + ), + ) + val result = useCase.execute(matrixClient, A_SECRET) + assertThat(result.isFailure).isTrue() + } + + @Test + fun `test un registration error`() = runTest { + val matrixClient = FakeMatrixClient() + val useCase = createDefaultUnregisterUnifiedPushUseCase( + unifiedPushStore = FakeUnifiedPushStore( + getEndpointResult = { "aEndpoint" }, + getPushGatewayResult = { "aGateway" }, + ), + pusherSubscriber = FakePusherSubscriber( + unregisterPusherResult = { _, _, _ -> Result.failure(AN_EXCEPTION) } + ) + ) + val result = useCase.execute(matrixClient, A_SECRET) + assertThat(result.isFailure).isTrue() + } + + private fun createDefaultUnregisterUnifiedPushUseCase( + unifiedPushStore: UnifiedPushStore = FakeUnifiedPushStore(), + pusherSubscriber: PusherSubscriber = FakePusherSubscriber() + ): DefaultUnregisterUnifiedPushUseCase { + val context = InstrumentationRegistry.getInstrumentation().context + return DefaultUnregisterUnifiedPushUseCase( + context = context, + unifiedPushStore = unifiedPushStore, + pusherSubscriber = pusherSubscriber + ) + } +} From 4e7f5c46b8bd4983ce25efd227ae8ea241e63e9f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 00:14:00 +0200 Subject: [PATCH 044/252] Refactor: change signature to always have `clientSecret` first, since it's acting as a key. --- .../pushproviders/unifiedpush/UnifiedPushStore.kt | 12 ++++++------ .../unifiedpush/UnregisterUnifiedPushUseCase.kt | 4 ++-- .../VectorUnifiedPushMessagingReceiver.kt | 4 ++-- .../DefaultUnregisterUnifiedPushUseCaseTest.kt | 8 ++++---- .../unifiedpush/FakeUnifiedPushStore.kt | 12 ++++++------ .../VectorUnifiedPushMessagingReceiverTest.kt | 14 +++++++------- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushStore.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushStore.kt index 397ca381eb..dc1bd86d9e 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushStore.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushStore.kt @@ -28,9 +28,9 @@ import javax.inject.Inject interface UnifiedPushStore { fun getEndpoint(clientSecret: String): String? - fun storeUpEndpoint(endpoint: String?, clientSecret: String) + fun storeUpEndpoint(clientSecret: String, endpoint: String?) fun getPushGateway(clientSecret: String): String? - fun storePushGateway(gateway: String?, clientSecret: String) + fun storePushGateway(clientSecret: String, gateway: String?) fun getDistributorValue(userId: UserId): String? fun setDistributorValue(userId: UserId, value: String) } @@ -53,10 +53,10 @@ class DefaultUnifiedPushStore @Inject constructor( /** * Store UnifiedPush Endpoint to the SharedPrefs. * - * @param endpoint the endpoint to store * @param clientSecret the client secret, to identify the session + * @param endpoint the endpoint to store */ - override fun storeUpEndpoint(endpoint: String?, clientSecret: String) { + override fun storeUpEndpoint(clientSecret: String, endpoint: String?) { defaultPrefs.edit { putString(PREFS_ENDPOINT_OR_TOKEN + clientSecret, endpoint) } @@ -75,10 +75,10 @@ class DefaultUnifiedPushStore @Inject constructor( /** * Store Push Gateway to the SharedPrefs. * - * @param gateway the push gateway to store * @param clientSecret the client secret, to identify the session + * @param gateway the push gateway to store */ - override fun storePushGateway(gateway: String?, clientSecret: String) { + override fun storePushGateway(clientSecret: String, gateway: String?) { defaultPrefs.edit { putString(PREFS_PUSH_GATEWAY + clientSecret, gateway) } diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt index 1eb9876ae4..65d924740d 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt @@ -43,8 +43,8 @@ class DefaultUnregisterUnifiedPushUseCase @Inject constructor( } return pusherSubscriber.unregisterPusher(matrixClient, endpoint, gateway) .onSuccess { - unifiedPushStore.storeUpEndpoint(null, clientSecret) - unifiedPushStore.storePushGateway(null, clientSecret) + unifiedPushStore.storeUpEndpoint(clientSecret, null) + unifiedPushStore.storePushGateway(clientSecret, null) UnifiedPush.unregisterApp(context) } } diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt index 41e82eab1c..a52b1b0e6e 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiver.kt @@ -73,13 +73,13 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() { Timber.tag(loggerTag.value).i("onNewEndpoint: $endpoint") coroutineScope.launch { val gateway = unifiedPushGatewayResolver.getGateway(endpoint) - unifiedPushStore.storePushGateway(gateway, instance) + unifiedPushStore.storePushGateway(instance, gateway) val result = newGatewayHandler.handle(endpoint, gateway, instance) .onFailure { Timber.tag(loggerTag.value).e(it, "Failed to handle new gateway") } .onSuccess { - unifiedPushStore.storeUpEndpoint(endpoint, instance) + unifiedPushStore.storeUpEndpoint(instance, endpoint) } endpointRegistrationHandler.registrationDone( RegistrationResult( diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt index 41090a16cc..1d04a07651 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt @@ -36,8 +36,8 @@ class DefaultUnregisterUnifiedPushUseCaseTest { @Test fun `test un registration successful`() = runTest { val lambda = lambdaRecorder { _: MatrixClient, _: String, _: String -> Result.success(Unit) } - val storeUpEndpointResult = lambdaRecorder { _: String?, _: String -> } - val storePushGatewayResult = lambdaRecorder { _: String?, _: String -> } + val storeUpEndpointResult = lambdaRecorder { _: String, _: String? -> } + val storePushGatewayResult = lambdaRecorder { _: String, _: String? -> } val matrixClient = FakeMatrixClient() val useCase = createDefaultUnregisterUnifiedPushUseCase( unifiedPushStore = FakeUnifiedPushStore( @@ -57,10 +57,10 @@ class DefaultUnregisterUnifiedPushUseCaseTest { .with(value(matrixClient), value("aEndpoint"), value("aGateway")) storeUpEndpointResult.assertions() .isCalledOnce() - .with(value(null), value(A_SECRET)) + .with(value(A_SECRET), value(null)) storePushGatewayResult.assertions() .isCalledOnce() - .with(value(null), value(A_SECRET)) + .with(value(A_SECRET), value(null)) } @Test diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt index 2fcdb367a9..bc806a941d 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt @@ -20,9 +20,9 @@ import io.element.android.libraries.matrix.api.core.UserId class FakeUnifiedPushStore( private val getEndpointResult: (String) -> String? = { TODO() }, - private val storeUpEndpointResult: (String?, String) -> Unit = { _, _ -> TODO() }, + private val storeUpEndpointResult: (String, String?) -> Unit = { _, _ -> TODO() }, private val getPushGatewayResult: (String) -> String? = { TODO() }, - private val storePushGatewayResult: (String?, String) -> Unit = { _, _ -> TODO() }, + private val storePushGatewayResult: (String, String?) -> Unit = { _, _ -> TODO() }, private val getDistributorValueResult: (UserId) -> String? = { TODO() }, private val setDistributorValueResult: (UserId, String) -> Unit = { _, _ -> TODO() }, ) : UnifiedPushStore { @@ -30,16 +30,16 @@ class FakeUnifiedPushStore( return getEndpointResult(clientSecret) } - override fun storeUpEndpoint(endpoint: String?, clientSecret: String) { - storeUpEndpointResult(endpoint, clientSecret) + override fun storeUpEndpoint(clientSecret: String, endpoint: String?) { + storeUpEndpointResult(clientSecret, endpoint) } override fun getPushGateway(clientSecret: String): String? { return getPushGatewayResult(clientSecret) } - override fun storePushGateway(gateway: String?, clientSecret: String) { - storePushGatewayResult(gateway, clientSecret) + override fun storePushGateway(clientSecret: String, gateway: String?) { + storePushGatewayResult(clientSecret, gateway) } override fun getDistributorValue(userId: UserId): String? { diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiverTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiverTest.kt index 6c14d1efee..e2054caacb 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiverTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/VectorUnifiedPushMessagingReceiverTest.kt @@ -99,8 +99,8 @@ class VectorUnifiedPushMessagingReceiverTest { @Test fun `onNewEndpoint run the expected tasks`() = runTest { val context = InstrumentationRegistry.getInstrumentation().context - val storePushGatewayResult = lambdaRecorder { _, _ -> } - val storeUpEndpointResult = lambdaRecorder { _, _ -> } + val storePushGatewayResult = lambdaRecorder { _, _ -> } + val storeUpEndpointResult = lambdaRecorder { _, _ -> } val unifiedPushStore = FakeUnifiedPushStore( storePushGatewayResult = storePushGatewayResult, storeUpEndpointResult = storeUpEndpointResult, @@ -130,17 +130,17 @@ class VectorUnifiedPushMessagingReceiverTest { } storePushGatewayResult.assertions() .isCalledOnce() - .with(value("aGateway"), value(A_SECRET)) + .with(value(A_SECRET), value("aGateway")) storeUpEndpointResult.assertions() .isCalledOnce() - .with(value("anEndpoint"), value(A_SECRET)) + .with(value(A_SECRET), value("anEndpoint")) } @Test fun `onNewEndpoint, if registration fails, the endpoint should not be stored`() = runTest { val context = InstrumentationRegistry.getInstrumentation().context - val storePushGatewayResult = lambdaRecorder { _, _ -> } - val storeUpEndpointResult = lambdaRecorder { _, _ -> } + val storePushGatewayResult = lambdaRecorder { _, _ -> } + val storeUpEndpointResult = lambdaRecorder { _, _ -> } val unifiedPushStore = FakeUnifiedPushStore( storePushGatewayResult = storePushGatewayResult, storeUpEndpointResult = storeUpEndpointResult, @@ -170,7 +170,7 @@ class VectorUnifiedPushMessagingReceiverTest { } storePushGatewayResult.assertions() .isCalledOnce() - .with(value("aGateway"), value(A_SECRET)) + .with(value(A_SECRET), value("aGateway")) storeUpEndpointResult.assertions() .isNeverCalled() } From cf3b2bba91595f5f4080c45b72107737febb6cbb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 00:20:24 +0200 Subject: [PATCH 045/252] Add test on isRelevant --- .../troubleshoot/FirebaseAvailabilityTestTest.kt | 12 ++++++++++++ .../firebase/troubleshoot/FirebaseTokenTestTest.kt | 13 +++++++++++++ .../unifiedpush/troubleshoot/UnifiedPushTestTest.kt | 2 +- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt index 4b85683942..fae5ba9f12 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseAvailabilityTestTest.kt @@ -19,7 +19,9 @@ package io.element.android.libraries.pushproviders.firebase.troubleshoot import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.pushproviders.firebase.FakeIsPlayServiceAvailable +import io.element.android.libraries.pushproviders.firebase.FirebaseConfig import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState +import io.element.android.libraries.troubleshoot.api.test.TestFilterData import io.element.android.services.toolbox.test.strings.FakeStringProvider import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest @@ -59,4 +61,14 @@ class FirebaseAvailabilityTestTest { assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false)) } } + + @Test + fun `test FirebaseAvailabilityTest isRelevant`() { + val sut = FirebaseAvailabilityTest( + isPlayServiceAvailable = FakeIsPlayServiceAvailable(false), + stringProvider = FakeStringProvider(), + ) + assertThat(sut.isRelevant(TestFilterData(currentPushProviderName = "unknown"))).isFalse() + assertThat(sut.isRelevant(TestFilterData(currentPushProviderName = FirebaseConfig.NAME))).isTrue() + } } diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTestTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTestTest.kt index 2d8de62ad9..245a6095d4 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTestTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/troubleshoot/FirebaseTokenTestTest.kt @@ -19,8 +19,10 @@ package io.element.android.libraries.pushproviders.firebase.troubleshoot import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.pushproviders.firebase.FakeFirebaseTroubleshooter +import io.element.android.libraries.pushproviders.firebase.FirebaseConfig import io.element.android.libraries.pushproviders.firebase.InMemoryFirebaseStore import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState +import io.element.android.libraries.troubleshoot.api.test.TestFilterData import io.element.android.services.toolbox.test.strings.FakeStringProvider import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest @@ -75,6 +77,17 @@ class FirebaseTokenTestTest { } } + @Test + fun `test FirebaseTokenTest isRelevant`() { + val sut = FirebaseTokenTest( + firebaseStore = InMemoryFirebaseStore(null), + firebaseTroubleshooter = FakeFirebaseTroubleshooter(), + stringProvider = FakeStringProvider(), + ) + assertThat(sut.isRelevant(TestFilterData(currentPushProviderName = "unknown"))).isFalse() + assertThat(sut.isRelevant(TestFilterData(currentPushProviderName = FirebaseConfig.NAME))).isTrue() + } + companion object { private const val FAKE_TOKEN = "abcdefghijk" } diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushTestTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushTestTest.kt index 7fc4d2c0a5..9f79f7363b 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushTestTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushTestTest.kt @@ -85,7 +85,7 @@ class UnifiedPushTestTest { } @Test - fun `test isRelevant`() = runTest { + fun `test isRelevant`() { val sut = UnifiedPushTest( unifiedPushDistributorProvider = FakeUnifiedPushDistributorProvider(), openDistributorWebPageAction = FakeOpenDistributorWebPageAction(), From c71c491302d32125aaf74c1ac3e755eb333263ed Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 00:26:28 +0200 Subject: [PATCH 046/252] Clean up --- .../firebase/DefaultFirebaseNewTokenHandlerTest.kt | 2 +- .../unifiedpush/DefaultUnifiedPushGatewayResolverTest.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt index 47838ddcdb..af2b98ea07 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt @@ -82,7 +82,7 @@ class DefaultFirebaseNewTokenHandlerTest { A_USER_ID -> FakeUserPushStore(pushProviderName = FirebaseConfig.NAME) A_USER_ID_2 -> FakeUserPushStore(pushProviderName = "Other") A_USER_ID_3 -> FakeUserPushStore(pushProviderName = FirebaseConfig.NAME) - else -> throw IllegalStateException() + else -> error("Unexpected sessionId: $sessionId") } } ), diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayResolverTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayResolverTest.kt index 34086f6f68..e180e36c38 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayResolverTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayResolverTest.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.pushproviders.unifiedpush import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.pushproviders.unifiedpush.network.DiscoveryResponse import io.element.android.libraries.pushproviders.unifiedpush.network.DiscoveryUnifiedPush import io.element.android.tests.testutils.testCoroutineDispatchers @@ -96,7 +97,7 @@ class DefaultUnifiedPushGatewayResolverTest { @Test fun `when a custom url is not reachable, the default url is returned`() = runTest { val unifiedPushApiFactory = FakeUnifiedPushApiFactory( - discoveryResponse = { throw Exception() } + discoveryResponse = { throw AN_EXCEPTION } ) val sut = createDefaultUnifiedPushGatewayResolver( unifiedPushApiFactory = unifiedPushApiFactory From f04253287f465c28d5f7986380492fa02f43421d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 09:12:40 +0200 Subject: [PATCH 047/252] Introduce lambdaError instead of using TODO, to handle error when a lambda is invoked and it should not. --- .../test/permalink/FakePermalinkParser.kt | 3 ++- .../matrix/test/pushers/FakePushersService.kt | 5 ++-- .../FakeNotifiableEventResolver.kt | 3 ++- .../push/impl/push/DefaultPushHandlerTest.kt | 7 +++--- .../impl/test/FakePushGatewayNotifyRequest.kt | 3 ++- .../libraries/push/impl/test/FakeTestPush.kt | 3 ++- .../push/test/FakePusherSubscriber.kt | 5 ++-- .../push/test/test/FakePushHandler.kt | 3 ++- .../firebase/FakeFirebaseNewTokenHandler.kt | 4 +++- libraries/pushproviders/test/build.gradle.kts | 1 + .../pushproviders/test/FakePushProvider.kt | 4 ++-- .../FakeRegisterUnifiedPushUseCase.kt | 3 ++- .../FakeUnifiedPushGatewayResolver.kt | 4 +++- .../FakeUnifiedPushNewGatewayHandler.kt | 4 +++- .../unifiedpush/FakeUnifiedPushStore.kt | 13 ++++++----- .../FakeUnregisterUnifiedPushUseCase.kt | 3 ++- .../clientsecret/FakePushClientSecret.kt | 5 ++-- .../mentions/MentionSpanProvider.kt | 2 +- .../tests/testutils/EnsureNeverCalled.kt | 10 ++++---- .../android/tests/testutils/lambda/Error.kt | 23 +++++++++++++++++++ .../tests/testutils/lambda/LambdaRecorder.kt | 2 +- 21 files changed, 77 insertions(+), 33 deletions(-) create mode 100644 tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/Error.kt diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/permalink/FakePermalinkParser.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/permalink/FakePermalinkParser.kt index d1ffb70f99..525746e690 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/permalink/FakePermalinkParser.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/permalink/FakePermalinkParser.kt @@ -18,9 +18,10 @@ package io.element.android.libraries.matrix.test.permalink import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser +import io.element.android.tests.testutils.lambda.lambdaError class FakePermalinkParser( - private var result: () -> PermalinkData = { TODO("Not implemented") } + private var result: () -> PermalinkData = { lambdaError() } ) : PermalinkParser { fun givenResult(result: PermalinkData) { this.result = { result } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/pushers/FakePushersService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/pushers/FakePushersService.kt index adbb2f5b25..3ede3b272f 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/pushers/FakePushersService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/pushers/FakePushersService.kt @@ -19,10 +19,11 @@ package io.element.android.libraries.matrix.test.pushers import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData import io.element.android.libraries.matrix.api.pusher.UnsetHttpPusherData +import io.element.android.tests.testutils.lambda.lambdaError class FakePushersService( - private val setHttpPusherResult: (SetHttpPusherData) -> Result = { TODO() }, - private val unsetHttpPusherResult: (UnsetHttpPusherData) -> Result = { TODO() }, + private val setHttpPusherResult: (SetHttpPusherData) -> Result = { lambdaError() }, + private val unsetHttpPusherResult: (UnsetHttpPusherData) -> Result = { lambdaError() }, ) : PushersService { override suspend fun setHttpPusher(setHttpPusherData: SetHttpPusherData) = setHttpPusherResult(setHttpPusherData) override suspend fun unsetHttpPusher(unsetHttpPusherData: UnsetHttpPusherData): Result = unsetHttpPusherResult(unsetHttpPusherData) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt index 02cdced886..a9ccabaa6d 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt @@ -20,9 +20,10 @@ 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.SessionId import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent +import io.element.android.tests.testutils.lambda.lambdaError class FakeNotifiableEventResolver( - private val notifiableEventResult: (SessionId, RoomId, EventId) -> NotifiableEvent? = { _, _, _ -> TODO() } + private val notifiableEventResult: (SessionId, RoomId, EventId) -> NotifiableEvent? = { _, _, _ -> lambdaError() } ) : NotifiableEventResolver { override suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? { return notifiableEventResult(sessionId, roomId, eventId) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt index 5eaa879429..bae296a574 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt @@ -41,6 +41,7 @@ import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStore import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory import io.element.android.libraries.pushstore.test.userpushstore.clientsecret.FakePushClientSecret +import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -240,9 +241,9 @@ class DefaultPushHandlerTest { } private fun createDefaultPushHandler( - onNotifiableEventReceived: (NotifiableEvent) -> Unit = { TODO() }, - notifiableEventResult: (SessionId, RoomId, EventId) -> NotifiableEvent? = { _, _, _ -> TODO() }, - incrementPushCounterResult: () -> Unit = { TODO() }, + onNotifiableEventReceived: (NotifiableEvent) -> Unit = { lambdaError() }, + notifiableEventResult: (SessionId, RoomId, EventId) -> NotifiableEvent? = { _, _, _ -> lambdaError() }, + incrementPushCounterResult: () -> Unit = { lambdaError() }, userPushStore: UserPushStore = FakeUserPushStore(), pushClientSecret: PushClientSecret = FakePushClientSecret(), buildMeta: BuildMeta = aBuildMeta(), diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakePushGatewayNotifyRequest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakePushGatewayNotifyRequest.kt index 6fee8ffbf5..d0fa5a546f 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakePushGatewayNotifyRequest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakePushGatewayNotifyRequest.kt @@ -17,9 +17,10 @@ package io.element.android.libraries.push.impl.test import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest +import io.element.android.tests.testutils.lambda.lambdaError class FakePushGatewayNotifyRequest( - private val executeResult: (PushGatewayNotifyRequest.Params) -> Unit = { TODO() } + private val executeResult: (PushGatewayNotifyRequest.Params) -> Unit = { lambdaError() } ) : PushGatewayNotifyRequest { override suspend fun execute(params: PushGatewayNotifyRequest.Params) { executeResult(params) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakeTestPush.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakeTestPush.kt index 0f6f1e1862..d7bf8c8c42 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakeTestPush.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/FakeTestPush.kt @@ -17,9 +17,10 @@ package io.element.android.libraries.push.impl.test import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig +import io.element.android.tests.testutils.lambda.lambdaError class FakeTestPush( - private val executeResult: (CurrentUserPushConfig) -> Unit = { TODO() } + private val executeResult: (CurrentUserPushConfig) -> Unit = { lambdaError() } ) : TestPush { override suspend fun execute(config: CurrentUserPushConfig) { executeResult(config) diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePusherSubscriber.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePusherSubscriber.kt index bf45ccabbb..8338bb1e4c 100644 --- a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePusherSubscriber.kt +++ b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/FakePusherSubscriber.kt @@ -18,10 +18,11 @@ package io.element.android.libraries.push.test import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.pushproviders.api.PusherSubscriber +import io.element.android.tests.testutils.lambda.lambdaError class FakePusherSubscriber( - private val registerPusherResult: (MatrixClient, String, String) -> Result = { _, _, _ -> TODO() }, - private val unregisterPusherResult: (MatrixClient, String, String) -> Result = { _, _, _ -> TODO() }, + private val registerPusherResult: (MatrixClient, String, String) -> Result = { _, _, _ -> lambdaError() }, + private val unregisterPusherResult: (MatrixClient, String, String) -> Result = { _, _, _ -> lambdaError() }, ) : PusherSubscriber { override suspend fun registerPusher(matrixClient: MatrixClient, pushKey: String, gateway: String): Result { return registerPusherResult(matrixClient, pushKey, gateway) diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/test/FakePushHandler.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/test/FakePushHandler.kt index c6933a8867..c370250bd0 100644 --- a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/test/FakePushHandler.kt +++ b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/test/FakePushHandler.kt @@ -18,9 +18,10 @@ package io.element.android.libraries.push.test.test import io.element.android.libraries.pushproviders.api.PushData import io.element.android.libraries.pushproviders.api.PushHandler +import io.element.android.tests.testutils.lambda.lambdaError class FakePushHandler( - private val handleResult: (PushData) -> Unit = { TODO() } + private val handleResult: (PushData) -> Unit = { lambdaError() } ) : PushHandler { override suspend fun handle(pushData: PushData) { handleResult(pushData) diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseNewTokenHandler.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseNewTokenHandler.kt index 3db2b37cfc..aa66f0288c 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseNewTokenHandler.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseNewTokenHandler.kt @@ -16,8 +16,10 @@ package io.element.android.libraries.pushproviders.firebase +import io.element.android.tests.testutils.lambda.lambdaError + class FakeFirebaseNewTokenHandler( - private val handleResult: (String) -> Unit = { TODO() } + private val handleResult: (String) -> Unit = { lambdaError() } ) : FirebaseNewTokenHandler { override suspend fun handle(firebaseToken: String) { handleResult(firebaseToken) diff --git a/libraries/pushproviders/test/build.gradle.kts b/libraries/pushproviders/test/build.gradle.kts index ddb68ed43f..9a0d2c139c 100644 --- a/libraries/pushproviders/test/build.gradle.kts +++ b/libraries/pushproviders/test/build.gradle.kts @@ -24,4 +24,5 @@ android { dependencies { implementation(projects.libraries.matrix.api) implementation(projects.libraries.pushproviders.api) + implementation(projects.tests.testutils) } diff --git a/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt b/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt index ef938f71c8..e46516772d 100644 --- a/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt +++ b/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt @@ -27,8 +27,8 @@ class FakePushProvider( private val isAvailable: Boolean = true, private val distributors: List = listOf(Distributor("aDistributorValue", "aDistributorName")), private val currentUserPushConfig: CurrentUserPushConfig? = null, - private val registerWithResult: (MatrixClient, Distributor) -> Result = { _, _ -> TODO() }, - private val unregisterWithResult: (MatrixClient) -> Result = { TODO() }, + private val registerWithResult: (MatrixClient, Distributor) -> Result = { _, _ -> lambdaError() }, + private val unregisterWithResult: (MatrixClient) -> Result = { lambdaError() }, ) : PushProvider { override fun isAvailable(): Boolean = isAvailable diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt index b1995af699..1800903dea 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeRegisterUnifiedPushUseCase.kt @@ -17,9 +17,10 @@ package io.element.android.libraries.pushproviders.unifiedpush import io.element.android.libraries.pushproviders.api.Distributor +import io.element.android.tests.testutils.lambda.lambdaError class FakeRegisterUnifiedPushUseCase( - private val result: (Distributor, String) -> Result = { _, _ -> TODO("Not yet implemented") } + private val result: (Distributor, String) -> Result = { _, _ -> lambdaError() } ) : RegisterUnifiedPushUseCase { override suspend fun execute(distributor: Distributor, clientSecret: String): Result { return result(distributor, clientSecret) diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushGatewayResolver.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushGatewayResolver.kt index 37b70fb438..0bc52fbae8 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushGatewayResolver.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushGatewayResolver.kt @@ -16,8 +16,10 @@ package io.element.android.libraries.pushproviders.unifiedpush +import io.element.android.tests.testutils.lambda.lambdaError + class FakeUnifiedPushGatewayResolver( - private val getGatewayResult: (String) -> String = { TODO() }, + private val getGatewayResult: (String) -> String = { lambdaError() }, ) : UnifiedPushGatewayResolver { override suspend fun getGateway(endpoint: String): String { return getGatewayResult(endpoint) diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushNewGatewayHandler.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushNewGatewayHandler.kt index 4e84e5e198..b8d70baada 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushNewGatewayHandler.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushNewGatewayHandler.kt @@ -16,8 +16,10 @@ package io.element.android.libraries.pushproviders.unifiedpush +import io.element.android.tests.testutils.lambda.lambdaError + class FakeUnifiedPushNewGatewayHandler( - private val handleResult: suspend (String, String, String) -> Result = { _, _, _ -> TODO() }, + private val handleResult: suspend (String, String, String) -> Result = { _, _, _ -> lambdaError() }, ) : UnifiedPushNewGatewayHandler { override suspend fun handle(endpoint: String, pushGateway: String, clientSecret: String): Result { return handleResult(endpoint, pushGateway, clientSecret) diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt index bc806a941d..aa381d9535 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnifiedPushStore.kt @@ -17,14 +17,15 @@ package io.element.android.libraries.pushproviders.unifiedpush import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.tests.testutils.lambda.lambdaError class FakeUnifiedPushStore( - private val getEndpointResult: (String) -> String? = { TODO() }, - private val storeUpEndpointResult: (String, String?) -> Unit = { _, _ -> TODO() }, - private val getPushGatewayResult: (String) -> String? = { TODO() }, - private val storePushGatewayResult: (String, String?) -> Unit = { _, _ -> TODO() }, - private val getDistributorValueResult: (UserId) -> String? = { TODO() }, - private val setDistributorValueResult: (UserId, String) -> Unit = { _, _ -> TODO() }, + private val getEndpointResult: (String) -> String? = { lambdaError() }, + private val storeUpEndpointResult: (String, String?) -> Unit = { _, _ -> lambdaError() }, + private val getPushGatewayResult: (String) -> String? = { lambdaError() }, + private val storePushGatewayResult: (String, String?) -> Unit = { _, _ -> lambdaError() }, + private val getDistributorValueResult: (UserId) -> String? = { lambdaError() }, + private val setDistributorValueResult: (UserId, String) -> Unit = { _, _ -> lambdaError() }, ) : UnifiedPushStore { override fun getEndpoint(clientSecret: String): String? { return getEndpointResult(clientSecret) diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnregisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnregisterUnifiedPushUseCase.kt index 7aca65fe0d..9f3293420a 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnregisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeUnregisterUnifiedPushUseCase.kt @@ -17,9 +17,10 @@ package io.element.android.libraries.pushproviders.unifiedpush import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.tests.testutils.lambda.lambdaError class FakeUnregisterUnifiedPushUseCase( - private val result: (MatrixClient, String) -> Result = { _, _ -> TODO("Not yet implemented") } + private val result: (MatrixClient, String) -> Result = { _, _ -> lambdaError() } ) : UnregisterUnifiedPushUseCase { override suspend fun execute(matrixClient: MatrixClient, clientSecret: String): Result { return result(matrixClient, clientSecret) diff --git a/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt index 0853804049..25759ecc45 100644 --- a/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt +++ b/libraries/pushstore/test/src/main/kotlin/io/element/android/libraries/pushstore/test/userpushstore/clientsecret/FakePushClientSecret.kt @@ -18,10 +18,11 @@ package io.element.android.libraries.pushstore.test.userpushstore.clientsecret import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret +import io.element.android.tests.testutils.lambda.lambdaError class FakePushClientSecret( - private val getSecretForUserResult: (SessionId) -> String = { TODO() }, - private val getUserIdFromSecretResult: (String) -> SessionId? = { TODO() } + private val getSecretForUserResult: (SessionId) -> String = { lambdaError() }, + private val getUserIdFromSecretResult: (String) -> SessionId? = { lambdaError() } ) : PushClientSecret { override suspend fun getSecretForUser(userId: SessionId): String { return getSecretForUserResult(userId) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt index f7da518feb..e5c9f4793c 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt @@ -164,7 +164,7 @@ internal fun MentionSpanPreview() { eventId = null, viaParameters = persistentListOf(), ) - else -> TODO() + else -> throw AssertionError("Unexpected value $uriString") } } }, diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt index f2f2c31fed..aaf7dc54a1 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt @@ -16,26 +16,28 @@ package io.element.android.tests.testutils +import io.element.android.tests.testutils.lambda.lambdaError + class EnsureNeverCalled : () -> Unit { override fun invoke() { - throw AssertionError("Should not be called") + lambdaError() } } class EnsureNeverCalledWithParam : (T) -> Unit { override fun invoke(p1: T) { - throw AssertionError("Should not be called and is called with $p1") + lambdaError("Should not be called and is called with $p1") } } class EnsureNeverCalledWithParamAndResult : (T) -> R { override fun invoke(p1: T): R { - throw AssertionError("Should not be called and is called with $p1") + lambdaError("Should not be called and is called with $p1") } } class EnsureNeverCalledWithTwoParams : (T, U) -> Unit { override fun invoke(p1: T, p2: U) { - throw AssertionError("Should not be called and is called with $p1 and $p2") + lambdaError("Should not be called and is called with $p1 and $p2") } } diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/Error.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/Error.kt new file mode 100644 index 0000000000..c7fc10495a --- /dev/null +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/Error.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.tests.testutils.lambda + +fun lambdaError( + message: String = "This lambda should never be called." +): Nothing { + throw AssertionError(message) +} diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/LambdaRecorder.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/LambdaRecorder.kt index 4e49560398..8455d34ef6 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/LambdaRecorder.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/lambda/LambdaRecorder.kt @@ -26,7 +26,7 @@ abstract class LambdaRecorder internal constructor( internal fun onInvoke(vararg params: Any?) { if (assertNoInvocation) { - throw AssertionError("This lambda should never be called.") + lambdaError() } parametersSequence.add(params.toList()) } From de032fa4260c941e9c2efa59bff067f2247288ab Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 09:33:41 +0200 Subject: [PATCH 048/252] Use lambdaError() instead of `throw NotImplementedError()` --- .../io/element/android/appnav/intent/IntentResolverTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt index 074009cacc..0a0b507742 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt @@ -33,6 +33,7 @@ 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.A_THREAD_ID import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser +import io.element.android.tests.testutils.lambda.lambdaError import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith @@ -229,7 +230,7 @@ class IntentResolverTest { } private fun createIntentResolver( - permalinkParserResult: () -> PermalinkData = { throw NotImplementedError() } + permalinkParserResult: () -> PermalinkData = { lambdaError() } ): IntentResolver { return IntentResolver( deeplinkParser = DeeplinkParser(), From b8b66b357087581a8a470f9fbe3eb87a054be769 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 09:34:31 +0200 Subject: [PATCH 049/252] Add missing import --- .../android/libraries/pushproviders/test/FakePushProvider.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt b/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt index e46516772d..7b37d0d296 100644 --- a/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt +++ b/libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/FakePushProvider.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider +import io.element.android.tests.testutils.lambda.lambdaError class FakePushProvider( override val index: Int = 0, From 0639bc6a5b01d7a522e0a996347b3a7e8b9c22d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 23 May 2024 09:44:05 +0200 Subject: [PATCH 050/252] Simplify `RegisterUnifiedPushUseCase`, fix test --- .../unifiedpush/RegisterUnifiedPushUseCase.kt | 25 ++++++------------- .../DefaultRegisterUnifiedPushUseCaseTest.kt | 9 ++++--- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt index 1839bb4d18..6670f18ea2 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/RegisterUnifiedPushUseCase.kt @@ -22,11 +22,8 @@ import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout import org.unifiedpush.android.connector.UnifiedPush import javax.inject.Inject @@ -40,27 +37,21 @@ interface RegisterUnifiedPushUseCase { class DefaultRegisterUnifiedPushUseCase @Inject constructor( @ApplicationContext private val context: Context, private val endpointRegistrationHandler: EndpointRegistrationHandler, - private val coroutineScope: CoroutineScope, ) : RegisterUnifiedPushUseCase { override suspend fun execute(distributor: Distributor, clientSecret: String): Result { UnifiedPush.saveDistributor(context, distributor.value) - val completable = CompletableDeferred>() - val job = coroutineScope.launch { - val result = endpointRegistrationHandler.state - .filter { it.clientSecret == clientSecret } - .first() - .result - completable.complete(result) - } // This will trigger the callback // VectorUnifiedPushMessagingReceiver.onNewEndpoint UnifiedPush.registerApp(context = context, instance = clientSecret) // Wait for VectorUnifiedPushMessagingReceiver.onNewEndpoint to proceed - return withTimeout(30.seconds) { - completable.await() - } - .onFailure { - job.cancel() + return runCatching { + withTimeout(30.seconds) { + val result = endpointRegistrationHandler.state + .filter { it.clientSecret == clientSecret } + .first() + .result + result.getOrThrow() } + } } } diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt index 99886be2dd..f898a4f804 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt @@ -25,12 +25,14 @@ import io.element.android.libraries.pushproviders.unifiedpush.registration.Endpo import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner +import kotlin.time.Duration.Companion.seconds @RunWith(RobolectricTestRunner::class) class DefaultRegisterUnifiedPushUseCaseTest { @@ -64,15 +66,15 @@ class DefaultRegisterUnifiedPushUseCaseTest { assertThat(result.isSuccess).isFalse() } - @Ignore("Find a solution to test timeout") @Test - fun `test registration timeout`() = runTest { + fun `test registration timeout`() = runTest(StandardTestDispatcher()) { val endpointRegistrationHandler = EndpointRegistrationHandler() val useCase = createDefaultRegisterUnifiedPushUseCase( endpointRegistrationHandler = endpointRegistrationHandler ) val aDistributor = Distributor("aValue", "aName") val result = useCase.execute(aDistributor, A_SECRET) + advanceTimeBy(30.seconds) assertThat(result.isSuccess).isFalse() } @@ -83,7 +85,6 @@ class DefaultRegisterUnifiedPushUseCaseTest { return DefaultRegisterUnifiedPushUseCase( context = context, endpointRegistrationHandler = endpointRegistrationHandler, - coroutineScope = this@createDefaultRegisterUnifiedPushUseCase ) } } From ce23135e4acced33db2c955a565975f6cc714980 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 09:51:56 +0200 Subject: [PATCH 051/252] Do not fail un-registration if Firebase token is not known. Fixes #2895 --- .../pushproviders/firebase/FirebasePushProvider.kt | 11 +++++------ .../firebase/FirebasePushProviderTest.kt | 7 ++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt index b88d70b157..0228f8f74b 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProvider.kt @@ -64,14 +64,13 @@ class FirebasePushProvider @Inject constructor( override suspend fun getCurrentDistributor(matrixClient: MatrixClient) = firebaseDistributor override suspend fun unregister(matrixClient: MatrixClient): Result { - val pushKey = firebaseStore.getFcmToken() ?: return Result.failure( - IllegalStateException( - "Unable to unregister pusher, Firebase token is not known." - ) - ).also { + val pushKey = firebaseStore.getFcmToken() + return if (pushKey == null) { Timber.tag(loggerTag.value).w("Unable to unregister pusher, Firebase token is not known.") + Result.success(Unit) + } else { + pusherSubscriber.unregisterPusher(matrixClient, pushKey, FirebaseConfig.PUSHER_HTTP_URL) } - return pusherSubscriber.unregisterPusher(matrixClient, pushKey, FirebaseConfig.PUSHER_HTTP_URL) } override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? { diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt index 3c82682767..880be2f053 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt @@ -134,17 +134,14 @@ class FirebasePushProviderTest { } @Test - fun `unregister ko no token`() = runTest { + fun `unregister no token - in this case, the error is ignored`() = runTest { val firebasePushProvider = createFirebasePushProvider( firebaseStore = InMemoryFirebaseStore( token = null ), - pusherSubscriber = FakePusherSubscriber( - unregisterPusherResult = { _, _, _ -> Result.success(Unit) } - ) ) val result = firebasePushProvider.unregister(FakeMatrixClient()) - assertThat(result.isFailure).isTrue() + assertThat(result.isSuccess).isTrue() } @Test From d066f03eabdbb030b7016a0de3e1d510655d400d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 09:58:29 +0200 Subject: [PATCH 052/252] Add logs on pusher registration --- .../android/libraries/push/impl/DefaultPushService.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 bf2b1ed542..47e26fb920 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 @@ -51,13 +51,16 @@ class DefaultPushService @Inject constructor( pushProvider: PushProvider, distributor: Distributor, ): Result { + Timber.d("Registering with ${pushProvider.name}/${distributor.name}}") val userPushStore = userPushStoreFactory.getOrCreate(matrixClient.sessionId) val currentPushProviderName = userPushStore.getPushProviderName() val currentPushProvider = pushProviders.find { it.name == currentPushProviderName } val currentDistributorValue = currentPushProvider?.getCurrentDistributor(matrixClient)?.value if (currentPushProviderName != pushProvider.name || currentDistributorValue != distributor.value) { // Unregister previous one if any - currentPushProvider?.unregister(matrixClient) + currentPushProvider + ?.also { Timber.d("Unregistering previous push provider $currentPushProviderName/$currentDistributorValue") } + ?.unregister(matrixClient) ?.onFailure { Timber.w(it, "Failed to unregister previous push provider") return Result.failure(it) From b716482e43678f035b419e025b59fbf725bbb495 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 10:19:06 +0200 Subject: [PATCH 053/252] No need to use advanceTimeBy --- .../unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt index f898a4f804..6bc6c01d3c 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt @@ -27,12 +27,10 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import kotlin.time.Duration.Companion.seconds @RunWith(RobolectricTestRunner::class) class DefaultRegisterUnifiedPushUseCaseTest { @@ -74,7 +72,6 @@ class DefaultRegisterUnifiedPushUseCaseTest { ) val aDistributor = Distributor("aValue", "aName") val result = useCase.execute(aDistributor, A_SECRET) - advanceTimeBy(30.seconds) assertThat(result.isSuccess).isFalse() } From 788d9e06311ff9665c442d6caf2a335d72347a71 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 10:20:58 +0200 Subject: [PATCH 054/252] No need to use StandardTestDispatcher --- .../unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt index 6bc6c01d3c..cda9516b65 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultRegisterUnifiedPushUseCaseTest.kt @@ -25,7 +25,6 @@ import io.element.android.libraries.pushproviders.unifiedpush.registration.Endpo import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test @@ -65,7 +64,7 @@ class DefaultRegisterUnifiedPushUseCaseTest { } @Test - fun `test registration timeout`() = runTest(StandardTestDispatcher()) { + fun `test registration timeout`() = runTest { val endpointRegistrationHandler = EndpointRegistrationHandler() val useCase = createDefaultRegisterUnifiedPushUseCase( endpointRegistrationHandler = endpointRegistrationHandler From 3796def1c9b6304d96c24d805a1cfc22f1998d58 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 10:29:07 +0200 Subject: [PATCH 055/252] Ignore local errors when unregistering UnifiedPush, to not block switching to another push provider. --- .../UnregisterUnifiedPushUseCase.kt | 7 ++++- ...DefaultUnregisterUnifiedPushUseCaseTest.kt | 28 ++++++++++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt index 65d924740d..769f6507d5 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.pushproviders.api.PusherSubscriber import org.unifiedpush.android.connector.UnifiedPush +import timber.log.Timber import javax.inject.Inject interface UnregisterUnifiedPushUseCase { @@ -39,7 +40,11 @@ class DefaultUnregisterUnifiedPushUseCase @Inject constructor( val endpoint = unifiedPushStore.getEndpoint(clientSecret) val gateway = unifiedPushStore.getPushGateway(clientSecret) if (endpoint == null || gateway == null) { - return Result.failure(IllegalStateException("No endpoint or gateway found for client secret")) + Timber.w("No endpoint or gateway found for client secret") + // Ensure we don't have any remaining data, but ignore this error + unifiedPushStore.storeUpEndpoint(clientSecret, null) + unifiedPushStore.storePushGateway(clientSecret, null) + return Result.success(Unit) } return pusherSubscriber.unregisterPusher(matrixClient, endpoint, gateway) .onSuccess { diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt index 1d04a07651..dfa03707cc 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnregisterUnifiedPushUseCaseTest.kt @@ -64,29 +64,49 @@ class DefaultUnregisterUnifiedPushUseCaseTest { } @Test - fun `test un registration error - no endpoint`() = runTest { + fun `test un registration error - no endpoint - will not unregister but return success`() = runTest { val matrixClient = FakeMatrixClient() + val storeUpEndpointResult = lambdaRecorder { _: String, _: String? -> } + val storePushGatewayResult = lambdaRecorder { _: String, _: String? -> } val useCase = createDefaultUnregisterUnifiedPushUseCase( unifiedPushStore = FakeUnifiedPushStore( getEndpointResult = { null }, getPushGatewayResult = { "aGateway" }, + storeUpEndpointResult = storeUpEndpointResult, + storePushGatewayResult = storePushGatewayResult, ), ) val result = useCase.execute(matrixClient, A_SECRET) - assertThat(result.isFailure).isTrue() + assertThat(result.isSuccess).isTrue() + storeUpEndpointResult.assertions() + .isCalledOnce() + .with(value(A_SECRET), value(null)) + storePushGatewayResult.assertions() + .isCalledOnce() + .with(value(A_SECRET), value(null)) } @Test - fun `test un registration error - no gateway`() = runTest { + fun `test un registration error - no gateway - will not unregister but return success`() = runTest { val matrixClient = FakeMatrixClient() + val storeUpEndpointResult = lambdaRecorder { _: String, _: String? -> } + val storePushGatewayResult = lambdaRecorder { _: String, _: String? -> } val useCase = createDefaultUnregisterUnifiedPushUseCase( unifiedPushStore = FakeUnifiedPushStore( getEndpointResult = { "aEndpoint" }, getPushGatewayResult = { null }, + storeUpEndpointResult = storeUpEndpointResult, + storePushGatewayResult = storePushGatewayResult, ), ) val result = useCase.execute(matrixClient, A_SECRET) - assertThat(result.isFailure).isTrue() + assertThat(result.isSuccess).isTrue() + storeUpEndpointResult.assertions() + .isCalledOnce() + .with(value(A_SECRET), value(null)) + storePushGatewayResult.assertions() + .isCalledOnce() + .with(value(A_SECRET), value(null)) } @Test From 4113bae938408701b0873b1a07da03d77f53b40b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 10:47:31 +0200 Subject: [PATCH 056/252] Add Konsist test `Fake classes must be named using Fake and the interface it fakes` --- .../android/tests/konsist/KonsistClassNameTest.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt index 9ecf90ebe6..092901e1e3 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.bumble.appyx.core.node.Node import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.withAllParentsOf +import com.lemonappdev.konsist.api.ext.list.withNameContaining import com.lemonappdev.konsist.api.verify.assertTrue import io.element.android.libraries.architecture.Presenter import org.junit.Test @@ -62,4 +63,16 @@ class KonsistClassNameTest { it.name.endsWith("Provider") && (it.name.contains("IconList") || it.name.contains(providedType)) } } + + @Test + fun `Fake classes must be named using Fake and the interface it fakes`() { + Konsist.scopeFromProject() + .classes() + .withNameContaining("Fake") + .assertTrue { + val interfaceName = it.name.replace("Fake", "") + it.name.startsWith("Fake") && + it.parents.any { parent -> parent.name.replace(".", "") == interfaceName } + } + } } From d79fcb3aaa7d926ddf1dbb4254c63daabfb8767f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 10:51:48 +0200 Subject: [PATCH 057/252] Rename class (code quality) --- .../state/{FakeWelcomeState.kt => FakeWelcomeScreenState.kt} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/welcome/state/{FakeWelcomeState.kt => FakeWelcomeScreenState.kt} (94%) diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/welcome/state/FakeWelcomeState.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/welcome/state/FakeWelcomeScreenState.kt similarity index 94% rename from features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/welcome/state/FakeWelcomeState.kt rename to features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/welcome/state/FakeWelcomeScreenState.kt index 6b4d4b2287..66ffe24285 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/welcome/state/FakeWelcomeState.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/welcome/state/FakeWelcomeScreenState.kt @@ -16,7 +16,7 @@ package io.element.android.features.ftue.impl.welcome.state -class FakeWelcomeState : WelcomeScreenState { +class FakeWelcomeScreenState : WelcomeScreenState { private var isWelcomeScreenNeeded = true override fun isWelcomeScreenNeeded(): Boolean { From f930796f572d1ebcfcfe24467aaf6915ff69f92f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 10:55:22 +0200 Subject: [PATCH 058/252] Move FakeBugReporterMode to FakeBugReporter.Mode --- .../impl/bugreport/BugReportPresenterTest.kt | 6 +++--- .../impl/bugreport/FakeBugReporter.kt | 20 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt index 69617f30f8..9b07b1c942 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt @@ -144,7 +144,7 @@ class BugReportPresenterTest { @Test fun `present - send success`() = runTest { val presenter = createPresenter( - FakeBugReporter(mode = FakeBugReporterMode.Success), + FakeBugReporter(mode = FakeBugReporter.Mode.Success), FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true), FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), ) @@ -170,7 +170,7 @@ class BugReportPresenterTest { @Test fun `present - send failure`() = runTest { val presenter = createPresenter( - FakeBugReporter(mode = FakeBugReporterMode.Failure), + FakeBugReporter(mode = FakeBugReporter.Mode.Failure), FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true), FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), ) @@ -219,7 +219,7 @@ class BugReportPresenterTest { @Test fun `present - send cancel`() = runTest { val presenter = createPresenter( - FakeBugReporter(mode = FakeBugReporterMode.Cancel), + FakeBugReporter(mode = FakeBugReporter.Mode.Cancel), FakeCrashDataStore(crashData = A_CRASH_DATA, appHasCrashed = true), FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), ) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt index cce2d5d144..a4d4f83da9 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt @@ -22,7 +22,13 @@ import io.element.android.libraries.matrix.test.A_FAILURE_REASON import kotlinx.coroutines.delay import java.io.File -class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Success) : BugReporter { +class FakeBugReporter(val mode: Mode = Mode.Success) : BugReporter { + enum class Mode { + Success, + Failure, + Cancel + } + override suspend fun sendBugReport( withDevicesLogs: Boolean, withCrashLogs: Boolean, @@ -37,12 +43,12 @@ class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Succes listener?.onProgress(50) delay(100) when (mode) { - FakeBugReporterMode.Success -> Unit - FakeBugReporterMode.Failure -> { + Mode.Success -> Unit + Mode.Failure -> { listener?.onUploadFailed(A_FAILURE_REASON) return } - FakeBugReporterMode.Cancel -> { + Mode.Cancel -> { listener?.onUploadCancelled() return } @@ -64,9 +70,3 @@ class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Succes // No op } } - -enum class FakeBugReporterMode { - Success, - Failure, - Cancel -} From c295cae7ff790497f9e5b12db398d4dccb909b8d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 10:56:35 +0200 Subject: [PATCH 059/252] Rename class (code quality) --- ...terFake.kt => FakePermissionsPresenter.kt} | 2 +- .../impl/send/SendLocationPresenterTest.kt | 30 +++++++++---------- .../impl/show/ShowLocationPresenterTest.kt | 26 ++++++++-------- 3 files changed, 29 insertions(+), 29 deletions(-) rename features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/{PermissionsPresenterFake.kt => FakePermissionsPresenter.kt} (95%) diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsPresenterFake.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt similarity index 95% rename from features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsPresenterFake.kt rename to features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt index e79ac9e453..8bbaf5c428 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/PermissionsPresenterFake.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/permissions/FakePermissionsPresenter.kt @@ -18,7 +18,7 @@ package io.element.android.features.location.impl.common.permissions import androidx.compose.runtime.Composable -class PermissionsPresenterFake : PermissionsPresenter { +class FakePermissionsPresenter : PermissionsPresenter { val events = mutableListOf() private fun handleEvent(event: PermissionsEvents) { diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt index b6b469c44a..af4ac6c9c7 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt @@ -24,9 +24,9 @@ import im.vector.app.features.analytics.plan.Composer import io.element.android.features.location.api.Location import io.element.android.features.location.impl.aPermissionsState import io.element.android.features.location.impl.common.actions.FakeLocationActions +import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsEvents import io.element.android.features.location.impl.common.permissions.PermissionsPresenter -import io.element.android.features.location.impl.common.permissions.PermissionsPresenterFake import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.matrix.api.room.location.AssetType @@ -45,7 +45,7 @@ class SendLocationPresenterTest { @get:Rule val warmUpRule = WarmUpRule() - private val permissionsPresenterFake = PermissionsPresenterFake() + private val fakePermissionsPresenter = FakePermissionsPresenter() private val fakeMatrixRoom = FakeMatrixRoom() private val fakeAnalyticsService = FakeAnalyticsService() private val fakeMessageComposerContext = FakeMessageComposerContext() @@ -53,7 +53,7 @@ class SendLocationPresenterTest { private val fakeBuildMeta = aBuildMeta(applicationName = "app name") private val sendLocationPresenter: SendLocationPresenter = SendLocationPresenter( permissionsPresenterFactory = object : PermissionsPresenter.Factory { - override fun create(permissions: List): PermissionsPresenter = permissionsPresenterFake + override fun create(permissions: List): PermissionsPresenter = fakePermissionsPresenter }, room = fakeMatrixRoom, analyticsService = fakeAnalyticsService, @@ -64,7 +64,7 @@ class SendLocationPresenterTest { @Test fun `initial state with permissions granted`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.AllGranted, shouldShowRationale = false, @@ -90,7 +90,7 @@ class SendLocationPresenterTest { @Test fun `initial state with permissions partially granted`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.SomeGranted, shouldShowRationale = false, @@ -116,7 +116,7 @@ class SendLocationPresenterTest { @Test fun `initial state with permissions denied`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = false, @@ -142,7 +142,7 @@ class SendLocationPresenterTest { @Test fun `initial state with permissions denied once`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = true, @@ -168,7 +168,7 @@ class SendLocationPresenterTest { @Test fun `rationale dialog dismiss`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = true, @@ -199,7 +199,7 @@ class SendLocationPresenterTest { @Test fun `rationale dialog continue`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = true, @@ -221,13 +221,13 @@ class SendLocationPresenterTest { // Continue the dialog sends permission request to the permissions presenter myLocationState.eventSink(SendLocationEvents.RequestPermissions) - assertThat(permissionsPresenterFake.events.last()).isEqualTo(PermissionsEvents.RequestPermissions) + assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvents.RequestPermissions) } } @Test fun `permission denied dialog dismiss`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = false, @@ -258,7 +258,7 @@ class SendLocationPresenterTest { @Test fun `share sender location`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.AllGranted, shouldShowRationale = false, @@ -314,7 +314,7 @@ class SendLocationPresenterTest { @Test fun `share pin location`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = false, @@ -370,7 +370,7 @@ class SendLocationPresenterTest { @Test fun `composer context passes through analytics`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = false, @@ -418,7 +418,7 @@ class SendLocationPresenterTest { @Test fun `open settings activity`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = false, diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index ff80a3935d..dab964b6e1 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -23,9 +23,9 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.location.api.Location import io.element.android.features.location.impl.aPermissionsState import io.element.android.features.location.impl.common.actions.FakeLocationActions +import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsEvents import io.element.android.features.location.impl.common.permissions.PermissionsPresenter -import io.element.android.features.location.impl.common.permissions.PermissionsPresenterFake import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.tests.testutils.WarmUpRule @@ -38,13 +38,13 @@ class ShowLocationPresenterTest { @get:Rule val warmUpRule = WarmUpRule() - private val permissionsPresenterFake = PermissionsPresenterFake() + private val fakePermissionsPresenter = FakePermissionsPresenter() private val fakeLocationActions = FakeLocationActions() private val fakeBuildMeta = aBuildMeta(applicationName = "app name") private val location = Location(1.23, 4.56, 7.8f) private val presenter = ShowLocationPresenter( permissionsPresenterFactory = object : PermissionsPresenter.Factory { - override fun create(permissions: List): PermissionsPresenter = permissionsPresenterFake + override fun create(permissions: List): PermissionsPresenter = fakePermissionsPresenter }, fakeLocationActions, fakeBuildMeta, @@ -54,7 +54,7 @@ class ShowLocationPresenterTest { @Test fun `emits initial state with no location permission`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = false, @@ -74,7 +74,7 @@ class ShowLocationPresenterTest { @Test fun `emits initial state location permission denied once`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = true, @@ -94,7 +94,7 @@ class ShowLocationPresenterTest { @Test fun `emits initial state with location permission`() = runTest { - permissionsPresenterFake.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted)) + fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted)) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -109,7 +109,7 @@ class ShowLocationPresenterTest { @Test fun `emits initial state with partial location permission`() = runTest { - permissionsPresenterFake.givenState(aPermissionsState(permissions = PermissionsState.Permissions.SomeGranted)) + fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.SomeGranted)) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -137,7 +137,7 @@ class ShowLocationPresenterTest { @Test fun `centers on user location`() = runTest { - permissionsPresenterFake.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted)) + fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted)) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -165,7 +165,7 @@ class ShowLocationPresenterTest { @Test fun `rationale dialog dismiss`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = true, @@ -196,7 +196,7 @@ class ShowLocationPresenterTest { @Test fun `rationale dialog continue`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = true, @@ -218,13 +218,13 @@ class ShowLocationPresenterTest { // Continue the dialog sends permission request to the permissions presenter trackLocationState.eventSink(ShowLocationEvents.RequestPermissions) - assertThat(permissionsPresenterFake.events.last()).isEqualTo(PermissionsEvents.RequestPermissions) + assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvents.RequestPermissions) } } @Test fun `permission denied dialog dismiss`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = false, @@ -255,7 +255,7 @@ class ShowLocationPresenterTest { @Test fun `open settings activity`() = runTest { - permissionsPresenterFake.givenState( + fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, shouldShowRationale = false, From 7b372e24b4b669cb314c299f1694783f0b8124f2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 10:57:16 +0200 Subject: [PATCH 060/252] Rename class (code quality) --- .../features/migration/impl/MigrationPresenterTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/MigrationPresenterTest.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/MigrationPresenterTest.kt index 3ea0625f76..29be8682e3 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/MigrationPresenterTest.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/MigrationPresenterTest.kt @@ -37,7 +37,7 @@ class MigrationPresenterTest { @Test fun `present - no migration should occurs if ApplicationMigrationVersion is the last one`() = runTest { - val migrations = (1..10).map { FakeMigration(it) } + val migrations = (1..10).map { FakeAppMigration(it) } val store = InMemoryMigrationStore(migrations.maxOf { it.order }) val presenter = createPresenter( migrationStore = store, @@ -57,7 +57,7 @@ class MigrationPresenterTest { @Test fun `present - testing all migrations`() = runTest { val store = InMemoryMigrationStore(0) - val migrations = (1..10).map { FakeMigration(it) } + val migrations = (1..10).map { FakeAppMigration(it) } val presenter = createPresenter( migrationStore = store, migrations = migrations.toSet(), @@ -81,13 +81,13 @@ class MigrationPresenterTest { private fun createPresenter( migrationStore: MigrationStore = InMemoryMigrationStore(0), - migrations: Set = setOf(FakeMigration(1)), + migrations: Set = setOf(FakeAppMigration(1)), ) = MigrationPresenter( migrationStore = migrationStore, migrations = migrations, ) -private class FakeMigration( +private class FakeAppMigration( override val order: Int, var migrateLambda: LambdaNoParamRecorder = lambdaRecorder { -> }, ) : AppMigration { From 8215661ada89f67194f285f41c3d763a8a843648 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 10:57:53 +0200 Subject: [PATCH 061/252] Rename class (code quality) --- .../features/migration/impl/migrations/AppMigration02Test.kt | 4 ++-- ...eStoreFactory.kt => FakeSessionPreferencesStoreFactory.kt} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/{FakeSessionPreferenceStoreFactory.kt => FakeSessionPreferencesStoreFactory.kt} (97%) diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02Test.kt index 1a077fda2e..6bb2f5babd 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02Test.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration02Test.kt @@ -17,7 +17,7 @@ package io.element.android.features.migration.impl.migrations import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.preferences.test.FakeSessionPreferenceStoreFactory +import io.element.android.libraries.preferences.test.FakeSessionPreferencesStoreFactory import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.aSessionData @@ -33,7 +33,7 @@ class AppMigration02Test { updateData(aSessionData()) } val sessionPreferencesStore = InMemorySessionPreferencesStore(isSessionVerificationSkipped = false) - val sessionPreferencesStoreFactory = FakeSessionPreferenceStoreFactory( + val sessionPreferencesStoreFactory = FakeSessionPreferencesStoreFactory( getLambda = lambdaRecorder { _, _, -> sessionPreferencesStore }, ) val migration = AppMigration02(sessionStore = sessionStore, sessionPreferenceStoreFactory = sessionPreferencesStoreFactory) diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/FakeSessionPreferenceStoreFactory.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/FakeSessionPreferencesStoreFactory.kt similarity index 97% rename from libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/FakeSessionPreferenceStoreFactory.kt rename to libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/FakeSessionPreferencesStoreFactory.kt index 264ac4ec3a..96761a9712 100644 --- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/FakeSessionPreferenceStoreFactory.kt +++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/FakeSessionPreferencesStoreFactory.kt @@ -24,7 +24,7 @@ import io.element.android.tests.testutils.lambda.LambdaTwoParamsRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder import kotlinx.coroutines.CoroutineScope -class FakeSessionPreferenceStoreFactory( +class FakeSessionPreferencesStoreFactory( var getLambda: LambdaTwoParamsRecorder = lambdaRecorder { _, _ -> throw NotImplementedError() }, var removeLambda: LambdaOneParamRecorder = lambdaRecorder { _ -> }, ) : SessionPreferencesStoreFactory { From 8e864adf728ff2065b1b5055431aae5417f2ba65 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 10:59:54 +0200 Subject: [PATCH 062/252] Exclude FakeImageLoader from this test. --- .../io/element/android/tests/konsist/KonsistClassNameTest.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt index 092901e1e3..af7eb44618 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt @@ -21,6 +21,7 @@ import com.bumble.appyx.core.node.Node import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.withAllParentsOf import com.lemonappdev.konsist.api.ext.list.withNameContaining +import com.lemonappdev.konsist.api.ext.list.withoutName import com.lemonappdev.konsist.api.verify.assertTrue import io.element.android.libraries.architecture.Presenter import org.junit.Test @@ -69,6 +70,9 @@ class KonsistClassNameTest { Konsist.scopeFromProject() .classes() .withNameContaining("Fake") + .withoutName( + "FakeImageLoader", + ) .assertTrue { val interfaceName = it.name.replace("Fake", "") it.name.startsWith("Fake") && From 6e4c9d3171a870191432d5461037faa5c4ca20f5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 May 2024 11:00:54 +0200 Subject: [PATCH 063/252] Rename class (code quality) --- .../libraries/voicerecorder/impl/VoiceRecorderImplTest.kt | 4 ++-- ...{FakeAudioRecorderFactory.kt => FakeAudioReaderFactory.kt} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/{FakeAudioRecorderFactory.kt => FakeAudioReaderFactory.kt} (97%) diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt index 9bb1ace7b1..022661bc11 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt @@ -28,7 +28,7 @@ import io.element.android.libraries.voicerecorder.impl.audio.AudioConfig import io.element.android.libraries.voicerecorder.impl.audio.SampleRate import io.element.android.libraries.voicerecorder.impl.di.VoiceRecorderModule import io.element.android.libraries.voicerecorder.test.FakeAudioLevelCalculator -import io.element.android.libraries.voicerecorder.test.FakeAudioRecorderFactory +import io.element.android.libraries.voicerecorder.test.FakeAudioReaderFactory import io.element.android.libraries.voicerecorder.test.FakeEncoder import io.element.android.libraries.voicerecorder.test.FakeFileSystem import io.element.android.libraries.voicerecorder.test.FakeVoiceFileManager @@ -136,7 +136,7 @@ class VoiceRecorderImplTest { return VoiceRecorderImpl( dispatchers = testCoroutineDispatchers(), timeSource = timeSource, - audioReaderFactory = FakeAudioRecorderFactory( + audioReaderFactory = FakeAudioReaderFactory( audio = AUDIO, ), encoder = FakeEncoder(fakeFileSystem), diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioRecorderFactory.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReaderFactory.kt similarity index 97% rename from libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioRecorderFactory.kt rename to libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReaderFactory.kt index 02d8b4742c..657b8d6ee9 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioRecorderFactory.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReaderFactory.kt @@ -21,7 +21,7 @@ import io.element.android.libraries.voicerecorder.impl.audio.Audio import io.element.android.libraries.voicerecorder.impl.audio.AudioConfig import io.element.android.libraries.voicerecorder.impl.audio.AudioReader -class FakeAudioRecorderFactory( +class FakeAudioReaderFactory( private val audio: List