From 2e06132fcadd08917caba1b1fda02280404a0466 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Thu, 25 Jan 2024 13:33:46 +0100 Subject: [PATCH] chore: address comments --- ...=> CertificateRevocationListRepository.kt} | 40 +--- .../kalium/logic/feature/UserSessionScope.kt | 22 +- ...> CertificateRevocationListCheckWorker.kt} | 50 ++--- .../usecase/CheckRevocationListUseCase.kt | 6 +- ...ertificateRevocationListRepositoryTest.kt} | 6 +- .../CertificateRevocationListCheckTest.kt | 124 ++++++++++ .../logic/feature/e2ei/CheckCrlWorkerTest.kt | 211 ------------------ .../usecase/CheckRevocationListUseCaseTest.kt | 22 +- 8 files changed, 168 insertions(+), 313 deletions(-) rename logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/{CrlRepository.kt => CertificateRevocationListRepository.kt} (72%) rename logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/{CheckCrlWorker.kt => CertificateRevocationListCheckWorker.kt} (52%) rename logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/{CrlRepositoryTest.kt => CertificateRevocationListRepositoryTest.kt} (93%) create mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateRevocationListCheckTest.kt delete mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/CheckCrlWorkerTest.kt diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/CrlRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/CertificateRevocationListRepository.kt similarity index 72% rename from logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/CrlRepository.kt rename to logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/CertificateRevocationListRepository.kt index b34245de0d2..bce8b8e4058 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/CrlRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/CertificateRevocationListRepository.kt @@ -18,23 +18,18 @@ package com.wire.kalium.logic.data.e2ei import com.wire.kalium.logic.CoreFailure -import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.configuration.UserConfigRepository import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.functional.map import com.wire.kalium.logic.wrapApiRequest -import com.wire.kalium.logic.wrapStorageRequest import com.wire.kalium.network.api.base.unbound.acme.ACMEApi import com.wire.kalium.persistence.config.CRLUrlExpirationList import com.wire.kalium.persistence.config.CRLWithExpiration import com.wire.kalium.persistence.dao.MetadataDAO import io.ktor.http.Url import io.ktor.http.protocolWithAuthority -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import kotlinx.datetime.Instant -interface CrlRepository { +interface CertificateRevocationListRepository { /** * Returns CRLs with expiration time. @@ -42,44 +37,16 @@ interface CrlRepository { * @return the [CRLUrlExpirationList] representing a list of CRLs with expiration time. */ suspend fun getCRLs(): CRLUrlExpirationList? - - /** - * Observes the last CRL check instant. - * - * @return A [Flow] of [Instant] objects representing the last CRL check instant. - * It emits `null` if no CRL check has occurred. - */ - suspend fun lastCrlCheckInstantFlow(): Flow - - /** - * Sets the last CRL check date. - * - * @param instant The instant representing the date and time of the last CRL check. - * @return Either a [StorageFailure] if the operation fails, or [Unit] if successful. - */ - suspend fun setLastCRLCheckInstant(instant: Instant): Either - suspend fun addOrUpdateCRL(url: String, timestamp: ULong) suspend fun getCurrentClientCrlUrl(): Either suspend fun getClientDomainCRL(url: String): Either } -internal class CrlRepositoryDataSource( +internal class CertificateRevocationListRepositoryDataSource( private val acmeApi: ACMEApi, private val metadataDAO: MetadataDAO, private val userConfigRepository: UserConfigRepository -) : CrlRepository { - - override suspend fun lastCrlCheckInstantFlow(): Flow = - metadataDAO.valueByKeyFlow(CRL_CHECK_INSTANT_KEY).map { instant -> - instant?.let { Instant.parse(it) } - } - - override suspend fun setLastCRLCheckInstant(instant: Instant): Either = - wrapStorageRequest { - metadataDAO.insertValue(instant.toString(), CRL_CHECK_INSTANT_KEY) - } - +) : CertificateRevocationListRepository { override suspend fun getCRLs(): CRLUrlExpirationList? = metadataDAO.getSerializable(CRL_LIST_KEY, CRLUrlExpirationList.serializer()) @@ -125,6 +92,5 @@ internal class CrlRepositoryDataSource( companion object { const val CRL_LIST_KEY = "crl_list_key" - const val CRL_CHECK_INSTANT_KEY = "crl_check_instant_key" } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt index 928afcd2354..a70bee3be7b 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt @@ -77,8 +77,8 @@ import com.wire.kalium.logic.data.conversation.ProposalTimer import com.wire.kalium.logic.data.conversation.SubconversationRepositoryImpl import com.wire.kalium.logic.data.conversation.UpdateKeyingMaterialThresholdProvider import com.wire.kalium.logic.data.conversation.UpdateKeyingMaterialThresholdProviderImpl -import com.wire.kalium.logic.data.e2ei.CrlRepository -import com.wire.kalium.logic.data.e2ei.CrlRepositoryDataSource +import com.wire.kalium.logic.data.e2ei.CertificateRevocationListRepository +import com.wire.kalium.logic.data.e2ei.CertificateRevocationListRepositoryDataSource import com.wire.kalium.logic.data.e2ei.E2EIRepository import com.wire.kalium.logic.data.e2ei.E2EIRepositoryImpl import com.wire.kalium.logic.data.event.EventDataSource @@ -207,8 +207,8 @@ import com.wire.kalium.logic.feature.conversation.mls.OneOnOneResolverImpl import com.wire.kalium.logic.feature.debug.DebugScope import com.wire.kalium.logic.feature.e2ei.ACMECertificatesSyncWorker import com.wire.kalium.logic.feature.e2ei.ACMECertificatesSyncWorkerImpl -import com.wire.kalium.logic.feature.e2ei.CheckCrlWorker -import com.wire.kalium.logic.feature.e2ei.CheckCrlWorkerImpl +import com.wire.kalium.logic.feature.e2ei.CertificateRevocationListCheckWorker +import com.wire.kalium.logic.feature.e2ei.CertificateRevocationListCheckWorkerImpl import com.wire.kalium.logic.feature.e2ei.usecase.CheckRevocationListUseCase import com.wire.kalium.logic.feature.e2ei.usecase.CheckRevocationListUseCaseImpl import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIUseCase @@ -1509,8 +1509,8 @@ class UserSessionScope internal constructor( userStorage.database.clientDAO, userStorage.database.metadataDAO, ) - private val crlRepository: CrlRepository - get() = CrlRepositoryDataSource( + private val certificateRevocationListRepository: CertificateRevocationListRepository + get() = CertificateRevocationListRepositoryDataSource( acmeApi = globalScope.unboundNetworkContainer.acmeApi, metadataDAO = userStorage.database.metadataDAO, userConfigRepository = userConfigRepository @@ -1527,9 +1527,9 @@ class UserSessionScope internal constructor( ) } - private val checkCrlWorker: CheckCrlWorker by lazy { - CheckCrlWorkerImpl( - crlRepository = crlRepository, + private val certificateRevocationListCheckWorker: CertificateRevocationListCheckWorker by lazy { + CertificateRevocationListCheckWorkerImpl( + certificateRevocationListRepository = certificateRevocationListRepository, incrementalSyncRepository = incrementalSyncRepository, checkRevocationList = checkRevocationList, ) @@ -1888,7 +1888,7 @@ class UserSessionScope internal constructor( private val checkRevocationList: CheckRevocationListUseCase get() = CheckRevocationListUseCaseImpl( - crlRepository = crlRepository, + certificateRevocationListRepository = certificateRevocationListRepository, currentClientIdProvider = clientIdProvider, mlsClientProvider = mlsClientProvider, mLSConversationsVerificationStatusesHandler = mlsConversationsVerificationStatusesHandler @@ -1927,7 +1927,7 @@ class UserSessionScope internal constructor( } launch { - checkCrlWorker.execute() + certificateRevocationListCheckWorker.execute() } launch { diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CheckCrlWorker.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateRevocationListCheckWorker.kt similarity index 52% rename from logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CheckCrlWorker.kt rename to logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateRevocationListCheckWorker.kt index 3fcf37f5213..1f312f6755f 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CheckCrlWorker.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateRevocationListCheckWorker.kt @@ -17,74 +17,50 @@ */ package com.wire.kalium.logic.feature.e2ei -import com.wire.kalium.logic.data.e2ei.CrlRepository +import com.wire.kalium.logic.data.e2ei.CertificateRevocationListRepository import com.wire.kalium.logic.data.sync.IncrementalSyncRepository import com.wire.kalium.logic.data.sync.IncrementalSyncStatus import com.wire.kalium.logic.feature.e2ei.usecase.CheckRevocationListUseCase import com.wire.kalium.logic.functional.map -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collectLatest +import com.wire.kalium.logic.kaliumLogger import kotlinx.coroutines.flow.filter import kotlinx.datetime.Clock -import kotlin.time.Duration -import kotlin.time.Duration.Companion.hours /** - * Represents an interface for a CheckCrlWorker, - * which is should keep the [CrlRepository] healthy. + * This worker will wait until the sync is done and then check the CRLs if needed. * - * It will wait until the incremental sync is live and then check the CRLs if needed. */ -internal interface CheckCrlWorker { +internal interface CertificateRevocationListCheckWorker { suspend fun execute() } /** - * Base implementation of [CheckCrlWorker]. - * @param crlRepository The CRL repository. + * Base implementation of [CertificateRevocationListCheckWorker]. + * @param certificateRevocationListRepository The CRL repository. * @param incrementalSyncRepository The incremental sync repository. * @param checkRevocationList The check revocation list use case. - * @param minIntervalBetweenRefills The minimum interval between CRL checks. * */ -internal class CheckCrlWorkerImpl( - private val crlRepository: CrlRepository, +internal class CertificateRevocationListCheckWorkerImpl( + private val certificateRevocationListRepository: CertificateRevocationListRepository, private val incrementalSyncRepository: IncrementalSyncRepository, - private val checkRevocationList: CheckRevocationListUseCase, - private val minIntervalBetweenRefills: Duration = MIN_INTERVAL_BETWEEN_REFILLS -) : CheckCrlWorker { + private val checkRevocationList: CheckRevocationListUseCase +) : CertificateRevocationListCheckWorker { - /** - * Check the CRLs and update the expiration time if needed. - */ override suspend fun execute() { - crlRepository.lastCrlCheckInstantFlow().collectLatest { lastCheck -> - val now = Clock.System.now() - val nextCheckTime = lastCheck?.plus(minIntervalBetweenRefills) ?: now - val delayUntilNextCheck = nextCheckTime - now - delay(delayUntilNextCheck) - waitUntilLiveAndCheckCRLs() - crlRepository.setLastCRLCheckInstant(Clock.System.now()) - } - } - - private suspend fun waitUntilLiveAndCheckCRLs() { incrementalSyncRepository.incrementalSyncState .filter { it is IncrementalSyncStatus.Live } .collect { - crlRepository.getCRLs()?.cRLWithExpirationList?.forEach { crl -> + kaliumLogger.i("Checking certificate revocation list (CRL)..") + certificateRevocationListRepository.getCRLs()?.cRLWithExpirationList?.forEach { crl -> if (crl.expiration < Clock.System.now().epochSeconds.toULong()) { checkRevocationList(crl.url).map { newExpirationTime -> newExpirationTime?.let { - crlRepository.addOrUpdateCRL(crl.url, it) + certificateRevocationListRepository.addOrUpdateCRL(crl.url, it) } } } } } } - - private companion object { - val MIN_INTERVAL_BETWEEN_REFILLS = 24.hours - } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/CheckRevocationListUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/CheckRevocationListUseCase.kt index 6398fab33e1..b2821f034e7 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/CheckRevocationListUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/CheckRevocationListUseCase.kt @@ -19,7 +19,7 @@ package com.wire.kalium.logic.feature.e2ei.usecase import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.data.client.MLSClientProvider -import com.wire.kalium.logic.data.e2ei.CrlRepository +import com.wire.kalium.logic.data.e2ei.CertificateRevocationListRepository import com.wire.kalium.logic.data.id.CurrentClientIdProvider import com.wire.kalium.logic.feature.conversation.MLSConversationsVerificationStatusesHandler import com.wire.kalium.logic.functional.Either @@ -34,13 +34,13 @@ interface CheckRevocationListUseCase { } internal class CheckRevocationListUseCaseImpl( - private val crlRepository: CrlRepository, + private val certificateRevocationListRepository: CertificateRevocationListRepository, private val currentClientIdProvider: CurrentClientIdProvider, private val mlsClientProvider: MLSClientProvider, private val mLSConversationsVerificationStatusesHandler: MLSConversationsVerificationStatusesHandler ) : CheckRevocationListUseCase { override suspend fun invoke(url: String): Either { - return crlRepository.getClientDomainCRL(url).flatMap { + return certificateRevocationListRepository.getClientDomainCRL(url).flatMap { currentClientIdProvider().flatMap { clientId -> mlsClientProvider.getMLSClient(clientId).map { mlsClient -> mlsClient.registerCrl(url, it).run { diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/CrlRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/CertificateRevocationListRepositoryTest.kt similarity index 93% rename from logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/CrlRepositoryTest.kt rename to logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/CertificateRevocationListRepositoryTest.kt index f68d3b2a23a..de488a7c2c8 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/CrlRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/CertificateRevocationListRepositoryTest.kt @@ -18,7 +18,7 @@ package com.wire.kalium.logic.data.e2ei import com.wire.kalium.logic.configuration.UserConfigRepository -import com.wire.kalium.logic.data.e2ei.CrlRepositoryDataSource.Companion.CRL_LIST_KEY +import com.wire.kalium.logic.data.e2ei.CertificateRevocationListRepositoryDataSource.Companion.CRL_LIST_KEY import com.wire.kalium.network.api.base.unbound.acme.ACMEApi import com.wire.kalium.persistence.config.CRLWithExpiration import com.wire.kalium.persistence.config.CRLUrlExpirationList @@ -32,7 +32,7 @@ import io.mockative.verify import kotlinx.coroutines.test.runTest import kotlin.test.Test -class CrlRepositoryTest { +class CertificateRevocationListRepositoryTest { @Test fun givenAnEmptyStoredList_whenUpdatingCRLs_thenAddNewCRL() = runTest { @@ -101,7 +101,7 @@ class CrlRepositoryTest { @Mock val userConfigRepository = mock(classOf()) - fun arrange() = this to CrlRepositoryDataSource(acmeApi, metadataDAO, userConfigRepository) + fun arrange() = this to CertificateRevocationListRepositoryDataSource(acmeApi, metadataDAO, userConfigRepository) suspend fun withEmptyList() = apply { given(metadataDAO).coroutine { diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateRevocationListCheckTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateRevocationListCheckTest.kt new file mode 100644 index 00000000000..5d2e16dde4b --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateRevocationListCheckTest.kt @@ -0,0 +1,124 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.feature.e2ei + +import com.wire.kalium.logic.data.e2ei.CertificateRevocationListRepository +import com.wire.kalium.logic.data.sync.IncrementalSyncRepository +import com.wire.kalium.logic.data.sync.IncrementalSyncStatus +import com.wire.kalium.logic.feature.e2ei.usecase.CheckRevocationListUseCase +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.persistence.config.CRLUrlExpirationList +import com.wire.kalium.persistence.config.CRLWithExpiration +import io.mockative.Mock +import io.mockative.any +import io.mockative.classOf +import io.mockative.eq +import io.mockative.given +import io.mockative.mock +import io.mockative.once +import io.mockative.verify +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import kotlin.test.Test + +class CertificateRevocationListCheckTest { + + @Test + fun givenExpiredCRL_whenTimeElapses_thenCheckRevocationList() = runTest { + val (arrangement, checkCrlWorker) = Arrangement() + .withIncrementalSyncState(flowOf(IncrementalSyncStatus.Live)) + .withExpiredCRL() + .withCheckRevocationListResult() + .arrange() + + checkCrlWorker.execute() + + verify(arrangement.certificateRevocationListRepository) + .suspendFunction(arrangement.certificateRevocationListRepository::getCRLs) + .wasInvoked(exactly = once) + + verify(arrangement.checkRevocationList) + .suspendFunction(arrangement.checkRevocationList::invoke) + .with(eq(DUMMY_URL)) + .wasInvoked(exactly = once) + + verify(arrangement.certificateRevocationListRepository) + .suspendFunction(arrangement.certificateRevocationListRepository::addOrUpdateCRL) + .with(eq(DUMMY_URL), eq(FUTURE_TIMESTAMP)) + .wasInvoked(exactly = once) + + } + + private class Arrangement { + + @Mock + val certificateRevocationListRepository = mock(classOf()) + + @Mock + val incrementalSyncRepository = mock(classOf()) + + @Mock + val checkRevocationList = mock(classOf()) + + fun arrange() = this to CertificateRevocationListCheckWorkerImpl( + certificateRevocationListRepository, incrementalSyncRepository, checkRevocationList + ) + + fun withNoCRL() = apply { + given(certificateRevocationListRepository) + .suspendFunction(certificateRevocationListRepository::getCRLs) + .whenInvoked() + .thenReturn(null) + } + + fun withNonExpiredCRL() = apply { + given(certificateRevocationListRepository) + .suspendFunction(certificateRevocationListRepository::getCRLs) + .whenInvoked() + .thenReturn(CRLUrlExpirationList(listOf(CRLWithExpiration(DUMMY_URL, FUTURE_TIMESTAMP)))) + } + + fun withExpiredCRL() = apply { + given(certificateRevocationListRepository) + .suspendFunction(certificateRevocationListRepository::getCRLs) + .whenInvoked() + .thenReturn(CRLUrlExpirationList(listOf(CRLWithExpiration(DUMMY_URL, TIMESTAMP)))) + } + fun withCheckRevocationListResult() = apply { + given(checkRevocationList) + .suspendFunction(checkRevocationList::invoke) + .whenInvokedWith(any()) + .thenReturn(Either.Right(FUTURE_TIMESTAMP)) + } + + fun withIncrementalSyncState(flow: Flow) = apply { + given(incrementalSyncRepository) + .getter(incrementalSyncRepository::incrementalSyncState) + .whenInvoked() + .thenReturn(flow) + } + + } + + companion object { + const val DUMMY_URL = "https://dummy.url" + val TIMESTAMP = 633218892.toULong() // Wednesday, 24 January 1990 22:08:12 + val FUTURE_TIMESTAMP = 4104511692.toULong() // Sunday, 24 January 2100 22:08:12 + } +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/CheckCrlWorkerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/CheckCrlWorkerTest.kt deleted file mode 100644 index fbe102aeeab..00000000000 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/CheckCrlWorkerTest.kt +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Wire - * Copyright (C) 2024 Wire Swiss GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.wire.kalium.logic.feature.e2ei - -import com.wire.kalium.logic.StorageFailure -import com.wire.kalium.logic.data.e2ei.CrlRepository -import com.wire.kalium.logic.data.sync.IncrementalSyncRepository -import com.wire.kalium.logic.data.sync.IncrementalSyncStatus -import com.wire.kalium.logic.feature.e2ei.usecase.CheckRevocationListUseCase -import com.wire.kalium.logic.functional.Either -import com.wire.kalium.persistence.config.CRLUrlExpirationList -import com.wire.kalium.persistence.config.CRLWithExpiration -import io.mockative.Mock -import io.mockative.any -import io.mockative.classOf -import io.mockative.eq -import io.mockative.given -import io.mockative.mock -import io.mockative.once -import io.mockative.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.advanceTimeBy -import kotlinx.coroutines.test.runTest -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant -import kotlin.test.Test -import kotlin.time.Duration.Companion.hours - -class CheckCrlWorkerTest { - - @Test - fun givenLastCheckWasRecentAndSyncIsLive_whenWorkerIsRunning_thenShouldNotCheckCRLsExpiration() = - runTest { - val lastCheck = Clock.System.now() - 23.hours - val (arrangement, checkCrlWorker) = Arrangement() - .withObserveLastCRLCheckInstantReturning(flowOf(lastCheck)) - .withIncrementalSyncState(flowOf(IncrementalSyncStatus.Live)) - .withSetLastCRLCheckInstantReturning(Either.Right(Unit)) - .withNoCRL() - .arrange() - - launch { - checkCrlWorker.execute() - } - - verify(arrangement.crlRepository) - .suspendFunction(arrangement.crlRepository::getCRLs) - .wasNotInvoked() - } - - @Test - fun givenLastCheckWasLongAgoAndSyncIsLive_whenWorkerIsRunning_thenShouldCheckCRLsExpiration() = runTest { - val lastCheck = Clock.System.now() - 24.hours - val (arrangement, checkCrlWorker) = Arrangement() - .withObserveLastCRLCheckInstantReturning(flowOf(lastCheck)) - .withIncrementalSyncState(flowOf(IncrementalSyncStatus.Live)) - .withSetLastCRLCheckInstantReturning(Either.Right(Unit)) - .withNonExpiredCRL() - .arrange() - - checkCrlWorker.execute() - - verify(arrangement.crlRepository) - .suspendFunction(arrangement.crlRepository::getCRLs) - .wasInvoked(exactly = once) - } - - @OptIn(ExperimentalCoroutinesApi::class) - @Test - fun givenLastCheckWasRecentAndSyncIsLive_whenTimeElapses_thenShouldCheckCRLsExpiration() = runTest { - val lastCheck = Clock.System.now() - 23.hours - val (arrangement, checkCrlWorker) = Arrangement() - .withObserveLastCRLCheckInstantReturning(flowOf(lastCheck)) - .withIncrementalSyncState(flowOf(IncrementalSyncStatus.Live)) - .withSetLastCRLCheckInstantReturning(Either.Right(Unit)) - .withNonExpiredCRL() - .arrange() - - launch { - checkCrlWorker.execute() - } - - verify(arrangement.crlRepository) - .suspendFunction(arrangement.crlRepository::getCRLs) - .wasNotInvoked() - - // Advance time until it's time to refill - advanceTimeBy(2.hours) - - verify(arrangement.crlRepository) - .suspendFunction(arrangement.crlRepository::getCRLs) - .wasInvoked(exactly = once) - } - - @Test - fun givenExpiredCRL_whenTimeElapses_thenCheckRevocationList() = runTest { - val lastCheck = Clock.System.now() - 24.hours - val (arrangement, checkCrlWorker) = Arrangement() - .withObserveLastCRLCheckInstantReturning(flowOf(lastCheck)) - .withIncrementalSyncState(flowOf(IncrementalSyncStatus.Live)) - .withSetLastCRLCheckInstantReturning(Either.Right(Unit)) - .withExpiredCRL() - .withCheckRevocationListResult() - .arrange() - - checkCrlWorker.execute() - - verify(arrangement.crlRepository) - .suspendFunction(arrangement.crlRepository::getCRLs) - .wasInvoked(exactly = once) - - verify(arrangement.checkRevocationList) - .suspendFunction(arrangement.checkRevocationList::invoke) - .with(eq(DUMMY_URL)) - .wasInvoked(exactly = once) - - verify(arrangement.crlRepository) - .suspendFunction(arrangement.crlRepository::addOrUpdateCRL) - .with(eq(DUMMY_URL), eq(FUTURE_TIMESTAMP)) - .wasInvoked(exactly = once) - - } - - private class Arrangement { - - @Mock - val crlRepository = mock(classOf()) - - @Mock - val incrementalSyncRepository = mock(classOf()) - - @Mock - val checkRevocationList = mock(classOf()) - - fun arrange() = this to CheckCrlWorkerImpl( - crlRepository, incrementalSyncRepository, checkRevocationList - ) - - fun withNoCRL() = apply { - given(crlRepository) - .suspendFunction(crlRepository::getCRLs) - .whenInvoked() - .thenReturn(null) - } - - fun withNonExpiredCRL() = apply { - given(crlRepository) - .suspendFunction(crlRepository::getCRLs) - .whenInvoked() - .thenReturn(CRLUrlExpirationList(listOf(CRLWithExpiration(DUMMY_URL, FUTURE_TIMESTAMP)))) - } - - fun withExpiredCRL() = apply { - given(crlRepository) - .suspendFunction(crlRepository::getCRLs) - .whenInvoked() - .thenReturn(CRLUrlExpirationList(listOf(CRLWithExpiration(DUMMY_URL, TIMESTAMP)))) - } - fun withCheckRevocationListResult() = apply { - given(checkRevocationList) - .suspendFunction(checkRevocationList::invoke) - .whenInvokedWith(any()) - .thenReturn(Either.Right(FUTURE_TIMESTAMP)) - } - - fun withObserveLastCRLCheckInstantReturning(flow: Flow) = apply { - given(crlRepository) - .suspendFunction(crlRepository::lastCrlCheckInstantFlow) - .whenInvoked() - .thenReturn(flow) - } - - fun withIncrementalSyncState(flow: Flow) = apply { - given(incrementalSyncRepository) - .getter(incrementalSyncRepository::incrementalSyncState) - .whenInvoked() - .thenReturn(flow) - } - - fun withSetLastCRLCheckInstantReturning(result: Either) = apply { - given(crlRepository) - .suspendFunction(crlRepository::setLastCRLCheckInstant) - .whenInvokedWith(any()) - .thenReturn(result) - } - } - - companion object { - const val DUMMY_URL = "https://dummy.url" - val TIMESTAMP = 633218892.toULong() // Wednesday, 24 January 1990 22:08:12 - val FUTURE_TIMESTAMP = 4104511692.toULong() // Sunday, 24 January 2100 22:08:12 - } -} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/CheckRevocationListUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/CheckRevocationListUseCaseTest.kt index 603c1fc78b6..51d3a7511c7 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/CheckRevocationListUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/CheckRevocationListUseCaseTest.kt @@ -22,7 +22,7 @@ import com.wire.kalium.cryptography.MLSClient import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.E2EIFailure import com.wire.kalium.logic.data.client.MLSClientProvider -import com.wire.kalium.logic.data.e2ei.CrlRepository +import com.wire.kalium.logic.data.e2ei.CertificateRevocationListRepository import com.wire.kalium.logic.data.id.CurrentClientIdProvider import com.wire.kalium.logic.feature.conversation.MLSConversationsVerificationStatusesHandler import com.wire.kalium.logic.framework.TestClient @@ -54,8 +54,8 @@ class CheckRevocationListUseCaseTest { val result = checkRevocationList.invoke(DUMMY_URL) result.shouldFail() - verify(arrangement.crlRepository) - .suspendFunction(arrangement.crlRepository::getClientDomainCRL) + verify(arrangement.certificateRevocationListRepository) + .suspendFunction(arrangement.certificateRevocationListRepository::getClientDomainCRL) .with(any()) .wasInvoked(once) @@ -76,8 +76,8 @@ class CheckRevocationListUseCaseTest { val result = checkRevocationList.invoke(DUMMY_URL) result.shouldFail() - verify(arrangement.crlRepository) - .suspendFunction(arrangement.crlRepository::getClientDomainCRL) + verify(arrangement.certificateRevocationListRepository) + .suspendFunction(arrangement.certificateRevocationListRepository::getClientDomainCRL) .with(any()) .wasInvoked(once) @@ -178,7 +178,7 @@ class CheckRevocationListUseCaseTest { internal class Arrangement { @Mock - val crlRepository = mock(classOf()) + val certificateRevocationListRepository = mock(classOf()) @Mock val mlsClient = mock(classOf()) @@ -196,22 +196,22 @@ class CheckRevocationListUseCaseTest { mock(classOf()) fun arrange() = this to CheckRevocationListUseCaseImpl( - crlRepository = crlRepository, + certificateRevocationListRepository = certificateRevocationListRepository, currentClientIdProvider = currentClientIdProvider, mlsClientProvider = mlsClientProvider, mLSConversationsVerificationStatusesHandler = mLSConversationsVerificationStatusesHandler ) fun withE2EIRepositoryFailure() = apply { - given(crlRepository) - .suspendFunction(crlRepository::getClientDomainCRL) + given(certificateRevocationListRepository) + .suspendFunction(certificateRevocationListRepository::getClientDomainCRL) .whenInvokedWith(any()) .thenReturn(Either.Left(E2EIFailure.Generic(Exception()))) } fun withE2EIRepositorySuccess() = apply { - given(crlRepository) - .suspendFunction(crlRepository::getClientDomainCRL) + given(certificateRevocationListRepository) + .suspendFunction(certificateRevocationListRepository::getClientDomainCRL) .whenInvokedWith(any()) .thenReturn(Either.Right("result".toByteArray())) }