From 584be65dd2ca88928b44a5b4a0621069c61d3405 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 3 Jul 2025 17:26:16 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../currency/CardCryptoCurrencyFactory.kt | 13 +- .../DefaultCardCryptoCurrencyFactory.kt | 34 +-- .../DefaultCardCryptoCurrencyFactoryTest.kt | 4 +- .../multi/DefaultMultiNetworkStatusFetcher.kt | 45 +--- .../DefaultSingleNetworkStatusFetcher.kt | 36 +-- .../DefaultMultiNetworkStatusFetcherTest.kt | 232 +++--------------- .../DefaultSingleNetworkStatusFetcherTest.kt | 108 ++------ 7 files changed, 118 insertions(+), 354 deletions(-) diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/CardCryptoCurrencyFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/CardCryptoCurrencyFactory.kt index 83d76a648e..137e425ac6 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/currency/CardCryptoCurrencyFactory.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/CardCryptoCurrencyFactory.kt @@ -20,7 +20,18 @@ interface CardCryptoCurrencyFactory { * @param network network */ @Throws - suspend fun create(userWalletId: UserWalletId, network: Network): List + suspend fun create(userWalletId: UserWalletId, network: Network): List { + return create(userWalletId = userWalletId, networks = setOf(network))[network].orEmpty() + } + + /** + * Universal method for creating list of [CryptoCurrency] in [networks] for any card + * + * @param userWalletId user wallet id that determines type of card + * @param networks networks + */ + @Throws + suspend fun create(userWalletId: UserWalletId, networks: Set): Map> /** * Universal method for creating list of [CryptoCurrency] in [network] for any card diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt index 286947afa4..58b6c3415b 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactory.kt @@ -2,7 +2,6 @@ package com.tangem.data.common.currency import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.ExcludedBlockchains -import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.datasource.local.token.UserTokensResponseStore @@ -36,40 +35,45 @@ internal class DefaultCardCryptoCurrencyFactory( private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory(excludedBlockchains) } - override suspend fun create(userWalletId: UserWalletId, network: Network): List { + override suspend fun create( + userWalletId: UserWalletId, + networks: Set, + ): Map> { val userWallet = userWalletsStore.getSyncStrict(key = userWalletId) - val blockchain = Blockchain.fromNetworkId(networkId = network.backendId) - // multi-currency wallet if (userWallet !is UserWallet.Cold || userWallet.isMultiCurrency) { - return getMultiWalletCurrencies(userWallet = userWallet, networks = setOf(network))[network].orEmpty() + 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 (cardBlockchain != blockchain) return emptyList() + val cardNetworkId = userWallet.scanResponse.cardTypesResolver.getBlockchain().toNetworkId() + val cardNetwork = networks.firstOrNull { it.backendId == cardNetworkId } + + if (cardNetwork == null) return emptyMap() // single-currency wallet with token (NODL) if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) { - return createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse) + val currencies = createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse) + return mapOf(cardNetwork to currencies) } // single-currency wallet - return createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse).let(::listOf) + val primaryCurrency = createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse) + return mapOf(cardNetwork to listOf(primaryCurrency)) } - override suspend fun createByRawId(userWalletId: UserWalletId, networkRawId: Network.RawID): List { + override suspend fun createByRawId(userWalletId: UserWalletId, network: Network.RawID): List { val userWallet = userWalletsStore.getSyncStrict(key = userWalletId) - val blockchain = networkRawId.toBlockchain() + val blockchain = network.toBlockchain() // multi-currency wallet if (userWallet.isMultiCurrency || userWallet !is UserWallet.Cold) { return getMultiWalletCurrenciesByRawId( userWallet = userWallet, - rawIds = setOf(networkRawId), - )[networkRawId].orEmpty() + rawIds = setOf(network), + )[network].orEmpty() } // check if the blockchain of single-currency wallet is the same as network @@ -143,13 +147,15 @@ internal class DefaultCardCryptoCurrencyFactory( val response = userTokensResponseStore.getSyncOrNull(userWalletId = userWallet.walletId) ?: return emptyMap() - return responseCryptoCurrenciesFactory.createCurrencies( + val existingNetworkWithCurrencies = responseCryptoCurrenciesFactory.createCurrencies( tokens = response.tokens.filter { token -> networks.any { it.backendId == token.networkId && it.derivationPath.value == token.derivationPath } }, scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY] ) .groupBy(CryptoCurrency::network) + + return networks.associateWith { emptyList() } + existingNetworkWithCurrencies } private suspend fun getMultiWalletCurrenciesByRawId( diff --git a/data/common/src/test/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactoryTest.kt b/data/common/src/test/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactoryTest.kt index 0622887743..944a461dee 100644 --- a/data/common/src/test/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactoryTest.kt +++ b/data/common/src/test/kotlin/com/tangem/data/common/currency/DefaultCardCryptoCurrencyFactoryTest.kt @@ -266,7 +266,9 @@ internal class DefaultCardCryptoCurrencyFactoryTest { CreateCurrenciesForMultiWalletModel( multiWallet = createMultiWallet(), userTokensResponse = createUserTokensResponse(), - expected = Result.success(emptyMap()), + expected = Result.success( + setOf(ethereum.network, bitcoin.network).associateWith { emptyList() }, + ), ), CreateCurrenciesForMultiWalletModel( multiWallet = createMultiWallet(), diff --git a/data/networks/src/main/java/com/tangem/data/networks/multi/DefaultMultiNetworkStatusFetcher.kt b/data/networks/src/main/java/com/tangem/data/networks/multi/DefaultMultiNetworkStatusFetcher.kt index aa05c31589..d94944b2a4 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/multi/DefaultMultiNetworkStatusFetcher.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/multi/DefaultMultiNetworkStatusFetcher.kt @@ -7,11 +7,10 @@ import com.tangem.data.networks.fetcher.CommonNetworkStatusFetcher import com.tangem.data.networks.store.NetworksStatusesStore 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.util.cardTypesResolver import com.tangem.domain.core.utils.eitherOn +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher -import com.tangem.domain.wallets.models.requireColdWallet import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -22,7 +21,6 @@ import javax.inject.Inject * Default implementation of [MultiNetworkStatusFetcher] * * @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 @@ -32,7 +30,6 @@ import javax.inject.Inject @Suppress("LongParameterList") internal class DefaultMultiNetworkStatusFetcher @Inject constructor( private val networksStatusesStore: NetworksStatusesStore, - private val userWalletsStore: UserWalletsStore, private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory, private val commonNetworkStatusFetcher: CommonNetworkStatusFetcher, private val dispatchers: CoroutineDispatcherProvider, @@ -41,8 +38,8 @@ internal class DefaultMultiNetworkStatusFetcher @Inject constructor( override suspend fun invoke(params: MultiNetworkStatusFetcher.Params) = eitherOn(dispatchers.default) { networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, networks = params.networks) - val userWallet = catch( - block = { userWalletsStore.getSyncStrict(key = params.userWalletId) }, + val networksCurrencies = catch( + block = { createNetworksCurrenciesMap(params) }, catch = { networksStatusesStore.setSourceAsOnlyCache( userWalletId = params.userWalletId, @@ -53,31 +50,6 @@ internal class DefaultMultiNetworkStatusFetcher @Inject constructor( }, ) - val cardTypesResolver = userWallet.requireColdWallet().cardTypesResolver // TODO [REDACTED_TASK_KEY] - val isWalletSupported = with(cardTypesResolver) { - isMultiwalletAllowed() || isSingleWalletWithToken() - } - - ensure(isWalletSupported) { - networksStatusesStore.setSourceAsOnlyCache( - userWalletId = params.userWalletId, - networks = params.networks, - ) - IllegalStateException("User wallet is not multi-currency") - } - - val networksCurrencies = if (cardTypesResolver.isMultiwalletAllowed()) { - cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard( - userWallet = userWallet, - networks = params.networks, - ) - } else { - cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken( - scanResponse = userWallet.scanResponse, - ) - .groupBy { it.network } - } - val result = coroutineScope { params.networks .map { network -> @@ -98,4 +70,13 @@ internal class DefaultMultiNetworkStatusFetcher @Inject constructor( IllegalStateException("Failed to fetch network statuses") } } + + private suspend fun createNetworksCurrenciesMap( + params: MultiNetworkStatusFetcher.Params, + ): Map> { + return cardCryptoCurrencyFactory.create( + userWalletId = params.userWalletId, + networks = params.networks, + ) + } } \ No newline at end of file diff --git a/data/networks/src/main/java/com/tangem/data/networks/single/DefaultSingleNetworkStatusFetcher.kt b/data/networks/src/main/java/com/tangem/data/networks/single/DefaultSingleNetworkStatusFetcher.kt index 4db17e1818..c89ba87fe5 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/single/DefaultSingleNetworkStatusFetcher.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/single/DefaultSingleNetworkStatusFetcher.kt @@ -1,47 +1,27 @@ package com.tangem.data.networks.single import arrow.core.Either -import com.tangem.data.common.currency.CardCryptoCurrencyFactory -import com.tangem.data.networks.fetcher.CommonNetworkStatusFetcher -import com.tangem.data.networks.store.NetworksStatusesStore -import com.tangem.data.networks.store.setSourceAsCache -import com.tangem.domain.core.utils.catchOn +import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.networks.single.SingleNetworkStatusFetcher -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import javax.inject.Inject /** * Default implementation of [SingleNetworkStatusFetcher] * - * @property commonNetworkStatusFetcher common network status fetcher - * @property networksStatusesStore networks statuses store - * @property cardCryptoCurrencyFactory card crypto currency factory - * @property dispatchers dispatchers + * @property multiNetworkStatusFetcher multi network status fetcher * [REDACTED_AUTHOR] */ internal class DefaultSingleNetworkStatusFetcher @Inject constructor( - private val commonNetworkStatusFetcher: CommonNetworkStatusFetcher, - private val networksStatusesStore: NetworksStatusesStore, - private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory, - private val dispatchers: CoroutineDispatcherProvider, + private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, ) : SingleNetworkStatusFetcher { override suspend fun invoke(params: SingleNetworkStatusFetcher.Params): Either { - return Either.catchOn(dispatchers.default) { - networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, network = params.network) - - val networkCurrencies = cardCryptoCurrencyFactory.create( + return multiNetworkStatusFetcher( + params = MultiNetworkStatusFetcher.Params( userWalletId = params.userWalletId, - network = params.network, - ) - - commonNetworkStatusFetcher.fetch( - userWalletId = params.userWalletId, - network = params.network, - networkCurrencies = networkCurrencies.toSet(), - ) - .onLeft { throw it } - } + networks = setOf(params.network), + ), + ) } } \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/multi/DefaultMultiNetworkStatusFetcherTest.kt b/data/networks/src/test/java/com/tangem/data/networks/multi/DefaultMultiNetworkStatusFetcherTest.kt index 4284eb4610..3bad3aaceb 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/multi/DefaultMultiNetworkStatusFetcherTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/multi/DefaultMultiNetworkStatusFetcherTest.kt @@ -1,19 +1,15 @@ package com.tangem.data.networks.multi import arrow.core.Either -import com.google.common.truth.Truth +import arrow.core.left import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory -import com.tangem.common.test.domain.wallet.MockUserWalletFactory +import com.tangem.common.test.utils.assertEither import com.tangem.data.common.currency.CardCryptoCurrencyFactory import com.tangem.data.networks.fetcher.CommonNetworkStatusFetcher import com.tangem.data.networks.store.NetworksStatusesStore 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.* @@ -29,13 +25,11 @@ import org.junit.jupiter.api.TestInstance internal class DefaultMultiNetworkStatusFetcherTest { private val networksStatusesStore: NetworksStatusesStore = mockk(relaxUnitFun = true) - private val userWalletsStore: UserWalletsStore = mockk() private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk() private val commonNetworkStatusFetcher: CommonNetworkStatusFetcher = mockk() private val fetcher = DefaultMultiNetworkStatusFetcher( networksStatusesStore = networksStatusesStore, - userWalletsStore = userWalletsStore, cardCryptoCurrencyFactory = cardCryptoCurrencyFactory, commonNetworkStatusFetcher = commonNetworkStatusFetcher, dispatchers = TestingCoroutineDispatcherProvider(), @@ -43,16 +37,17 @@ internal class DefaultMultiNetworkStatusFetcherTest { @BeforeEach fun resetMocks() { - clearMocks(networksStatusesStore, userWalletsStore, cardCryptoCurrencyFactory, commonNetworkStatusFetcher) + clearMocks(networksStatusesStore, cardCryptoCurrencyFactory, commonNetworkStatusFetcher) } @Test - fun `fetch successfully for multi-currency card`() = runTest { + fun `fetch successfully`() = runTest { // Arrange - val networks = setOf(ethereum.network, cardano.network) - val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks) - val userWallet = MockUserWalletFactory.create() - val cardTypesResolver = mockk() + val params = MultiNetworkStatusFetcher.Params( + userWalletId = userWalletId, + networks = setOf(ethereum.network, cardano.network), + ) + val networksCurrencies = mapOf( ethereum.network to listOf(ethereum), cardano.network to listOf(cardano), @@ -60,16 +55,7 @@ internal class DefaultMultiNetworkStatusFetcherTest { val ethereumFetcherResult = Either.Right(Unit) val cardanoFetcherResult = Either.Right(Unit) - coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit - every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet - - mockkStatic(UserWallet.Cold::cardTypesResolver) - every { userWallet.cardTypesResolver } returns cardTypesResolver - coEvery { cardTypesResolver.isMultiwalletAllowed() } returns true - - coEvery { - cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks) - } returns networksCurrencies + coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.networks) } returns networksCurrencies coEvery { commonNetworkStatusFetcher.fetch( @@ -92,14 +78,11 @@ internal class DefaultMultiNetworkStatusFetcherTest { // Assert val expected = Either.Right(Unit) - - Truth.assertThat(actual).isEqualTo(expected) + assertEither(actual, expected) coVerifyOrder { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) - userWalletsStore.getSyncStrict(key = userWalletId) - cardTypesResolver.isMultiwalletAllowed() - cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks) + cardCryptoCurrencyFactory.create(params.userWalletId, params.networks) commonNetworkStatusFetcher.fetch( userWalletId = params.userWalletId, network = ethereum.network, @@ -114,75 +97,17 @@ internal class DefaultMultiNetworkStatusFetcherTest { coVerify(inverse = true) { networksStatusesStore.setSourceAsOnlyCache(userWalletId = any(), networks = any()) - cardTypesResolver.isSingleWalletWithToken() - cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(scanResponse = any()) - } - } - - @Test - fun `fetch successfully for single-currency card with token`() = runTest { - // Arrange - val networks = setOf(ethereum.network) - val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks) - val userWallet = MockUserWalletFactory.create() - val cardTypesResolver = mockk() - val networksCurrencies = listOf(ethereum) - val ethereumFetcherResult = Either.Right(Unit) - - coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit - every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet - - mockkStatic(UserWallet.Cold::cardTypesResolver) - every { userWallet.cardTypesResolver } returns cardTypesResolver - coEvery { cardTypesResolver.isMultiwalletAllowed() } returns false - coEvery { cardTypesResolver.isSingleWalletWithToken() } returns true - - 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() + val params = MultiNetworkStatusFetcher.Params( + userWalletId = userWalletId, + networks = setOf(ethereum.network, cardano.network), + ) + val networksCurrencies = mapOf( ethereum.network to listOf(ethereum), cardano.network to listOf(cardano), @@ -190,16 +115,7 @@ internal class DefaultMultiNetworkStatusFetcherTest { 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.Cold::cardTypesResolver) - every { userWallet.cardTypesResolver } returns cardTypesResolver - coEvery { cardTypesResolver.isMultiwalletAllowed() } returns true - - coEvery { - cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks) - } returns networksCurrencies + coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.networks) } returns networksCurrencies coEvery { commonNetworkStatusFetcher.fetch( @@ -222,15 +138,11 @@ internal class DefaultMultiNetworkStatusFetcherTest { // 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) + assertEither(actual, expected) coVerifyOrder { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) - userWalletsStore.getSyncStrict(key = userWalletId) - cardTypesResolver.isMultiwalletAllowed() - cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks) + cardCryptoCurrencyFactory.create(params.userWalletId, params.networks) commonNetworkStatusFetcher.fetch( userWalletId = params.userWalletId, network = ethereum.network, @@ -245,18 +157,17 @@ internal class DefaultMultiNetworkStatusFetcherTest { 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() + val params = MultiNetworkStatusFetcher.Params( + userWalletId = userWalletId, + networks = setOf(ethereum.network, cardano.network), + ) + val networksCurrencies = mapOf( ethereum.network to listOf(ethereum), cardano.network to listOf(cardano), @@ -264,16 +175,7 @@ internal class DefaultMultiNetworkStatusFetcherTest { 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.Cold::cardTypesResolver) - every { userWallet.cardTypesResolver } returns cardTypesResolver - coEvery { cardTypesResolver.isMultiwalletAllowed() } returns true - - coEvery { - cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks) - } returns networksCurrencies + coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.networks) } returns networksCurrencies coEvery { commonNetworkStatusFetcher.fetch( @@ -296,15 +198,11 @@ internal class DefaultMultiNetworkStatusFetcherTest { // 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) + assertEither(actual, expected) coVerifyOrder { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) - userWalletsStore.getSyncStrict(key = userWalletId) - cardTypesResolver.isMultiwalletAllowed() - cardCryptoCurrencyFactory.createCurrenciesForMultiCurrencyCard(userWallet, params.networks) + cardCryptoCurrencyFactory.create(params.userWalletId, params.networks) commonNetworkStatusFetcher.fetch( userWalletId = params.userWalletId, network = ethereum.network, @@ -319,85 +217,35 @@ internal class DefaultMultiNetworkStatusFetcherTest { coVerify(inverse = true) { networksStatusesStore.setSourceAsOnlyCache(userWalletId = any(), networks = any()) - cardTypesResolver.isSingleWalletWithToken() - cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(scanResponse = any()) } } @Test - fun `fetch failure if userWalletsStore throws exception`() = runTest { + fun `fetch failure if cardCryptoCurrencyFactory throws exception`() = runTest { // Arrange - val networks = setOf(ethereum.network, cardano.network) - val params = MultiNetworkStatusFetcher.Params(userWalletId = userWalletId, networks = networks) - val userWalletStoreException = IllegalStateException() + val params = MultiNetworkStatusFetcher.Params( + userWalletId = userWalletId, + networks = setOf(ethereum.network, cardano.network), + ) - 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 + val factoryException = IllegalStateException() + + coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.networks) } throws factoryException // Act val actual = fetcher(params) // Assert - val expected = Either.Left(userWalletStoreException) - - Truth.assertThat(actual).isEqualTo(expected) + val expected = factoryException.left() + assertEither(actual, expected) coVerifyOrder { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) - userWalletsStore.getSyncStrict(key = userWalletId) + cardCryptoCurrencyFactory.create(params.userWalletId, params.networks) 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() - - coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.networks) } returns Unit - every { userWalletsStore.getSyncStrict(key = userWalletId) } returns userWallet - - mockkStatic(UserWallet.Cold::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()) - } + coVerify(inverse = true) { commonNetworkStatusFetcher.fetch(any(), any(), any()) } } private companion object { diff --git a/data/networks/src/test/java/com/tangem/data/networks/single/DefaultSingleNetworkStatusFetcherTest.kt b/data/networks/src/test/java/com/tangem/data/networks/single/DefaultSingleNetworkStatusFetcherTest.kt index 979f32b859..f994a4bbeb 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/single/DefaultSingleNetworkStatusFetcherTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/single/DefaultSingleNetworkStatusFetcherTest.kt @@ -2,16 +2,15 @@ package com.tangem.data.networks.single import arrow.core.Either import arrow.core.left -import com.google.common.truth.Truth import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory -import com.tangem.data.common.currency.CardCryptoCurrencyFactory -import com.tangem.data.networks.fetcher.CommonNetworkStatusFetcher -import com.tangem.data.networks.store.NetworksStatusesStore -import com.tangem.data.networks.store.setSourceAsCache +import com.tangem.common.test.utils.assertEither +import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.networks.single.SingleNetworkStatusFetcher import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider -import io.mockk.* +import io.mockk.clearMocks +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -23,114 +22,51 @@ import org.junit.jupiter.api.TestInstance @TestInstance(TestInstance.Lifecycle.PER_CLASS) internal class DefaultSingleNetworkStatusFetcherTest { - private val commonNetworkStatusFetcher: CommonNetworkStatusFetcher = mockk() - private val networksStatusesStore: NetworksStatusesStore = mockk(relaxUnitFun = true) - private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk() + private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher = mockk() - private val fetcher = DefaultSingleNetworkStatusFetcher( - commonNetworkStatusFetcher = commonNetworkStatusFetcher, - networksStatusesStore = networksStatusesStore, - cardCryptoCurrencyFactory = cardCryptoCurrencyFactory, - dispatchers = TestingCoroutineDispatcherProvider(), - ) + private val fetcher = DefaultSingleNetworkStatusFetcher(multiNetworkStatusFetcher = multiNetworkStatusFetcher) @BeforeEach fun resetMocks() { - clearMocks(commonNetworkStatusFetcher, networksStatusesStore, cardCryptoCurrencyFactory) + clearMocks(multiNetworkStatusFetcher) } @Test fun `fetch successfully`() = runTest { // Arrange val params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = ethereum.network) - val networkCurrencies = listOf(ethereum) - val commonFetcherResult = Either.Right(Unit) + val multiParams = MultiNetworkStatusFetcher.Params(userWalletId, setOf(params.network)) + val multiFetcherResult = Either.Right(Unit) - 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 + coEvery { multiNetworkStatusFetcher(params = multiParams) } returns multiFetcherResult // Act val actual = fetcher(params) // Assert - val expected = commonFetcherResult + val expected = multiFetcherResult + assertEither(actual, expected) - Truth.assertThat(actual).isEqualTo(expected) - - coVerifyOrder { - networksStatusesStore.setSourceAsCache(params.userWalletId, params.network) - cardCryptoCurrencyFactory.create(params.userWalletId, params.network) - commonNetworkStatusFetcher.fetch(params.userWalletId, params.network, setOf(ethereum)) - } + coVerify(exactly = 1) { multiNetworkStatusFetcher(params = multiParams) } } @Test - fun `fetch failure if cardCryptoCurrencyFactory throws exception`() = runTest { + fun `fetch failure`() = runTest { // Arrange val params = SingleNetworkStatusFetcher.Params(userWalletId = userWalletId, network = ethereum.network) - val factoryException = IllegalStateException() + val multiParams = MultiNetworkStatusFetcher.Params(userWalletId, setOf(params.network)) + val multiFetcherResult = IllegalStateException("Error").left() - coEvery { networksStatusesStore.setSourceAsCache(params.userWalletId, params.network) } returns Unit - coEvery { cardCryptoCurrencyFactory.create(params.userWalletId, params.network) } throws factoryException + coEvery { multiNetworkStatusFetcher(params = multiParams) } returns multiFetcherResult // Act val actual = fetcher(params) // Arrange - val expected = Either.Left(factoryException) - Truth.assertThat(actual).isEqualTo(expected) + val expected = multiFetcherResult + assertEither(actual, expected) - coVerifyOrder { - networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, network = params.network) - cardCryptoCurrencyFactory.create(userWalletId = params.userWalletId, network = params.network) - } - - coVerify(inverse = true) { - commonNetworkStatusFetcher.fetch(userWalletId = any(), network = any(), networkCurrencies = any()) - } - } - - @Test - fun `fetch failure if commonNetworkStatusFetcher returns exception`() = runTest { - // Arrange - 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), - ) - } + coVerify(exactly = 1) { multiNetworkStatusFetcher(params = multiParams) } } private companion object {