Updated on 2026-08-14
This commit is contained in:
parent
68615198ae
commit
9c296c7ced
10 changed files with 226 additions and 80 deletions
|
|
@ -113,6 +113,7 @@ dependencies {
|
|||
implementation(projects.domain.legacy)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
implementation(projects.domain.account)
|
||||
implementation(projects.domain.account.status)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.core)
|
||||
api(projects.domain.common)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.tangem.tap.di.domain
|
|||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.fetcher.SingleAccountListFetcher
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.account.status.usecase.RecoverCryptoPortfolioUseCase
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||
import com.tangem.domain.account.tokens.MainAccountTokensMigration
|
||||
import com.tangem.domain.account.usecase.*
|
||||
import dagger.Module
|
||||
|
|
@ -50,10 +52,12 @@ internal object AccountDomainModule {
|
|||
fun provideRecoverCryptoPortfolioUseCase(
|
||||
accountsCRUDRepository: AccountsCRUDRepository,
|
||||
mainAccountTokensMigration: MainAccountTokensMigration,
|
||||
cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
||||
): RecoverCryptoPortfolioUseCase {
|
||||
return RecoverCryptoPortfolioUseCase(
|
||||
crudRepository = accountsCRUDRepository,
|
||||
mainAccountTokensMigration = mainAccountTokensMigration,
|
||||
cryptoCurrencyBalanceFetcher = cryptoCurrencyBalanceFetcher,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.account.status.usecase.GetAccountCurrencyByAddressUseCa
|
|||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
|
||||
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
|
|
@ -70,14 +71,12 @@ internal object AccountStatusUseCaseModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSaveCryptoCurrenciesUseCase(
|
||||
fun provideManageCryptoCurrenciesUseCase(
|
||||
singleAccountListSupplier: SingleAccountListSupplier,
|
||||
accountsCRUDRepository: AccountsCRUDRepository,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
derivationsRepository: DerivationsRepository,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
||||
stakingIdFactory: StakingIdFactory,
|
||||
networksCleaner: NetworksCleaner,
|
||||
stakingCleaner: StakingCleaner,
|
||||
|
|
@ -89,9 +88,7 @@ internal object AccountStatusUseCaseModule {
|
|||
accountsCRUDRepository = accountsCRUDRepository,
|
||||
currenciesRepository = currenciesRepository,
|
||||
derivationsRepository = derivationsRepository,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
|
||||
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
|
||||
cryptoCurrencyBalanceFetcher = cryptoCurrencyBalanceFetcher,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
networksCleaner = networksCleaner,
|
||||
stakingCleaner = stakingCleaner,
|
||||
|
|
@ -100,4 +97,24 @@ internal object AccountStatusUseCaseModule {
|
|||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCryptoCurrencyBalanceFetcher(
|
||||
accountsCRUDRepository: AccountsCRUDRepository,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
stakingIdFactory: StakingIdFactory,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): CryptoCurrencyBalanceFetcher {
|
||||
return CryptoCurrencyBalanceFetcher(
|
||||
accountsCRUDRepository = accountsCRUDRepository,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
|
||||
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import arrow.core.raise.Raise
|
|||
import arrow.core.raise.catch
|
||||
import com.tangem.domain.account.producer.SingleAccountListProducer
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.core.utils.eitherOn
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
|
|
@ -14,11 +15,8 @@ import com.tangem.domain.models.account.AccountId
|
|||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||
import com.tangem.domain.networks.utils.NetworksCleaner
|
||||
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
||||
import com.tangem.domain.staking.StakingIdFactory
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||
import com.tangem.domain.staking.utils.StakingCleaner
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||
|
|
@ -30,14 +28,15 @@ import timber.log.Timber
|
|||
* Use case for saving crypto currencies to a specific account.
|
||||
*
|
||||
* @property singleAccountListSupplier Supplier to get account details.
|
||||
* @property accountsCRUDRepository Repository for performing CRUD operations on accounts.
|
||||
* @property currenciesRepository Repository for managing currencies.
|
||||
* @property derivationsRepository Repository for deriving public keys.
|
||||
* @property multiNetworkStatusFetcher Fetcher for updating network statuses.
|
||||
* @property multiQuoteStatusFetcher Fetcher for updating quote statuses.
|
||||
* @property multiYieldBalanceFetcher Fetcher for updating yield balances.
|
||||
* @property cryptoCurrencyBalanceFetcher Fetcher for updating crypto currency balances.
|
||||
* @property stakingIdFactory Factory for creating staking IDs.
|
||||
* @property networksCleaner Cleaner for removing obsolete network data.
|
||||
* @property stakingCleaner Cleaner for removing obsolete staking data.
|
||||
* @property expressServiceFetcher Fetcher for updating express service data.
|
||||
* @property parallelUpdatingScope Coroutine scope for parallel updates.
|
||||
* @property dispatchers Coroutine dispatchers for managing threading.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -48,9 +47,7 @@ class ManageCryptoCurrenciesUseCase(
|
|||
private val accountsCRUDRepository: AccountsCRUDRepository,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val derivationsRepository: DerivationsRepository,
|
||||
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
private val cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val networksCleaner: NetworksCleaner,
|
||||
private val stakingCleaner: StakingCleaner,
|
||||
|
|
@ -98,7 +95,7 @@ class ManageCryptoCurrenciesUseCase(
|
|||
launch { accountsCRUDRepository.syncTokens(userWalletId) }
|
||||
}
|
||||
|
||||
refreshBalances(userWalletId = userWalletId, currencies = modifiedCurrencyList.added)
|
||||
cryptoCurrencyBalanceFetcher(userWalletId = userWalletId, currencies = modifiedCurrencyList.added)
|
||||
refreshExpress(userWalletId = userWalletId, currencies = modifiedCurrencyList.total)
|
||||
clearMetadata(userWalletId = userWalletId, currencies = modifiedCurrencyList.removed)
|
||||
}
|
||||
|
|
@ -132,7 +129,7 @@ class ManageCryptoCurrenciesUseCase(
|
|||
saveAccount(account = account.copy(cryptoCurrencies = modifiedCurrencyList.total.toSet()))
|
||||
|
||||
parallelUpdatingScope.launch {
|
||||
refreshBalances(userWalletId = userWalletId, currencies = listOf(tokenToAdd))
|
||||
cryptoCurrencyBalanceFetcher(userWalletId = userWalletId, currencies = listOf(tokenToAdd))
|
||||
refreshExpress(userWalletId = userWalletId, currencies = modifiedCurrencyList.total)
|
||||
}
|
||||
|
||||
|
|
@ -253,46 +250,6 @@ class ManageCryptoCurrenciesUseCase(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun refreshBalances(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
if (currencies.isEmpty()) return
|
||||
|
||||
coroutineScope {
|
||||
launch { refreshNetworks(userWalletId = userWalletId, currencies = currencies) }
|
||||
launch { refreshYieldBalances(userWalletId = userWalletId, currencies = currencies) }
|
||||
launch { refreshQuotes(currencies = currencies) }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshNetworks(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
multiNetworkStatusFetcher(
|
||||
params = MultiNetworkStatusFetcher.Params(
|
||||
userWalletId = userWalletId,
|
||||
networks = currencies.mapTo(hashSetOf(), CryptoCurrency::network),
|
||||
),
|
||||
)
|
||||
|
||||
accountsCRUDRepository.syncTokens(userWalletId)
|
||||
}
|
||||
|
||||
private suspend fun refreshYieldBalances(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
val stakingIds = currencies.mapNotNullTo(hashSetOf()) {
|
||||
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull()
|
||||
}
|
||||
|
||||
multiYieldBalanceFetcher(
|
||||
params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun refreshQuotes(currencies: List<CryptoCurrency>) {
|
||||
multiQuoteStatusFetcher(
|
||||
params = MultiQuoteStatusFetcher.Params(
|
||||
currenciesIds = currencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
|
||||
appCurrencyId = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun refreshExpress(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
if (currencies.isEmpty()) return
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.domain.account.usecase
|
||||
package com.tangem.domain.account.status.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
|
|
@ -9,6 +9,7 @@ import arrow.core.raise.ensure
|
|||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.models.ArchivedAccount
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||
import com.tangem.domain.account.tokens.MainAccountTokensMigration
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
|
|
@ -25,6 +26,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
class RecoverCryptoPortfolioUseCase(
|
||||
private val crudRepository: AccountsCRUDRepository,
|
||||
private val mainAccountTokensMigration: MainAccountTokensMigration,
|
||||
private val cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
||||
) {
|
||||
|
||||
/**
|
||||
|
|
@ -53,6 +55,8 @@ class RecoverCryptoPortfolioUseCase(
|
|||
derivationIndex = recoveredAccount.derivationIndex,
|
||||
)
|
||||
|
||||
refreshBalances(accountId = accountId)
|
||||
|
||||
recoveredAccount
|
||||
}
|
||||
|
||||
|
|
@ -93,6 +97,23 @@ class RecoverCryptoPortfolioUseCase(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun refreshBalances(accountId: AccountId): Either<Error, Unit> = either {
|
||||
val currencies = catch(
|
||||
block = { crudRepository.getAccountSync(accountId = accountId) },
|
||||
catch = { raise(Error.DataOperationFailed(cause = it)) },
|
||||
)
|
||||
.getOrElse { raise(Error.DataOperationFailed(message = "Account not found: $accountId")) }
|
||||
.cryptoCurrencies
|
||||
.toList()
|
||||
|
||||
catch(
|
||||
block = {
|
||||
cryptoCurrencyBalanceFetcher(userWalletId = accountId.userWalletId, currencies = currencies)
|
||||
},
|
||||
catch = { raise(Error.DataOperationFailed(cause = it)) },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents possible errors that can occur during the add operation
|
||||
*/
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
||||
import com.tangem.domain.staking.StakingIdFactory
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||
import com.tangem.domain.tokens.wallet.FetchingSource
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Utility class responsible for fetching and refreshing the balances of various crypto currencies
|
||||
* associated with a user's wallet.
|
||||
*
|
||||
* @property accountsCRUDRepository Repository for managing account data.
|
||||
* @property multiNetworkStatusFetcher Fetcher for updating network statuses.
|
||||
* @property multiQuoteStatusFetcher Fetcher for updating quote statuses.
|
||||
* @property multiYieldBalanceFetcher Fetcher for updating yield balances.
|
||||
* @property stakingIdFactory Factory for creating staking IDs.
|
||||
* @property parallelUpdatingScope Coroutine scope for parallel balance updates.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class CryptoCurrencyBalanceFetcher(
|
||||
private val accountsCRUDRepository: AccountsCRUDRepository,
|
||||
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val parallelUpdatingScope: CoroutineScope,
|
||||
) {
|
||||
|
||||
private val mutex = Mutex()
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
if (currencies.isEmpty()) return
|
||||
|
||||
parallelUpdatingScope.launch {
|
||||
mutex.withLock {
|
||||
refreshBalances(userWalletId, currencies)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshBalances(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
coroutineScope {
|
||||
val results = listOf(
|
||||
async {
|
||||
FetchingSource.NETWORK to refreshNetworks(userWalletId = userWalletId, currencies = currencies)
|
||||
},
|
||||
async {
|
||||
FetchingSource.STAKING to refreshYieldBalances(userWalletId = userWalletId, currencies = currencies)
|
||||
},
|
||||
async { FetchingSource.QUOTE to refreshQuotes(currencies = currencies) },
|
||||
)
|
||||
.awaitAll()
|
||||
|
||||
val errors = results.mapNotNull { (source, maybeResult) ->
|
||||
val error = maybeResult.leftOrNull() ?: return@mapNotNull null
|
||||
|
||||
source to error
|
||||
}
|
||||
|
||||
check(errors.isEmpty()) {
|
||||
val message = "Failed to fetch next sources for $userWalletId:\n" +
|
||||
errors.joinToString(separator = "\n") { "${it.first.name} – ${it.second}" }
|
||||
|
||||
Timber.e(message)
|
||||
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshNetworks(
|
||||
userWalletId: UserWalletId,
|
||||
currencies: List<CryptoCurrency>,
|
||||
): Either<Throwable, Unit> = either {
|
||||
val either = multiNetworkStatusFetcher(
|
||||
params = MultiNetworkStatusFetcher.Params(
|
||||
userWalletId = userWalletId,
|
||||
networks = currencies.mapTo(hashSetOf(), CryptoCurrency::network),
|
||||
),
|
||||
)
|
||||
|
||||
arrow.core.raise.catch(
|
||||
block = { accountsCRUDRepository.syncTokens(userWalletId) },
|
||||
catch = {
|
||||
Timber.e(it, "Failed to sync tokens for wallet: $userWalletId")
|
||||
},
|
||||
)
|
||||
|
||||
return either
|
||||
}
|
||||
|
||||
private suspend fun refreshYieldBalances(
|
||||
userWalletId: UserWalletId,
|
||||
currencies: List<CryptoCurrency>,
|
||||
): Either<Throwable, Unit> {
|
||||
val stakingIds = currencies.mapNotNullTo(hashSetOf()) {
|
||||
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull()
|
||||
}
|
||||
|
||||
return multiYieldBalanceFetcher(
|
||||
params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun refreshQuotes(currencies: List<CryptoCurrency>): Either<Throwable, Unit> {
|
||||
return multiQuoteStatusFetcher(
|
||||
params = MultiQuoteStatusFetcher.Params(
|
||||
currenciesIds = currencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
|
||||
appCurrencyId = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.domain.account.usecase
|
||||
package com.tangem.domain.account.status.usecase
|
||||
|
||||
import arrow.core.None
|
||||
import arrow.core.left
|
||||
|
|
@ -8,17 +8,17 @@ import com.google.common.truth.Truth
|
|||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.models.ArchivedAccount
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.account.status.usecase.RecoverCryptoPortfolioUseCase.Error.DataOperationFailed
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||
import com.tangem.domain.account.tokens.MainAccountTokensMigration
|
||||
import com.tangem.domain.account.usecase.RecoverCryptoPortfolioUseCase.Error
|
||||
import com.tangem.domain.account.utils.createAccount
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.account.*
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -28,14 +28,16 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
|
||||
private val crudRepository: AccountsCRUDRepository = mockk(relaxUnitFun = true)
|
||||
private val mainAccountTokensMigration: MainAccountTokensMigration = mockk()
|
||||
private val cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher = mockk(relaxUnitFun = true)
|
||||
private val useCase = RecoverCryptoPortfolioUseCase(
|
||||
crudRepository = crudRepository,
|
||||
mainAccountTokensMigration = mainAccountTokensMigration,
|
||||
cryptoCurrencyBalanceFetcher = cryptoCurrencyBalanceFetcher,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun resetMocks() {
|
||||
clearMocks(crudRepository)
|
||||
clearMocks(crudRepository, mainAccountTokensMigration, cryptoCurrencyBalanceFetcher)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -57,6 +59,7 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
coEvery { crudRepository.getAccountListSync(userWalletId) } returns accountList.toOption()
|
||||
coEvery { crudRepository.getArchivedAccountSync(account.accountId) } returns archivedAccount.toOption()
|
||||
coEvery { mainAccountTokensMigration.migrate(userWalletId, account.derivationIndex) } returns Unit.right()
|
||||
coEvery { crudRepository.getAccountSync(account.accountId) } returns account.toOption()
|
||||
|
||||
// Act
|
||||
val actual = useCase(account.accountId)
|
||||
|
|
@ -69,21 +72,23 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
crudRepository.getAccountListSync(userWalletId)
|
||||
crudRepository.getArchivedAccountSync(account.accountId)
|
||||
crudRepository.saveAccounts(updatedAccountList)
|
||||
crudRepository.getAccountSync(account.accountId)
|
||||
cryptoCurrencyBalanceFetcher(userWalletId, account.cryptoCurrencies.toList())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke should return error if getAccounts returns None`() = runTest {
|
||||
// Arrange
|
||||
val accountId = AccountId.forCryptoPortfolio(
|
||||
val accountId = AccountId.Companion.forCryptoPortfolio(
|
||||
userWalletId = userWalletId,
|
||||
derivationIndex = DerivationIndex.Main,
|
||||
derivationIndex = DerivationIndex.Companion.Main,
|
||||
)
|
||||
|
||||
coEvery { crudRepository.getAccountListSync(userWalletId) } returns None
|
||||
|
||||
// Act
|
||||
val actual = useCase(accountId).leftOrNull() as Error.DataOperationFailed
|
||||
val actual = useCase(accountId).leftOrNull() as DataOperationFailed
|
||||
|
||||
// Assert
|
||||
val expected = IllegalStateException("Account list not found for wallet $userWalletId")
|
||||
|
|
@ -100,9 +105,9 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
@Test
|
||||
fun `invoke should return error if getAccounts throws exception`() = runTest {
|
||||
// Arrange
|
||||
val accountId = AccountId.forCryptoPortfolio(
|
||||
val accountId = AccountId.Companion.forCryptoPortfolio(
|
||||
userWalletId = userWalletId,
|
||||
derivationIndex = DerivationIndex.Main,
|
||||
derivationIndex = DerivationIndex.Companion.Main,
|
||||
)
|
||||
val exception = IllegalStateException("Test error")
|
||||
|
||||
|
|
@ -112,7 +117,7 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
val actual = useCase(accountId)
|
||||
|
||||
// Assert
|
||||
val expected = Error.DataOperationFailed(exception).left()
|
||||
val expected = DataOperationFailed(exception).left()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifySequence { crudRepository.getAccountListSync(userWalletId) }
|
||||
|
|
@ -126,7 +131,7 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
fun `invoke should return error if getArchivedAccount throws exception`() = runTest {
|
||||
// Arrange
|
||||
val account = createAccount(userWalletId)
|
||||
val accountList = AccountList.empty(userWalletId)
|
||||
val accountList = AccountList.Companion.empty(userWalletId)
|
||||
val exception = IllegalStateException("Test error")
|
||||
|
||||
coEvery { crudRepository.getAccountListSync(userWalletId) } returns accountList.toOption()
|
||||
|
|
@ -136,7 +141,7 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
val actual = useCase(account.accountId)
|
||||
|
||||
// Assert
|
||||
val expected = Error.DataOperationFailed(exception).left()
|
||||
val expected = DataOperationFailed(exception).left()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifySequence {
|
||||
|
|
@ -150,13 +155,13 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
fun `invoke should return error if getArchivedAccount returns null`() = runTest {
|
||||
// Arrange
|
||||
val account = createAccount(userWalletId)
|
||||
val accountList = AccountList.empty(userWalletId)
|
||||
val accountList = AccountList.Companion.empty(userWalletId)
|
||||
|
||||
coEvery { crudRepository.getAccountListSync(userWalletId) } returns accountList.toOption()
|
||||
coEvery { crudRepository.getArchivedAccountSync(account.accountId) } returns None
|
||||
|
||||
// Act
|
||||
val actual = useCase(account.accountId).leftOrNull() as Error.DataOperationFailed
|
||||
val actual = useCase(account.accountId).leftOrNull() as DataOperationFailed
|
||||
|
||||
// Assert
|
||||
val expected = IllegalStateException("Account not found: ${account.accountId}")
|
||||
|
|
@ -174,7 +179,7 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
fun `invoke should return error if saveAccounts throws exception`() = runTest {
|
||||
// Arrange
|
||||
val account = createAccount(userWalletId)
|
||||
val accountList = AccountList.empty(userWalletId)
|
||||
val accountList = AccountList.Companion.empty(userWalletId)
|
||||
val archivedAccount = ArchivedAccount(
|
||||
accountId = account.accountId,
|
||||
name = account.accountName,
|
||||
|
|
@ -195,7 +200,7 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
val actual = useCase(account.accountId)
|
||||
|
||||
// Assert
|
||||
val expected = Error.DataOperationFailed(exception).left()
|
||||
val expected = DataOperationFailed(exception).left()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifySequence {
|
||||
|
|
@ -205,6 +210,23 @@ class RecoverCryptoPortfolioUseCaseTest {
|
|||
}
|
||||
}
|
||||
|
||||
private fun createAccount(
|
||||
userWalletId: UserWalletId,
|
||||
name: String = "Test Account",
|
||||
icon: CryptoPortfolioIcon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex: Int = Random.nextInt(1, 21),
|
||||
): Account.CryptoPortfolio {
|
||||
val derivationIndex = DerivationIndex(derivationIndex).getOrNull()!!
|
||||
|
||||
return Account.CryptoPortfolio(
|
||||
accountId = AccountId.forCryptoPortfolio(userWalletId = userWalletId, derivationIndex = derivationIndex),
|
||||
accountName = AccountName(name).getOrNull()!!,
|
||||
icon = icon,
|
||||
derivationIndex = derivationIndex,
|
||||
cryptoCurrencies = emptySet(),
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val userWalletId = UserWalletId("011")
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ package com.tangem.domain.tokens.wallet
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal enum class FetchingSource {
|
||||
enum class FetchingSource {
|
||||
NETWORK,
|
||||
QUOTE,
|
||||
STAKING,
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import com.tangem.core.ui.message.EventMessageAction
|
|||
import com.tangem.core.ui.message.ToastMessage
|
||||
import com.tangem.core.ui.utils.showErrorDialog
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.status.usecase.RecoverCryptoPortfolioUseCase
|
||||
import com.tangem.domain.account.usecase.GetArchivedAccountsUseCase
|
||||
import com.tangem.domain.account.usecase.RecoverCryptoPortfolioUseCase
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.features.account.ArchivedAccountListComponent
|
||||
import com.tangem.features.account.archived.entity.AccountArchivedUM
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.features.account.createedit.error
|
||||
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.domain.account.status.usecase.RecoverCryptoPortfolioUseCase
|
||||
import com.tangem.domain.account.usecase.AddCryptoPortfolioUseCase
|
||||
import com.tangem.domain.account.usecase.GetUnoccupiedAccountIndexUseCase
|
||||
import com.tangem.domain.account.usecase.RecoverCryptoPortfolioUseCase
|
||||
import com.tangem.domain.account.usecase.UpdateCryptoPortfolioUseCase
|
||||
|
||||
sealed interface AccountFeatureError : UniversalError {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue