Add a way to override default homeserver.

This commit is contained in:
Benoit Marty 2025-02-20 17:10:43 +01:00 committed by Benoit Marty
parent e5ed0a0641
commit 0a5784992f
14 changed files with 145 additions and 121 deletions

View file

@ -7,7 +7,8 @@
package io.element.android.features.login.impl.accountprovider
import io.element.android.features.login.impl.util.defaultAccountProvider
import io.element.android.appconfig.AuthenticationConfig
import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn
import kotlinx.coroutines.flow.MutableStateFlow
@ -16,7 +17,18 @@ import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
@SingleIn(AppScope::class)
class AccountProviderDataSource @Inject constructor() {
class AccountProviderDataSource @Inject constructor(
enterpriseService: EnterpriseService,
) {
private val defaultAccountProvider = (enterpriseService.defaultHomeserver() ?: AuthenticationConfig.MATRIX_ORG_URL).let { url ->
AccountProvider(
url = url,
subtitle = null,
isPublic = url == AuthenticationConfig.MATRIX_ORG_URL,
isMatrixOrg = url == AuthenticationConfig.MATRIX_ORG_URL,
)
}
private val accountProvider: MutableStateFlow<AccountProvider> = MutableStateFlow(
defaultAccountProvider
)

View file

@ -1,18 +0,0 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.login.impl.util
import io.element.android.appconfig.AuthenticationConfig
import io.element.android.features.login.impl.accountprovider.AccountProvider
val defaultAccountProvider = AccountProvider(
url = AuthenticationConfig.DEFAULT_HOMESERVER_URL,
subtitle = null,
isPublic = AuthenticationConfig.DEFAULT_HOMESERVER_URL == AuthenticationConfig.MATRIX_ORG_URL,
isMatrixOrg = AuthenticationConfig.DEFAULT_HOMESERVER_URL == AuthenticationConfig.MATRIX_ORG_URL,
)

View file

@ -7,10 +7,8 @@
package io.element.android.features.login.impl.changeserver
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.login.impl.accountprovider.AccountProvider
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
import io.element.android.libraries.architecture.AsyncData
@ -18,6 +16,7 @@ import io.element.android.libraries.matrix.test.A_HOMESERVER
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.test
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
@ -28,13 +27,7 @@ class ChangeServerPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = ChangeServerPresenter(
FakeMatrixAuthenticationService(),
AccountProviderDataSource()
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
createPresenter().test {
val initialState = awaitItem()
assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized)
}
@ -43,13 +36,9 @@ class ChangeServerPresenterTest {
@Test
fun `present - change server ok`() = runTest {
val authenticationService = FakeMatrixAuthenticationService()
val presenter = ChangeServerPresenter(
authenticationService,
AccountProviderDataSource()
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
createPresenter(
authenticationService = authenticationService,
).test {
val initialState = awaitItem()
assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized)
authenticationService.givenHomeserver(A_HOMESERVER)
@ -63,14 +52,7 @@ class ChangeServerPresenterTest {
@Test
fun `present - change server error`() = runTest {
val authenticationService = FakeMatrixAuthenticationService()
val presenter = ChangeServerPresenter(
authenticationService,
AccountProviderDataSource()
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
createPresenter().test {
val initialState = awaitItem()
assertThat(initialState.changeServerAction).isEqualTo(AsyncData.Uninitialized)
initialState.eventSink.invoke(ChangeServerEvents.ChangeServer(AccountProvider(url = A_HOMESERVER_URL)))
@ -84,4 +66,12 @@ class ChangeServerPresenterTest {
assertThat(finalState.changeServerAction).isEqualTo(AsyncData.Uninitialized)
}
}
private fun createPresenter(
authenticationService: FakeMatrixAuthenticationService = FakeMatrixAuthenticationService(),
accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()),
) = ChangeServerPresenter(
authenticationService = authenticationService,
accountProviderDataSource = accountProviderDataSource
)
}

View file

@ -11,10 +11,10 @@ import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.login.impl.DefaultLoginUserStory
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
import io.element.android.features.login.impl.screens.createaccount.AccountCreationNotSupported
import io.element.android.features.login.impl.util.defaultAccountProvider
import io.element.android.features.login.impl.web.FakeWebClientUrlForAuthenticationRetriever
import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationRetriever
import io.element.android.libraries.architecture.AsyncData
@ -44,7 +44,7 @@ class ConfirmAccountProviderPresenterTest {
val initialState = awaitItem()
assertThat(initialState.isAccountCreation).isFalse()
assertThat(initialState.submitEnabled).isTrue()
assertThat(initialState.accountProvider).isEqualTo(defaultAccountProvider)
assertThat(initialState.accountProvider.url).isEqualTo(FakeEnterpriseService.A_FAKE_HOMESERVER)
assertThat(initialState.loginFlow).isEqualTo(AsyncData.Uninitialized)
}
}
@ -350,7 +350,7 @@ class ConfirmAccountProviderPresenterTest {
private fun createConfirmAccountProviderPresenter(
params: ConfirmAccountProviderPresenter.Params = ConfirmAccountProviderPresenter.Params(isAccountCreation = false),
accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(),
accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()),
matrixAuthenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(),
defaultOidcActionFlow: DefaultOidcActionFlow = DefaultOidcActionFlow(),
defaultLoginUserStory: DefaultLoginUserStory = DefaultLoginUserStory(),

View file

@ -8,8 +8,8 @@
package io.element.android.features.login.impl.screens.createaccount
import com.google.common.truth.Truth.assertThat
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
import io.element.android.features.login.impl.util.defaultAccountProvider
import io.element.android.libraries.matrix.api.auth.external.ExternalSession
import kotlinx.serialization.SerializationException
import org.junit.Assert.assertThrows
@ -27,9 +27,7 @@ class DefaultMessageParserTest {
@Test
fun `DefaultMessageParser is able to parse correct message`() {
val sut = DefaultMessageParser(
AccountProviderDataSource()
)
val sut = createDefaultMessageParser()
assertThat(sut.parse(validMessage)).isEqualTo(
anExternalSession(
homeserverUrl = "home_server",
@ -39,9 +37,7 @@ class DefaultMessageParserTest {
@Test
fun `DefaultMessageParser should throw Exception in case of error`() {
val sut = DefaultMessageParser(
AccountProviderDataSource()
)
val sut = createDefaultMessageParser()
// kotlinx.serialization.json.internal.JsonDecodingException
assertThrows(SerializationException::class.java) { sut.parse("invalid json") }
// missing userId
@ -60,16 +56,20 @@ class DefaultMessageParserTest {
@Test
fun `DefaultMessageParser should be successful even is homeserver url is missing`() {
val sut = DefaultMessageParser(
AccountProviderDataSource()
)
val sut = createDefaultMessageParser()
// missing homeServer
assertThat(sut.parse(validMessage.replace(""""home_server": "home_server",""", ""))).isEqualTo(
anExternalSession(
homeserverUrl = defaultAccountProvider.url,
homeserverUrl = FakeEnterpriseService.A_FAKE_HOMESERVER,
)
)
}
private fun createDefaultMessageParser(): DefaultMessageParser {
return DefaultMessageParser(
AccountProviderDataSource(FakeEnterpriseService())
)
}
}
internal fun anExternalSession(

View file

@ -7,13 +7,10 @@
package io.element.android.features.login.impl.screens.loginpassword
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.login.impl.DefaultLoginUserStory
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
import io.element.android.features.login.impl.util.defaultAccountProvider
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.test.A_HOMESERVER
@ -23,6 +20,7 @@ import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.test
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
@ -33,19 +31,9 @@ class LoginPasswordPresenterTest {
@Test
fun `present - initial state`() = runTest {
val authenticationService = FakeMatrixAuthenticationService()
val accountProviderDataSource = AccountProviderDataSource()
val loginUserStory = DefaultLoginUserStory()
val presenter = LoginPasswordPresenter(
authenticationService,
accountProviderDataSource,
loginUserStory,
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
createLoginPasswordPresenter().test {
val initialState = awaitItem()
assertThat(initialState.accountProvider).isEqualTo(defaultAccountProvider)
assertThat(initialState.accountProvider.url).isEqualTo(FakeEnterpriseService.A_FAKE_HOMESERVER)
assertThat(initialState.formState).isEqualTo(LoginFormState.Default)
assertThat(initialState.loginAction).isEqualTo(AsyncData.Uninitialized)
assertThat(initialState.submitEnabled).isFalse()
@ -55,17 +43,10 @@ class LoginPasswordPresenterTest {
@Test
fun `present - enter login and password`() = runTest {
val authenticationService = FakeMatrixAuthenticationService()
val accountProviderDataSource = AccountProviderDataSource()
val loginUserStory = DefaultLoginUserStory()
val presenter = LoginPasswordPresenter(
authenticationService,
accountProviderDataSource,
loginUserStory,
)
authenticationService.givenHomeserver(A_HOMESERVER)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
createLoginPasswordPresenter(
authenticationService = authenticationService,
).test {
val initialState = awaitItem()
initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME))
val loginState = awaitItem()
@ -81,17 +62,12 @@ class LoginPasswordPresenterTest {
@Test
fun `present - submit`() = runTest {
val authenticationService = FakeMatrixAuthenticationService()
val accountProviderDataSource = AccountProviderDataSource()
val loginUserStory = DefaultLoginUserStory().apply { setLoginFlowIsDone(false) }
val presenter = LoginPasswordPresenter(
authenticationService,
accountProviderDataSource,
loginUserStory,
)
authenticationService.givenHomeserver(A_HOMESERVER)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val loginUserStory = DefaultLoginUserStory().apply { setLoginFlowIsDone(false) }
createLoginPasswordPresenter(
authenticationService = authenticationService,
defaultLoginUserStory = loginUserStory,
).test {
assertThat(loginUserStory.loginFlowIsDone.value).isFalse()
val initialState = awaitItem()
initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME))
@ -110,17 +86,10 @@ class LoginPasswordPresenterTest {
@Test
fun `present - submit with error`() = runTest {
val authenticationService = FakeMatrixAuthenticationService()
val accountProviderDataSource = AccountProviderDataSource()
val loginUserStory = DefaultLoginUserStory()
val presenter = LoginPasswordPresenter(
authenticationService,
accountProviderDataSource,
loginUserStory,
)
authenticationService.givenHomeserver(A_HOMESERVER)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
createLoginPasswordPresenter(
authenticationService = authenticationService,
).test {
val initialState = awaitItem()
initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME))
initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD))
@ -138,17 +107,10 @@ class LoginPasswordPresenterTest {
@Test
fun `present - clear error`() = runTest {
val authenticationService = FakeMatrixAuthenticationService()
val accountProviderDataSource = AccountProviderDataSource()
val loginUserStory = DefaultLoginUserStory()
val presenter = LoginPasswordPresenter(
authenticationService,
accountProviderDataSource,
loginUserStory,
)
authenticationService.givenHomeserver(A_HOMESERVER)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
createLoginPasswordPresenter(
authenticationService = authenticationService,
).test {
val initialState = awaitItem()
initialState.eventSink.invoke(LoginPasswordEvents.SetLogin(A_USER_NAME))
initialState.eventSink.invoke(LoginPasswordEvents.SetPassword(A_PASSWORD))
@ -167,4 +129,14 @@ class LoginPasswordPresenterTest {
assertThat(clearedState.loginAction).isEqualTo(AsyncData.Uninitialized)
}
}
private fun createLoginPasswordPresenter(
authenticationService: FakeMatrixAuthenticationService = FakeMatrixAuthenticationService(),
accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()),
defaultLoginUserStory: DefaultLoginUserStory = DefaultLoginUserStory()
): LoginPasswordPresenter = LoginPasswordPresenter(
authenticationService = authenticationService,
accountProviderDataSource = accountProviderDataSource,
defaultLoginUserStory = defaultLoginUserStory,
)
}