Updated on 2026-08-14
This commit is contained in:
parent
c613890b9a
commit
e96b780d21
10 changed files with 577 additions and 272 deletions
|
|
@ -2,17 +2,40 @@ package com.tangem.datasource.local.network.entity
|
||||||
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
import com.tangem.datasource.local.network.entity.NetworkStatusDM.NoAccount
|
||||||
|
import com.tangem.datasource.local.network.entity.NetworkStatusDM.Verified
|
||||||
import com.tangem.domain.tokens.model.Network
|
import com.tangem.domain.tokens.model.Network
|
||||||
import dev.onenowy.moshipolymorphicadapter.annotations.NameLabel
|
import dev.onenowy.moshipolymorphicadapter.annotations.NameLabel
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Network status for storage in the local cache. Supports two types - the [Verified] and [NoAccount].
|
||||||
|
*
|
||||||
|
* @see [com.tangem.domain.tokens.model.NetworkStatus]
|
||||||
|
*/
|
||||||
sealed interface NetworkStatusDM {
|
sealed interface NetworkStatusDM {
|
||||||
|
|
||||||
|
/** Network id */
|
||||||
val networkId: Network.ID
|
val networkId: Network.ID
|
||||||
|
|
||||||
|
/** Derivation path */
|
||||||
val derivationPath: DerivationPath
|
val derivationPath: DerivationPath
|
||||||
|
|
||||||
|
/** Selected address */
|
||||||
val selectedAddress: String
|
val selectedAddress: String
|
||||||
|
|
||||||
|
/** Available address */
|
||||||
val availableAddresses: Set<Address>
|
val availableAddresses: Set<Address>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verified
|
||||||
|
*
|
||||||
|
* @property networkId network id
|
||||||
|
* @property derivationPath derivation path
|
||||||
|
* @property selectedAddress selected address
|
||||||
|
* @property availableAddresses available addresses
|
||||||
|
* @property amounts amounts
|
||||||
|
*/
|
||||||
@NameLabel("amounts")
|
@NameLabel("amounts")
|
||||||
data class Verified(
|
data class Verified(
|
||||||
@Json(name = "network_id") override val networkId: Network.ID,
|
@Json(name = "network_id") override val networkId: Network.ID,
|
||||||
|
|
@ -22,6 +45,16 @@ sealed interface NetworkStatusDM {
|
||||||
@Json(name = "amounts") val amounts: Map<String, BigDecimal>,
|
@Json(name = "amounts") val amounts: Map<String, BigDecimal>,
|
||||||
) : NetworkStatusDM
|
) : NetworkStatusDM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No account
|
||||||
|
*
|
||||||
|
* @property networkId network id
|
||||||
|
* @property derivationPath derivation path
|
||||||
|
* @property selectedAddress selected address
|
||||||
|
* @property availableAddresses available addresses
|
||||||
|
* @property amountToCreateAccount amount to create account
|
||||||
|
* @property errorMessage error message
|
||||||
|
*/
|
||||||
@NameLabel("amount_to_create_account")
|
@NameLabel("amount_to_create_account")
|
||||||
data class NoAccount(
|
data class NoAccount(
|
||||||
@Json(name = "network_id") override val networkId: Network.ID,
|
@Json(name = "network_id") override val networkId: Network.ID,
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,15 @@ package com.tangem.data.networks.multi
|
||||||
|
|
||||||
import arrow.core.raise.catch
|
import arrow.core.raise.catch
|
||||||
import arrow.core.raise.ensure
|
import arrow.core.raise.ensure
|
||||||
import com.tangem.blockchain.common.Blockchain
|
|
||||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|
||||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
|
||||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
import com.tangem.data.networks.fetcher.CommonNetworkStatusFetcher
|
||||||
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
||||||
import com.tangem.data.networks.store.setSourceAsCache
|
import com.tangem.data.networks.store.setSourceAsCache
|
||||||
import com.tangem.data.networks.store.setSourceAsOnlyCache
|
import com.tangem.data.networks.store.setSourceAsOnlyCache
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
|
||||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
|
||||||
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
|
||||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.common.util.cardTypesResolver
|
import com.tangem.domain.common.util.cardTypesResolver
|
||||||
import com.tangem.domain.core.utils.eitherOn
|
import com.tangem.domain.core.utils.eitherOn
|
||||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
|
||||||
import com.tangem.domain.tokens.model.Network
|
|
||||||
import com.tangem.domain.wallets.models.UserWallet
|
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
|
|
@ -31,28 +20,24 @@ import javax.inject.Inject
|
||||||
/**
|
/**
|
||||||
* Default implementation of [MultiNetworkStatusFetcher]
|
* Default implementation of [MultiNetworkStatusFetcher]
|
||||||
*
|
*
|
||||||
* @property singleNetworkStatusFetcher single network status fetcher
|
|
||||||
* @property networksStatusesStore networks statuses store
|
* @property networksStatusesStore networks statuses store
|
||||||
|
* @property userWalletsStore user wallets store
|
||||||
|
* @property cardCryptoCurrencyFactory card crypto currency factory
|
||||||
|
* @property commonNetworkStatusFetcher common network status fetcher
|
||||||
|
* @property dispatchers dispatchers
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
internal class DefaultMultiNetworkStatusFetcher @Inject constructor(
|
internal class DefaultMultiNetworkStatusFetcher @Inject constructor(
|
||||||
excludedBlockchains: ExcludedBlockchains,
|
|
||||||
private val networksStatusesStore: NetworksStatusesStoreV2,
|
private val networksStatusesStore: NetworksStatusesStoreV2,
|
||||||
private val userWalletsStore: UserWalletsStore,
|
private val userWalletsStore: UserWalletsStore,
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
|
||||||
private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
|
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
|
||||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
|
private val commonNetworkStatusFetcher: CommonNetworkStatusFetcher,
|
||||||
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : MultiNetworkStatusFetcher {
|
) : MultiNetworkStatusFetcher {
|
||||||
|
|
||||||
private val responseCurrenciesFactory by lazy { ResponseCryptoCurrenciesFactory(excludedBlockchains) }
|
|
||||||
|
|
||||||
override suspend fun invoke(params: MultiNetworkStatusFetcher.Params) = eitherOn(dispatchers.default) {
|
override suspend fun invoke(params: MultiNetworkStatusFetcher.Params) = eitherOn(dispatchers.default) {
|
||||||
// Optimization!
|
|
||||||
// Every singleNetworkStatusFetcher with applyRefresh as true will refresh every network in the store.
|
|
||||||
// So if we update all networks at once, it will be more efficient.
|
|
||||||
networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, networks = params.networks)
|
networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, networks = params.networks)
|
||||||
|
|
||||||
val userWallet = catch(
|
val userWallet = catch(
|
||||||
|
|
@ -67,27 +52,39 @@ internal class DefaultMultiNetworkStatusFetcher @Inject constructor(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
val isNotSingleWallet = with(userWallet.scanResponse.cardTypesResolver) {
|
val cardTypesResolver = userWallet.cardTypesResolver
|
||||||
|
val isWalletSupported = with(cardTypesResolver) {
|
||||||
isMultiwalletAllowed() || isSingleWalletWithToken()
|
isMultiwalletAllowed() || isSingleWalletWithToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure(isNotSingleWallet) {
|
ensure(isWalletSupported) {
|
||||||
networksStatusesStore.setSourceAsOnlyCache(userWalletId = params.userWalletId, networks = params.networks)
|
networksStatusesStore.setSourceAsOnlyCache(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
networks = params.networks,
|
||||||
|
)
|
||||||
IllegalStateException("User wallet is not multi-currency")
|
IllegalStateException("User wallet is not multi-currency")
|
||||||
}
|
}
|
||||||
|
|
||||||
val networksCurrencies = createCurrencies(userWallet = userWallet, networks = params.networks)
|
val networksCurrencies = if (cardTypesResolver.isMultiwalletAllowed()) {
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(
|
||||||
|
userWallet = userWallet,
|
||||||
|
networks = params.networks,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(
|
||||||
|
scanResponse = userWallet.scanResponse,
|
||||||
|
)
|
||||||
|
.groupBy { it.network }
|
||||||
|
}
|
||||||
|
|
||||||
val result = coroutineScope {
|
val result = coroutineScope {
|
||||||
params.networks
|
params.networks
|
||||||
.map { network ->
|
.map { network ->
|
||||||
async {
|
async {
|
||||||
singleNetworkStatusFetcher(
|
commonNetworkStatusFetcher.fetch(
|
||||||
params = SingleNetworkStatusFetcher.Params.Prepared(
|
userWalletId = params.userWalletId,
|
||||||
userWalletId = params.userWalletId,
|
network = network,
|
||||||
network = network,
|
networkCurrencies = networksCurrencies[network].orEmpty().toSet(),
|
||||||
addedNetworkCurrencies = networksCurrencies.filter { it.network == network }.toSet(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,31 +97,4 @@ internal class DefaultMultiNetworkStatusFetcher @Inject constructor(
|
||||||
IllegalStateException("Failed to fetch network statuses")
|
IllegalStateException("Failed to fetch network statuses")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createCurrencies(userWallet: UserWallet, networks: Set<Network>): Set<CryptoCurrency> {
|
|
||||||
val blockchains = networks.map { Blockchain.fromNetworkId(networkId = it.backendId) }
|
|
||||||
|
|
||||||
// multi-currency wallet
|
|
||||||
if (userWallet.isMultiCurrency) return getMultiWalletCurrencies(userWallet = userWallet, networks = networks)
|
|
||||||
|
|
||||||
// check if the blockchain of single-currency wallet is the same as network
|
|
||||||
val cardBlockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain()
|
|
||||||
if (!blockchains.contains(cardBlockchain)) return emptySet()
|
|
||||||
|
|
||||||
return cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse).toSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getMultiWalletCurrencies(userWallet: UserWallet, networks: Set<Network>): Set<CryptoCurrency> {
|
|
||||||
val response = appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
|
|
||||||
key = PreferencesKeys.getUserTokensKey(userWallet.walletId.stringValue),
|
|
||||||
) ?: return emptySet()
|
|
||||||
|
|
||||||
return responseCurrenciesFactory.createCurrencies(
|
|
||||||
tokens = response.tokens.filter { token ->
|
|
||||||
networks.any { it.backendId == token.networkId && it.derivationPath.value == token.derivationPath }
|
|
||||||
},
|
|
||||||
scanResponse = userWallet.scanResponse,
|
|
||||||
)
|
|
||||||
.toSet()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,70 +2,46 @@ package com.tangem.data.networks.single
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
|
import com.tangem.data.networks.fetcher.CommonNetworkStatusFetcher
|
||||||
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
||||||
import com.tangem.data.networks.store.setSourceAsCache
|
import com.tangem.data.networks.store.setSourceAsCache
|
||||||
import com.tangem.data.networks.store.setSourceAsOnlyCache
|
|
||||||
import com.tangem.data.networks.store.storeStatus
|
|
||||||
import com.tangem.data.networks.utils.NetworkStatusFactory
|
|
||||||
import com.tangem.domain.core.utils.catchOn
|
import com.tangem.domain.core.utils.catchOn
|
||||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of [SingleNetworkStatusFetcher]
|
* Default implementation of [SingleNetworkStatusFetcher]
|
||||||
*
|
*
|
||||||
* @property walletManagersFacade wallet managers facade
|
* @property commonNetworkStatusFetcher common network status fetcher
|
||||||
* @property networksStatusesStore networks statuses store
|
* @property networksStatusesStore networks statuses store
|
||||||
* @property cardCryptoCurrencyFactory card crypto currency factory
|
* @property cardCryptoCurrencyFactory card crypto currency factory
|
||||||
* @property dispatchers dispatchers
|
* @property dispatchers dispatchers
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
internal class DefaultSingleNetworkStatusFetcher @Inject constructor(
|
internal class DefaultSingleNetworkStatusFetcher @Inject constructor(
|
||||||
private val walletManagersFacade: WalletManagersFacade,
|
private val commonNetworkStatusFetcher: CommonNetworkStatusFetcher,
|
||||||
private val networksStatusesStore: NetworksStatusesStoreV2,
|
private val networksStatusesStore: NetworksStatusesStoreV2,
|
||||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : SingleNetworkStatusFetcher {
|
) : SingleNetworkStatusFetcher {
|
||||||
|
|
||||||
override suspend fun invoke(params: SingleNetworkStatusFetcher.Params) = Either.catchOn(dispatchers.default) {
|
override suspend fun invoke(params: SingleNetworkStatusFetcher.Params): Either<Throwable, Unit> {
|
||||||
val networkCurrencies = when (params) {
|
return Either.catchOn(dispatchers.default) {
|
||||||
is SingleNetworkStatusFetcher.Params.Prepared -> params.addedNetworkCurrencies
|
networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, network = params.network)
|
||||||
is SingleNetworkStatusFetcher.Params.Simple -> {
|
|
||||||
networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, network = params.network)
|
|
||||||
|
|
||||||
cardCryptoCurrencyFactory.create(
|
val networkCurrencies = cardCryptoCurrencyFactory.create(
|
||||||
userWalletId = params.userWalletId,
|
|
||||||
network = params.network,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = withContext(dispatchers.io) {
|
|
||||||
walletManagersFacade.update(
|
|
||||||
userWalletId = params.userWalletId,
|
userWalletId = params.userWalletId,
|
||||||
network = params.network,
|
network = params.network,
|
||||||
extraTokens = networkCurrencies
|
|
||||||
.filterIsInstance<CryptoCurrency.Token>()
|
|
||||||
.toSet(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = params.network,
|
||||||
|
networkCurrencies = networkCurrencies.toSet(),
|
||||||
|
)
|
||||||
|
.onLeft { throw it }
|
||||||
}
|
}
|
||||||
|
|
||||||
val status = NetworkStatusFactory.create(
|
|
||||||
network = params.network,
|
|
||||||
updatingResult = result,
|
|
||||||
addedCurrencies = networkCurrencies.toSet(),
|
|
||||||
)
|
|
||||||
|
|
||||||
networksStatusesStore.storeStatus(userWalletId = params.userWalletId, status = status)
|
|
||||||
}
|
}
|
||||||
.onLeft {
|
|
||||||
Timber.e("Failed to fetch network status for $params: $it")
|
|
||||||
networksStatusesStore.setSourceAsOnlyCache(userWalletId = params.userWalletId, network = params.network)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,119 +1,409 @@
|
||||||
package com.tangem.data.networks.multi
|
package com.tangem.data.networks.multi
|
||||||
|
|
||||||
|
import arrow.core.Either
|
||||||
|
import com.google.common.truth.Truth
|
||||||
|
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||||
|
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
|
||||||
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
|
import com.tangem.data.networks.fetcher.CommonNetworkStatusFetcher
|
||||||
|
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
||||||
|
import com.tangem.data.networks.store.setSourceAsCache
|
||||||
|
import com.tangem.data.networks.store.setSourceAsOnlyCache
|
||||||
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
|
import com.tangem.domain.common.CardTypesResolver
|
||||||
|
import com.tangem.domain.common.util.cardTypesResolver
|
||||||
|
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||||
|
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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
// internal class DefaultMultiNetworkStatusFetcherTest {
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
//
|
internal class DefaultMultiNetworkStatusFetcherTest {
|
||||||
// private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher = mockk()
|
|
||||||
// private val networksStatusesStore: NetworksStatusesStoreV2 = mockk(relaxed = true)
|
private val networksStatusesStore: NetworksStatusesStoreV2 = mockk(relaxUnitFun = true)
|
||||||
//
|
private val userWalletsStore: UserWalletsStore = mockk()
|
||||||
// private val fetcher = DefaultMultiNetworkStatusFetcher(
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk()
|
||||||
// singleNetworkStatusFetcher = singleNetworkStatusFetcher,
|
private val commonNetworkStatusFetcher: CommonNetworkStatusFetcher = mockk()
|
||||||
// networksStatusesStore = networksStatusesStore,
|
|
||||||
// )
|
private val fetcher = DefaultMultiNetworkStatusFetcher(
|
||||||
//
|
networksStatusesStore = networksStatusesStore,
|
||||||
// @Test
|
userWalletsStore = userWalletsStore,
|
||||||
// fun `fetch networks statuses successfully`() = runTest {
|
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||||
// val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = ethereumAndStellar)
|
commonNetworkStatusFetcher = commonNetworkStatusFetcher,
|
||||||
//
|
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||||
// val ethParams = SingleNetworkStatusFetcher.Params(
|
)
|
||||||
// userWalletId = userWalletId,
|
|
||||||
// network = ethereumAndStellar.first(),
|
@BeforeEach
|
||||||
// applyRefresh = false,
|
fun resetMocks() {
|
||||||
// )
|
clearMocks(networksStatusesStore, userWalletsStore, cardCryptoCurrencyFactory, commonNetworkStatusFetcher)
|
||||||
//
|
}
|
||||||
// val stellarParams = SingleNetworkStatusFetcher.Params(
|
|
||||||
// userWalletId = userWalletId,
|
@Test
|
||||||
// network = ethereumAndStellar.last(),
|
fun `fetch successfully for multi-currency card`() = runTest {
|
||||||
// applyRefresh = false,
|
// Arrange
|
||||||
// )
|
val networks = setOf(ethereum.network, cardano.network)
|
||||||
//
|
val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks)
|
||||||
// coEvery { singleNetworkStatusFetcher(ethParams) } returns Unit.right()
|
val userWallet = MockUserWalletFactory.create()
|
||||||
// coEvery { singleNetworkStatusFetcher(stellarParams) } returns Unit.right()
|
val cardTypesResolver = mockk<CardTypesResolver>()
|
||||||
//
|
val networksCurrencies = mapOf(
|
||||||
// val actual = fetcher(params)
|
ethereum.network to listOf(ethereum),
|
||||||
//
|
cardano.network to listOf(cardano),
|
||||||
// coVerify {
|
)
|
||||||
// networksStatusesStore.refresh(userWalletId = userWalletId, networks = ethereumAndStellar)
|
val ethereumFetcherResult = Either.Right(Unit)
|
||||||
// singleNetworkStatusFetcher(ethParams)
|
val cardanoFetcherResult = Either.Right(Unit)
|
||||||
// singleNetworkStatusFetcher(stellarParams)
|
|
||||||
// }
|
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
|
||||||
//
|
every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet
|
||||||
// Truth.assertThat(actual.isRight()).isTrue()
|
|
||||||
// }
|
mockkStatic(UserWallet::cardTypesResolver)
|
||||||
//
|
every { userWallet.cardTypesResolver } returns cardTypesResolver
|
||||||
// @Test
|
coEvery { cardTypesResolver.isMultiwalletAllowed() } returns true
|
||||||
// fun `fetch networks statuses failure if one of them fails`() = runTest {
|
|
||||||
// val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = ethereumAndStellar)
|
coEvery {
|
||||||
//
|
cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks)
|
||||||
// val ethParams = SingleNetworkStatusFetcher.Params(
|
} returns networksCurrencies
|
||||||
// userWalletId = userWalletId,
|
|
||||||
// network = ethereumAndStellar.first(),
|
coEvery {
|
||||||
// applyRefresh = false,
|
commonNetworkStatusFetcher.fetch(
|
||||||
// )
|
userWalletId = params.userWalletId,
|
||||||
//
|
network = ethereum.network,
|
||||||
// val stellarParams = SingleNetworkStatusFetcher.Params(
|
networkCurrencies = setOf(ethereum),
|
||||||
// userWalletId = userWalletId,
|
)
|
||||||
// network = ethereumAndStellar.last(),
|
} returns ethereumFetcherResult
|
||||||
// applyRefresh = false,
|
|
||||||
// )
|
coEvery {
|
||||||
//
|
commonNetworkStatusFetcher.fetch(
|
||||||
// val ethException = IllegalStateException("eth")
|
userWalletId = params.userWalletId,
|
||||||
// coEvery { singleNetworkStatusFetcher(ethParams) } returns ethException.left()
|
network = cardano.network,
|
||||||
// coEvery { singleNetworkStatusFetcher(stellarParams) } returns Unit.right()
|
networkCurrencies = setOf(cardano),
|
||||||
//
|
)
|
||||||
// val actual = fetcher(params)
|
} returns cardanoFetcherResult
|
||||||
//
|
|
||||||
// coVerify {
|
// Act
|
||||||
// networksStatusesStore.refresh(userWalletId = userWalletId, networks = ethereumAndStellar)
|
val actual = fetcher(params)
|
||||||
// singleNetworkStatusFetcher(ethParams)
|
|
||||||
// singleNetworkStatusFetcher(stellarParams)
|
// Assert
|
||||||
// }
|
val expected = Either.Right(Unit)
|
||||||
//
|
|
||||||
// val expected = IllegalStateException("Failed to fetch network statuses")
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
//
|
|
||||||
// Truth.assertThat(actual.isLeft()).isTrue()
|
coVerifyOrder {
|
||||||
// Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks)
|
||||||
// Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
userWalletsStore.getSyncStrict(key = userWalletId)
|
||||||
// }
|
cardTypesResolver.isMultiwalletAllowed()
|
||||||
//
|
cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks)
|
||||||
// @Test
|
commonNetworkStatusFetcher.fetch(
|
||||||
// fun `fetch networks statuses failure if all of them fails`() = runTest {
|
userWalletId = params.userWalletId,
|
||||||
// val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = ethereumAndStellar)
|
network = ethereum.network,
|
||||||
//
|
networkCurrencies = setOf(ethereum),
|
||||||
// val ethParams = SingleNetworkStatusFetcher.Params(
|
)
|
||||||
// userWalletId = userWalletId,
|
commonNetworkStatusFetcher.fetch(
|
||||||
// network = ethereumAndStellar.first(),
|
userWalletId = params.userWalletId,
|
||||||
// applyRefresh = false,
|
network = cardano.network,
|
||||||
// )
|
networkCurrencies = setOf(cardano),
|
||||||
//
|
)
|
||||||
// val stellarParams = SingleNetworkStatusFetcher.Params(
|
}
|
||||||
// userWalletId = userWalletId,
|
|
||||||
// network = ethereumAndStellar.last(),
|
coVerify(inverse = true) {
|
||||||
// applyRefresh = false,
|
networksStatusesStore.setSourceAsOnlyCache(userWalletId = any(), networks = any())
|
||||||
// )
|
cardTypesResolver.isSingleWalletWithToken()
|
||||||
//
|
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(scanResponse = any())
|
||||||
// coEvery { singleNetworkStatusFetcher(ethParams) } returns IllegalStateException("eth").left()
|
}
|
||||||
// coEvery { singleNetworkStatusFetcher(stellarParams) } returns IllegalStateException("stellar").left()
|
}
|
||||||
//
|
|
||||||
// val actual = fetcher(params)
|
@Test
|
||||||
//
|
fun `fetch successfully for single-currency card with token`() = runTest {
|
||||||
// coVerify {
|
// Arrange
|
||||||
// networksStatusesStore.refresh(userWalletId = userWalletId, networks = ethereumAndStellar)
|
val networks = setOf(ethereum.network)
|
||||||
// singleNetworkStatusFetcher(ethParams)
|
val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks)
|
||||||
// singleNetworkStatusFetcher(stellarParams)
|
val userWallet = MockUserWalletFactory.create()
|
||||||
// }
|
val cardTypesResolver = mockk<CardTypesResolver>()
|
||||||
//
|
val networksCurrencies = listOf(ethereum)
|
||||||
// val expected = IllegalStateException("Failed to fetch network statuses")
|
val ethereumFetcherResult = Either.Right(Unit)
|
||||||
//
|
|
||||||
// Truth.assertThat(actual.isLeft()).isTrue()
|
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
|
||||||
// Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet
|
||||||
// Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
|
||||||
// }
|
mockkStatic(UserWallet::cardTypesResolver)
|
||||||
//
|
every { userWallet.cardTypesResolver } returns cardTypesResolver
|
||||||
// private companion object {
|
coEvery { cardTypesResolver.isMultiwalletAllowed() } returns false
|
||||||
// val userWalletId = UserWalletId("011")
|
coEvery { cardTypesResolver.isSingleWalletWithToken() } returns true
|
||||||
// val ethereumAndStellar = MockCryptoCurrencyFactory().ethereumAndStellar.map { it.network }.toSet()
|
|
||||||
// }
|
coEvery {
|
||||||
// }
|
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse)
|
||||||
|
} returns networksCurrencies
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = ethereum.network,
|
||||||
|
networkCurrencies = setOf(ethereum),
|
||||||
|
)
|
||||||
|
} returns ethereumFetcherResult
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
val expected = Either.Right(Unit)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks)
|
||||||
|
userWalletsStore.getSyncStrict(key = userWalletId)
|
||||||
|
cardTypesResolver.isMultiwalletAllowed()
|
||||||
|
cardTypesResolver.isSingleWalletWithToken()
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse)
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = ethereum.network,
|
||||||
|
networkCurrencies = setOf(ethereum),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
networksStatusesStore.setSourceAsOnlyCache(userWalletId = any(), networks = any())
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(any(), any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fetch failure if one of them fails`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val networks = setOf(ethereum.network, cardano.network)
|
||||||
|
val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks)
|
||||||
|
val userWallet = MockUserWalletFactory.create()
|
||||||
|
val cardTypesResolver = mockk<CardTypesResolver>()
|
||||||
|
val networksCurrencies = mapOf(
|
||||||
|
ethereum.network to listOf(ethereum),
|
||||||
|
cardano.network to listOf(cardano),
|
||||||
|
)
|
||||||
|
val ethereumFetcherResult = Either.Left(IllegalStateException())
|
||||||
|
val cardanoFetcherResult = Either.Right(Unit)
|
||||||
|
|
||||||
|
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
|
||||||
|
every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet
|
||||||
|
|
||||||
|
mockkStatic(UserWallet::cardTypesResolver)
|
||||||
|
every { userWallet.cardTypesResolver } returns cardTypesResolver
|
||||||
|
coEvery { cardTypesResolver.isMultiwalletAllowed() } returns true
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks)
|
||||||
|
} returns networksCurrencies
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = ethereum.network,
|
||||||
|
networkCurrencies = setOf(ethereum),
|
||||||
|
)
|
||||||
|
} returns ethereumFetcherResult
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = cardano.network,
|
||||||
|
networkCurrencies = setOf(cardano),
|
||||||
|
)
|
||||||
|
} returns cardanoFetcherResult
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
val expected = Either.Left(IllegalStateException("Failed to fetch network statuses"))
|
||||||
|
Truth.assertThat(actual.isLeft()).isTrue()
|
||||||
|
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected.leftOrNull()!!::class.java)
|
||||||
|
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.leftOrNull()!!.message)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks)
|
||||||
|
userWalletsStore.getSyncStrict(key = userWalletId)
|
||||||
|
cardTypesResolver.isMultiwalletAllowed()
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks)
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = ethereum.network,
|
||||||
|
networkCurrencies = setOf(ethereum),
|
||||||
|
)
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = cardano.network,
|
||||||
|
networkCurrencies = setOf(cardano),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
networksStatusesStore.setSourceAsOnlyCache(userWalletId = any(), networks = any())
|
||||||
|
cardTypesResolver.isSingleWalletWithToken()
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(scanResponse = any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fetch failure if all of them fails`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val networks = setOf(ethereum.network, cardano.network)
|
||||||
|
val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks)
|
||||||
|
val userWallet = MockUserWalletFactory.create()
|
||||||
|
val cardTypesResolver = mockk<CardTypesResolver>()
|
||||||
|
val networksCurrencies = mapOf(
|
||||||
|
ethereum.network to listOf(ethereum),
|
||||||
|
cardano.network to listOf(cardano),
|
||||||
|
)
|
||||||
|
val ethereumFetcherResult = Either.Left(IllegalStateException("ethereum"))
|
||||||
|
val cardanoFetcherResult = Either.Left(IllegalStateException("cardano"))
|
||||||
|
|
||||||
|
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
|
||||||
|
every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet
|
||||||
|
|
||||||
|
mockkStatic(UserWallet::cardTypesResolver)
|
||||||
|
every { userWallet.cardTypesResolver } returns cardTypesResolver
|
||||||
|
coEvery { cardTypesResolver.isMultiwalletAllowed() } returns true
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks)
|
||||||
|
} returns networksCurrencies
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = ethereum.network,
|
||||||
|
networkCurrencies = setOf(ethereum),
|
||||||
|
)
|
||||||
|
} returns ethereumFetcherResult
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = cardano.network,
|
||||||
|
networkCurrencies = setOf(cardano),
|
||||||
|
)
|
||||||
|
} returns cardanoFetcherResult
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
val expected = Either.Left(IllegalStateException("Failed to fetch network statuses"))
|
||||||
|
Truth.assertThat(actual.isLeft()).isTrue()
|
||||||
|
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected.leftOrNull()!!::class.java)
|
||||||
|
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.leftOrNull()!!.message)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks)
|
||||||
|
userWalletsStore.getSyncStrict(key = userWalletId)
|
||||||
|
cardTypesResolver.isMultiwalletAllowed()
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks)
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = ethereum.network,
|
||||||
|
networkCurrencies = setOf(ethereum),
|
||||||
|
)
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = cardano.network,
|
||||||
|
networkCurrencies = setOf(cardano),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
networksStatusesStore.setSourceAsOnlyCache(userWalletId = any(), networks = any())
|
||||||
|
cardTypesResolver.isSingleWalletWithToken()
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(scanResponse = any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fetch failure if userWalletsStore throws exception`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val networks = setOf(ethereum.network, cardano.network)
|
||||||
|
val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks)
|
||||||
|
val userWalletStoreException = IllegalStateException()
|
||||||
|
|
||||||
|
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
|
||||||
|
every { userWalletsStore.getSyncStrict(key = userWalletId) } throws userWalletStoreException
|
||||||
|
coEvery { networksStatusesStore.setSourceAsOnlyCache(params.userWalletId, params.networks) } returns Unit
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
val expected = Either.Left(userWalletStoreException)
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks)
|
||||||
|
userWalletsStore.getSyncStrict(key = userWalletId)
|
||||||
|
networksStatusesStore.setSourceAsOnlyCache(params.userWalletId, params.networks)
|
||||||
|
}
|
||||||
|
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(any(), any())
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(scanResponse = any())
|
||||||
|
commonNetworkStatusFetcher.fetch(any(), any(), any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fetch failure if card is single-currency`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val networks = setOf(ethereum.network, cardano.network)
|
||||||
|
val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks)
|
||||||
|
val userWallet = MockUserWalletFactory.create()
|
||||||
|
val cardTypesResolver = mockk<CardTypesResolver>()
|
||||||
|
|
||||||
|
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit
|
||||||
|
every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet
|
||||||
|
|
||||||
|
mockkStatic(UserWallet::cardTypesResolver)
|
||||||
|
every { userWallet.cardTypesResolver } returns cardTypesResolver
|
||||||
|
coEvery { cardTypesResolver.isMultiwalletAllowed() } returns false
|
||||||
|
coEvery { cardTypesResolver.isSingleWalletWithToken() } returns false
|
||||||
|
|
||||||
|
coEvery { networksStatusesStore.setSourceAsOnlyCache(params.userWalletId, params.networks) } returns Unit
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
val expected = Either.Left(IllegalStateException("User wallet is not multi-currency"))
|
||||||
|
|
||||||
|
Truth.assertThat(actual.isLeft()).isTrue()
|
||||||
|
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected.leftOrNull()!!::class.java)
|
||||||
|
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.leftOrNull()!!.message)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks)
|
||||||
|
userWalletsStore.getSyncStrict(key = userWalletId)
|
||||||
|
cardTypesResolver.isMultiwalletAllowed()
|
||||||
|
cardTypesResolver.isSingleWalletWithToken()
|
||||||
|
networksStatusesStore.setSourceAsOnlyCache(params.userWalletId, params.networks)
|
||||||
|
}
|
||||||
|
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
networksStatusesStore.setSourceAsOnlyCache(userWalletId = any(), networks = any())
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(any(), any())
|
||||||
|
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(scanResponse = any())
|
||||||
|
commonNetworkStatusFetcher.fetch(any(), any(), any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
val userWalletId = UserWalletId("011")
|
||||||
|
val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||||
|
val ethereum = cryptoCurrencyFactory.ethereum
|
||||||
|
val cardano = cryptoCurrencyFactory.cardano
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,97 +1,141 @@
|
||||||
package com.tangem.data.networks.single
|
package com.tangem.data.networks.single
|
||||||
|
|
||||||
|
import arrow.core.Either
|
||||||
|
import arrow.core.left
|
||||||
import com.google.common.truth.Truth
|
import com.google.common.truth.Truth
|
||||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||||
|
import com.tangem.data.networks.fetcher.CommonNetworkStatusFetcher
|
||||||
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
import com.tangem.data.networks.store.NetworksStatusesStoreV2
|
||||||
import com.tangem.data.networks.store.setSourceAsCache
|
import com.tangem.data.networks.store.setSourceAsCache
|
||||||
import com.tangem.data.networks.store.storeSuccess
|
|
||||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||||
import com.tangem.domain.tokens.model.NetworkStatus
|
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
|
||||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||||
import io.mockk.coEvery
|
import io.mockk.*
|
||||||
import io.mockk.coVerify
|
|
||||||
import io.mockk.coVerifyOrder
|
|
||||||
import io.mockk.mockk
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
|
||||||
/**
|
/**
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
internal class DefaultSingleNetworkStatusFetcherTest {
|
internal class DefaultSingleNetworkStatusFetcherTest {
|
||||||
|
|
||||||
private val walletManagersFacade: WalletManagersFacade = mockk(relaxUnitFun = true)
|
private val commonNetworkStatusFetcher: CommonNetworkStatusFetcher = mockk()
|
||||||
private val networksStatusesStore: NetworksStatusesStoreV2 = mockk(relaxUnitFun = true)
|
private val networksStatusesStore: NetworksStatusesStoreV2 = mockk(relaxUnitFun = true)
|
||||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk()
|
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk()
|
||||||
|
|
||||||
private val fetcher = DefaultSingleNetworkStatusFetcher(
|
private val fetcher = DefaultSingleNetworkStatusFetcher(
|
||||||
walletManagersFacade = walletManagersFacade,
|
commonNetworkStatusFetcher = commonNetworkStatusFetcher,
|
||||||
networksStatusesStore = networksStatusesStore,
|
networksStatusesStore = networksStatusesStore,
|
||||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun resetMocks() {
|
||||||
|
clearMocks(commonNetworkStatusFetcher, networksStatusesStore, cardCryptoCurrencyFactory)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `fetch network status successfully`() = runTest {
|
fun `fetch successfully`() = runTest {
|
||||||
val params = createParams()
|
// Arrange
|
||||||
|
val params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = ethereum.network)
|
||||||
|
val networkCurrencies = listOf(ethereum)
|
||||||
|
val commonFetcherResult = Either.Right(Unit)
|
||||||
|
|
||||||
coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } returns listOf(ethereum)
|
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.network) } returns Unit
|
||||||
|
coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } returns networkCurrencies
|
||||||
val result = UpdateWalletManagerResult.MissedDerivation
|
coEvery {
|
||||||
coEvery { walletManagersFacade.update(params.userWalletId, params.network, emptySet()) } returns result
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = params.network,
|
||||||
|
networkCurrencies = networkCurrencies.toSet(),
|
||||||
|
)
|
||||||
|
} returns commonFetcherResult
|
||||||
|
|
||||||
|
// Act
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
val expected = commonFetcherResult
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
networksStatusesStore.setSourceAsCache(params.userWalletId, params.network)
|
networksStatusesStore.setSourceAsCache(params.userWalletId, params.network)
|
||||||
cardCryptoCurrencyFactory.create(params.userWalletId, params.network)
|
cardCryptoCurrencyFactory.create(params.userWalletId, params.network)
|
||||||
walletManagersFacade.update(params.userWalletId, params.network, emptySet())
|
commonNetworkStatusFetcher.fetch(params.userWalletId, params.network, setOf(ethereum))
|
||||||
networksStatusesStore.storeSuccess(
|
|
||||||
userWalletId = params.userWalletId,
|
|
||||||
status = NetworkStatus(params.network, NetworkStatus.MissedDerivation),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Truth.assertThat(actual.isRight()).isTrue()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `fetch network status failure`() = runTest {
|
fun `fetch failure if cardCryptoCurrencyFactory throws exception`() = runTest {
|
||||||
val params = createParams()
|
// Arrange
|
||||||
|
val params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = ethereum.network)
|
||||||
|
val factoryException = IllegalStateException()
|
||||||
|
|
||||||
val exception = IllegalStateException()
|
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.network) } returns Unit
|
||||||
coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } throws exception
|
coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } throws factoryException
|
||||||
|
|
||||||
|
// Act
|
||||||
val actual = fetcher(params)
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
val expected = Either.Left(factoryException)
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, network = params.network)
|
networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, network = params.network)
|
||||||
cardCryptoCurrencyFactory.create(userWalletId = params.userWalletId, network = params.network)
|
cardCryptoCurrencyFactory.create(userWalletId = params.userWalletId, network = params.network)
|
||||||
// networksStatusesStore.setSourceAsOnlyCache(userWalletId = params.userWalletId, network = params.network)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(inverse = true) {
|
coVerify(inverse = true) {
|
||||||
walletManagersFacade.update(userWalletId = any(), network = any(), extraTokens = any())
|
commonNetworkStatusFetcher.fetch(userWalletId = any(), network = any(), networkCurrencies = any())
|
||||||
// networksStatusesStore.storeSuccess(userWalletId = any(), status = any())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Truth.assertThat(actual.isLeft()).isTrue()
|
|
||||||
Truth.assertThat(actual.leftOrNull()).isEqualTo(exception)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createParams(): SingleNetworkStatusFetcher.Params {
|
@Test
|
||||||
return SingleNetworkStatusFetcher.Params.Simple(
|
fun `fetch failure if commonNetworkStatusFetcher returns exception`() = runTest {
|
||||||
userWalletId = UserWalletId("011"),
|
// Arrange
|
||||||
network = ethereum.network,
|
val params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = ethereum.network)
|
||||||
)
|
val networkCurrencies = listOf(ethereum)
|
||||||
|
val commonFetcherResult = IllegalStateException().left()
|
||||||
|
|
||||||
|
coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.network) } returns Unit
|
||||||
|
coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } returns networkCurrencies
|
||||||
|
coEvery {
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = params.network,
|
||||||
|
networkCurrencies = networkCurrencies.toSet(),
|
||||||
|
)
|
||||||
|
} returns commonFetcherResult
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = fetcher(params)
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
val expected = commonFetcherResult
|
||||||
|
|
||||||
|
Truth.assertThat(actual).isEqualTo(expected)
|
||||||
|
|
||||||
|
coVerifyOrder {
|
||||||
|
networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, network = params.network)
|
||||||
|
cardCryptoCurrencyFactory.create(userWalletId = params.userWalletId, network = params.network)
|
||||||
|
commonNetworkStatusFetcher.fetch(
|
||||||
|
userWalletId = params.userWalletId,
|
||||||
|
network = params.network,
|
||||||
|
networkCurrencies = setOf(ethereum),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
|
||||||
|
val userWalletId = UserWalletId("011")
|
||||||
val ethereum = MockCryptoCurrencyFactory().ethereum
|
val ethereum = MockCryptoCurrencyFactory().ethereum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ import com.tangem.domain.tokens.model.Network
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetcher of network status [Network] for wallet with [UserWalletId]
|
* Fetcher of network status [Network] for multi-currency wallet with [UserWalletId]
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package com.tangem.domain.networks.single
|
package com.tangem.domain.networks.single
|
||||||
|
|
||||||
import com.tangem.domain.core.flow.FlowFetcher
|
import com.tangem.domain.core.flow.FlowFetcher
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
|
||||||
import com.tangem.domain.tokens.model.Network
|
import com.tangem.domain.tokens.model.Network
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
|
||||||
|
|
@ -12,18 +11,11 @@ import com.tangem.domain.wallets.models.UserWalletId
|
||||||
*/
|
*/
|
||||||
interface SingleNetworkStatusFetcher : FlowFetcher<SingleNetworkStatusFetcher.Params> {
|
interface SingleNetworkStatusFetcher : FlowFetcher<SingleNetworkStatusFetcher.Params> {
|
||||||
|
|
||||||
/** Params */
|
/**
|
||||||
sealed interface Params {
|
* Params
|
||||||
|
*
|
||||||
val userWalletId: UserWalletId
|
* @property userWalletId user wallet id
|
||||||
val network: Network
|
* @property network network
|
||||||
|
*/
|
||||||
data class Simple(override val userWalletId: UserWalletId, override val network: Network) : Params
|
data class Params(val userWalletId: UserWalletId, val network: Network)
|
||||||
|
|
||||||
data class Prepared(
|
|
||||||
override val userWalletId: UserWalletId,
|
|
||||||
override val network: Network,
|
|
||||||
val addedNetworkCurrencies: Set<CryptoCurrency>,
|
|
||||||
) : Params
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -119,7 +119,7 @@ class FetchCurrencyStatusUseCase(
|
||||||
|
|
||||||
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(userWalletId: UserWalletId, network: Network) {
|
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(userWalletId: UserWalletId, network: Network) {
|
||||||
singleNetworkStatusFetcher(
|
singleNetworkStatusFetcher(
|
||||||
params = SingleNetworkStatusFetcher.Params.Simple(userWalletId = userWalletId, network = network),
|
params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = network),
|
||||||
)
|
)
|
||||||
.mapLeft { CurrencyStatusError.DataError(it) }
|
.mapLeft { CurrencyStatusError.DataError(it) }
|
||||||
.bind()
|
.bind()
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class UpdateDelayedNetworkStatusUseCase(
|
||||||
|
|
||||||
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(userWalletId: UserWalletId, network: Network) {
|
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(userWalletId: UserWalletId, network: Network) {
|
||||||
singleNetworkStatusFetcher(
|
singleNetworkStatusFetcher(
|
||||||
params = SingleNetworkStatusFetcher.Params.Simple(userWalletId = userWalletId, network = network),
|
params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = network),
|
||||||
)
|
)
|
||||||
.mapLeft(CurrencyStatusError::DataError)
|
.mapLeft(CurrencyStatusError::DataError)
|
||||||
.bind()
|
.bind()
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class SendTransactionUseCase(
|
||||||
return sendResult
|
return sendResult
|
||||||
.onRight {
|
.onRight {
|
||||||
singleNetworkStatusFetcher(
|
singleNetworkStatusFetcher(
|
||||||
params = SingleNetworkStatusFetcher.Params.Simple(
|
params = SingleNetworkStatusFetcher.Params(
|
||||||
userWalletId = userWallet.walletId,
|
userWalletId = userWallet.walletId,
|
||||||
network = network,
|
network = network,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue