From e3ec61bc4b6e03e8b151929519702ef779cbb013 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 11 Sep 2025 16:16:44 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/data/RuntimeUserWalletsStore.kt | 3 + .../data/UserWalletsStoreRepositoryProxy.kt | 5 +- .../tap/di/domain/AccountDomainModule.kt | 13 + .../local/userwallet/UserWalletsStore.kt | 2 + .../DefaultAccountsCRUDRepository.kt | 12 +- .../DefaultAccountsCRUDRepositoryTest.kt | 90 ++++++ .../repository/AccountsCRUDRepository.kt | 15 +- .../GetUnoccupiedAccountIndexUseCase.kt | 2 +- .../usecase/IsAccountsModeEnabledUseCase.kt | 66 ++++ .../GetUnoccupiedAccountIndexUseCaseTest.kt | 8 +- .../IsAccountsModeEnabledUseCaseTest.kt | 301 ++++++++++++++++++ 11 files changed, 509 insertions(+), 8 deletions(-) create mode 100644 domain/account/src/main/java/com/tangem/domain/account/usecase/IsAccountsModeEnabledUseCase.kt create mode 100644 domain/account/src/test/kotlin/com/tangem/domain/account/usecase/IsAccountsModeEnabledUseCaseTest.kt diff --git a/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt b/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt index d780f3d15a..d0fd68250c 100644 --- a/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt +++ b/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt @@ -19,6 +19,9 @@ internal class RuntimeUserWalletsStore( override val userWallets: Flow> get() = userWalletsListManager.userWallets + override val userWalletsSync: List + get() = userWalletsListManager.userWalletsSync + override fun getSyncOrNull(key: UserWalletId): UserWallet? { return userWalletsListManager.userWalletsSync.firstOrNull { it.walletId == key } } diff --git a/app/src/main/java/com/tangem/tap/data/UserWalletsStoreRepositoryProxy.kt b/app/src/main/java/com/tangem/tap/data/UserWalletsStoreRepositoryProxy.kt index c5275bf034..d41ea29466 100644 --- a/app/src/main/java/com/tangem/tap/data/UserWalletsStoreRepositoryProxy.kt +++ b/app/src/main/java/com/tangem/tap/data/UserWalletsStoreRepositoryProxy.kt @@ -3,9 +3,9 @@ package com.tangem.tap.data import com.tangem.common.CompletionResult import com.tangem.common.catching import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.core.wallets.UserWalletsListRepository import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.core.wallets.UserWalletsListRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -24,6 +24,9 @@ class UserWalletsStoreRepositoryProxy( } } + override val userWalletsSync: List + get() = userWalletsListRepository.userWallets.value.orEmpty() + override fun getSyncOrNull(key: UserWalletId): UserWallet? { return userWalletsListRepository.userWallets.value?.find { it.walletId == key } } diff --git a/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt index 8cf8edf8d6..c4036dd181 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt @@ -1,5 +1,6 @@ package com.tangem.tap.di.domain +import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.repository.AccountsCRUDRepository import com.tangem.domain.account.usecase.* import dagger.Module @@ -49,4 +50,16 @@ internal object AccountDomainModule { ): GetUnoccupiedAccountIndexUseCase { return GetUnoccupiedAccountIndexUseCase(crudRepository = accountsCRUDRepository) } + + @Provides + @Singleton + fun provideIsAccountsModeEnabledUseCase( + accountsCRUDRepository: AccountsCRUDRepository, + accountsFeatureToggles: AccountsFeatureToggles, + ): IsAccountsModeEnabledUseCase { + return IsAccountsModeEnabledUseCase( + crudRepository = accountsCRUDRepository, + accountsFeatureToggles = accountsFeatureToggles, + ) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt index b4d50f01de..8f00255449 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt @@ -15,6 +15,8 @@ interface UserWalletsStore { val userWallets: Flow> + val userWalletsSync: List + fun getSyncOrNull(key: UserWalletId): UserWallet? fun getSyncStrict(key: UserWalletId): UserWallet diff --git a/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt b/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt index 80e69fc536..a9ceb3026e 100644 --- a/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt +++ b/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt @@ -25,6 +25,7 @@ import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext /** @@ -108,7 +109,7 @@ internal class DefaultAccountsCRUDRepository( walletAccountsSaver.pushAndStore(userWalletId = userWalletId, response = accountsResponse) } - override suspend fun getTotalAccountsCount(userWalletId: UserWalletId): Option = option { + override suspend fun getTotalAccountsCountSync(userWalletId: UserWalletId): Option = option { val accountListResponse = getAccountsResponseSync(userWalletId = userWalletId) ensureNotNull(accountListResponse) @@ -116,10 +117,19 @@ internal class DefaultAccountsCRUDRepository( return accountListResponse.wallet.totalAccounts.toOption() } + override fun getTotalAccountsCount(userWalletId: UserWalletId): Flow> { + return getAccountsResponseStore(userWalletId = userWalletId).data + .map { it?.wallet?.totalAccounts.toOption() } + } + override fun getUserWallet(userWalletId: UserWalletId): UserWallet { return userWalletsStore.getSyncStrict(userWalletId) } + override fun getUserWallets(): Flow> = userWalletsStore.userWallets + + override fun getUserWalletsSync(): List = userWalletsStore.userWalletsSync + private suspend fun getETag(userWalletId: UserWalletId): String? { return eTagsStore.getSyncOrNull(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts) } diff --git a/data/account/src/test/java/com/tangem/data/account/repository/DefaultAccountsCRUDRepositoryTest.kt b/data/account/src/test/java/com/tangem/data/account/repository/DefaultAccountsCRUDRepositoryTest.kt index 5f18006b72..1e7c97fc64 100644 --- a/data/account/src/test/java/com/tangem/data/account/repository/DefaultAccountsCRUDRepositoryTest.kt +++ b/data/account/src/test/java/com/tangem/data/account/repository/DefaultAccountsCRUDRepositoryTest.kt @@ -648,4 +648,94 @@ class DefaultAccountsCRUDRepositoryTest { } } } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class GetTotalAccountsCountSync { + + @Test + fun `getTotalAccountsCountSync returns None if account list response is null`() = runTest { + // Arrange + accountsResponseStoreFlow.value = null + + // Act + val actual = repository.getTotalAccountsCountSync(userWalletId) + + // Assert + Truth.assertThat(actual).isEqualTo(None) + + verifyOrder { + accountsResponseStoreFactory.create(userWalletId) + accountsResponseStore.data + } + } + + @Test + fun `getTotalAccountsCountSync returns Some with totalAccounts when response is valid`() = runTest { + // Arrange + val totalAccounts = 5 + val response = mockk { + every { this@mockk.wallet.totalAccounts } returns totalAccounts + } + + accountsResponseStoreFlow.value = response + + // Act + val actual = repository.getTotalAccountsCountSync(userWalletId) + + // Assert + val expected = totalAccounts.toOption() + Truth.assertThat(actual).isEqualTo(expected) + + verifyOrder { + accountsResponseStoreFactory.create(userWalletId) + accountsResponseStore.data + } + } + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class GetTotalAccountsCount { + + @Test + fun `getTotalAccountsCount emits 0 when account list response is null`() = runTest { + // Arrange + accountsResponseStoreFlow.value = null + + // Act + val flow = repository.getTotalAccountsCount(userWalletId) + val actual = getEmittedValues(flow) + + // Assert + Truth.assertThat(actual).containsExactly(None) + + verifyOrder { + accountsResponseStoreFactory.create(userWalletId) + accountsResponseStore.data + } + } + + @Test + fun `getTotalAccountsCount emits correct value when response is valid`() = runTest { + // Arrange + val totalAccounts = 7 + val response = mockk { + every { this@mockk.wallet.totalAccounts } returns totalAccounts + } + + accountsResponseStoreFlow.value = response + + // Act + val flow = repository.getTotalAccountsCount(userWalletId) + val actual = getEmittedValues(flow) + + // Assert + Truth.assertThat(actual).containsExactly(totalAccounts.toOption()) + verifyOrder { + accountsResponseStoreFactory.create(userWalletId) + accountsResponseStore.data + } + } + } } \ No newline at end of file diff --git a/domain/account/src/main/java/com/tangem/domain/account/repository/AccountsCRUDRepository.kt b/domain/account/src/main/java/com/tangem/domain/account/repository/AccountsCRUDRepository.kt index 8904840bcb..e58992d600 100644 --- a/domain/account/src/main/java/com/tangem/domain/account/repository/AccountsCRUDRepository.kt +++ b/domain/account/src/main/java/com/tangem/domain/account/repository/AccountsCRUDRepository.kt @@ -73,7 +73,14 @@ interface AccountsCRUDRepository { * * @param userWalletId the unique identifier of the user wallet */ - suspend fun getTotalAccountsCount(userWalletId: UserWalletId): Option + suspend fun getTotalAccountsCountSync(userWalletId: UserWalletId): Option + + /** + * Provides a flow of the total count of accounts associated with a specific user wallet including archived accounts + * + * @param userWalletId the unique identifier of the user wallet + */ + fun getTotalAccountsCount(userWalletId: UserWalletId): Flow> /** * Retrieves a user wallet by its unique identifier @@ -82,4 +89,10 @@ interface AccountsCRUDRepository { * @return the [UserWallet] associated with the given identifier */ fun getUserWallet(userWalletId: UserWalletId): UserWallet + + /** Provides a flow of all user wallets */ + fun getUserWallets(): Flow> + + /** Synchronously retrieves all user wallets */ + fun getUserWalletsSync(): List } \ No newline at end of file diff --git a/domain/account/src/main/java/com/tangem/domain/account/usecase/GetUnoccupiedAccountIndexUseCase.kt b/domain/account/src/main/java/com/tangem/domain/account/usecase/GetUnoccupiedAccountIndexUseCase.kt index f511ab03b8..9fbb2ed7d5 100644 --- a/domain/account/src/main/java/com/tangem/domain/account/usecase/GetUnoccupiedAccountIndexUseCase.kt +++ b/domain/account/src/main/java/com/tangem/domain/account/usecase/GetUnoccupiedAccountIndexUseCase.kt @@ -35,7 +35,7 @@ class GetUnoccupiedAccountIndexUseCase( private suspend fun Raise.getTotalAccountsCount(userWalletId: UserWalletId): Int { return catch( - block = { crudRepository.getTotalAccountsCount(userWalletId = userWalletId) }, + block = { crudRepository.getTotalAccountsCountSync(userWalletId = userWalletId) }, catch = { raise(Error.DataOperationFailed(cause = it)) }, ) .getOrElse { raise(Error.DataNotFound) } diff --git a/domain/account/src/main/java/com/tangem/domain/account/usecase/IsAccountsModeEnabledUseCase.kt b/domain/account/src/main/java/com/tangem/domain/account/usecase/IsAccountsModeEnabledUseCase.kt new file mode 100644 index 0000000000..9c295b058d --- /dev/null +++ b/domain/account/src/main/java/com/tangem/domain/account/usecase/IsAccountsModeEnabledUseCase.kt @@ -0,0 +1,66 @@ +package com.tangem.domain.account.usecase + +import arrow.core.Option +import arrow.core.getOrElse +import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles +import com.tangem.domain.account.repository.AccountsCRUDRepository +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.isMultiCurrency +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.* + +/** + * Use case to determine if the accounts mode is enabled. + * Accounts mode is considered enabled if there are at least two accounts in any of the user wallets that support + * multiple currencies. + * + * @property crudRepository repository to interact with user wallets and their accounts + * +[REDACTED_AUTHOR] + */ +class IsAccountsModeEnabledUseCase( + private val crudRepository: AccountsCRUDRepository, + private val accountsFeatureToggles: AccountsFeatureToggles, +) { + + @OptIn(ExperimentalCoroutinesApi::class) + operator fun invoke(): Flow { + if (!accountsFeatureToggles.isFeatureEnabled) return flowOf(value = false) + + return crudRepository.getUserWallets() + .flatMapLatest { userWallets -> + val totalAccountsCountList = getTotalAccountsCountList(userWallets) + + combine(flows = totalAccountsCountList) { it.toList().isModeEnabled() } + } + .onEmpty { emit(false) } + } + + suspend fun invokeSync(): Boolean { + if (!accountsFeatureToggles.isFeatureEnabled) return false + + return crudRepository.getUserWalletsSync() + .map { userWallet -> + // If the wallet does not support multiple currencies, we consider its account count as 0 + if (!userWallet.isMultiCurrency) return@map 0 + + crudRepository.getTotalAccountsCountSync(userWalletId = userWallet.walletId).getOrZero() + } + .isModeEnabled() + } + + private fun getTotalAccountsCountList(userWallets: List): List> { + return userWallets + .map { userWallet -> + // If the wallet does not support multiple currencies, we consider its account count as 0 + if (!userWallet.isMultiCurrency) return@map flowOf(0) + + crudRepository.getTotalAccountsCount(userWalletId = userWallet.walletId) + .map { maybeCount -> maybeCount.getOrZero() } + } + } + + private fun Option.getOrZero(): Int = getOrElse { 0 } + + private fun List.isModeEnabled(): Boolean = any { it >= 2 } +} \ No newline at end of file diff --git a/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/GetUnoccupiedAccountIndexUseCaseTest.kt b/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/GetUnoccupiedAccountIndexUseCaseTest.kt index a0ba98b709..5f85fe95ef 100644 --- a/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/GetUnoccupiedAccountIndexUseCaseTest.kt +++ b/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/GetUnoccupiedAccountIndexUseCaseTest.kt @@ -30,7 +30,7 @@ class GetUnoccupiedAccountIndexUseCaseTest { @Test fun `invoke should return next unoccupied index when repository returns count`() = runTest { // Arrange - coEvery { crudRepository.getTotalAccountsCount(userWalletId) } returns 3.toOption() + coEvery { crudRepository.getTotalAccountsCountSync(userWalletId) } returns 3.toOption() // Act val actual = useCase(userWalletId = userWalletId) @@ -39,14 +39,14 @@ class GetUnoccupiedAccountIndexUseCaseTest { val expected = DerivationIndex(4) Truth.assertThat(actual).isEqualTo(expected) - coVerify { crudRepository.getTotalAccountsCount(userWalletId) } + coVerify { crudRepository.getTotalAccountsCountSync(userWalletId) } } @Test fun `invoke should return error if repository throws exception`() = runTest { // Arrange val exception = IllegalStateException("Test error") - coEvery { crudRepository.getTotalAccountsCount(userWalletId) } throws exception + coEvery { crudRepository.getTotalAccountsCountSync(userWalletId) } throws exception // Act val actual = useCase(userWalletId = userWalletId) @@ -55,6 +55,6 @@ class GetUnoccupiedAccountIndexUseCaseTest { val expected = GetUnoccupiedAccountIndexUseCase.Error.DataOperationFailed(exception).left() Truth.assertThat(actual).isEqualTo(expected) - coVerify { crudRepository.getTotalAccountsCount(userWalletId) } + coVerify { crudRepository.getTotalAccountsCountSync(userWalletId) } } } \ No newline at end of file diff --git a/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/IsAccountsModeEnabledUseCaseTest.kt b/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/IsAccountsModeEnabledUseCaseTest.kt new file mode 100644 index 0000000000..06b7c267d0 --- /dev/null +++ b/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/IsAccountsModeEnabledUseCaseTest.kt @@ -0,0 +1,301 @@ +package com.tangem.domain.account.usecase + +import arrow.core.none +import arrow.core.some +import com.google.common.truth.Truth +import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles +import com.tangem.domain.account.repository.AccountsCRUDRepository +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.models.wallet.isMultiCurrency +import io.mockk.* +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class IsAccountsModeEnabledUseCaseTest { + + private val accountsCRUDRepository: AccountsCRUDRepository = mockk() + private val featureToggles: AccountsFeatureToggles = mockk() + + private val useCase = IsAccountsModeEnabledUseCase(accountsCRUDRepository, featureToggles) + + @AfterEach + fun tearDown() { + clearMocks(accountsCRUDRepository, featureToggles) + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class Invoke { + + @Test + fun `returns false when feature is disabled`() = runTest { + // Arrange + every { featureToggles.isFeatureEnabled } returns false + + // Act + val actual = useCase.invoke().firstOrNull() + + // Assert + Truth.assertThat(actual).isFalse() + + verify(exactly = 1) { featureToggles.isFeatureEnabled } + verify(inverse = true) { accountsCRUDRepository.getUserWallets() } + } + + @Test + fun `returns false when getUserWallets emits empty flow`() = runTest { + // Arrange + every { featureToggles.isFeatureEnabled } returns true + every { accountsCRUDRepository.getUserWallets() } returns emptyFlow() + + // Act + val actual = useCase.invoke().firstOrNull() + + // Assert + Truth.assertThat(actual).isFalse() + + verifyOrder { + featureToggles.isFeatureEnabled + accountsCRUDRepository.getUserWallets() + } + + verify(inverse = true) { accountsCRUDRepository.getTotalAccountsCount(any()) } + } + + @Test + fun `returns false when getUserWallets emits one wallet with isMultiCurrency false`() = runTest { + // Arrange + val wallet = createUserWallet(isMultiCurrency = false) + + every { featureToggles.isFeatureEnabled } returns true + every { accountsCRUDRepository.getUserWallets() } returns flowOf(listOf(wallet)) + + // Act + val actual = useCase.invoke().first() + + // Assert + Truth.assertThat(actual).isFalse() + + verifyOrder { + featureToggles.isFeatureEnabled + accountsCRUDRepository.getUserWallets() + } + + verify(inverse = true) { accountsCRUDRepository.getTotalAccountsCount(any()) } + } + + @Test + fun `returns true when getUserWallets emits one wallet with isMultiCurrency true`() = runTest { + // Arrange + val wallet = createUserWallet(isMultiCurrency = true) + + every { featureToggles.isFeatureEnabled } returns true + every { accountsCRUDRepository.getUserWallets() } returns flowOf(listOf(wallet)) + every { accountsCRUDRepository.getTotalAccountsCount(wallet.walletId) } returns flowOf(2.some()) + + // Act + val actual = useCase.invoke().first() + + // Assert + Truth.assertThat(actual).isTrue() + + verifyOrder { + featureToggles.isFeatureEnabled + accountsCRUDRepository.getUserWallets() + accountsCRUDRepository.getTotalAccountsCount(wallet.walletId) + } + } + + @Test + fun `returns false when getUserWallets emits one wallet with isMultiCurrency true and None counts`() = runTest { + // Arrange + val wallet = createUserWallet(isMultiCurrency = true) + + every { featureToggles.isFeatureEnabled } returns true + every { accountsCRUDRepository.getUserWallets() } returns flowOf(listOf(wallet)) + every { accountsCRUDRepository.getTotalAccountsCount(wallet.walletId) } returns flowOf(none()) + + // Act + val actual = useCase.invoke().first() + + // Assert + Truth.assertThat(actual).isFalse() + + verifyOrder { + featureToggles.isFeatureEnabled + accountsCRUDRepository.getUserWallets() + accountsCRUDRepository.getTotalAccountsCount(wallet.walletId) + } + } + + @Test + fun `returns true when getUserWallets emits two wallets, one isMultiCurrency false, one true`() = runTest { + // Arrange + val wallet1 = createUserWallet(isMultiCurrency = false) + val wallet2 = createUserWallet(isMultiCurrency = true) + + every { featureToggles.isFeatureEnabled } returns true + every { accountsCRUDRepository.getUserWallets() } returns flowOf(listOf(wallet1, wallet2)) + every { accountsCRUDRepository.getTotalAccountsCount(wallet2.walletId) } returns flowOf(2.some()) + + // Act + val actual = useCase.invoke().first() + + // Assert + Truth.assertThat(actual).isTrue() + + verifyOrder { + featureToggles.isFeatureEnabled + accountsCRUDRepository.getUserWallets() + accountsCRUDRepository.getTotalAccountsCount(wallet2.walletId) + } + + verify(inverse = true) { accountsCRUDRepository.getTotalAccountsCount(wallet1.walletId) } + } + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class InvokeSync { + + @Test + fun `returns false when feature is disabled`() = runTest { + // Arrange + every { featureToggles.isFeatureEnabled } returns false + + // Act + val actual = useCase.invokeSync() + + // Assert + Truth.assertThat(actual).isFalse() + + verify(exactly = 1) { featureToggles.isFeatureEnabled } + verify(inverse = true) { accountsCRUDRepository.getUserWalletsSync() } + } + + @Test + fun `returns false when getUserWalletsSync returns empty list`() = runTest { + // Arrange + every { featureToggles.isFeatureEnabled } returns true + every { accountsCRUDRepository.getUserWalletsSync() } returns emptyList() + + // Act + val actual = useCase.invokeSync() + + // Assert + Truth.assertThat(actual).isFalse() + + verifyOrder { + featureToggles.isFeatureEnabled + accountsCRUDRepository.getUserWalletsSync() + } + + coVerify(inverse = true) { accountsCRUDRepository.getTotalAccountsCountSync(any()) } + } + + @Test + fun `returns false when getUserWalletsSync returns one wallet with isMultiCurrency false`() = runTest { + // Arrange + val wallet = createUserWallet(isMultiCurrency = false) + + every { featureToggles.isFeatureEnabled } returns true + every { accountsCRUDRepository.getUserWalletsSync() } returns listOf(wallet) + + // Act + val actual = useCase.invokeSync() + + // Assert + Truth.assertThat(actual).isFalse() + + verifyOrder { + featureToggles.isFeatureEnabled + accountsCRUDRepository.getUserWalletsSync() + } + + coVerify(inverse = true) { accountsCRUDRepository.getTotalAccountsCountSync(any()) } + } + + @Test + fun `returns true when getUserWalletsSync returns one wallet with isMultiCurrency true`() = runTest { + // Arrange + val wallet = createUserWallet(isMultiCurrency = true) + + every { featureToggles.isFeatureEnabled } returns true + every { accountsCRUDRepository.getUserWalletsSync() } returns listOf(wallet) + coEvery { accountsCRUDRepository.getTotalAccountsCountSync(wallet.walletId) } returns 2.some() + + // Act + val actual = useCase.invokeSync() + + // Assert + Truth.assertThat(actual).isTrue() + + coVerifyOrder { + featureToggles.isFeatureEnabled + accountsCRUDRepository.getUserWalletsSync() + accountsCRUDRepository.getTotalAccountsCountSync(wallet.walletId) + } + } + + @Test + fun `returns false when getUserWalletsSync returns multi wallet with None counts`() = runTest { + // Arrange + val wallet = createUserWallet(isMultiCurrency = true) + + every { featureToggles.isFeatureEnabled } returns true + every { accountsCRUDRepository.getUserWalletsSync() } returns listOf(wallet) + coEvery { accountsCRUDRepository.getTotalAccountsCountSync(wallet.walletId) } returns none() + + // Act + val actual = useCase.invokeSync() + + // Assert + Truth.assertThat(actual).isFalse() + + coVerifyOrder { + featureToggles.isFeatureEnabled + accountsCRUDRepository.getUserWalletsSync() + accountsCRUDRepository.getTotalAccountsCountSync(wallet.walletId) + } + } + + @Test + fun `returns true when getUserWalletsSync returns multi and single wallets`() = runTest { + // Arrange + val wallet1 = createUserWallet(isMultiCurrency = false) + val wallet2 = createUserWallet(isMultiCurrency = true) + + every { featureToggles.isFeatureEnabled } returns true + every { accountsCRUDRepository.getUserWalletsSync() } returns listOf(wallet1, wallet2) + coEvery { accountsCRUDRepository.getTotalAccountsCountSync(wallet2.walletId) } returns 2.some() + + // Act + val actual = useCase.invokeSync() + + // Assert + Truth.assertThat(actual).isTrue() + + coVerifyOrder { + featureToggles.isFeatureEnabled + accountsCRUDRepository.getUserWalletsSync() + accountsCRUDRepository.getTotalAccountsCountSync(wallet2.walletId) + } + + coVerify(inverse = true) { accountsCRUDRepository.getTotalAccountsCountSync(wallet1.walletId) } + } + } + + private fun createUserWallet(isMultiCurrency: Boolean): UserWallet = mockk { + every { this@mockk.walletId } returns UserWalletId(stringValue = "011") + every { this@mockk.isMultiCurrency } returns isMultiCurrency + } +} \ No newline at end of file