From 468843f7b91c8ebbec36ec4b7cd3790cfba2571b Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 24 Oct 2025 14:45:20 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../account/converter/AccountListConverter.kt | 2 +- .../account/converter/AccountConverterExt.kt | 2 +- ...SaveWalletAccountsResponseConverterTest.kt | 10 +-- .../domain/account/models/AccountList.kt | 16 ++-- .../domain/account/models/AccountListTest.kt | 36 ++++---- .../usecase/GetArchivedAccountsUseCaseTest.kt | 87 +++---------------- .../GetUnoccupiedAccountIndexUseCaseTest.kt | 12 +-- .../tangem/domain/account/utils/AccountExt.kt | 4 +- .../viewmodel/TesterAccountsViewModel.kt | 8 +- 9 files changed, 54 insertions(+), 123 deletions(-) diff --git a/data/account/src/main/kotlin/com/tangem/data/account/converter/AccountListConverter.kt b/data/account/src/main/kotlin/com/tangem/data/account/converter/AccountListConverter.kt index b2744a7452..6a3bf7a736 100644 --- a/data/account/src/main/kotlin/com/tangem/data/account/converter/AccountListConverter.kt +++ b/data/account/src/main/kotlin/com/tangem/data/account/converter/AccountListConverter.kt @@ -29,7 +29,7 @@ internal class AccountListConverter @AssistedInject constructor( override fun convert(value: GetWalletAccountsResponse): AccountList { return AccountList( userWalletId = userWallet.walletId, - accounts = value.accounts.map(cryptoPortfolioConverter::convert).toSet(), + accounts = value.accounts.map(cryptoPortfolioConverter::convert), totalAccounts = value.wallet.totalAccounts, sortType = TokensSortTypeConverter.convert(value.wallet.sort), groupType = TokensGroupTypeConverter.convert(value.wallet.group), diff --git a/data/account/src/test/java/com/tangem/data/account/converter/AccountConverterExt.kt b/data/account/src/test/java/com/tangem/data/account/converter/AccountConverterExt.kt index 12ac6dda89..a62a9840fb 100644 --- a/data/account/src/test/java/com/tangem/data/account/converter/AccountConverterExt.kt +++ b/data/account/src/test/java/com/tangem/data/account/converter/AccountConverterExt.kt @@ -77,7 +77,7 @@ internal fun createAccountList( ): AccountList { return AccountList( userWalletId = userWalletId, - accounts = setOf(createCryptoPortfolio(userWalletId)), + accounts = listOf(createCryptoPortfolio(userWalletId)), totalAccounts = 1, sortType = sortType, groupType = groupType, diff --git a/data/account/src/test/java/com/tangem/data/account/converter/SaveWalletAccountsResponseConverterTest.kt b/data/account/src/test/java/com/tangem/data/account/converter/SaveWalletAccountsResponseConverterTest.kt index 35e95170e1..0cc9314151 100644 --- a/data/account/src/test/java/com/tangem/data/account/converter/SaveWalletAccountsResponseConverterTest.kt +++ b/data/account/src/test/java/com/tangem/data/account/converter/SaveWalletAccountsResponseConverterTest.kt @@ -2,8 +2,6 @@ package com.tangem.data.account.converter import com.google.common.truth.Truth import com.tangem.datasource.api.tangemTech.models.account.SaveWalletAccountsResponse -import com.tangem.domain.account.models.AccountList -import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.AccountName import com.tangem.domain.models.wallet.UserWalletId import org.junit.jupiter.api.Test @@ -16,13 +14,7 @@ class SaveWalletAccountsResponseConverterTest { fun convert() { // Arrange val userWalletId = UserWalletId("011") - - val accountList = AccountList( - userWalletId = userWalletId, - accounts = setOf(Account.CryptoPortfolio.createMainAccount(userWalletId = userWalletId)), - totalAccounts = 1, - ) - .getOrNull()!! + val accountList = createAccountList(userWalletId) // Act val actual = SaveWalletAccountsResponseConverter.convert(value = accountList) diff --git a/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt b/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt index 80cf0e378a..872e82d16f 100644 --- a/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt +++ b/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt @@ -16,7 +16,7 @@ import kotlinx.serialization.Serializable * Represents a list of accounts associated with a user wallet * * @property userWalletId the user wallet id associated with the account list - * @property accounts a set of accounts belonging to the user wallet + * @property accounts a list of accounts belonging to the user wallet * @property totalAccounts the total number of accounts * [REDACTED_AUTHOR] @@ -24,7 +24,7 @@ import kotlinx.serialization.Serializable @Serializable data class AccountList private constructor( val userWalletId: UserWalletId, - val accounts: Set, + val accounts: List, val totalAccounts: Int, val sortType: TokensSortType, val groupType: TokensGroupType, @@ -48,11 +48,11 @@ data class AccountList private constructor( */ operator fun plus(other: Account): Either { val isNewAccount = this.accounts.none { it.accountId == other.accountId } - val accounts = this.accounts.toList().addOrReplace(other) { it.accountId == other.accountId } + val accounts = this.accounts.addOrReplace(other) { it.accountId == other.accountId } return invoke( userWalletId = this.userWalletId, - accounts = accounts.toSet(), + accounts = accounts, totalAccounts = this.totalAccounts + if (isNewAccount) 1 else 0, sortType = this.sortType, groupType = this.groupType, @@ -68,7 +68,7 @@ data class AccountList private constructor( */ operator fun minus(other: Account): Either { val isExistingAccount = this.accounts.any { it.accountId == other.accountId } - val accounts = this.accounts.toMutableSet().apply { + val accounts = this.accounts.toMutableList().apply { removeIf { it.accountId == other.accountId } } @@ -140,7 +140,7 @@ data class AccountList private constructor( */ operator fun invoke( userWalletId: UserWalletId, - accounts: Set, + accounts: List, totalAccounts: Int, sortType: TokensSortType = TokensSortType.NONE, groupType: TokensGroupType = TokensGroupType.NONE, @@ -190,7 +190,7 @@ data class AccountList private constructor( ): AccountList { return AccountList( userWalletId = userWalletId, - accounts = setOf( + accounts = listOf( Account.CryptoPortfolio.createMainAccount( userWalletId = userWalletId, cryptoCurrencies = cryptoCurrencies, @@ -202,7 +202,7 @@ data class AccountList private constructor( ) } - private fun Set.mainAccountsCount(): Int { + private fun List.mainAccountsCount(): Int { return count { (it as? Account.CryptoPortfolio)?.isMainAccount == true } } } diff --git a/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt b/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt index 60eea11794..96020e2f35 100644 --- a/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt +++ b/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt @@ -28,7 +28,7 @@ class AccountListTest { val accountList = AccountList( userWalletId = userWalletId, - accounts = setOf(mainAccount), + accounts = listOf(mainAccount), totalAccounts = 1, ) .getOrNull()!! @@ -69,7 +69,7 @@ class AccountListTest { // Assert val expected = AccountList( userWalletId = userWalletId, - accounts = setOf(Account.CryptoPortfolio.createMainAccount(userWalletId = userWalletId)), + accounts = listOf(Account.CryptoPortfolio.createMainAccount(userWalletId = userWalletId)), totalAccounts = 1, ).getOrNull()!! @@ -96,17 +96,17 @@ class AccountListTest { private fun provideTestModels() = listOf( CreateTestModel( - accounts = emptySet(), + accounts = emptyList(), expected = AccountList.Error.EmptyAccountsList.left(), ), CreateTestModel( - accounts = setOf( + accounts = listOf( createAccount(userWalletId = userWalletId, derivationIndex = 1), ), expected = AccountList.Error.MainAccountNotFound.left(), ), CreateTestModel( - accounts = setOf( + accounts = listOf( Account.CryptoPortfolio.createMainAccount(userWalletId), Account.CryptoPortfolio.createMainAccount(userWalletId).copy( icon = CryptoPortfolioIcon.ofDefaultCustomAccount(), @@ -131,7 +131,7 @@ class AccountListTest { expected = AccountList.Error.ExceedsMaxAccountsCount.left(), ), CreateTestModel( - accounts = setOf( + accounts = listOf( createAccount(userWalletId = userWalletId, derivationIndex = 0), createAccount(userWalletId = userWalletId, derivationIndex = 1), createAccount(userWalletId = userWalletId, derivationIndex = 1), @@ -139,7 +139,7 @@ class AccountListTest { expected = AccountList.Error.DuplicateAccountIds.left(), ), CreateTestModel( - accounts = setOf( + accounts = listOf( createAccount(userWalletId = userWalletId, name = "Name", derivationIndex = 0), createAccount(userWalletId = userWalletId, name = "Name", derivationIndex = 1), ), @@ -149,7 +149,7 @@ class AccountListTest { } data class CreateTestModel( - val accounts: Set, + val accounts: List, val expected: Either, ) @@ -176,13 +176,13 @@ class AccountListTest { PlusTestModel( initial = AccountList( userWalletId = userWalletId, - accounts = setOf(mainAccount), + accounts = listOf(mainAccount), totalAccounts = 1, ).getOrNull()!!, toAdd = newAccount, expected = AccountList( userWalletId = userWalletId, - accounts = setOf(mainAccount, newAccount), + accounts = listOf(mainAccount, newAccount), totalAccounts = 2, ), ) @@ -196,13 +196,13 @@ class AccountListTest { PlusTestModel( initial = AccountList( userWalletId = userWalletId, - accounts = setOf(mainAccount), + accounts = listOf(mainAccount), totalAccounts = 1, ).getOrNull()!!, toAdd = newAccount, expected = AccountList( userWalletId = userWalletId, - accounts = setOf(newAccount), + accounts = listOf(newAccount), totalAccounts = 1, ), ) @@ -249,13 +249,13 @@ class AccountListTest { MinusTestModel( initial = AccountList( userWalletId = userWalletId, - accounts = setOf(mainAccount, secondaryAccount), + accounts = listOf(mainAccount, secondaryAccount), totalAccounts = 2, ).getOrNull()!!, toRemove = secondaryAccount, expected = AccountList( userWalletId = userWalletId, - accounts = setOf(mainAccount), + accounts = listOf(mainAccount), totalAccounts = 1, ), ) @@ -269,13 +269,13 @@ class AccountListTest { MinusTestModel( initial = AccountList( userWalletId = userWalletId, - accounts = setOf(mainAccount), + accounts = listOf(mainAccount), totalAccounts = 1, ).getOrNull()!!, toRemove = notInList, expected = AccountList( userWalletId = userWalletId, - accounts = setOf(mainAccount), + accounts = listOf(mainAccount), totalAccounts = 1, ), ) @@ -288,7 +288,7 @@ class AccountListTest { MinusTestModel( initial = AccountList( userWalletId = userWalletId, - accounts = setOf(mainAccount), + accounts = listOf(mainAccount), totalAccounts = 1, ).getOrNull()!!, toRemove = mainAccount, @@ -304,7 +304,7 @@ class AccountListTest { MinusTestModel( initial = AccountList( userWalletId = userWalletId, - accounts = setOf(mainAccount, secondaryAccount), + accounts = listOf(mainAccount, secondaryAccount), totalAccounts = 2, ).getOrNull()!!, toRemove = mainAccount, diff --git a/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/GetArchivedAccountsUseCaseTest.kt b/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/GetArchivedAccountsUseCaseTest.kt index 5fd7b2ccce..6c926ab2ec 100644 --- a/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/GetArchivedAccountsUseCaseTest.kt +++ b/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/GetArchivedAccountsUseCaseTest.kt @@ -1,7 +1,5 @@ package com.tangem.domain.account.usecase -import arrow.core.None -import arrow.core.toOption import com.google.common.truth.Truth import com.tangem.domain.account.models.ArchivedAccount import com.tangem.domain.account.repository.AccountsCRUDRepository @@ -12,7 +10,6 @@ import com.tangem.domain.models.wallet.UserWalletId import io.mockk.* import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch @@ -23,6 +20,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance +@Suppress("UnusedFlow") @OptIn(ExperimentalCoroutinesApi::class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) class GetArchivedAccountsUseCaseTest { @@ -43,76 +41,20 @@ class GetArchivedAccountsUseCaseTest { mockk(), mockk(), ) - coEvery { crudRepository.getArchivedAccountListSync(userWalletId) } returns archivedAccounts.toOption() + every { crudRepository.getArchivedAccounts(userWalletId) } returns flowOf(archivedAccounts) // Act val actual = getEmittedValues(useCase(userWalletId)) // Assert - val expected = listOf(archivedAccounts.lceContent()) + val expected = listOf( + lceLoading(), + archivedAccounts.lceContent(), + ) Truth.assertThat(actual).isEqualTo(expected) coVerifyOrder { - crudRepository.getArchivedAccountListSync(userWalletId) - crudRepository.getArchivedAccounts(userWalletId) - } - - coVerify(exactly = 0) { crudRepository.fetchArchivedAccounts(any()) } - } - - @Test - fun `invoke should emit loading and fetch when accounts not found`() = runTest { - // Arrange - val archivedAccounts = listOf( - mockk(), - mockk(), - ) - - coEvery { crudRepository.getArchivedAccountListSync(userWalletId) } returns None - every { crudRepository.getArchivedAccounts(userWalletId) } returns flowOf(archivedAccounts) - - // Act - val actual = getEmittedValues(useCase(userWalletId)) - - // Assert - val expected = listOf( - lceLoading(), - archivedAccounts.lceContent(), - ) - Truth.assertThat(actual).isEqualTo(expected) - - coVerify(exactly = 1) { - crudRepository.getArchivedAccountListSync(userWalletId) - crudRepository.fetchArchivedAccounts(userWalletId) - crudRepository.getArchivedAccounts(userWalletId) - } - } - - @Test - fun `invoke should emit error if getArchivedAccountsSync throws exception`() = runTest { - // Arrange - val exception = IllegalStateException("Test error") - val archivedAccounts = listOf( - mockk(), - mockk(), - ) - - coEvery { crudRepository.getArchivedAccountListSync(userWalletId) } throws exception - every { crudRepository.getArchivedAccounts(userWalletId) } returns flowOf(archivedAccounts) - - // Act - val actual = getEmittedValues(useCase(userWalletId)) - - // Assert - val expected = listOf( - lceLoading(), - archivedAccounts.lceContent(), - ) - Truth.assertThat(actual).isEqualTo(expected) - - coVerify(exactly = 1) { - crudRepository.getArchivedAccountListSync(userWalletId) crudRepository.fetchArchivedAccounts(userWalletId) crudRepository.getArchivedAccounts(userWalletId) } @@ -121,11 +63,14 @@ class GetArchivedAccountsUseCaseTest { @Test fun `invoke should emit error if fetchArchivedAccounts throws exception`() = runTest { // Arrange - val exception = IllegalStateException("Fetch error") + val exception = IllegalStateException("Test error") + val archivedAccounts = listOf( + mockk(), + mockk(), + ) - coEvery { crudRepository.getArchivedAccountListSync(userWalletId) } returns None - every { crudRepository.getArchivedAccounts(userWalletId) } returns emptyFlow() coEvery { crudRepository.fetchArchivedAccounts(userWalletId) } throws exception + every { crudRepository.getArchivedAccounts(userWalletId) } returns flowOf(archivedAccounts) // Act val actual = getEmittedValues(useCase(userWalletId)) @@ -135,14 +80,10 @@ class GetArchivedAccountsUseCaseTest { lceLoading(), exception.lceError(), ) - Truth.assertThat(actual).isEqualTo(expected) - coVerify(exactly = 1) { - crudRepository.getArchivedAccountListSync(userWalletId) - crudRepository.fetchArchivedAccounts(userWalletId) - crudRepository.getArchivedAccounts(userWalletId) - } + coVerifyOrder { crudRepository.fetchArchivedAccounts(userWalletId) } + coVerify(inverse = true) { crudRepository.getArchivedAccounts(any()) } } @OptIn(ExperimentalCoroutinesApi::class) 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 09fa80ad9b..c469708e18 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 @@ -31,7 +31,7 @@ class GetUnoccupiedAccountIndexUseCaseTest { @Test fun `invoke should return error if repository returns 0`() = runTest { // Arrange - coEvery { crudRepository.getTotalActiveAccountsCountSync(userWalletId) } returns 0.toOption() + coEvery { crudRepository.getTotalAccountsCountSync(userWalletId) } returns 0.toOption() // Act val actual = useCase(userWalletId = userWalletId).leftOrNull() as Error.DataOperationFailed @@ -42,13 +42,13 @@ class GetUnoccupiedAccountIndexUseCaseTest { Truth.assertThat(actual.cause).isInstanceOf(expected::class.java) Truth.assertThat(actual.cause).hasMessageThat().isEqualTo(expected.message) - coVerify { crudRepository.getTotalActiveAccountsCountSync(userWalletId) } + coVerify { crudRepository.getTotalAccountsCountSync(userWalletId) } } @Test fun `invoke should return next unoccupied index when repository returns count`() = runTest { // Arrange - coEvery { crudRepository.getTotalActiveAccountsCountSync(userWalletId) } returns 3.toOption() + coEvery { crudRepository.getTotalAccountsCountSync(userWalletId) } returns 3.toOption() // Act val actual = useCase(userWalletId = userWalletId) @@ -57,14 +57,14 @@ class GetUnoccupiedAccountIndexUseCaseTest { val expected = DerivationIndex(3) Truth.assertThat(actual).isEqualTo(expected) - coVerify { crudRepository.getTotalActiveAccountsCountSync(userWalletId) } + coVerify { crudRepository.getTotalAccountsCountSync(userWalletId) } } @Test fun `invoke should return error if repository throws exception`() = runTest { // Arrange val exception = IllegalStateException("Test error") - coEvery { crudRepository.getTotalActiveAccountsCountSync(userWalletId) } throws exception + coEvery { crudRepository.getTotalAccountsCountSync(userWalletId) } throws exception // Act val actual = useCase(userWalletId = userWalletId) @@ -73,6 +73,6 @@ class GetUnoccupiedAccountIndexUseCaseTest { val expected = Error.DataOperationFailed(exception).left() Truth.assertThat(actual).isEqualTo(expected) - coVerify { crudRepository.getTotalActiveAccountsCountSync(userWalletId) } + coVerify { crudRepository.getTotalAccountsCountSync(userWalletId) } } } \ No newline at end of file diff --git a/domain/account/src/test/kotlin/com/tangem/domain/account/utils/AccountExt.kt b/domain/account/src/test/kotlin/com/tangem/domain/account/utils/AccountExt.kt index 9f452c4145..dbcbe4ead7 100644 --- a/domain/account/src/test/kotlin/com/tangem/domain/account/utils/AccountExt.kt +++ b/domain/account/src/test/kotlin/com/tangem/domain/account/utils/AccountExt.kt @@ -4,8 +4,8 @@ import com.tangem.domain.models.account.* import com.tangem.domain.models.wallet.UserWalletId import kotlin.random.Random -fun createAccounts(userWalletId: UserWalletId, count: Int): Set { - return buildSet { +fun createAccounts(userWalletId: UserWalletId, count: Int): List { + return buildList { add(Account.CryptoPortfolio.createMainAccount(userWalletId)) repeat(count - 1) { diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/accounts/viewmodel/TesterAccountsViewModel.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/accounts/viewmodel/TesterAccountsViewModel.kt index c4880f9e35..41c3f707e8 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/accounts/viewmodel/TesterAccountsViewModel.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/accounts/viewmodel/TesterAccountsViewModel.kt @@ -106,7 +106,7 @@ internal class TesterAccountsViewModel @Inject constructor( AccountsUM.Button(title = "Fetch accounts", onClick = ::fetchAccounts), AccountsUM.Button(title = "Fill out the list (up to 20)", onClick = ::fillOutAccountList), AccountsUM.Button(title = "Archive all", onClick = ::archiveAllAccounts), - // AccountsUM.Button(title = "Sort by derivation index", onClick = ::sortAccountsByIndex), + AccountsUM.Button(title = "Sort by derivation index", onClick = ::sortAccountsByIndex), AccountsUM.Button(title = "Clear ETag") { clearETag() }, ), ) @@ -217,7 +217,7 @@ internal class TesterAccountsViewModel @Inject constructor( withContext(dispatchers.default) { val updatedAccountList = AccountList.invoke( userWalletId = accountList.userWalletId, - accounts = setOf(accountList.mainAccount), + accounts = listOf(accountList.mainAccount), totalAccounts = accountList.totalAccounts, sortType = accountList.sortType, groupType = accountList.groupType, @@ -231,7 +231,6 @@ internal class TesterAccountsViewModel @Inject constructor( } } - @Suppress("UnusedPrivateMember") private fun sortAccountsByIndex(title: String) { val userWalletId = getUserWallet()?.walletId ?: return val accountList = walletAccounts.value[userWalletId] ?: return @@ -244,8 +243,7 @@ internal class TesterAccountsViewModel @Inject constructor( userWalletId = accountList.userWalletId, accounts = accountList.accounts .filterIsInstance() - .sortedBy { it.derivationIndex.value } - .toSet(), + .sortedBy { it.derivationIndex.value }, totalAccounts = accountList.totalAccounts, sortType = accountList.sortType, groupType = accountList.groupType,