Browse Source

Always use the custom url, even if no Matrix gateway is detected.

pull/3388/head
Benoit Marty 2 weeks ago
parent
commit
a81448c243
  1. 6
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt
  2. 7
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/DefaultTestPushTest.kt
  3. 27
      libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/Fixtures.kt
  4. 1
      libraries/pushproviders/unifiedpush/build.gradle.kts
  5. 4
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushConfig.kt
  6. 22
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt
  7. 3
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/network/UnifiedPushApi.kt
  8. 90
      libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushMatrixGatewayTest.kt
  9. 10
      libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayResolverTest.kt
  10. 126
      libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushMatrixGatewayTestTest.kt

6
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt

@ -29,6 +29,7 @@ import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider import io.element.android.libraries.pushproviders.api.PushProvider
import io.element.android.libraries.pushproviders.test.FakePushProvider import io.element.android.libraries.pushproviders.test.FakePushProvider
import io.element.android.libraries.pushproviders.test.aCurrentUserPushConfig
import io.element.android.libraries.pushstore.api.UserPushStoreFactory 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.FakeUserPushStore
import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory
@ -57,10 +58,7 @@ class DefaultPushServiceTest {
@Test @Test
fun `test push ok`() = runTest { fun `test push ok`() = runTest {
val aConfig = CurrentUserPushConfig( val aConfig = aCurrentUserPushConfig()
url = "aUrl",
pushKey = "aPushKey",
)
val testPushResult = lambdaRecorder<CurrentUserPushConfig, Unit> { } val testPushResult = lambdaRecorder<CurrentUserPushConfig, Unit> { }
val aPushProvider = FakePushProvider( val aPushProvider = FakePushProvider(
currentUserPushConfig = aConfig currentUserPushConfig = aConfig

7
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/test/DefaultTestPushTest.kt

@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.test
import io.element.android.appconfig.PushConfig import io.element.android.appconfig.PushConfig
import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig import io.element.android.libraries.pushproviders.test.aCurrentUserPushConfig
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.lambda.value
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@ -33,10 +33,7 @@ class DefaultTestPushTest {
executeResult = executeResult, executeResult = executeResult,
) )
) )
val aConfig = CurrentUserPushConfig( val aConfig = aCurrentUserPushConfig()
url = "aUrl",
pushKey = "aPushKey",
)
defaultTestPush.execute(aConfig) defaultTestPush.execute(aConfig)
executeResult.assertions() executeResult.assertions()
.isCalledOnce() .isCalledOnce()

27
libraries/pushproviders/test/src/main/kotlin/io/element/android/libraries/pushproviders/test/Fixtures.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
*
* https://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.test
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
fun aCurrentUserPushConfig(
url: String = "aUrl",
pushKey: String = "aPushKey",
) = CurrentUserPushConfig(
url = url,
pushKey = pushKey,
)

1
libraries/pushproviders/unifiedpush/build.gradle.kts

@ -60,6 +60,7 @@ dependencies {
testImplementation(libs.test.turbine) testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.push.test)
testImplementation(projects.libraries.pushproviders.test)
testImplementation(projects.libraries.pushstore.test) testImplementation(projects.libraries.pushstore.test)
testImplementation(projects.tests.testutils) testImplementation(projects.tests.testutils)
testImplementation(projects.services.toolbox.test) testImplementation(projects.services.toolbox.test)

4
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushConfig.kt

@ -17,11 +17,13 @@
package io.element.android.libraries.pushproviders.unifiedpush package io.element.android.libraries.pushproviders.unifiedpush
object UnifiedPushConfig { object UnifiedPushConfig {
const val PUSH_GATEWAY_PATH: String = "_matrix/push/v1/notify"
/** /**
* It is the push gateway for UnifiedPush. * It is the push gateway for UnifiedPush.
* Note: default_push_gateway_http_url should have path '/_matrix/push/v1/notify' * Note: default_push_gateway_http_url should have path '/_matrix/push/v1/notify'
*/ */
const val DEFAULT_PUSH_GATEWAY_HTTP_URL: String = "https://matrix.gateway.unifiedpush.org/_matrix/push/v1/notify" const val DEFAULT_PUSH_GATEWAY_HTTP_URL: String = "https://matrix.gateway.unifiedpush.org/$PUSH_GATEWAY_PATH"
const val UNIFIED_PUSH_DISTRIBUTORS_URL = "https://unifiedpush.org/users/distributors/" const val UNIFIED_PUSH_DISTRIBUTORS_URL = "https://unifiedpush.org/users/distributors/"

22
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayResolver.kt

@ -18,6 +18,7 @@ package io.element.android.libraries.pushproviders.unifiedpush
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
@ -34,12 +35,18 @@ class DefaultUnifiedPushGatewayResolver @Inject constructor(
private val coroutineDispatchers: CoroutineDispatchers, private val coroutineDispatchers: CoroutineDispatchers,
) : UnifiedPushGatewayResolver { ) : UnifiedPushGatewayResolver {
override suspend fun getGateway(endpoint: String): String { override suspend fun getGateway(endpoint: String): String {
val gateway = UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL val url = tryOrNull(
try { onError = { Timber.d(it, "Cannot parse endpoint as an URL") }
val url = URL(endpoint) ) {
URL(endpoint)
}
return if (url == null) {
Timber.d("Using default gateway")
UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL
} else {
val port = if (url.port != -1) ":${url.port}" else "" val port = if (url.port != -1) ":${url.port}" else ""
val customBase = "${url.protocol}://${url.host}$port" val customBase = "${url.protocol}://${url.host}$port"
val customUrl = "$customBase/_matrix/push/v1/notify" val customUrl = "$customBase/${UnifiedPushConfig.PUSH_GATEWAY_PATH}"
Timber.i("Testing $customUrl") Timber.i("Testing $customUrl")
return withContext(coroutineDispatchers.io) { return withContext(coroutineDispatchers.io) {
val api = unifiedPushApiFactory.create(customBase) val api = unifiedPushApiFactory.create(customBase)
@ -47,16 +54,13 @@ class DefaultUnifiedPushGatewayResolver @Inject constructor(
val discoveryResponse = api.discover() val discoveryResponse = api.discover()
if (discoveryResponse.unifiedpush.gateway == "matrix") { if (discoveryResponse.unifiedpush.gateway == "matrix") {
Timber.d("Using custom gateway") Timber.d("Using custom gateway")
return@withContext customUrl
} }
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
Timber.tag("UnifiedPushHelper").e(throwable) Timber.tag("UnifiedPushHelper").e(throwable)
} }
return@withContext gateway // Always return the custom url.
customUrl
} }
} catch (e: Throwable) {
Timber.d(e, "Cannot try custom gateway")
} }
return gateway
} }
} }

3
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/network/UnifiedPushApi.kt

@ -16,9 +16,10 @@
package io.element.android.libraries.pushproviders.unifiedpush.network package io.element.android.libraries.pushproviders.unifiedpush.network
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig
import retrofit2.http.GET import retrofit2.http.GET
interface UnifiedPushApi { interface UnifiedPushApi {
@GET("_matrix/push/v1/notify") @GET(UnifiedPushConfig.PUSH_GATEWAY_PATH)
suspend fun discover(): DiscoveryResponse suspend fun discover(): DiscoveryResponse
} }

90
libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushMatrixGatewayTest.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.unifiedpush.troubleshoot
import com.squareup.anvil.annotations.ContributesMultibinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.pushproviders.api.PushProvider
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushApiFactory
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
import io.element.android.libraries.troubleshoot.api.test.TestFilterData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
@ContributesMultibinding(AppScope::class)
class UnifiedPushMatrixGatewayTest @Inject constructor(
private val unifiedPushApiFactory: UnifiedPushApiFactory,
private val coroutineDispatchers: CoroutineDispatchers,
private val pushProvider: PushProvider,
) : NotificationTroubleshootTest {
override val order = 450
private val delegate = NotificationTroubleshootTestDelegate(
defaultName = "Test push gateway",
defaultDescription = "Ensure that the push gateway is valid.",
visibleWhenIdle = false,
fakeDelay = NotificationTroubleshootTestDelegate.SHORT_DELAY,
)
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
override fun isRelevant(data: TestFilterData): Boolean {
return data.currentPushProviderName == UnifiedPushConfig.NAME
}
override suspend fun run(coroutineScope: CoroutineScope) {
delegate.start()
val config = pushProvider.getCurrentUserPushConfig()
if (config == null) {
delegate.updateState(
description = "No current push provider",
status = NotificationTroubleshootTestState.Status.Failure(false)
)
} else {
val gatewayBaseUrl = config.url.removeSuffix("/${UnifiedPushConfig.PUSH_GATEWAY_PATH}")
// Checking if the gateway is a Matrix gateway
coroutineScope.launch(coroutineDispatchers.io) {
val api = unifiedPushApiFactory.create(gatewayBaseUrl)
try {
val discoveryResponse = api.discover()
if (discoveryResponse.unifiedpush.gateway == "matrix") {
delegate.updateState(
description = "${config.url} is a Matrix gateway.",
status = NotificationTroubleshootTestState.Status.Success
)
} else {
delegate.updateState(
description = "${config.url} is not a Matrix gateway.",
status = NotificationTroubleshootTestState.Status.Failure(false)
)
}
} catch (throwable: Throwable) {
delegate.updateState(
description = "Fail to check the gateway ${config.url}: ${throwable.localizedMessage}",
status = NotificationTroubleshootTestState.Status.Failure(false)
)
}
}
}
}
override suspend fun reset() = delegate.reset()
}

10
libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayResolverTest.kt

@ -25,23 +25,23 @@ import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
class DefaultUnifiedPushGatewayResolverTest { internal val matrixDiscoveryResponse = {
private val matrixDiscoveryResponse = {
DiscoveryResponse( DiscoveryResponse(
unifiedpush = DiscoveryUnifiedPush( unifiedpush = DiscoveryUnifiedPush(
gateway = "matrix" gateway = "matrix"
) )
) )
} }
private val invalidDiscoveryResponse = { internal val invalidDiscoveryResponse = {
DiscoveryResponse( DiscoveryResponse(
unifiedpush = DiscoveryUnifiedPush( unifiedpush = DiscoveryUnifiedPush(
gateway = "" gateway = ""
) )
) )
} }
class DefaultUnifiedPushGatewayResolverTest {
@Test @Test
fun `when a custom url provide a correct matrix gateway, the custom url is returned`() = runTest { fun `when a custom url provide a correct matrix gateway, the custom url is returned`() = runTest {
val unifiedPushApiFactory = FakeUnifiedPushApiFactory( val unifiedPushApiFactory = FakeUnifiedPushApiFactory(

126
libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/troubleshoot/UnifiedPushMatrixGatewayTestTest.kt

@ -0,0 +1,126 @@
/*
* 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.troubleshoot
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
import io.element.android.libraries.pushproviders.test.FakePushProvider
import io.element.android.libraries.pushproviders.test.aCurrentUserPushConfig
import io.element.android.libraries.pushproviders.unifiedpush.FakeUnifiedPushApiFactory
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig
import io.element.android.libraries.pushproviders.unifiedpush.invalidDiscoveryResponse
import io.element.android.libraries.pushproviders.unifiedpush.matrixDiscoveryResponse
import io.element.android.libraries.pushproviders.unifiedpush.network.DiscoveryResponse
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
import io.element.android.libraries.troubleshoot.api.test.TestFilterData
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
class UnifiedPushMatrixGatewayTestTest {
@Test
fun `test UnifiedPushMatrixGatewayTest success`() = runTest {
val sut = createUnifiedPushMatrixGatewayTest(
currentUserPushConfig = aCurrentUserPushConfig(),
discoveryResponse = matrixDiscoveryResponse,
)
launch {
sut.run(this)
}
sut.state.test {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
val lastItem = awaitItem()
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
}
}
@Test
fun `test UnifiedPushMatrixGatewayTest no config found`() = runTest {
val sut = createUnifiedPushMatrixGatewayTest(
currentUserPushConfig = null,
discoveryResponse = matrixDiscoveryResponse,
)
launch {
sut.run(this)
}
sut.state.test {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
val lastItem = awaitItem()
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false))
}
}
@Test
fun `test UnifiedPushMatrixGatewayTest not valid gateway`() = runTest {
val sut = createUnifiedPushMatrixGatewayTest(
currentUserPushConfig = aCurrentUserPushConfig(),
discoveryResponse = invalidDiscoveryResponse,
)
launch {
sut.run(this)
}
sut.state.test {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
val lastItem = awaitItem()
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false))
// Reset the error
sut.reset()
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
}
}
@Test
fun `test UnifiedPushMatrixGatewayTest network error`() = runTest {
val sut = createUnifiedPushMatrixGatewayTest(
currentUserPushConfig = aCurrentUserPushConfig(),
discoveryResponse = { throw RuntimeException("Network error") },
)
launch {
sut.run(this)
}
sut.state.test {
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(false))
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
val lastItem = awaitItem()
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false))
}
}
@Test
fun `test isRelevant`() = runTest {
val sut = createUnifiedPushMatrixGatewayTest()
assertThat(sut.isRelevant(TestFilterData(currentPushProviderName = UnifiedPushConfig.NAME))).isTrue()
assertThat(sut.isRelevant(TestFilterData(currentPushProviderName = "other"))).isFalse()
}
private fun TestScope.createUnifiedPushMatrixGatewayTest(
currentUserPushConfig: CurrentUserPushConfig? = null,
discoveryResponse: () -> DiscoveryResponse = matrixDiscoveryResponse,
): UnifiedPushMatrixGatewayTest {
return UnifiedPushMatrixGatewayTest(
unifiedPushApiFactory = FakeUnifiedPushApiFactory(discoveryResponse),
coroutineDispatchers = testCoroutineDispatchers(),
pushProvider = FakePushProvider(currentUserPushConfig = currentUserPushConfig),
)
}
}
Loading…
Cancel
Save