Updated on 2026-08-14
This commit is contained in:
parent
17a97c66b6
commit
5a8f4299a6
8 changed files with 621 additions and 2 deletions
|
|
@ -7,21 +7,28 @@ import javax.inject.Inject
|
|||
/**
|
||||
* Container for converter factories related to accounts.
|
||||
*
|
||||
* @property accountsListCF factory for creating an account list converter
|
||||
* @property getWalletAccountsResponseCF factory for creating a wallet accounts response converter
|
||||
* @property accountsListCF factory for creating an account list converter
|
||||
* @property cryptoPortfolioCF factory for creating a crypto portfolio converter
|
||||
* @property userWalletsStore store for accessing user wallet data
|
||||
*
|
||||
* @constructor Creates an instance of the container with injected factories.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class AccountConverterFactoryContainer @Inject constructor(
|
||||
val getWalletAccountsResponseCF: GetWalletAccountsResponseConverter.Factory,
|
||||
private val getWalletAccountsResponseCF: GetWalletAccountsResponseConverter.Factory,
|
||||
private val accountsListCF: AccountListConverter.Factory,
|
||||
private val cryptoPortfolioCF: CryptoPortfolioConverter.Factory,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
) {
|
||||
|
||||
fun createWalletAccountsResponseConverter(userWalletId: UserWalletId): GetWalletAccountsResponseConverter {
|
||||
val userWallet = userWalletsStore.getSyncStrict(key = userWalletId)
|
||||
|
||||
return getWalletAccountsResponseCF.create(userWallet)
|
||||
}
|
||||
|
||||
fun createAccountListConverter(userWalletId: UserWalletId): AccountListConverter {
|
||||
val userWallet = userWalletsStore.getSyncStrict(key = userWalletId)
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,17 @@ internal class DefaultAccountsCRUDRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun saveAccountsLocally(accountList: AccountList) {
|
||||
val converter = convertersContainer.createWalletAccountsResponseConverter(
|
||||
userWalletId = accountList.userWalletId,
|
||||
)
|
||||
|
||||
walletAccountsSaver.store(
|
||||
userWalletId = accountList.userWalletId,
|
||||
response = converter.convert(accountList),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun saveAccounts(accountList: AccountList) {
|
||||
val converter = convertersContainer.createCryptoPortfolioConverter(userWalletId = accountList.userWalletId)
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,13 @@ interface AccountsCRUDRepository {
|
|||
*/
|
||||
suspend fun fetchArchivedAccounts(userWalletId: UserWalletId)
|
||||
|
||||
/**
|
||||
* Saves a list of accounts to the repository
|
||||
*
|
||||
* @param accountList the list of accounts to be saved.
|
||||
*/
|
||||
suspend fun saveAccountsLocally(accountList: AccountList)
|
||||
|
||||
/**
|
||||
* Saves a list of accounts to the repository
|
||||
*
|
||||
|
|
|
|||
|
|
@ -58,6 +58,18 @@ internal object AccountStatusUseCaseModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideApplyTokenListSortingUseCaseV2(
|
||||
accountsCRUDRepository: AccountsCRUDRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): ApplyTokenListSortingUseCaseV2 {
|
||||
return ApplyTokenListSortingUseCaseV2(
|
||||
accountsCRUDRepository = accountsCRUDRepository,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetWalletTotalBalanceUseCaseV2(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import com.tangem.domain.models.account.Account
|
|||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
typealias AccountCryptoCurrencies = Map<Account, List<CryptoCurrency>>
|
||||
|
||||
/**
|
||||
* Combines an [Account] with its corresponding [CryptoCurrency].
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
package com.tangem.domain.account.status.usecase
|
||||
|
||||
import arrow.core.*
|
||||
import arrow.core.raise.*
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencies
|
||||
import com.tangem.domain.core.utils.eitherOn
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokens.error.TokenListSortingError
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import timber.log.Timber
|
||||
|
||||
private typealias SortingErrorByAccountId = MutableMap<AccountId, TokenListSortingError>
|
||||
|
||||
/**
|
||||
* Use case to apply token list sorting and grouping preferences for accounts.
|
||||
*
|
||||
* @property accountsCRUDRepository Repository for CRUD operations on accounts.
|
||||
* @property dispatchers Coroutine dispatcher provider for managing threading.
|
||||
*/
|
||||
class ApplyTokenListSortingUseCaseV2(
|
||||
private val accountsCRUDRepository: AccountsCRUDRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Applies the token list sorting and grouping preferences for the given accounts.
|
||||
*
|
||||
* @param sortedTokensIdsByAccount A map of accounts to their sorted token IDs.
|
||||
* @param isGroupedByNetwork Boolean indicating if tokens should be grouped by network.
|
||||
* @param isSortedByBalance Boolean indicating if tokens should be sorted by balance.
|
||||
* @return Either a [TokenListSortingError] or Unit on success.
|
||||
*/
|
||||
suspend operator fun invoke(
|
||||
sortedTokensIdsByAccount: AccountCryptoCurrencies,
|
||||
isGroupedByNetwork: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
): Either<TokenListSortingError, Unit> = eitherOn(dispatchers.default) {
|
||||
ensure(sortedTokensIdsByAccount.isNotEmpty()) {
|
||||
raise(TokenListSortingError.TokenListIsEmpty)
|
||||
}
|
||||
|
||||
val userWalletId = sortedTokensIdsByAccount.keys.first().userWalletId
|
||||
|
||||
val accountList = getAccountList(userWalletId)
|
||||
|
||||
val maybeSortedAccountList = sortAccountList(
|
||||
accountList = accountList,
|
||||
sortedTokensIdsByAccount = sortedTokensIdsByAccount,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
isGroupedByNetwork = isGroupedByNetwork,
|
||||
)
|
||||
|
||||
val sortedAccountList = maybeSortedAccountList.getOrNull()
|
||||
val isSortingTypeChanged = accountList.isSortedByBalance() != isSortedByBalance
|
||||
val isGroupingTypeChanged = accountList.isGroupedByNetwork() != isGroupedByNetwork
|
||||
|
||||
if (sortedAccountList != null || isSortingTypeChanged || isGroupingTypeChanged) {
|
||||
applySorting(sortedAccountList ?: accountList)
|
||||
}
|
||||
|
||||
maybeSortedAccountList.toEitherNeg()
|
||||
.onLeft { errorByAccountId ->
|
||||
Timber.e(
|
||||
"""
|
||||
Unable to sort tokens for accounts: ${
|
||||
errorByAccountId.entries.joinToString { "${it.key.value}: ${it.value}" }
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
raise(TokenListSortingError.UnableToSortAccounts(errorByAccountId.keys.map { it.value }))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.getAccountList(userWalletId: UserWalletId): AccountList {
|
||||
return catchOrDataError {
|
||||
accountsCRUDRepository.getAccountListSync(userWalletId = userWalletId).getOrElse {
|
||||
throw IllegalStateException("Account list not found for wallet $userWalletId")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun AccountList.isSortedByBalance(): Boolean = sortType == TokensSortType.BALANCE
|
||||
|
||||
private fun AccountList.isGroupedByNetwork(): Boolean = groupType == TokensGroupType.NETWORK
|
||||
|
||||
private fun Raise<TokenListSortingError>.sortAccountList(
|
||||
accountList: AccountList,
|
||||
sortedTokensIdsByAccount: AccountCryptoCurrencies,
|
||||
isSortedByBalance: Boolean,
|
||||
isGroupedByNetwork: Boolean,
|
||||
): Ior<SortingErrorByAccountId, AccountList> {
|
||||
val errors = mutableMapOf<AccountId, TokenListSortingError>()
|
||||
|
||||
val updatedAccountList = AccountList(
|
||||
userWalletId = accountList.userWalletId,
|
||||
accounts = accountList.accounts.sortTokens(sortedTokensIdsByAccount, errors),
|
||||
totalAccounts = accountList.totalAccounts,
|
||||
sortType = if (isSortedByBalance) TokensSortType.BALANCE else TokensSortType.NONE,
|
||||
groupType = if (isGroupedByNetwork) TokensGroupType.NETWORK else TokensGroupType.NONE,
|
||||
).getOrElse {
|
||||
raise(TokenListSortingError.UnableToSortTokenList)
|
||||
}
|
||||
|
||||
return if (errors.isEmpty()) {
|
||||
updatedAccountList.rightIor()
|
||||
} else if (errors.size == sortedTokensIdsByAccount.keys.size) {
|
||||
errors.leftIor()
|
||||
} else {
|
||||
(errors to updatedAccountList).bothIor()
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Account>.sortTokens(
|
||||
sortedTokensIdsByAccount: AccountCryptoCurrencies,
|
||||
errors: MutableMap<AccountId, TokenListSortingError>,
|
||||
): List<Account> {
|
||||
return map { account ->
|
||||
if (account !is Account.CryptoPortfolio) return@map account
|
||||
|
||||
val sortedCurrencies = sortedTokensIdsByAccount[account] ?: return@map account
|
||||
|
||||
val accountCurrencies = account.cryptoCurrencies.toList()
|
||||
.sortTokens(sortedCurrencies = sortedCurrencies)
|
||||
.getOrElse {
|
||||
errors[account.accountId] = it
|
||||
return@map account
|
||||
}
|
||||
.toSet()
|
||||
|
||||
account.copy(cryptoCurrencies = accountCurrencies)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrency>.sortTokens(
|
||||
sortedCurrencies: List<CryptoCurrency>,
|
||||
): Either<TokenListSortingError, List<CryptoCurrency>> = either {
|
||||
val nonEmptySortedTokensIds = ensureNotNull(sortedCurrencies.toNonEmptySetOrNull()) {
|
||||
TokenListSortingError.TokenListIsEmpty
|
||||
}
|
||||
|
||||
val sortedTokens = sortedMapOf<Int, CryptoCurrency>()
|
||||
|
||||
distinct().forEach { currency ->
|
||||
val index = nonEmptySortedTokensIds.indexOfFirst { sortedCurrency ->
|
||||
sortedCurrency.id == currency.id
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
sortedTokens[index] = currency
|
||||
} else {
|
||||
raise(TokenListSortingError.UnableToSortTokenList)
|
||||
}
|
||||
}
|
||||
|
||||
ensureNotNull(sortedTokens.values.toNonEmptyListOrNull()) {
|
||||
TokenListSortingError.TokenListIsEmpty
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.applySorting(accountList: AccountList) {
|
||||
coroutineScope {
|
||||
val results = awaitAll(
|
||||
async {
|
||||
Either.catch { accountsCRUDRepository.saveAccountsLocally(accountList) }
|
||||
},
|
||||
async {
|
||||
Either.catch { accountsCRUDRepository.syncTokens(accountList.userWalletId) }
|
||||
},
|
||||
)
|
||||
|
||||
ensure(results.none { it.isLeft() }) {
|
||||
val message = results.mapNotNull { it.leftOrNull() }.joinToString()
|
||||
raise(TokenListSortingError.DataError(IllegalStateException(message)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Ior<SortingErrorByAccountId, AccountList>.toEitherNeg(): Either<SortingErrorByAccountId, AccountList> {
|
||||
return when (this) {
|
||||
is Ior.Left -> this.value.left()
|
||||
is Ior.Right -> this.value.right()
|
||||
is Ior.Both -> this.leftValue.left()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <A> Raise<TokenListSortingError>.catchOrDataError(block: suspend () -> A): A {
|
||||
return catch(
|
||||
block = { block() },
|
||||
catch = { raise(TokenListSortingError.DataError(it)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,375 @@
|
|||
package com.tangem.domain.account.status.usecase
|
||||
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import arrow.core.some
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokens.error.TokenListSortingError
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerifyOrder
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class ApplyTokenListSortingUseCaseTest {
|
||||
|
||||
private val accountsCRUDRepository = mockk<AccountsCRUDRepository>(relaxUnitFun = true)
|
||||
|
||||
private val useCase = ApplyTokenListSortingUseCaseV2(
|
||||
accountsCRUDRepository = accountsCRUDRepository,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
|
||||
private val userWalletId = UserWalletId(stringValue = "011")
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
clearMocks(accountsCRUDRepository)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when tokens are empty then error should be received`() = runTest {
|
||||
// Act
|
||||
val actual = useCase(
|
||||
sortedTokensIdsByAccount = emptyMap(),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = TokenListSortingError.TokenListIsEmpty.left()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifyOrder(inverse = true) {
|
||||
accountsCRUDRepository.getAccountListSync(userWalletId = any())
|
||||
accountsCRUDRepository.saveAccountsLocally(accountList = any())
|
||||
accountsCRUDRepository.syncTokens(userWalletId = any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when getAccountListSync throws exception then error should be received`() = runTest {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(userWalletId = userWalletId, cryptoCurrencies = emptySet())
|
||||
|
||||
val exception = IllegalStateException("No internet connection")
|
||||
coEvery { accountsCRUDRepository.getAccountListSync(userWalletId) } throws exception
|
||||
|
||||
// Act
|
||||
val actual = useCase(
|
||||
sortedTokensIdsByAccount = mapOf(accountList.mainAccount to emptyList()),
|
||||
isGroupedByNetwork = true,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = TokenListSortingError.DataError(exception).left()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifyOrder { accountsCRUDRepository.getAccountListSync(userWalletId) }
|
||||
|
||||
coVerifyOrder(inverse = true) {
|
||||
accountsCRUDRepository.saveAccountsLocally(accountList = any())
|
||||
accountsCRUDRepository.syncTokens(userWalletId = any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when saveAccountsLocally throws exception then error should be received`() = runTest {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(userWalletId = userWalletId, cryptoCurrencies = emptySet())
|
||||
|
||||
coEvery { accountsCRUDRepository.getAccountListSync(userWalletId) } returns accountList.some()
|
||||
|
||||
val exception = IllegalStateException("No internet connection")
|
||||
coEvery { accountsCRUDRepository.saveAccountsLocally(accountList) } throws exception
|
||||
|
||||
// Act
|
||||
val actual = useCase(
|
||||
sortedTokensIdsByAccount = mapOf(accountList.mainAccount to emptyList()),
|
||||
isGroupedByNetwork = true,
|
||||
isSortedByBalance = false,
|
||||
).leftOrNull() as TokenListSortingError.DataError
|
||||
|
||||
// Assert
|
||||
val expected = TokenListSortingError.DataError(IllegalStateException(exception.toString()))
|
||||
Truth.assertThat(actual.cause).isInstanceOf(expected.cause::class.java)
|
||||
Truth.assertThat(actual.cause).hasMessageThat().isEqualTo(expected.cause.message)
|
||||
|
||||
coVerifyOrder {
|
||||
accountsCRUDRepository.getAccountListSync(userWalletId)
|
||||
accountsCRUDRepository.saveAccountsLocally(accountList)
|
||||
accountsCRUDRepository.syncTokens(userWalletId = userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when syncTokens throws exception then error should be received`() = runTest {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(userWalletId = userWalletId, cryptoCurrencies = emptySet())
|
||||
|
||||
coEvery { accountsCRUDRepository.getAccountListSync(userWalletId) } returns accountList.some()
|
||||
|
||||
val exception = IllegalStateException("No internet connection")
|
||||
coEvery { accountsCRUDRepository.syncTokens(userWalletId) } throws exception
|
||||
|
||||
// Act
|
||||
val actual = useCase(
|
||||
sortedTokensIdsByAccount = mapOf(accountList.mainAccount to emptyList()),
|
||||
isGroupedByNetwork = true,
|
||||
isSortedByBalance = false,
|
||||
).leftOrNull() as TokenListSortingError.DataError
|
||||
|
||||
// Assert
|
||||
val expected = TokenListSortingError.DataError(IllegalStateException(exception.toString()))
|
||||
Truth.assertThat(actual.cause).isInstanceOf(expected.cause::class.java)
|
||||
Truth.assertThat(actual.cause).hasMessageThat().isEqualTo(expected.cause.message)
|
||||
|
||||
coVerifyOrder {
|
||||
accountsCRUDRepository.getAccountListSync(userWalletId)
|
||||
accountsCRUDRepository.saveAccountsLocally(accountList)
|
||||
accountsCRUDRepository.syncTokens(userWalletId = userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when apply sorting for sorted and grouped list then correct args should be used`() = runTest {
|
||||
// Arrange
|
||||
val token1 = mockk<CryptoCurrency>(relaxed = true)
|
||||
val token2 = mockk<CryptoCurrency>(relaxed = true)
|
||||
val token3 = mockk<CryptoCurrency>(relaxed = true)
|
||||
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = setOf(token1, token2, token3),
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
coEvery { accountsCRUDRepository.getAccountListSync(userWalletId) } returns accountList.some()
|
||||
|
||||
val sortedTokens = setOf(token2, token3, token1)
|
||||
val updatedAccountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = sortedTokens,
|
||||
sortType = TokensSortType.BALANCE,
|
||||
groupType = TokensGroupType.NETWORK,
|
||||
)
|
||||
|
||||
// Act
|
||||
val actual = useCase(
|
||||
sortedTokensIdsByAccount = mapOf(accountList.mainAccount to sortedTokens.toList()),
|
||||
isGroupedByNetwork = true,
|
||||
isSortedByBalance = true,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = Unit.right()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifyOrder {
|
||||
accountsCRUDRepository.getAccountListSync(userWalletId)
|
||||
accountsCRUDRepository.saveAccountsLocally(updatedAccountList)
|
||||
accountsCRUDRepository.syncTokens(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when apply sorting for unsorted and grouped list then correct args should be used`() = runTest {
|
||||
// Arrange
|
||||
val token1 = mockk<CryptoCurrency>(relaxed = true)
|
||||
val token2 = mockk<CryptoCurrency>(relaxed = true)
|
||||
val token3 = mockk<CryptoCurrency>(relaxed = true)
|
||||
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = setOf(token1, token2, token3),
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
coEvery { accountsCRUDRepository.getAccountListSync(userWalletId) } returns accountList.some()
|
||||
|
||||
val sortedTokens = setOf(token2, token3, token1)
|
||||
val updatedAccountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = sortedTokens,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NETWORK,
|
||||
)
|
||||
|
||||
// Act
|
||||
val actual = useCase(
|
||||
sortedTokensIdsByAccount = mapOf(accountList.mainAccount to sortedTokens.toList()),
|
||||
isGroupedByNetwork = true,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = Unit.right()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifyOrder {
|
||||
accountsCRUDRepository.getAccountListSync(userWalletId)
|
||||
accountsCRUDRepository.saveAccountsLocally(updatedAccountList)
|
||||
accountsCRUDRepository.syncTokens(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when apply sorting for sorted and ungrouped list then correct args should be used`() = runTest {
|
||||
// Arrange
|
||||
val token1 = mockk<CryptoCurrency>(relaxed = true)
|
||||
val token2 = mockk<CryptoCurrency>(relaxed = true)
|
||||
val token3 = mockk<CryptoCurrency>(relaxed = true)
|
||||
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = setOf(token1, token2, token3),
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
coEvery { accountsCRUDRepository.getAccountListSync(userWalletId) } returns accountList.some()
|
||||
|
||||
val sortedTokens = setOf(token2, token3, token1)
|
||||
val updatedAccountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = sortedTokens,
|
||||
sortType = TokensSortType.BALANCE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
|
||||
// Act
|
||||
val actual = useCase(
|
||||
sortedTokensIdsByAccount = mapOf(accountList.mainAccount to sortedTokens.toList()),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = true,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = Unit.right()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifyOrder {
|
||||
accountsCRUDRepository.getAccountListSync(userWalletId)
|
||||
accountsCRUDRepository.saveAccountsLocally(updatedAccountList)
|
||||
accountsCRUDRepository.syncTokens(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when apply sorting for unsorted and ungrouped list then correct args should be used`() = runTest {
|
||||
// Arrange
|
||||
val token1 = mockk<CryptoCurrency>(relaxed = true)
|
||||
val token2 = mockk<CryptoCurrency>(relaxed = true)
|
||||
val token3 = mockk<CryptoCurrency>(relaxed = true)
|
||||
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = setOf(token1, token2, token3),
|
||||
sortType = TokensSortType.BALANCE,
|
||||
groupType = TokensGroupType.NETWORK,
|
||||
)
|
||||
coEvery { accountsCRUDRepository.getAccountListSync(userWalletId) } returns accountList.some()
|
||||
|
||||
val sortedTokens = setOf(token2, token3, token1)
|
||||
val updatedAccountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = sortedTokens,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
|
||||
// Act
|
||||
val actual = useCase(
|
||||
sortedTokensIdsByAccount = mapOf(accountList.mainAccount to sortedTokens.toList()),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = Unit.right()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifyOrder {
|
||||
accountsCRUDRepository.getAccountListSync(userWalletId)
|
||||
accountsCRUDRepository.saveAccountsLocally(updatedAccountList)
|
||||
accountsCRUDRepository.syncTokens(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when sorted tokens IDs do not contain all tokens IDs then error should be received`() = runTest {
|
||||
// Arrange
|
||||
val token1 = mockk<CryptoCurrency>(relaxed = true)
|
||||
val token2 = mockk<CryptoCurrency>(relaxed = true)
|
||||
val token3 = mockk<CryptoCurrency>(relaxed = true)
|
||||
|
||||
val customAccount = Account.CryptoPortfolio(
|
||||
accountId = AccountId.forCryptoPortfolio(userWalletId, DerivationIndex(1).getOrNull()!!),
|
||||
name = "Account 1",
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = 1,
|
||||
cryptoCurrencies = setOf(token1, token2, token3),
|
||||
)
|
||||
.getOrNull()!!
|
||||
|
||||
val accountList = (AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = setOf(token1, token2, token3),
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
) + customAccount).getOrNull()!!
|
||||
|
||||
coEvery { accountsCRUDRepository.getAccountListSync(userWalletId) } returns accountList.some()
|
||||
|
||||
val sortedTokensIdsByAccount: Map<Account, List<CryptoCurrency>> = mapOf(
|
||||
accountList.mainAccount to listOf(token3, token2, token1),
|
||||
customAccount to listOf(token2, token1), // missing token3
|
||||
)
|
||||
|
||||
val updatedAccountList = AccountList(
|
||||
userWalletId = userWalletId,
|
||||
accounts = listOf(
|
||||
accountList.mainAccount.copy(cryptoCurrencies = setOf(token3, token2, token1)),
|
||||
customAccount, // unchanged due to error
|
||||
),
|
||||
totalAccounts = accountList.totalAccounts,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
.getOrNull()!!
|
||||
|
||||
// Act
|
||||
val actual = useCase(
|
||||
sortedTokensIdsByAccount = sortedTokensIdsByAccount,
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = TokenListSortingError.UnableToSortAccounts(
|
||||
accountIds = listOf(customAccount.accountId.value),
|
||||
).left()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifyOrder {
|
||||
accountsCRUDRepository.getAccountListSync(userWalletId)
|
||||
accountsCRUDRepository.saveAccountsLocally(updatedAccountList)
|
||||
accountsCRUDRepository.syncTokens(userWalletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,5 +8,7 @@ sealed class TokenListSortingError {
|
|||
|
||||
object UnableToSortTokenList : TokenListSortingError()
|
||||
|
||||
data class UnableToSortAccounts(val accountIds: List<String>) : TokenListSortingError()
|
||||
|
||||
data class DataError(val cause: Throwable) : TokenListSortingError()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue