diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientModel.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientModel.kt index 822135a23c4..50399a6fb6f 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientModel.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientModel.kt @@ -20,7 +20,9 @@ package com.wire.kalium.logic.data.client import com.wire.kalium.cryptography.PreKeyCrypto import com.wire.kalium.logic.data.conversation.ClientId +import kotlinx.datetime.Clock import kotlinx.datetime.Instant +import kotlin.time.Duration.Companion.days data class RegisterClientParam( val password: String?, @@ -60,7 +62,11 @@ data class Client( val label: String?, val model: String?, val isMLSCapable: Boolean -) +) { + companion object { + val INACTIVE_DURATION = 28.days + } +} enum class ClientType { Temporary, @@ -86,3 +92,12 @@ data class OtherUserClient( val isValid: Boolean, val isVerified: Boolean ) + +/** + * True if the client is considered to be in active use. + * + * A client is considered active if it has connected to the backend within + * the `INACTIVE_DURATION`. + */ +val Client.isActive: Boolean + get() = lastActive?.let { (Clock.System.now() - it) < Client.INACTIVE_DURATION } ?: false diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCase.kt index 7151aba44e0..228343bc652 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCase.kt @@ -22,6 +22,7 @@ import com.wire.kalium.logic.NetworkFailure import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.client.Client import com.wire.kalium.logic.data.client.ClientRepository +import com.wire.kalium.logic.data.client.isActive import com.wire.kalium.logic.data.featureConfig.FeatureConfigRepository import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel import com.wire.kalium.logic.data.featureConfig.MLSModel @@ -99,10 +100,10 @@ internal class UpdateSupportedProtocolsUseCaseImpl( ): Boolean { val mlsIsSupported = mlsConfiguration.supportedProtocols.contains(SupportedProtocol.MLS) val mlsMigrationHasEnded = migrationConfiguration.hasMigrationEnded() - val allSelfClientsAreMLSCapable = selfClients.all { it.isMLSCapable } + val allSelfClientsAreMLSCapable = selfClients.filter { it.isActive }.all { it.isMLSCapable } kaliumLogger.d( "mls is supported = $mlsIsSupported, " + - "all self clients are mls capable = $allSelfClientsAreMLSCapable " + + "all active self clients are mls capable = $allSelfClientsAreMLSCapable " + "migration has ended = $mlsMigrationHasEnded" ) return mlsIsSupported && (mlsMigrationHasEnded || allSelfClientsAreMLSCapable) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/client/ClientTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/client/ClientTest.kt new file mode 100644 index 00000000000..eeb5e0b3c36 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/client/ClientTest.kt @@ -0,0 +1,53 @@ +/* + * Wire + * Copyright (C) 2023 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.data.client + +import com.wire.kalium.logic.framework.TestClient +import kotlinx.datetime.Clock +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.days + +class ClientTest { + + @Test + fun givenLastActiveIsNull_thenIsActiveIsFalse() { + val client = TestClient.CLIENT.copy( + lastActive = null + ) + assertFalse(client.isActive) + } + + @Test + fun givenLastActiveIsOlderThanInactivityDuration_thenIsActiveIsFalse() { + val client = TestClient.CLIENT.copy( + lastActive = Clock.System.now() - (Client.INACTIVE_DURATION + 1.days) + ) + assertFalse(client.isActive) + } + + @Test + fun givenLastActiveIsNewerThanInactivityDuration_thenIsActiveIsTrue() { + val client = TestClient.CLIENT.copy( + lastActive = Clock.System.now() - (Client.INACTIVE_DURATION - 1.days) + ) + assertTrue(client.isActive) + } + +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCaseTest.kt index 0b99e80bd20..1250a212dc7 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCaseTest.kt @@ -42,6 +42,7 @@ import io.mockative.once import io.mockative.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Clock import kotlinx.datetime.Instant import kotlin.test.Test @@ -129,7 +130,7 @@ class UpdateSupportedProtocolsUseCaseTest { } @Test - fun givenMlsIsSupportedAndAllClientsAreCapable_whenInvokingUseCase_thenMlsIsIncluded() = runTest { + fun givenMlsIsSupportedAndAllActiveClientsAreCapable_whenInvokingUseCase_thenMlsIsIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() .withGetFeatureConfigurationSuccessful( @@ -137,7 +138,7 @@ class UpdateSupportedProtocolsUseCaseTest { migrationConfiguration = ONGOING_MIGRATION_CONFIGURATION ) .withGetSelfClientsSuccessful(clients = listOf( - TestClient.CLIENT.copy(isMLSCapable = true) + TestClient.CLIENT.copy(isMLSCapable = true, lastActive = Clock.System.now()) )) .withUpdateSupportedProtocolsSuccessful() .arrange() @@ -151,7 +152,7 @@ class UpdateSupportedProtocolsUseCaseTest { } @Test - fun givenMlsIsSupportedAndAllClientsAreNotCapable_whenInvokingUseCase_thenMlsIsNotIncluded() = runTest { + fun givenMlsIsSupportedAndAnInactiveClientIsNotMlsCapable_whenInvokingUseCase_thenMlsIsIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() .withGetFeatureConfigurationSuccessful( @@ -159,8 +160,31 @@ class UpdateSupportedProtocolsUseCaseTest { migrationConfiguration = ONGOING_MIGRATION_CONFIGURATION ) .withGetSelfClientsSuccessful(clients = listOf( - TestClient.CLIENT.copy(isMLSCapable = true), - TestClient.CLIENT.copy(isMLSCapable = false) + TestClient.CLIENT.copy(isMLSCapable = true, lastActive = Clock.System.now()), + TestClient.CLIENT.copy(isMLSCapable = false, lastActive = Instant.DISTANT_PAST) + )) + .withUpdateSupportedProtocolsSuccessful() + .arrange() + + useCase.invoke() + + verify(arrangement.userRepository) + .suspendFunction(arrangement.userRepository::updateSupportedProtocols) + .with(matching { it.contains(SupportedProtocol.MLS) }) + .wasInvoked(exactly = once) + } + + @Test + fun givenMlsIsSupportedAndAllActiveClientsAreNotCapable_whenInvokingUseCase_thenMlsIsNotIncluded() = runTest { + val (arrangement, useCase) = Arrangement() + .withGetSelfUserSuccessful() + .withGetFeatureConfigurationSuccessful( + supportedProtocols = setOf(SupportedProtocol.MLS), + migrationConfiguration = ONGOING_MIGRATION_CONFIGURATION + ) + .withGetSelfClientsSuccessful(clients = listOf( + TestClient.CLIENT.copy(isMLSCapable = true, lastActive = Clock.System.now()), + TestClient.CLIENT.copy(isMLSCapable = false, lastActive = Clock.System.now()) )) .withUpdateSupportedProtocolsSuccessful() .arrange()