@@ -12,6 +12,7 @@ import com.google.common.truth.Truth.assertThat
1212import io.element.android.features.enterprise.api.EnterpriseService
1313import io.element.android.features.enterprise.test.FakeEnterpriseService
1414import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
15+ import io.element.android.libraries.matrix.api.paths.SessionPaths
1516import io.element.android.libraries.matrix.impl.ClientBuilderProvider
1617import io.element.android.libraries.matrix.impl.FakeClientBuilderProvider
1718import io.element.android.libraries.matrix.impl.auth.qrlogin.SdkQrCodeLoginData
@@ -22,6 +23,10 @@ import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiHomeserver
2223import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiLoginWithQrCodeHandler
2324import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiQrCodeData
2425import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
26+ import io.element.android.libraries.matrix.impl.storage.FakeSqliteStoreBuilder
27+ import io.element.android.libraries.matrix.impl.storage.FakeSqliteStoreBuilderProvider
28+ import io.element.android.libraries.matrix.impl.storage.SqliteStoreBuilder
29+ import io.element.android.libraries.matrix.impl.storage.SqliteStoreBuilderProvider
2530import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
2631import io.element.android.libraries.matrix.test.A_USER_ID
2732import io.element.android.libraries.matrix.test.auth.FakeOAuthRedirectUrlProvider
@@ -33,13 +38,18 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder
3338import io.element.android.tests.testutils.testCoroutineDispatchers
3439import kotlinx.coroutines.test.TestScope
3540import kotlinx.coroutines.test.runTest
41+ import org.junit.Rule
3642import org.junit.Test
43+ import org.junit.rules.TemporaryFolder
3744import org.matrix.rustcomponents.sdk.Client
3845import org.matrix.rustcomponents.sdk.ClientBuilder
3946import org.matrix.rustcomponents.sdk.HumanQrLoginException
4047import java.io.File
4148
4249class RustMatrixAuthenticationServiceTest {
50+ @get:Rule
51+ val temporaryFolder = TemporaryFolder ()
52+
4353 @Test
4454 fun `setHomeserver is successful` () = runTest {
4555 val sut = createRustMatrixAuthenticationService(
@@ -177,21 +187,89 @@ class RustMatrixAuthenticationServiceTest {
177187 override fun provide (): ClientBuilder = FakeFfiClientBuilder (buildResult = clients[index++ ])
178188 }
179189
190+ @Test
191+ fun `a new login does not delete the session data of the account which has just been logged in` () = runTest {
192+ val storeBuilderProvider = SessionDirectoryCreatingSqliteStoreBuilderProvider ()
193+ val sut = createRustMatrixAuthenticationService(
194+ clientBuilderProvider = FakeSequentialClientBuilderProvider (
195+ { aLoginFakeFfiClient() },
196+ { FakeFfiClient (withUtdHook = {}) },
197+ { aLoginFakeFfiClient() },
198+ ),
199+ sessionPathsFactory = SessionPathsFactory (temporaryFolder.newFolder(" base" ), temporaryFolder.newFolder(" cache" )),
200+ sqliteStoreBuilderProvider = storeBuilderProvider,
201+ )
202+
203+ assertThat(sut.setHomeserver(" matrix.org" ).isSuccess).isTrue()
204+ assertThat(sut.login(" alice" , " password" ).getOrNull()).isEqualTo(A_USER_ID )
205+ val loggedInSessionPaths = storeBuilderProvider.providedSessionPaths.first()
206+
207+ // Adding another account rotates the session paths, which must not touch the previous account.
208+ assertThat(sut.setHomeserver(" matrix.org" ).isSuccess).isTrue()
209+
210+ assertThat(loggedInSessionPaths.fileDirectory.exists()).isTrue()
211+ assertThat(loggedInSessionPaths.cacheDirectory.exists()).isTrue()
212+ }
213+
214+ @Test
215+ fun `a new login deletes the session data of a previous failed login attempt` () = runTest {
216+ val storeBuilderProvider = SessionDirectoryCreatingSqliteStoreBuilderProvider ()
217+ val sut = createRustMatrixAuthenticationService(
218+ clientBuilderProvider = FakeSequentialClientBuilderProvider (
219+ { aLoginFakeFfiClient() },
220+ { throw IllegalStateException (" Failed to build the client of the session" ) },
221+ { aLoginFakeFfiClient() },
222+ ),
223+ sessionPathsFactory = SessionPathsFactory (temporaryFolder.newFolder(" base" ), temporaryFolder.newFolder(" cache" )),
224+ sqliteStoreBuilderProvider = storeBuilderProvider,
225+ )
226+
227+ assertThat(sut.setHomeserver(" matrix.org" ).isSuccess).isTrue()
228+ assertThat(sut.login(" alice" , " password" ).isFailure).isTrue()
229+ val abandonedSessionPaths = storeBuilderProvider.providedSessionPaths.first()
230+
231+ assertThat(sut.setHomeserver(" matrix.org" ).isSuccess).isTrue()
232+
233+ assertThat(abandonedSessionPaths.fileDirectory.exists()).isFalse()
234+ assertThat(abandonedSessionPaths.cacheDirectory.exists()).isFalse()
235+ }
236+
237+ private fun aLoginFakeFfiClient () = FakeFfiClient (
238+ homeserverLoginDetailsResult = { FakeFfiHomeserverLoginDetails () },
239+ loginResult = { _, _ -> },
240+ )
241+
242+ /* *
243+ * A [SqliteStoreBuilderProvider] which creates the session directories, like the SDK does when it
244+ * opens its stores, and records them so that a test can assert on their lifecycle.
245+ */
246+ private class SessionDirectoryCreatingSqliteStoreBuilderProvider : SqliteStoreBuilderProvider {
247+ val providedSessionPaths = mutableListOf<SessionPaths >()
248+
249+ override fun provide (sessionPaths : SessionPaths ): SqliteStoreBuilder {
250+ sessionPaths.fileDirectory.mkdirs()
251+ sessionPaths.cacheDirectory.mkdirs()
252+ providedSessionPaths.add(sessionPaths)
253+ return FakeSqliteStoreBuilder ()
254+ }
255+ }
256+
180257 private fun TestScope.createRustMatrixAuthenticationService (
181258 sessionStore : SessionStore = InMemorySessionStore (updateUserProfileResult = { _ , _ , _ -> }),
182259 clientBuilderProvider : ClientBuilderProvider = FakeClientBuilderProvider (),
183260 enterpriseService : EnterpriseService = FakeEnterpriseService (),
261+ sessionPathsFactory : SessionPathsFactory = SessionPathsFactory (File ("/base"), File ("/cache")),
262+ sqliteStoreBuilderProvider : SqliteStoreBuilderProvider = FakeSqliteStoreBuilderProvider (),
184263 ): RustMatrixAuthenticationService {
185- val baseDirectory = File (" /base" )
186- val cacheDirectory = File (" /cache" )
187264 val rustMatrixClientFactory = createRustMatrixClientFactory(
188- cacheDirectory = cacheDirectory ,
265+ cacheDirectory = File ( " /cache " ) ,
189266 sessionStore = sessionStore,
190267 clientBuilderProvider = clientBuilderProvider,
191268 workManagerScheduler = FakeWorkManagerScheduler (submitLambda = {}),
269+ sqliteStoreBuilderProvider = sqliteStoreBuilderProvider,
192270 )
193271 return RustMatrixAuthenticationService (
194- sessionPathsFactory = SessionPathsFactory (baseDirectory, cacheDirectory) ,
272+ sessionPathsFactory = sessionPathsFactory ,
195273 coroutineDispatchers = testCoroutineDispatchers(),
196274 sessionStore = sessionStore,
197275 rustMatrixClientFactory = rustMatrixClientFactory,
0 commit comments