Updated on 2026-08-14

This commit is contained in:
Tangem 2023-07-28 10:18:27 +03:00
parent fc71201d0f
commit 3ff771e48b
2 changed files with 98 additions and 25 deletions

View file

@ -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<Token>,
isGrouped: Boolean,
sortedTokensIds: Set<Pair<Network.ID, Token.ID>>,
isGroupedByNetwork: Boolean,
isSortedByBalance: Boolean,
): Either<TokenListSortingError, Unit> {
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<TokenListSortingError>.sortTokens(
sortedTokensIds: Set<Pair<Network.ID, Token.ID>>,
unsortedTokens: Set<Token>,
): Set<Token> = withContext(dispatchers.default) {
val nonEmptySortedTokensIds = ensureNotNull(sortedTokensIds.toNonEmptySetOrNull()) {
TokenListSortingError.TokenListIsEmpty
}
val sortedTokens = sortedMapOf<Int, Token>()
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<TokenListSortingError>.getTokens(userWalletId: UserWalletId): Set<Token> {
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<TokenListSortingError>.applySorting(
userWalletId: UserWalletId,
tokens: Set<Token>,

View file

@ -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<DataError, Unit> = Unit.right()) =
MockTokensRepository(sortTokensResult, emptyFlow(), emptyFlow(), emptyFlow())
private fun getTokensRepository(
sortTokensResult: Either<DataError, Unit> = Unit.right(),
tokens: Flow<Either<DataError, Set<Token>>> = flowOf(MockTokens.tokens.right()),
): MockTokensRepository {
return MockTokensRepository(sortTokensResult, tokens, emptyFlow(), emptyFlow())
}
}