Ensure clearing the cache delete the CacheStore.

This commit is contained in:
Benoit Marty 2026-04-30 09:01:35 +02:00
parent 93717d0db0
commit ed58f7161e
8 changed files with 45 additions and 2 deletions

View file

@ -51,6 +51,7 @@ dependencies {
implementation(projects.appconfig) implementation(projects.appconfig)
implementation(projects.libraries.core) implementation(projects.libraries.core)
implementation(projects.libraries.architecture) implementation(projects.libraries.architecture)
implementation(projects.libraries.cachestore.api)
implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.api)
implementation(projects.libraries.designsystem) implementation(projects.libraries.designsystem)
implementation(projects.libraries.featureflag.api) implementation(projects.libraries.featureflag.api)
@ -114,6 +115,7 @@ dependencies {
testImplementation(projects.features.logout.test) testImplementation(projects.features.logout.test)
testImplementation(projects.libraries.indicator.test) testImplementation(projects.libraries.indicator.test)
testImplementation(projects.libraries.pushproviders.test) testImplementation(projects.libraries.pushproviders.test)
testImplementation(projects.libraries.cachestore.test)
testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.libraries.sessionStorage.test)
testImplementation(projects.services.appnavstate.impl) testImplementation(projects.services.appnavstate.impl)
testImplementation(projects.services.analytics.test) testImplementation(projects.services.analytics.test)

View file

@ -14,6 +14,7 @@ import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Provider import dev.zacsweers.metro.Provider
import io.element.android.features.invite.api.SeenInvitesStore import io.element.android.features.invite.api.SeenInvitesStore
import io.element.android.features.preferences.impl.DefaultCacheService import io.element.android.features.preferences.impl.DefaultCacheService
import io.element.android.libraries.cachestore.api.CacheStore
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.di.annotations.ApplicationContext
@ -37,8 +38,11 @@ class DefaultClearCacheUseCase(
private val pushService: PushService, private val pushService: PushService,
private val seenInvitesStore: SeenInvitesStore, private val seenInvitesStore: SeenInvitesStore,
private val activeRoomsHolder: ActiveRoomsHolder, private val activeRoomsHolder: ActiveRoomsHolder,
private val cacheStore: CacheStore,
) : ClearCacheUseCase { ) : ClearCacheUseCase {
override suspend fun invoke() = withContext(coroutineDispatchers.io) { override suspend fun invoke() = withContext(coroutineDispatchers.io) {
// Clear cache store
cacheStore.deleteAll()
// Active rooms should be disposed of before clearing the cache // Active rooms should be disposed of before clearing the cache
activeRoomsHolder.clear(matrixClient.sessionId) activeRoomsHolder.clear(matrixClient.sessionId)
// Clear Matrix cache // Clear Matrix cache

View file

@ -19,6 +19,8 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.push.test.FakePushService import io.element.android.libraries.push.test.FakePushService
import io.element.android.libraries.sessionstorage.test.InMemoryCacheStore
import io.element.android.libraries.sessionstorage.test.aCacheData
import io.element.android.services.appnavstate.impl.DefaultActiveRoomsHolder import io.element.android.services.appnavstate.impl.DefaultActiveRoomsHolder
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
@ -49,6 +51,9 @@ class DefaultClearCacheUseCaseTest {
) )
val seenInvitesStore = InMemorySeenInvitesStore(setOf(A_ROOM_ID)) val seenInvitesStore = InMemorySeenInvitesStore(setOf(A_ROOM_ID))
assertThat(seenInvitesStore.seenRoomIds().first()).isNotEmpty() assertThat(seenInvitesStore.seenRoomIds().first()).isNotEmpty()
val cacheStore = InMemoryCacheStore(
initialData = mapOf("key1" to aCacheData())
)
val sut = DefaultClearCacheUseCase( val sut = DefaultClearCacheUseCase(
context = InstrumentationRegistry.getInstrumentation().context, context = InstrumentationRegistry.getInstrumentation().context,
matrixClient = matrixClient, matrixClient = matrixClient,
@ -58,9 +63,11 @@ class DefaultClearCacheUseCaseTest {
pushService = pushService, pushService = pushService,
seenInvitesStore = seenInvitesStore, seenInvitesStore = seenInvitesStore,
activeRoomsHolder = activeRoomsHolder, activeRoomsHolder = activeRoomsHolder,
cacheStore = cacheStore,
) )
defaultCacheService.clearedCacheEventFlow.test { defaultCacheService.clearedCacheEventFlow.test {
sut.invoke() sut.invoke()
assertThat(cacheStore.dataMap).isEmpty()
clearCacheLambda.assertions().isCalledOnce() clearCacheLambda.assertions().isCalledOnce()
setIgnoreRegistrationErrorLambda.assertions().isCalledOnce() setIgnoreRegistrationErrorLambda.assertions().isCalledOnce()
.with(value(matrixClient.sessionId), value(false)) .with(value(matrixClient.sessionId), value(false))

View file

@ -11,4 +11,5 @@ interface CacheStore {
suspend fun storeData(key: String, data: CacheData) suspend fun storeData(key: String, data: CacheData)
suspend fun getData(key: String): CacheData? suspend fun getData(key: String): CacheData?
suspend fun deleteData(key: String) suspend fun deleteData(key: String)
suspend fun deleteAll()
} }

View file

@ -27,10 +27,14 @@ class DatabaseCacheStore(
override suspend fun storeData(key: String, data: CacheData) { override suspend fun storeData(key: String, data: CacheData) {
database.cacheDataQueries.insertData( database.cacheDataQueries.insertData(
data.toDbModel(key) data.toDbModel(key)
) ).await()
} }
override suspend fun deleteData(key: String) { override suspend fun deleteData(key: String) {
database.cacheDataQueries.deleteData(key) database.cacheDataQueries.deleteData(key).await()
}
override suspend fun deleteAll() {
database.cacheDataQueries.deleteAll().await()
} }
} }

View file

@ -23,3 +23,6 @@ INSERT OR REPLACE INTO CacheData VALUES ?;
deleteData: deleteData:
DELETE FROM CacheData WHERE key = ?; DELETE FROM CacheData WHERE key = ?;
deleteAll:
DELETE FROM CacheData;

View file

@ -65,4 +65,22 @@ class DatabaseCacheStoreTest {
databaseCacheStore.deleteData(A_KEY) databaseCacheStore.deleteData(A_KEY)
assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isNull() assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isNull()
} }
@Test
fun `deleteAll deletes all the data`() = runTest {
// Assert that no data is stored for the key
assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isNull()
// Store data
databaseCacheStore.storeData(A_KEY, CacheData(A_DATA_1, 1))
assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isEqualTo(
DbCacheData(
key = A_KEY,
value_ = A_DATA_1,
updatedAt = 1,
)
)
// Delete all data
databaseCacheStore.deleteAll()
assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isNull()
}
} }

View file

@ -26,4 +26,8 @@ class InMemoryCacheStore(
override suspend fun deleteData(key: String) { override suspend fun deleteData(key: String) {
dataMap.remove(key) dataMap.remove(key)
} }
override suspend fun deleteAll() {
dataMap.clear()
}
} }