Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-10 12:32:14 +05:00
parent 95ca24f18d
commit 73a6042277
5 changed files with 96 additions and 209 deletions

View file

@ -1,65 +1,35 @@
package com.tangem.domain.account.usecase
import arrow.core.Option
import arrow.core.getOrElse
import com.tangem.domain.account.repository.AccountsCRUDRepository
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.loadAndGet
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isMultiCurrency
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.supplier.MultiAccountListSupplier
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
/**
* 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.
* Accounts mode is considered enabled if any [AccountList] produced for the user's wallets contains at least two
* active accounts.
*
* @property crudRepository repository to perform CRUD operations on accounts.
* @property userWalletsListRepository repository to get the list of user wallets.
* @property multiAccountListSupplier supplier that provides a list of [AccountList]s for all user wallets
*
[REDACTED_AUTHOR]
*/
class IsAccountsModeEnabledUseCase(
private val crudRepository: AccountsCRUDRepository,
private val userWalletsListRepository: UserWalletsListRepository,
private val multiAccountListSupplier: MultiAccountListSupplier,
) {
@OptIn(ExperimentalCoroutinesApi::class)
operator fun invoke(): Flow<Boolean> {
return userWalletsListRepository.loadAndGet()
.flatMapLatest { userWallets ->
val totalAccountsCountList = getTotalAccountsCountList(userWallets)
combine(flows = totalAccountsCountList) { it.toList().isModeEnabled() }
}
.onEmpty { emit(false) }
return multiAccountListSupplier.invoke()
.map { accountsList -> accountsList.map(AccountList::activeAccounts).isModeEnabled() }
.distinctUntilChanged()
}
suspend fun invokeSync(): Boolean {
return userWalletsListRepository.userWallets.value.orEmpty()
.map { userWallet ->
// If the wallet does not support multiple currencies, we consider its account count as 0
if (!userWallet.isMultiCurrency) return@map 0
crudRepository.getTotalActiveAccountsCountSync(userWalletId = userWallet.walletId).getOrZero()
}
.isModeEnabled()
return multiAccountListSupplier.getSyncOrNull(Unit)
?.map(AccountList::activeAccounts)
?.isModeEnabled() == true
}
private fun getTotalAccountsCountList(userWallets: List<UserWallet>): List<Flow<Int>> {
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.getTotalActiveAccountsCount(userWalletId = userWallet.walletId)
.map { maybeCount -> maybeCount.getOrZero() }
}
}
private fun Option<Int>.getOrZero(): Int = getOrElse { 0 }
private fun List<Int>.isModeEnabled(): Boolean = any { it >= 2 }
}

View file

@ -1,15 +1,12 @@
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.repository.AccountsCRUDRepository
import com.tangem.domain.common.wallets.UserWalletsListRepository
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.MutableStateFlow
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.supplier.MultiAccountListSupplier
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
@ -18,21 +15,16 @@ import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@Suppress("UnusedFlow")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class IsAccountsModeEnabledUseCaseTest {
private val accountsCRUDRepository: AccountsCRUDRepository = mockk()
private val userWalletsListRepository: UserWalletsListRepository = mockk(relaxUnitFun = true)
private val multiAccountListSupplier: MultiAccountListSupplier = mockk()
private val useCase = IsAccountsModeEnabledUseCase(
crudRepository = accountsCRUDRepository,
userWalletsListRepository = userWalletsListRepository,
)
private val useCase = IsAccountsModeEnabledUseCase(multiAccountListSupplier = multiAccountListSupplier)
@AfterEach
fun tearDown() {
clearMocks(userWalletsListRepository, accountsCRUDRepository)
clearMocks(multiAccountListSupplier)
}
@Nested
@ -40,90 +32,55 @@ class IsAccountsModeEnabledUseCaseTest {
inner class Invoke {
@Test
fun `returns false when loadAndGet emits one wallet with isMultiCurrency false`() = runTest {
fun `returns false when supplier emits empty list`() = runTest {
// Arrange
val wallet = createUserWallet(isMultiCurrency = false)
every { userWalletsListRepository.userWallets } returns MutableStateFlow(listOf(wallet))
every { multiAccountListSupplier.invoke() } returns flowOf(emptyList())
// Act
val actual = useCase.invoke().first()
// Assert
Truth.assertThat(actual).isFalse()
coVerifyOrder {
userWalletsListRepository.load()
userWalletsListRepository.userWallets
}
verify(inverse = true) { accountsCRUDRepository.getTotalActiveAccountsCount(any()) }
}
@Test
fun `returns true when loadAndGet emits one wallet with isMultiCurrency true`() = runTest {
fun `returns false when supplier emits account list with one account`() = runTest {
// Arrange
val wallet = createUserWallet(isMultiCurrency = true)
every { userWalletsListRepository.userWallets } returns MutableStateFlow(listOf(wallet))
every { accountsCRUDRepository.getTotalActiveAccountsCount(wallet.walletId) } returns flowOf(2.some())
// Act
val actual = useCase.invoke().first()
// Assert
Truth.assertThat(actual).isTrue()
coVerifyOrder {
userWalletsListRepository.load()
userWalletsListRepository.userWallets
accountsCRUDRepository.getTotalActiveAccountsCount(wallet.walletId)
}
}
@Test
fun `returns false when loadAndGet emits one wallet with isMultiCurrency true and None counts`() = runTest {
// Arrange
val wallet = createUserWallet(isMultiCurrency = true)
every { userWalletsListRepository.userWallets } returns MutableStateFlow(listOf(wallet))
every { accountsCRUDRepository.getTotalActiveAccountsCount(wallet.walletId) } returns flowOf(none())
val accountList = createAccountList(activeAccounts = 1)
every { multiAccountListSupplier.invoke() } returns flowOf(listOf(accountList))
// Act
val actual = useCase.invoke().first()
// Assert
Truth.assertThat(actual).isFalse()
coVerifyOrder {
userWalletsListRepository.load()
userWalletsListRepository.userWallets
accountsCRUDRepository.getTotalActiveAccountsCount(wallet.walletId)
}
}
@Test
fun `returns true when loadAndGet emits two wallets, one isMultiCurrency false, one true`() = runTest {
fun `returns true when supplier emits account list with two accounts`() = runTest {
// Arrange
val wallet1 = createUserWallet(isMultiCurrency = false)
val wallet2 = createUserWallet(isMultiCurrency = true)
every { userWalletsListRepository.userWallets } returns MutableStateFlow(listOf(wallet1, wallet2))
every { accountsCRUDRepository.getTotalActiveAccountsCount(wallet2.walletId) } returns flowOf(2.some())
val accountList = createAccountList(activeAccounts = 2)
every { multiAccountListSupplier.invoke() } returns flowOf(listOf(accountList))
// Act
val actual = useCase.invoke().first()
// Assert
Truth.assertThat(actual).isTrue()
}
coVerifyOrder {
userWalletsListRepository.load()
userWalletsListRepository.userWallets
accountsCRUDRepository.getTotalActiveAccountsCount(wallet2.walletId)
}
@Test
fun `returns true when supplier emits multiple account lists, one with two accounts`() = runTest {
// Arrange
val accountList1 = createAccountList(activeAccounts = 1)
val accountList2 = createAccountList(activeAccounts = 2)
every { multiAccountListSupplier.invoke() } returns flowOf(listOf(accountList1, accountList2))
verify(inverse = true) { accountsCRUDRepository.getTotalActiveAccountsCount(wallet1.walletId) }
// Act
val actual = useCase.invoke().first()
// Assert
Truth.assertThat(actual).isTrue()
}
}
@ -132,109 +89,71 @@ class IsAccountsModeEnabledUseCaseTest {
inner class InvokeSync {
@Test
fun `returns false when getUserWalletsSync returns empty list`() = runTest {
fun `returns false when getSyncOrNull returns null`() = runTest {
// Arrange
every { userWalletsListRepository.userWallets.value } returns emptyList()
coEvery { multiAccountListSupplier.getSyncOrNull(Unit, any()) } returns null
// Act
val actual = useCase.invokeSync()
// Assert
Truth.assertThat(actual).isFalse()
verifyOrder {
userWalletsListRepository.userWallets.value
}
coVerify(inverse = true) { accountsCRUDRepository.getTotalActiveAccountsCountSync(any()) }
}
@Test
fun `returns false when getUserWalletsSync returns one wallet with isMultiCurrency false`() = runTest {
fun `returns false when getSyncOrNull returns empty list`() = runTest {
// Arrange
val wallet = createUserWallet(isMultiCurrency = false)
every { userWalletsListRepository.userWallets.value } returns listOf(wallet)
coEvery { multiAccountListSupplier.getSyncOrNull(Unit, any()) } returns emptyList()
// Act
val actual = useCase.invokeSync()
// Assert
Truth.assertThat(actual).isFalse()
verifyOrder {
userWalletsListRepository.userWallets.value
}
coVerify(inverse = true) { accountsCRUDRepository.getTotalActiveAccountsCountSync(any()) }
}
@Test
fun `returns true when getUserWalletsSync returns one wallet with isMultiCurrency true`() = runTest {
fun `returns false when getSyncOrNull returns account list with one account`() = runTest {
// Arrange
val wallet = createUserWallet(isMultiCurrency = true)
val accountList = createAccountList(activeAccounts = 1)
coEvery { multiAccountListSupplier.getSyncOrNull(Unit, any()) } returns listOf(accountList)
every { userWalletsListRepository.userWallets.value } returns listOf(wallet)
coEvery { accountsCRUDRepository.getTotalActiveAccountsCountSync(wallet.walletId) } returns 2.some()
// Act
val actual = useCase.invokeSync()
// Assert
Truth.assertThat(actual).isFalse()
}
@Test
fun `returns true when getSyncOrNull returns account list with two accounts`() = runTest {
// Arrange
val accountList = createAccountList(activeAccounts = 2)
coEvery { multiAccountListSupplier.getSyncOrNull(Unit, any()) } returns listOf(accountList)
// Act
val actual = useCase.invokeSync()
// Assert
Truth.assertThat(actual).isTrue()
coVerifyOrder {
userWalletsListRepository.userWallets.value
accountsCRUDRepository.getTotalActiveAccountsCountSync(wallet.walletId)
}
}
@Test
fun `returns false when getUserWalletsSync returns multi wallet with None counts`() = runTest {
fun `returns true when getSyncOrNull returns multiple account lists, one with two accounts`() = runTest {
// Arrange
val wallet = createUserWallet(isMultiCurrency = true)
every { userWalletsListRepository.userWallets.value } returns listOf(wallet)
coEvery { accountsCRUDRepository.getTotalActiveAccountsCountSync(wallet.walletId) } returns none()
// Act
val actual = useCase.invokeSync()
// Assert
Truth.assertThat(actual).isFalse()
coVerifyOrder {
userWalletsListRepository.userWallets.value
accountsCRUDRepository.getTotalActiveAccountsCountSync(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 { userWalletsListRepository.userWallets.value } returns listOf(wallet1, wallet2)
coEvery { accountsCRUDRepository.getTotalActiveAccountsCountSync(wallet2.walletId) } returns 2.some()
val accountList1 = createAccountList(activeAccounts = 1)
val accountList2 = createAccountList(activeAccounts = 2)
coEvery { multiAccountListSupplier.getSyncOrNull(Unit, any()) } returns listOf(accountList1, accountList2)
// Act
val actual = useCase.invokeSync()
// Assert
Truth.assertThat(actual).isTrue()
coVerifyOrder {
userWalletsListRepository.userWallets.value
accountsCRUDRepository.getTotalActiveAccountsCountSync(wallet2.walletId)
}
coVerify(inverse = true) { accountsCRUDRepository.getTotalActiveAccountsCountSync(wallet1.walletId) }
}
}
private fun createUserWallet(isMultiCurrency: Boolean): UserWallet = mockk {
every { this@mockk.walletId } returns UserWalletId(stringValue = "011")
every { this@mockk.isMultiCurrency } returns isMultiCurrency
private fun createAccountList(activeAccounts: Int): AccountList = mockk {
every { this@mockk.activeAccounts } returns activeAccounts
}
}