From 3ff771e48b44dc335564affae626c82e16fec41f Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 28 Jul 2023 10:18:27 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tokens/ApplyTokenListSortingUseCase.kt | 55 +++++++++++++-- .../ApplyTokenListSortingUseCaseTest.kt | 68 ++++++++++++++----- 2 files changed, 98 insertions(+), 25 deletions(-) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt index 53a38d6642..2004da059c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt @@ -7,10 +7,12 @@ import arrow.core.raise.either import arrow.core.raise.ensureNotNull import arrow.core.toNonEmptySetOrNull import com.tangem.domain.tokens.error.TokenListSortingError +import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.Token import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.withContext class ApplyTokenListSortingUseCase( @@ -20,21 +22,60 @@ class ApplyTokenListSortingUseCase( suspend operator fun invoke( userWalletId: UserWalletId, - sortedTokens: Set, - isGrouped: Boolean, + sortedTokensIds: Set>, + isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ): Either { return withContext(dispatchers.default) { either { - val nonEmptyTokens = ensureNotNull(sortedTokens.toNonEmptySetOrNull()) { - TokenListSortingError.TokenListIsEmpty - } - - applySorting(userWalletId, nonEmptyTokens, isGrouped, isSortedByBalance) + applySorting( + userWalletId = userWalletId, + tokens = sortTokens(sortedTokensIds, getTokens(userWalletId)), + isGrouped = isGroupedByNetwork, + isSortedByBalance = isSortedByBalance, + ) } } } + private suspend fun Raise.sortTokens( + sortedTokensIds: Set>, + unsortedTokens: Set, + ): Set = withContext(dispatchers.default) { + val nonEmptySortedTokensIds = ensureNotNull(sortedTokensIds.toNonEmptySetOrNull()) { + TokenListSortingError.TokenListIsEmpty + } + + val sortedTokens = sortedMapOf() + + unsortedTokens.forEach { token -> + val index = nonEmptySortedTokensIds.indexOfFirst { (networkId, tokenId) -> + networkId == token.networkId && tokenId == token.id + } + + if (index >= 0) { + sortedTokens[index] = token + } else { + raise(TokenListSortingError.UnableToSortTokenList) + } + } + + ensureNotNull(sortedTokens.values.toNonEmptySetOrNull()) { + TokenListSortingError.TokenListIsEmpty + } + } + + private suspend fun Raise.getTokens(userWalletId: UserWalletId): Set { + val tokens = catch( + block = { tokensRepository.getMultiCurrencyWalletTokens(userWalletId, refresh = false).firstOrNull() }, + catch = { raise(TokenListSortingError.DataError(it)) }, + ) + + return ensureNotNull(tokens?.toNonEmptySetOrNull()) { + TokenListSortingError.TokenListIsEmpty + } + } + private suspend fun Raise.applySorting( userWalletId: UserWalletId, tokens: Set, diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt index 0af91c7c22..6a666e453b 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt @@ -6,13 +6,17 @@ import arrow.core.right import com.tangem.domain.core.error.DataError import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.mock.MockTokens +import com.tangem.domain.tokens.model.Token import com.tangem.domain.tokens.repository.MockTokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test +import kotlin.random.Random internal class ApplyTokenListSortingUseCaseTest { @@ -28,8 +32,8 @@ internal class ApplyTokenListSortingUseCaseTest { // When val result = useCase( userWalletId = userWalletId, - sortedTokens = emptySet(), - isGrouped = false, + sortedTokensIds = emptySet(), + isGroupedByNetwork = false, isSortedByBalance = false, ) @@ -50,8 +54,8 @@ internal class ApplyTokenListSortingUseCaseTest { // When val result = useCase( userWalletId = userWalletId, - sortedTokens = MockTokens.tokens, - isGrouped = false, + sortedTokensIds = MockTokens.tokens.map { it.networkId to it.id }.toSet(), + isGroupedByNetwork = false, isSortedByBalance = false, ) @@ -62,7 +66,7 @@ internal class ApplyTokenListSortingUseCaseTest { @Test fun `when apply sorting for sorted and grouped list then correct args should be used`() = runTest { // Given - val expectedTokens = MockTokens.tokens + val expectedTokens = getSortedTokens() val expectedIsGrouped = true val expectedIsSorted = true @@ -72,8 +76,8 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokens = expectedTokens, - isGrouped = expectedIsGrouped, + sortedTokensIds = expectedTokens.map { it.networkId to it.id }.toSet(), + isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -86,7 +90,7 @@ internal class ApplyTokenListSortingUseCaseTest { @Test fun `when apply sorting for unsorted and grouped list then correct args should be used`() = runTest { // Given - val expectedTokens = MockTokens.tokens + val expectedTokens = getSortedTokens() val expectedIsGrouped = true val expectedIsSorted = false @@ -96,8 +100,8 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokens = expectedTokens, - isGrouped = expectedIsGrouped, + sortedTokensIds = expectedTokens.map { it.networkId to it.id }.toSet(), + isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -110,7 +114,7 @@ internal class ApplyTokenListSortingUseCaseTest { @Test fun `when apply sorting for sorted and ungrouped list then correct args should be used`() = runTest { // Given - val expectedTokens = MockTokens.tokens + val expectedTokens = getSortedTokens() val expectedIsGrouped = false val expectedIsSorted = true @@ -120,8 +124,8 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokens = expectedTokens, - isGrouped = expectedIsGrouped, + sortedTokensIds = expectedTokens.map { it.networkId to it.id }.toSet(), + isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -134,7 +138,7 @@ internal class ApplyTokenListSortingUseCaseTest { @Test fun `when apply sorting for unsorted and ungrouped list then correct args should be used`() = runTest { // Given - val expectedTokens = MockTokens.tokens + val expectedTokens = getSortedTokens() val expectedIsGrouped = false val expectedIsSorted = false @@ -144,8 +148,8 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokens = expectedTokens, - isGrouped = expectedIsGrouped, + sortedTokensIds = expectedTokens.map { it.networkId to it.id }.toSet(), + isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -155,12 +159,40 @@ internal class ApplyTokenListSortingUseCaseTest { assertEquals(expectedIsSorted, repository.isTokensSortedByBalanceAfterSortingApply) } + @Test + fun `when sorted tokens IDs do not contain all tokens IDs then error should be received`() = runTest { + // Given + val expectedResult = TokenListSortingError.UnableToSortTokenList.left() + + val repository = getTokensRepository() + val useCase = getUseCase(repository) + + // When + val result = useCase( + userWalletId = userWalletId, + sortedTokensIds = getSortedTokens().drop(n = 3).map { it.networkId to it.id }.toSet(), + isGroupedByNetwork = false, + isSortedByBalance = false, + ) + + // Then + assertEquals(expectedResult, result) + } + + private fun getSortedTokens() = MockTokens.tokens + .sortedBy { Random.nextInt(0, MockTokens.tokens.size) } + .toSet() + private fun getUseCase(tokensRepository: MockTokensRepository = getTokensRepository()) = ApplyTokenListSortingUseCase( tokensRepository = tokensRepository, dispatchers = TestingCoroutineDispatcherProvider(), ) - private fun getTokensRepository(sortTokensResult: Either = Unit.right()) = - MockTokensRepository(sortTokensResult, emptyFlow(), emptyFlow(), emptyFlow()) + private fun getTokensRepository( + sortTokensResult: Either = Unit.right(), + tokens: Flow>> = flowOf(MockTokens.tokens.right()), + ): MockTokensRepository { + return MockTokensRepository(sortTokensResult, tokens, emptyFlow(), emptyFlow()) + } } \ No newline at end of file