From 6394c7efde0adc837f85a844f7018dd597e4c85b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 6 Sep 2022 14:47:18 +0200 Subject: [PATCH] GetDeviceFullInfoListUseCase unit tests --- .../v2/GetDeviceFullInfoListUseCase.kt | 5 +- ...rrentSessionCrossSigningInfoUseCaseTest.kt | 16 +- .../v2/GetDeviceFullInfoListUseCaseTest.kt | 182 ++++++++++++++++++ .../im/vector/app/test/fakes/FakeSession.kt | 11 ++ 4 files changed, 199 insertions(+), 15 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt index 948d23de39..da2cf25f39 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt @@ -27,7 +27,6 @@ import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo import org.matrix.android.sdk.flow.flow import javax.inject.Inject -// TODO add unit tests class GetDeviceFullInfoListUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase, @@ -58,9 +57,9 @@ class GetDeviceFullInfoListUseCase @Inject constructor( .sortedByDescending { it.lastSeenTs } .map { deviceInfo -> val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId } - val trustLevelForShield = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo) + val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo) val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs ?: 0) - DeviceFullInfo(deviceInfo, cryptoDeviceInfo, trustLevelForShield, isInactive) + DeviceFullInfo(deviceInfo, cryptoDeviceInfo, roomEncryptionTrustLevel, isInactive) } } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt index 178f388c7f..f8ee1231ae 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt @@ -17,6 +17,7 @@ package im.vector.app.features.settings.devices.v2 import im.vector.app.test.fakes.FakeActiveSessionHolder +import im.vector.app.test.fakes.FakeSession import im.vector.app.test.test import im.vector.app.test.testDispatcher import io.mockk.every @@ -30,11 +31,8 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.matrix.android.sdk.api.auth.data.SessionParams -import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.util.toOptional -import org.matrix.android.sdk.flow.FlowSession -import org.matrix.android.sdk.flow.flow private const val A_DEVICE_ID = "device-id" @@ -59,7 +57,7 @@ class GetCurrentSessionCrossSigningInfoUseCaseTest { @Test fun `given the active session and existing cross signing info when getting these info then the result is correct`() = runTest(testDispatcher) { val fakeSession = givenSession(A_DEVICE_ID) - val fakeFlowSession = givenFlowSession(fakeSession) + val fakeFlowSession = fakeSession.givenFlowSession() val isCrossSigningVerified = true val mxCrossSigningInfo = givenMxCrossSigningInfo(isCrossSigningVerified) every { fakeFlowSession.liveCrossSigningInfo(any()) } returns flowOf(mxCrossSigningInfo.toOptional()) @@ -80,7 +78,7 @@ class GetCurrentSessionCrossSigningInfoUseCaseTest { @Test fun `given the active session and no existing cross signing info when getting these info then the result is correct`() = runTest(testDispatcher) { val fakeSession = givenSession(A_DEVICE_ID) - val fakeFlowSession = givenFlowSession(fakeSession) + val fakeFlowSession = fakeSession.givenFlowSession() val mxCrossSigningInfo = null every { fakeFlowSession.liveCrossSigningInfo(any()) } returns flowOf(mxCrossSigningInfo.toOptional()) val expectedResult = CurrentSessionCrossSigningInfo( @@ -108,7 +106,7 @@ class GetCurrentSessionCrossSigningInfoUseCaseTest { .finish() } - private fun givenSession(deviceId: String): Session { + private fun givenSession(deviceId: String): FakeSession { val sessionParams = mockk() every { sessionParams.deviceId } returns deviceId @@ -118,12 +116,6 @@ class GetCurrentSessionCrossSigningInfoUseCaseTest { return fakeSession } - private fun givenFlowSession(session: Session): FlowSession { - val fakeFlowSession = mockk() - every { session.flow() } returns fakeFlowSession - return fakeFlowSession - } - private fun givenMxCrossSigningInfo(isTrusted: Boolean) = mockk() .also { every { it.isTrusted() } returns isTrusted diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt new file mode 100644 index 0000000000..739d5c6668 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase +import im.vector.app.test.fakes.FakeActiveSessionHolder +import im.vector.app.test.test +import im.vector.app.test.testDispatcher +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.verify +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel + +private const val A_DEVICE_ID_1 = "device-id-1" +private const val A_DEVICE_ID_2 = "device-id-2" +private const val A_DEVICE_ID_3 = "device-id-3" +private const val A_TIMESTAMP_1 = 100L +private const val A_TIMESTAMP_2 = 200L +private const val A_TIMESTAMP_3 = 300L + +class GetDeviceFullInfoListUseCaseTest { + + private val fakeActiveSessionHolder = FakeActiveSessionHolder() + private val checkIfSessionIsInactiveUseCase = mockk() + private val getEncryptionTrustLevelForDeviceUseCase = mockk() + private val getCurrentSessionCrossSigningInfoUseCase = mockk() + + private val getDeviceFullInfoListUseCase = GetDeviceFullInfoListUseCase( + activeSessionHolder = fakeActiveSessionHolder.instance, + checkIfSessionIsInactiveUseCase = checkIfSessionIsInactiveUseCase, + getEncryptionTrustLevelForDeviceUseCase = getEncryptionTrustLevelForDeviceUseCase, + getCurrentSessionCrossSigningInfoUseCase = getCurrentSessionCrossSigningInfoUseCase, + ) + + @Before + fun setUp() { + mockkStatic("org.matrix.android.sdk.flow.FlowSessionKt") + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given active session when getting list of device full info then the result list is correct and sorted in descending order`() = runTest(testDispatcher) { + // Given + val currentSessionCrossSigningInfo = givenCurrentSessionCrossSigningInfo() + val fakeFlowSession = fakeActiveSessionHolder.fakeSession.givenFlowSession() + val cryptoDeviceInfo1 = givenACryptoDeviceInfo(A_DEVICE_ID_1) + val cryptoDeviceInfo2 = givenACryptoDeviceInfo(A_DEVICE_ID_2) + val cryptoDeviceInfo3 = givenACryptoDeviceInfo(A_DEVICE_ID_3) + val cryptoDeviceInfoList = listOf(cryptoDeviceInfo1, cryptoDeviceInfo2, cryptoDeviceInfo3) + every { fakeFlowSession.liveUserCryptoDevices(any()) } returns flowOf(cryptoDeviceInfoList) + val deviceInfo1 = givenADevicesInfo( + deviceId = A_DEVICE_ID_1, + lastSeenTs = A_TIMESTAMP_1, + isInactive = true, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, + cryptoDeviceInfo = cryptoDeviceInfo1 + ) + val deviceInfo2 = givenADevicesInfo( + deviceId = A_DEVICE_ID_2, + lastSeenTs = A_TIMESTAMP_2, + isInactive = false, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, + cryptoDeviceInfo = cryptoDeviceInfo2 + ) + val deviceInfo3 = givenADevicesInfo( + deviceId = A_DEVICE_ID_3, + lastSeenTs = A_TIMESTAMP_3, + isInactive = false, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, + cryptoDeviceInfo = cryptoDeviceInfo3 + ) + val deviceInfoList = listOf(deviceInfo1, deviceInfo2, deviceInfo3) + every { fakeFlowSession.liveMyDevicesInfo() } returns flowOf(deviceInfoList) + val expectedResult1 = DeviceFullInfo( + deviceInfo = deviceInfo1, + cryptoDeviceInfo = cryptoDeviceInfo1, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, + isInactive = true + ) + val expectedResult2 = DeviceFullInfo( + deviceInfo = deviceInfo2, + cryptoDeviceInfo = cryptoDeviceInfo2, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, + isInactive = false + ) + val expectedResult3 = DeviceFullInfo( + deviceInfo = deviceInfo3, + cryptoDeviceInfo = cryptoDeviceInfo3, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, + isInactive = false + ) + val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1) + + // When + val result = getDeviceFullInfoListUseCase.execute() + .test(this) + + // Then + result.assertValues(expectedResult) + .finish() + verify { + getCurrentSessionCrossSigningInfoUseCase.execute() + fakeFlowSession.liveUserCryptoDevices(fakeActiveSessionHolder.fakeSession.myUserId) + fakeFlowSession.liveMyDevicesInfo() + getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo1) + getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo2) + getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo3) + checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP_1) + checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP_2) + checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP_3) + } + } + + @Test + fun `given no active session when getting list then the result is empty`() = runTest(testDispatcher) { + // Given + fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null) + + // When + val result = getDeviceFullInfoListUseCase.execute() + .test(this) + + // Then + result.assertNoValues() + .finish() + } + + private fun givenCurrentSessionCrossSigningInfo(): CurrentSessionCrossSigningInfo { + val currentSessionCrossSigningInfo = mockk() + every { getCurrentSessionCrossSigningInfoUseCase.execute() } returns flowOf(currentSessionCrossSigningInfo) + return currentSessionCrossSigningInfo + } + + private fun givenACryptoDeviceInfo(deviceId: String): CryptoDeviceInfo { + val cryptoDeviceInfo = mockk() + every { cryptoDeviceInfo.deviceId } returns deviceId + return cryptoDeviceInfo + } + + private fun givenADevicesInfo( + deviceId: String, + lastSeenTs: Long, + isInactive: Boolean, + roomEncryptionTrustLevel: RoomEncryptionTrustLevel, + cryptoDeviceInfo: CryptoDeviceInfo, + ): DeviceInfo { + val deviceInfo = mockk() + every { deviceInfo.deviceId } returns deviceId + every { deviceInfo.lastSeenTs } returns lastSeenTs + every { getEncryptionTrustLevelForDeviceUseCase.execute(any(), cryptoDeviceInfo) } returns roomEncryptionTrustLevel + every { checkIfSessionIsInactiveUseCase.execute(lastSeenTs) } returns isInactive + + return deviceInfo + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt index 71bcde5807..35d23e35e8 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt @@ -32,6 +32,8 @@ import org.matrix.android.sdk.api.session.getRoomSummary import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.flow.FlowSession +import org.matrix.android.sdk.flow.flow class FakeSession( val fakeCryptoService: FakeCryptoService = FakeCryptoService(), @@ -76,6 +78,15 @@ class FakeSession( every { this@FakeSession.sessionParams } returns sessionParams } + /** + * Do not forget to call mockkStatic("org.matrix.android.sdk.flow.FlowSessionKt") in the setup method of the tests. + */ + fun givenFlowSession(): FlowSession { + val fakeFlowSession = mockk() + every { flow() } returns fakeFlowSession + return fakeFlowSession + } + companion object { fun withRoomSummary(roomSummary: RoomSummary) = FakeSession().apply {