mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 21:32:34 +00:00
Add some missing tests to improve coverage
This commit is contained in:
committed by
Jorge Martin Espinosa
parent
4f5855bfa5
commit
6c7f4d3b24
+84
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
|
import io.element.android.libraries.matrix.api.scanner.ContentScannerUrlProvider
|
||||||
|
import io.element.android.libraries.matrix.impl.auth.FakeProxyProvider
|
||||||
|
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
|
||||||
|
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory
|
||||||
|
import io.element.android.libraries.matrix.impl.storage.FakeSqliteStoreBuilderProvider
|
||||||
|
import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
|
||||||
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
|
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
||||||
|
import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler
|
||||||
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
|
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
||||||
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class RustTemporaryMatrixClientFactoryTest {
|
||||||
|
@Test
|
||||||
|
fun `create returns a TemporaryMatrixClient`() = runTest {
|
||||||
|
val sut = createRustTemporaryMatrixClientFactory()
|
||||||
|
val result = sut.create("https://matrix.org")
|
||||||
|
assertThat(result.isSuccess).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `create can fail gracefully`() = runTest {
|
||||||
|
val sut = createRustTemporaryMatrixClientFactory(
|
||||||
|
rustMatrixClientFactory = createRustMatrixClientFactory(
|
||||||
|
clientBuilderProvider = FakeClientBuilderProvider(
|
||||||
|
provideResult = { throw IllegalStateException("Failed to create client builder") }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val result = sut.create("https://matrix.org")
|
||||||
|
assertThat(result.isFailure).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TestScope.createRustTemporaryMatrixClientFactory(
|
||||||
|
sessionPathsFactory: SessionPathsFactory = SessionPathsFactory(File("/base"), File("/cache")),
|
||||||
|
rustMatrixClientFactory: RustMatrixClientFactory = createRustMatrixClientFactory(),
|
||||||
|
): RustTemporaryMatrixClientFactory {
|
||||||
|
return RustTemporaryMatrixClientFactory(
|
||||||
|
sessionPathsFactory = sessionPathsFactory,
|
||||||
|
rustMatrixClientFactory = rustMatrixClientFactory,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TestScope.createRustMatrixClientFactory(
|
||||||
|
cacheDirectory: File = File("/cache"),
|
||||||
|
sessionStore: SessionStore = InMemorySessionStore(
|
||||||
|
updateUserProfileResult = { _, _, _ -> },
|
||||||
|
),
|
||||||
|
clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(),
|
||||||
|
workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(),
|
||||||
|
contentScannerUrlProviderFactory: ContentScannerUrlProvider.Factory = { ContentScannerUrlProvider { Result.success(null) } },
|
||||||
|
) = RustMatrixClientFactory(
|
||||||
|
cacheDirectory = cacheDirectory,
|
||||||
|
appCoroutineScope = backgroundScope,
|
||||||
|
coroutineDispatchers = testCoroutineDispatchers(),
|
||||||
|
sessionStore = sessionStore,
|
||||||
|
userAgentProvider = SimpleUserAgentProvider(),
|
||||||
|
proxyProvider = FakeProxyProvider(),
|
||||||
|
clock = FakeSystemClock(),
|
||||||
|
analyticsService = FakeAnalyticsService(),
|
||||||
|
featureFlagService = FakeFeatureFlagService(),
|
||||||
|
timelineEventFilterFactory = FakeTimelineEventFilterFactory(),
|
||||||
|
clientBuilderProvider = clientBuilderProvider,
|
||||||
|
sqliteStoreBuilderProvider = FakeSqliteStoreBuilderProvider(),
|
||||||
|
workManagerScheduler = workManagerScheduler,
|
||||||
|
contentScannerUrlProviderFactory = contentScannerUrlProviderFactory,
|
||||||
|
)
|
||||||
|
}
|
||||||
+24
@@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.test.auth.FakeOAuthRedirectUrlProvide
|
|||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
@@ -50,6 +51,29 @@ class RustMatrixAuthenticationServiceTest {
|
|||||||
assertThat(sut.setHomeserver("matrix.org").isSuccess).isTrue()
|
assertThat(sut.setHomeserver("matrix.org").isSuccess).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `setHomeserver can fail gracefully and clean up the temporary client`() = runTest {
|
||||||
|
val closeResult = lambdaRecorder<Unit> {}
|
||||||
|
val sut = createRustMatrixAuthenticationService(
|
||||||
|
clientBuilderProvider = FakeClientBuilderProvider(
|
||||||
|
provideResult = {
|
||||||
|
FakeFfiClientBuilder(
|
||||||
|
buildResult = {
|
||||||
|
FakeFfiClient(
|
||||||
|
homeserverLoginDetailsResult = {
|
||||||
|
throw IllegalStateException("Failed to get homeserver login details")
|
||||||
|
},
|
||||||
|
closeResult = closeResult,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assertThat(sut.setHomeserver("matrix.org").isFailure).isTrue()
|
||||||
|
closeResult.assertions().isCalledOnce()
|
||||||
|
}
|
||||||
|
|
||||||
private fun TestScope.createRustMatrixAuthenticationService(
|
private fun TestScope.createRustMatrixAuthenticationService(
|
||||||
sessionStore: SessionStore = InMemorySessionStore(),
|
sessionStore: SessionStore = InMemorySessionStore(),
|
||||||
clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(),
|
clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(),
|
||||||
|
|||||||
+3
-1
@@ -56,7 +56,9 @@ class DefaultWellknownRetriever(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The passed `host` function may be a full URL, in that case we're only interested in the actual host
|
// The passed `host` function may be a full URL, in that case we're only interested in the actual host
|
||||||
val checkedHost = runCatchingExceptions { URL(host.ensureProtocol()).host.takeIfNotBlank() }.getOrNull() ?: host
|
val checkedHost = runCatchingExceptions { URL(host.ensureProtocol()).host.takeIfNotBlank() }
|
||||||
|
.getOrElse { return WellknownRetrieverResult.Error(IllegalArgumentException("host parameter is not valid", it)) }
|
||||||
|
?: host
|
||||||
|
|
||||||
// We instantiate different stores for different sources, so that we can cache them separately.
|
// We instantiate different stores for different sources, so that we can cache them separately.
|
||||||
// The ESS_CONFIG source is cached with a prefix to differentiate it from the WELLKNOWN_ENDPOINT.
|
// The ESS_CONFIG source is cached with a prefix to differentiate it from the WELLKNOWN_ENDPOINT.
|
||||||
|
|||||||
+93
-2
@@ -14,18 +14,22 @@ import com.google.common.truth.Truth.assertThat
|
|||||||
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
||||||
import io.element.android.features.wellknown.test.FakeElementWellknownStore
|
import io.element.android.features.wellknown.test.FakeElementWellknownStore
|
||||||
import io.element.android.features.wellknown.test.anElementWellKnown
|
import io.element.android.features.wellknown.test.anElementWellKnown
|
||||||
|
import io.element.android.libraries.core.uri.ensureProtocol
|
||||||
import io.element.android.libraries.matrix.api.UrlContentFetcher
|
import io.element.android.libraries.matrix.api.UrlContentFetcher
|
||||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||||
import io.element.android.libraries.wellknown.api.ElementWellKnown
|
import io.element.android.libraries.wellknown.api.ElementWellKnown
|
||||||
import io.element.android.libraries.wellknown.api.ElementWellKnownParser
|
import io.element.android.libraries.wellknown.api.ElementWellKnownParser
|
||||||
|
import io.element.android.libraries.wellknown.api.ElementWellknownStore
|
||||||
import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource
|
import io.element.android.libraries.wellknown.api.EnterpriseRemoteConfigSource
|
||||||
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
|
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
|
||||||
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.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runCurrent
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
class DefaultWellknownRetrieverTest {
|
class DefaultWellknownRetrieverTest {
|
||||||
@Test
|
@Test
|
||||||
@@ -68,6 +72,93 @@ class DefaultWellknownRetrieverTest {
|
|||||||
.isInstanceOf(WellknownRetrieverResult.NotFound::class.java)
|
.isInstanceOf(WellknownRetrieverResult.NotFound::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `get element wellknown will return an error if the provided host is not valid`() = runTest {
|
||||||
|
val sut = createDefaultWellknownRetriever()
|
||||||
|
assertThat(sut.getElementWellKnown("!not valid", EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT))
|
||||||
|
.isInstanceOf(WellknownRetrieverResult.Error::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `get element wellknown will return a cached value if present instead of fetching`() = runTest {
|
||||||
|
val host = URL(WELLKNOWN_URL).host.ensureProtocol()
|
||||||
|
val sut = createDefaultWellknownRetriever(
|
||||||
|
cacheStoreFactory = { FakeElementWellknownStore(initialData = mapOf(host to WellknownRetrieverResult.Success(expectedElementWellKnown))) }
|
||||||
|
)
|
||||||
|
assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT))
|
||||||
|
.isInstanceOf(WellknownRetrieverResult.Success::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `get element wellknown will return an outdated value first then fetch the current one`() = runTest {
|
||||||
|
val host = URL(WELLKNOWN_URL).host.ensureProtocol()
|
||||||
|
val resolver = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) }
|
||||||
|
val sut = createDefaultWellknownRetriever(
|
||||||
|
urlResolver = resolver,
|
||||||
|
cacheStoreFactory = { FakeElementWellknownStore(initialData = mapOf(host to WellknownRetrieverResult.Outdated(expectedElementWellKnown))) }
|
||||||
|
)
|
||||||
|
// The first call returns the outdated value
|
||||||
|
assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT))
|
||||||
|
.isInstanceOf(WellknownRetrieverResult.Outdated::class.java)
|
||||||
|
// The resolver is not called yet because the fetch is done in the background
|
||||||
|
resolver.assertions().isNeverCalled()
|
||||||
|
|
||||||
|
// We give it some time to run the background fetch
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
// The resolver is called during the background fetch
|
||||||
|
resolver.assertions().isCalledOnce().with(value(WELLKNOWN_URL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `get element wellknown will return an error if it happens when trying to retrieve the cached value`() = runTest {
|
||||||
|
val host = URL(WELLKNOWN_URL).host.ensureProtocol()
|
||||||
|
val sut = createDefaultWellknownRetriever(
|
||||||
|
cacheStoreFactory = { FakeElementWellknownStore(initialData = mapOf(host to WellknownRetrieverResult.Error(IllegalStateException("BOOM")))) }
|
||||||
|
)
|
||||||
|
assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT))
|
||||||
|
.isInstanceOf(WellknownRetrieverResult.Error::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `get element wellknown will return an error if it fails to serialize the retrieved value into the cache`() = runTest {
|
||||||
|
val sut = createDefaultWellknownRetriever(
|
||||||
|
elementWellKnownParser = { Result.failure(IllegalStateException("BOOM")) },
|
||||||
|
)
|
||||||
|
assertThat(sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT))
|
||||||
|
.isInstanceOf(WellknownRetrieverResult.Error::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WELLKNOWN_ENDPOINT source creates a store with no prefix`() = runTest {
|
||||||
|
val storeFactory = lambdaRecorder<String?, ElementWellknownStore> { FakeElementWellknownStore() }
|
||||||
|
val sut = createDefaultWellknownRetriever(cacheStoreFactory = storeFactory)
|
||||||
|
|
||||||
|
sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.WELLKNOWN_ENDPOINT)
|
||||||
|
|
||||||
|
storeFactory.assertions().isCalledOnce().with(value(null))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ESS_CONFIG source creates a store with ess prefix and checks the ESS config endpoint`() = runTest {
|
||||||
|
val essConfigUrl = "https://ess-config.example.com"
|
||||||
|
val storeFactory = lambdaRecorder<String?, ElementWellknownStore> { FakeElementWellknownStore() }
|
||||||
|
val resolver = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) }
|
||||||
|
val sut = createDefaultWellknownRetriever(
|
||||||
|
cacheStoreFactory = storeFactory,
|
||||||
|
enterpriseService = FakeEnterpriseService(
|
||||||
|
overrideWellKnownResult = { null },
|
||||||
|
essConfigEndpointUrlResult = { essConfigUrl },
|
||||||
|
),
|
||||||
|
urlResolver = resolver,
|
||||||
|
)
|
||||||
|
|
||||||
|
sut.getElementWellKnown(WELLKNOWN_URL, EnterpriseRemoteConfigSource.ESS_CONFIG)
|
||||||
|
|
||||||
|
storeFactory.assertions().isCalledOnce().with(value("ess_config"))
|
||||||
|
resolver.assertions().isCalledOnce().with(value(essConfigUrl))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `get element wellknown was overridden`() = runTest {
|
fun `get element wellknown was overridden`() = runTest {
|
||||||
val wellKnown = anElementWellKnown()
|
val wellKnown = anElementWellKnown()
|
||||||
@@ -91,12 +182,12 @@ class DefaultWellknownRetrieverTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun TestScope.createDefaultWellknownRetriever(
|
private fun TestScope.createDefaultWellknownRetriever(
|
||||||
cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(),
|
cacheStoreFactory: ElementWellknownStore.Factory = { FakeElementWellknownStore() },
|
||||||
elementWellKnownParser: ElementWellKnownParser = { Result.success(expectedElementWellKnown) },
|
elementWellKnownParser: ElementWellKnownParser = { Result.success(expectedElementWellKnown) },
|
||||||
enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }),
|
enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }),
|
||||||
urlResolver: UrlContentFetcher = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) },
|
urlResolver: UrlContentFetcher = SpyUrlContentFetcher { Result.success(WELLKNOWN_CONTENT.toByteArray()) },
|
||||||
) = DefaultWellknownRetriever(
|
) = DefaultWellknownRetriever(
|
||||||
elementWellknownStoreFactory = { cacheStore },
|
elementWellknownStoreFactory = cacheStoreFactory,
|
||||||
enterpriseService = enterpriseService,
|
enterpriseService = enterpriseService,
|
||||||
elementWellKnownParser = elementWellKnownParser,
|
elementWellKnownParser = elementWellKnownParser,
|
||||||
urlContentFetcher = urlResolver,
|
urlContentFetcher = urlResolver,
|
||||||
|
|||||||
Reference in New Issue
Block a user