From 59117bf97cb03a9178157cedb86e69e81de64b6c Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 14 May 2025 17:13:57 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../network/MockNetworkStatusFactory.kt | 8 +- .../multi/DefaultMultiNetworkStatusFetcher.kt | 12 +- .../DefaultSingleNetworkStatusFetcher.kt | 28 +- .../store/DefaultNetworksStatusesStoreV2.kt | 87 +--- .../networks/store/NetworkStatusesStoreExt.kt | 104 ++++ .../networks/store/NetworksStatusesStoreV2.kt | 39 +- .../DefaultSingleNetworkStatusFetcherTest.kt | 12 +- .../com/tangem/data/networks/store/GetTest.kt | 158 ++++++ ...alizationTest.kt => InitializationTest.kt} | 2 +- .../NetworkStatusesStoreUpdateMethodsTest.kt | 336 ------------ .../NetworksStatusesStoreGetMethodTest.kt | 83 --- .../store/ParameterizedStoreStatusTest.kt | 131 +++++ .../store/ParameterizedStoreSuccessTest.kt | 129 +++++ .../networks/store/ParameterizedStoreTest.kt | 95 ++++ .../networks/store/SetSourceAsCacheTest.kt | 154 ++++++ .../store/SetSourceAsOnlyCacheTest.kt | 214 ++++++++ .../store/StoreActualNetworkStatusTest.kt | 105 ---- .../data/networks/store/StoreStatusTest.kt | 247 +++++++++ .../data/networks/store/StoreSuccessTest.kt | 132 +++++ .../tangem/data/networks/store/StoreTest.kt | 132 +++++ .../networks/store/UpdateStatusSourceTest.kt | 477 ++++++++++++++++++ 21 files changed, 2046 insertions(+), 639 deletions(-) create mode 100644 data/networks/src/main/java/com/tangem/data/networks/store/NetworkStatusesStoreExt.kt create mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/GetTest.kt rename data/networks/src/test/java/com/tangem/data/networks/store/{NetworksStatusesStoreInitializationTest.kt => InitializationTest.kt} (98%) delete mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/NetworkStatusesStoreUpdateMethodsTest.kt delete mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/NetworksStatusesStoreGetMethodTest.kt create mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreStatusTest.kt create mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreSuccessTest.kt create mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreTest.kt create mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsCacheTest.kt create mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsOnlyCacheTest.kt delete mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/StoreActualNetworkStatusTest.kt create mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/StoreStatusTest.kt create mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/StoreSuccessTest.kt create mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/StoreTest.kt create mode 100644 data/networks/src/test/java/com/tangem/data/networks/store/UpdateStatusSourceTest.kt diff --git a/common/test/src/main/java/com/tangem/common/test/domain/network/MockNetworkStatusFactory.kt b/common/test/src/main/java/com/tangem/common/test/domain/network/MockNetworkStatusFactory.kt index 34153765ce..6a1da92250 100644 --- a/common/test/src/main/java/com/tangem/common/test/domain/network/MockNetworkStatusFactory.kt +++ b/common/test/src/main/java/com/tangem/common/test/domain/network/MockNetworkStatusFactory.kt @@ -14,7 +14,7 @@ object MockNetworkStatusFactory { private val defaultNetwork = MockCryptoCurrencyFactory().ethereum.network - fun createVerified(network: Network = defaultNetwork): NetworkStatus { + fun createVerified(network: Network = defaultNetwork, source: StatusSource = StatusSource.ACTUAL): NetworkStatus { return NetworkStatus( network = network, value = NetworkStatus.Verified( @@ -26,12 +26,12 @@ object MockNetworkStatusFactory { ), amounts = mapOf(), pendingTransactions = mapOf(), - source = StatusSource.ACTUAL, + source = source, ), ) } - fun createNoAccount(network: Network = defaultNetwork): NetworkStatus { + fun createNoAccount(network: Network = defaultNetwork, source: StatusSource = StatusSource.ACTUAL): NetworkStatus { return NetworkStatus( network = network, value = NetworkStatus.NoAccount( @@ -43,7 +43,7 @@ object MockNetworkStatusFactory { ), amountToCreateAccount = BigDecimal.ONE, errorMessage = "", - source = StatusSource.ACTUAL, + source = source, ), ) } 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 c606c84658..67a3ba3d44 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 @@ -8,6 +8,8 @@ import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.data.common.currency.CardCryptoCurrencyFactory import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory 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.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys @@ -51,12 +53,16 @@ internal class DefaultMultiNetworkStatusFetcher @Inject constructor( // 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.refresh(userWalletId = params.userWalletId, networks = params.networks) + networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, networks = params.networks) val userWallet = catch( block = { userWalletsStore.getSyncStrict(key = params.userWalletId) }, catch = { - networksStatusesStore.storeError(userWalletId = params.userWalletId, networks = params.networks) + networksStatusesStore.setSourceAsOnlyCache( + userWalletId = params.userWalletId, + networks = params.networks, + ) + raise(it) }, ) @@ -66,7 +72,7 @@ internal class DefaultMultiNetworkStatusFetcher @Inject constructor( } ensure(isNotSingleWallet) { - networksStatusesStore.storeError(userWalletId = params.userWalletId, networks = params.networks) + networksStatusesStore.setSourceAsOnlyCache(userWalletId = params.userWalletId, networks = params.networks) IllegalStateException("User wallet is not multi-currency") } 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 ca3b03ba67..ef23a8c4fc 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 @@ -3,11 +3,13 @@ package com.tangem.data.networks.single import arrow.core.Either import com.tangem.data.common.currency.CardCryptoCurrencyFactory import com.tangem.data.networks.store.NetworksStatusesStoreV2 +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.networks.single.SingleNetworkStatusFetcher import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext @@ -35,7 +37,7 @@ internal class DefaultSingleNetworkStatusFetcher @Inject constructor( val networkCurrencies = when (params) { is SingleNetworkStatusFetcher.Params.Prepared -> params.addedNetworkCurrencies is SingleNetworkStatusFetcher.Params.Simple -> { - networksStatusesStore.refresh(userWalletId = params.userWalletId, network = params.network) + networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, network = params.network) cardCryptoCurrencyFactory.create( userWalletId = params.userWalletId, @@ -60,28 +62,10 @@ internal class DefaultSingleNetworkStatusFetcher @Inject constructor( addedCurrencies = networkCurrencies.toSet(), ) - val statusValue = status.value - if (statusValue is NetworkStatus.Unreachable) { - val prevStatus = networksStatusesStore.getSyncOrNull( - userWalletId = params.userWalletId, - network = params.network, - ) - - if (prevStatus?.value is NetworkStatus.MissedDerivation) { - networksStatusesStore.storeUnreachableStatus(userWalletId = params.userWalletId, value = status) - } else { - networksStatusesStore.storeError( - userWalletId = params.userWalletId, - network = params.network, - value = statusValue, - ) - } - } else { - networksStatusesStore.storeSuccess(userWalletId = params.userWalletId, value = status) - } + networksStatusesStore.storeStatus(userWalletId = params.userWalletId, status = status) } .onLeft { Timber.e("Failed to fetch network status for $params: $it") - networksStatusesStore.storeError(userWalletId = params.userWalletId, network = params.network) + networksStatusesStore.setSourceAsOnlyCache(userWalletId = params.userWalletId, network = params.network) } } \ No newline at end of file diff --git a/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStoreV2.kt b/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStoreV2.kt index 943e6d1400..7bc6845b13 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStoreV2.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStoreV2.kt @@ -61,78 +61,43 @@ internal class DefaultNetworksStatusesStoreV2( return runtimeStore.getSyncOrNull()?.get(userWalletId.stringValue)?.firstOrNull { it.id == simpleStatusId } } - override suspend fun refresh(userWalletId: UserWalletId, network: Network) { - refresh(userWalletId = userWalletId, networks = setOf(network)) + override suspend fun updateStatusSource( + userWalletId: UserWalletId, + network: Network, + source: StatusSource, + ifNotFound: (SimpleNetworkStatus.Id) -> SimpleNetworkStatus?, + ) { + updateStatusSource( + userWalletId = userWalletId, + networks = setOf(network), + source = source, + ifNotFound = ifNotFound, + ) } - override suspend fun refresh(userWalletId: UserWalletId, networks: Set) { + override suspend fun updateStatusSource( + userWalletId: UserWalletId, + networks: Set, + source: StatusSource, + ifNotFound: (SimpleNetworkStatus.Id) -> SimpleNetworkStatus?, + ) { if (networks.isEmpty()) { - Timber.d("Nothing to refresh: networks is empty") + Timber.d("Nothing to update: networks is empty") return } - updateInRuntime(userWalletId = userWalletId, networks = networks) { status -> - status.copy(value = status.value.copySealed(source = StatusSource.CACHE)) + updateInRuntime(userWalletId = userWalletId, networks = networks, ifNotFound = ifNotFound) { status -> + status.copy(value = status.value.copySealed(source = source)) } } - override suspend fun storeSuccess(userWalletId: UserWalletId, value: NetworkStatus) { - if (value.value is NetworkStatus.Unreachable) { - val message = "Use storeError method to save unreachable status" - Timber.d(message) - - error(message) - } - - if (value.value.source != StatusSource.ACTUAL) { - val message = "Method storeActual can be called only with StatusSource.ACTUAL" - Timber.d(message) - - error(message) - } - + override suspend fun store(userWalletId: UserWalletId, status: NetworkStatus) { coroutineScope { - launch { storeInRuntime(userWalletId = userWalletId, status = value) } - launch { storeInPersistence(userWalletId = userWalletId, status = value) } + launch { storeInRuntime(userWalletId = userWalletId, status = status) } + launch { storeInPersistence(userWalletId = userWalletId, status = status) } } } - override suspend fun storeError(userWalletId: UserWalletId, network: Network, value: NetworkStatus.Unreachable?) { - updateInRuntime( - userWalletId = userWalletId, - networks = setOf(network), - ifNotFound = { id -> - value?.let { SimpleNetworkStatus(id = id, value = value) } - ?: createUnreachableStatus(id = id) - }, - update = { status -> - status.copy(value = status.value.copySealed(source = StatusSource.ONLY_CACHE)) - }, - ) - } - - override suspend fun storeError(userWalletId: UserWalletId, networks: Set) { - updateInRuntime( - userWalletId = userWalletId, - networks = networks, - ifNotFound = ::createUnreachableStatus, - update = { status -> - status.copy(value = status.value.copySealed(source = StatusSource.ONLY_CACHE)) - }, - ) - } - - override suspend fun storeUnreachableStatus(userWalletId: UserWalletId, value: NetworkStatus) { - if (value.value !is NetworkStatus.Unreachable) { - val message = "Use storeError method to save unreachable status" - Timber.d(message) - - error(message) - } - - storeInRuntime(userWalletId = userWalletId, status = value) - } - private suspend fun updateInRuntime( userWalletId: UserWalletId, networks: Set, @@ -189,8 +154,4 @@ internal class DefaultNetworksStatusesStoreV2( } } } - - private fun createUnreachableStatus(id: SimpleNetworkStatus.Id): SimpleNetworkStatus { - return SimpleNetworkStatus(id = id, value = NetworkStatus.Unreachable(address = null)) - } } \ No newline at end of file diff --git a/data/networks/src/main/java/com/tangem/data/networks/store/NetworkStatusesStoreExt.kt b/data/networks/src/main/java/com/tangem/data/networks/store/NetworkStatusesStoreExt.kt new file mode 100644 index 0000000000..3d7cd30e19 --- /dev/null +++ b/data/networks/src/main/java/com/tangem/data/networks/store/NetworkStatusesStoreExt.kt @@ -0,0 +1,104 @@ +package com.tangem.data.networks.store + +import com.tangem.data.networks.models.SimpleNetworkStatus +import com.tangem.domain.models.StatusSource +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.wallets.models.UserWalletId +import timber.log.Timber + +/** + * Store actual network [status] by [userWalletId]. + * If [status]'s source is not [StatusSource.ACTUAL], throws exception. + */ +internal suspend fun NetworksStatusesStoreV2.storeStatus(userWalletId: UserWalletId, status: NetworkStatus) { + if (status.value is NetworkStatus.Unreachable) { + val prevStatus = getSyncOrNull(userWalletId = userWalletId, network = status.network) + + /** + * Specific logic!!! + * If the previous status is [NetworkStatus.MissedDerivation] and current status is [NetworkStatus.Unreachable], + * then just store [NetworkStatus.MissedDerivation]. + */ + if (prevStatus?.value is NetworkStatus.MissedDerivation) { + store(userWalletId = userWalletId, status = status) + } else { + setSourceAsOnlyCache( + userWalletId = userWalletId, + network = status.network, + value = requireNotNull(status.value as? NetworkStatus.Unreachable), + ) + } + } else { + storeSuccess(userWalletId = userWalletId, status = status) + } +} + +/** + * Store actual success [status] by [userWalletId]. + * If [status]'s value is [NetworkStatus.Unreachable] and/or source is not [StatusSource.ACTUAL], throws exception. + */ +internal suspend fun NetworksStatusesStoreV2.storeSuccess(userWalletId: UserWalletId, status: NetworkStatus) { + if (status.value is NetworkStatus.Unreachable) { + val message = "Use storeError method to save unreachable status" + Timber.d(message) + + error(message) + } + + if (status.value.source != StatusSource.ACTUAL) { + val message = "Method storeActual can be called only with StatusSource.ACTUAL" + Timber.d(message) + + error(message) + } + + store(userWalletId = userWalletId, status = status) +} + +/** Set [StatusSource] as [StatusSource.CACHE] for [network] by [userWalletId] */ +internal suspend fun NetworksStatusesStoreV2.setSourceAsCache(userWalletId: UserWalletId, network: Network) { + setSourceAsCache(userWalletId = userWalletId, networks = setOf(network)) +} + +/** Set [StatusSource] as [StatusSource.CACHE] for [networks] by [userWalletId] */ +internal suspend fun NetworksStatusesStoreV2.setSourceAsCache(userWalletId: UserWalletId, networks: Set) { + updateStatusSource(userWalletId = userWalletId, networks = networks, source = StatusSource.CACHE) +} + +/** + * Set [StatusSource] as [StatusSource.ONLY_CACHE] for [network] by [userWalletId]. + * If the stored status is not found, store the [value] or a default [NetworkStatus.Unreachable] status. + */ +internal suspend fun NetworksStatusesStoreV2.setSourceAsOnlyCache( + userWalletId: UserWalletId, + network: Network, + value: NetworkStatus.Unreachable? = null, +) { + updateStatusSource( + userWalletId = userWalletId, + network = network, + source = StatusSource.ONLY_CACHE, + ifNotFound = { id -> + value?.let { SimpleNetworkStatus(id = id, value = value) } + ?: createUnreachableStatus(id = id) + }, + ) +} + +/** + * Set [StatusSource] as [StatusSource.ONLY_CACHE] for [networks] by [userWalletId]. + * If the stored status is not found, store a default [NetworkStatus.Unreachable] status. + */ +internal suspend fun NetworksStatusesStoreV2.setSourceAsOnlyCache(userWalletId: UserWalletId, networks: Set) { + updateStatusSource( + userWalletId = userWalletId, + networks = networks, + source = StatusSource.ONLY_CACHE, + ifNotFound = ::createUnreachableStatus, + ) +} + +private fun createUnreachableStatus(id: SimpleNetworkStatus.Id): SimpleNetworkStatus { + return SimpleNetworkStatus(id = id, value = NetworkStatus.Unreachable(address = null)) +} \ No newline at end of file diff --git a/data/networks/src/main/java/com/tangem/data/networks/store/NetworksStatusesStoreV2.kt b/data/networks/src/main/java/com/tangem/data/networks/store/NetworksStatusesStoreV2.kt index fb40156849..af6f25e12d 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/store/NetworksStatusesStoreV2.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/store/NetworksStatusesStoreV2.kt @@ -1,6 +1,7 @@ package com.tangem.data.networks.store import com.tangem.data.networks.models.SimpleNetworkStatus +import com.tangem.domain.models.StatusSource import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.wallets.models.UserWalletId @@ -15,25 +16,29 @@ internal interface NetworksStatusesStoreV2 { /** Get status of [network] by [userWalletId] synchronously or null */ suspend fun getSyncOrNull(userWalletId: UserWalletId, network: Network): SimpleNetworkStatus? - /** Refresh status of [network] by [userWalletId] */ - suspend fun refresh(userWalletId: UserWalletId, network: Network) + /** + * Update [source] of [network] by [userWalletId]. + * If the status is not found, create a new one by [ifNotFound]. + */ + suspend fun updateStatusSource( + userWalletId: UserWalletId, + network: Network, + source: StatusSource, + ifNotFound: (SimpleNetworkStatus.Id) -> SimpleNetworkStatus? = { null }, + ) - /** Refresh statuses of [networks] by [userWalletId] */ - suspend fun refresh(userWalletId: UserWalletId, networks: Set) - - /** Store success [value] by [userWalletId]. If status's value is unreachable, throws exception */ - @Throws - suspend fun storeSuccess(userWalletId: UserWalletId, value: NetworkStatus) + /** Update [source] of [networks] by [userWalletId]. If the status is not found, create a new one by [ifNotFound] */ + suspend fun updateStatusSource( + userWalletId: UserWalletId, + networks: Set, + source: StatusSource, + ifNotFound: (SimpleNetworkStatus.Id) -> SimpleNetworkStatus? = { null }, + ) /** - * Store error [value] by [userWalletId] and [network]. - * If [value] is null, default unreachable status will be stored. + * Store [status] by [userWalletId]. Rewrite the stored status with a new [status]. + * + * See complex methods in `NetworksStatusesStoreExt`. */ - suspend fun storeError(userWalletId: UserWalletId, network: Network, value: NetworkStatus.Unreachable? = null) - - /** Store unreachable [value] by [userWalletId] */ - suspend fun storeUnreachableStatus(userWalletId: UserWalletId, value: NetworkStatus) - - /** Store error for [networks] by [userWalletId] */ - suspend fun storeError(userWalletId: UserWalletId, networks: Set) + suspend fun store(userWalletId: UserWalletId, status: NetworkStatus) } \ No newline at end of file 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 bbd429ae23..813f2d20c1 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 @@ -4,6 +4,8 @@ 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.store.NetworksStatusesStoreV2 +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.tokens.model.NetworkStatus import com.tangem.domain.walletmanager.WalletManagersFacade @@ -45,12 +47,12 @@ internal class DefaultSingleNetworkStatusFetcherTest { val actual = fetcher(params) coVerifyOrder { - networksStatusesStore.refresh(params.userWalletId, params.network) + networksStatusesStore.setSourceAsCache(params.userWalletId, params.network) cardCryptoCurrencyFactory.create(params.userWalletId, params.network) walletManagersFacade.update(params.userWalletId, params.network, emptySet()) networksStatusesStore.storeSuccess( userWalletId = params.userWalletId, - value = NetworkStatus(params.network, NetworkStatus.MissedDerivation), + status = NetworkStatus(params.network, NetworkStatus.MissedDerivation), ) } @@ -67,14 +69,14 @@ internal class DefaultSingleNetworkStatusFetcherTest { val actual = fetcher(params) coVerifyOrder { - networksStatusesStore.refresh(userWalletId = params.userWalletId, network = params.network) + networksStatusesStore.setSourceAsCache(userWalletId = params.userWalletId, network = params.network) cardCryptoCurrencyFactory.create(userWalletId = params.userWalletId, network = params.network) - networksStatusesStore.storeError(userWalletId = params.userWalletId, network = params.network) + // networksStatusesStore.setSourceAsOnlyCache(userWalletId = params.userWalletId, network = params.network) } coVerify(inverse = true) { walletManagersFacade.update(userWalletId = any(), network = any(), extraTokens = any()) - networksStatusesStore.storeSuccess(userWalletId = any(), value = any()) + // networksStatusesStore.storeSuccess(userWalletId = any(), status = any()) } Truth.assertThat(actual.isLeft()).isTrue() diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/GetTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/GetTest.kt new file mode 100644 index 0000000000..eca488b451 --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/store/GetTest.kt @@ -0,0 +1,158 @@ +package com.tangem.data.networks.store + +import com.google.common.truth.Truth +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.domain.network.MockNetworkStatusFactory +import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.common.test.utils.getEmittedValues +import com.tangem.data.networks.models.SimpleNetworkStatus +import com.tangem.data.networks.toSimple +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.test.runTest +import org.junit.Test + +/** +[REDACTED_AUTHOR] + */ +internal class GetTest { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultNetworksStatusesStoreV2( + runtimeStore = runtimeStore, + persistenceDataStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `get if runtime store is empty`() = runTest { + val actual = store.get(userWalletId = userWalletId) + + val values = getEmittedValues(flow = actual) + + Truth.assertThat(values).isEqualTo(emptyList>()) + } + + @Test + fun `get if runtime store contains empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + + val actual = store.get(userWalletId = userWalletId) + + val values = getEmittedValues(flow = actual) + + Truth.assertThat(values).isEqualTo(emptyList>()) + } + + @Test + fun `get if runtime store contains portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to emptySet()), + ) + + val actual = store.get(userWalletId = userWalletId) + + val values = getEmittedValues(flow = actual) + + Truth.assertThat(values.size).isEqualTo(1) + Truth.assertThat(values).isEqualTo(listOf(emptySet())) + } + + @Test + fun `get if runtime store is not empty`() = runTest { + val status = MockNetworkStatusFactory.createVerified() + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(status.toSimple())), + ) + + val actual = store.get(userWalletId = userWalletId) + + val values = getEmittedValues(flow = actual) + + Truth.assertThat(values.size).isEqualTo(1) + Truth.assertThat(values).isEqualTo(listOf(setOf(status.toSimple()))) + } + + @Test + fun `getSyncOrNull if runtime store is empty`() = runTest { + val network = MockNetworkStatusFactory.createVerified().network + + val actual = store.getSyncOrNull(userWalletId = userWalletId, network = network) + + Truth.assertThat(actual).isEqualTo(null) + } + + @Test + fun `getSyncOrNull if runtime store contains empty map`() = runTest { + val network = MockNetworkStatusFactory.createVerified().network + + runtimeStore.store(value = emptyMap()) + + val actual = store.getSyncOrNull(userWalletId = userWalletId, network = network) + + Truth.assertThat(actual).isEqualTo(null) + } + + @Test + fun `getSyncOrNull if runtime store contains portfolio with empty statuses`() = runTest { + val network = MockNetworkStatusFactory.createVerified().network + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to emptySet()), + ) + + val actual = store.getSyncOrNull(userWalletId = userWalletId, network = network) + + Truth.assertThat(actual).isEqualTo(null) + } + + @Test + fun `getSyncOrNull if runtime store is not empty`() = runTest { + val unreachableStatus = MockNetworkStatusFactory.createUnreachable( + network = MockCryptoCurrencyFactory().createCoin(Blockchain.Ethereum).network, + ) + val missedDerivationStatus = MockNetworkStatusFactory.createMissedDerivation( + network = MockCryptoCurrencyFactory().createCoin(Blockchain.Bitcoin).network, + ) + val noAccountStatus = MockNetworkStatusFactory.createNoAccount( + network = MockCryptoCurrencyFactory().createCoin(Blockchain.Solana).network, + ) + val verifiedStatus = MockNetworkStatusFactory.createVerified( + network = MockCryptoCurrencyFactory().createCoin(Blockchain.Stellar).network, + ) + + runtimeStore.store( + value = mapOf( + userWalletId.stringValue to setOf( + unreachableStatus.toSimple(), + missedDerivationStatus.toSimple(), + noAccountStatus.toSimple(), + verifiedStatus.toSimple(), + ), + ), + ) + + val actual = listOf(unreachableStatus, missedDerivationStatus, noAccountStatus, verifiedStatus).map { + store.getSyncOrNull(userWalletId = userWalletId, network = it.network) + } + + val expected = listOf( + unreachableStatus.toSimple(), + missedDerivationStatus.toSimple(), + noAccountStatus.toSimple(), + verifiedStatus.toSimple(), + ) + + Truth.assertThat(actual).isEqualTo(expected) + } + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + } +} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/NetworksStatusesStoreInitializationTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/InitializationTest.kt similarity index 98% rename from data/networks/src/test/java/com/tangem/data/networks/store/NetworksStatusesStoreInitializationTest.kt rename to data/networks/src/test/java/com/tangem/data/networks/store/InitializationTest.kt index 94d819b827..8f17de78e9 100644 --- a/data/networks/src/test/java/com/tangem/data/networks/store/NetworksStatusesStoreInitializationTest.kt +++ b/data/networks/src/test/java/com/tangem/data/networks/store/InitializationTest.kt @@ -20,7 +20,7 @@ import org.junit.Test /** [REDACTED_AUTHOR] */ -internal class NetworksStatusesStoreInitializationTest { +internal class InitializationTest { @Test fun `test initialization if cache store is empty`() = runTest { diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/NetworkStatusesStoreUpdateMethodsTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/NetworkStatusesStoreUpdateMethodsTest.kt deleted file mode 100644 index d1f2080794..0000000000 --- a/data/networks/src/test/java/com/tangem/data/networks/store/NetworkStatusesStoreUpdateMethodsTest.kt +++ /dev/null @@ -1,336 +0,0 @@ -package com.tangem.data.networks.store - -import com.google.common.truth.Truth -import com.tangem.common.test.datastore.MockStateDataStore -import com.tangem.common.test.domain.network.MockNetworkStatusFactory -import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory -import com.tangem.data.networks.models.SimpleNetworkStatus -import com.tangem.data.networks.toDataModel -import com.tangem.data.networks.toSimple -import com.tangem.datasource.local.datastore.RuntimeSharedStore -import com.tangem.datasource.local.network.entity.NetworkStatusDM -import com.tangem.domain.models.StatusSource -import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.test.runTest -import org.junit.Test - -/** -[REDACTED_AUTHOR] - */ -internal class NetworkStatusesStoreUpdateMethodsTest { - - private val runtimeStore = RuntimeSharedStore() - private val persistenceStore = MockStateDataStore(default = emptyMap()) - - private val store = DefaultNetworksStatusesStoreV2( - runtimeStore = runtimeStore, - persistenceDataStore = persistenceStore, - dispatchers = TestingCoroutineDispatcherProvider(), - ) - - @Test - fun `refresh the single network if runtime store is empty`() = runTest { - store.refresh(userWalletId = userWalletId, network = network) - - val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) - } - - @Test - fun `refresh the single network if runtime store contains status with this network`() = runTest { - val status = MockNetworkStatusFactory.createVerified(network).toSimple() - - runtimeStore.store( - value = mapOf(userWalletId.stringValue to setOf(status)), - ) - - store.refresh(userWalletId = userWalletId, network = network) - - val runtimeExpected = mapOf( - userWalletId.stringValue to setOf( - status.copy(value = status.value.copySealed(source = StatusSource.CACHE)), - ), - ) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) - } - - @Test - fun `refresh the multi networks if runtime store is empty`() = runTest { - store.refresh(userWalletId = userWalletId, networks = networks) - - val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) - } - - @Test - fun `refresh the multi networks if runtime store contains status with this network`() = runTest { - val firstStatus = MockNetworkStatusFactory.createVerified(network = networks.first()).toSimple() - val secondStatus = MockNetworkStatusFactory.createVerified(network = networks.last()).toSimple() - - runtimeStore.store( - value = mapOf(userWalletId.stringValue to setOf(firstStatus, secondStatus)), - ) - - store.refresh(userWalletId = userWalletId, networks = networks) - - val runtimeExpected = mapOf( - userWalletId.stringValue to setOf( - firstStatus.copy(value = firstStatus.value.copySealed(source = StatusSource.CACHE)), - secondStatus.copy(value = secondStatus.value.copySealed(source = StatusSource.CACHE)), - ), - ) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) - } - - @Test - fun `store actual with any status sources`() = runTest { - val expectedErrorMessage = "Method storeActual can be called only with StatusSource.ACTUAL" - - // #1: StatusSource.ACTUAL - val status = MockNetworkStatusFactory.createVerified(network).copy( - value = MockNetworkStatusFactory.createVerified(network).value.copySealed(source = StatusSource.ACTUAL), - ) - - val actual = runCatching { store.storeSuccess(userWalletId, status) } - - val runtimeExpected = mapOf(userWalletId.stringValue to setOf(status.toSimple())) - val persistenceExpected = mapOf(userWalletId.stringValue to setOf(status.toDataModel()!!)) - - Truth.assertThat(actual.isSuccess).isTrue() - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) - - // #2: StatusSource.CACHE - val cacheStatus = MockNetworkStatusFactory.createVerified(network).copy( - value = MockNetworkStatusFactory.createVerified(network).value.copySealed(source = StatusSource.CACHE), - ) - - val cacheActual = runCatching { store.storeSuccess(userWalletId, cacheStatus) } - - Truth.assertThat(cacheActual.isFailure).isTrue() - Truth.assertThat(cacheActual.exceptionOrNull()).isInstanceOf(IllegalStateException::class.java) - Truth.assertThat(cacheActual.exceptionOrNull()).hasMessageThat().isEqualTo(expectedErrorMessage) - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) - - // #3: StatusSource.ONLY_CACHE - val onlyCacheStatus = MockNetworkStatusFactory.createVerified(network).copy( - value = MockNetworkStatusFactory.createVerified(network).value.copySealed(source = StatusSource.ONLY_CACHE), - ) - - val onlyCacheActual = runCatching { store.storeSuccess(userWalletId, onlyCacheStatus) } - - Truth.assertThat(onlyCacheActual.isFailure).isTrue() - Truth.assertThat(onlyCacheActual.exceptionOrNull()).isInstanceOf(IllegalStateException::class.java) - Truth.assertThat(onlyCacheActual.exceptionOrNull()).hasMessageThat().isEqualTo(expectedErrorMessage) - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) - } - - @Test - fun `store actual if runtime and cache stores contain status with this network`() = runTest { - val prevStatus = MockNetworkStatusFactory.createVerified(network).copy( - value = MockNetworkStatusFactory.createVerified(network).value.copySealed(source = StatusSource.ONLY_CACHE), - ) - - runtimeStore.store( - value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), - ) - - persistenceStore.updateData { - it.toMutableMap().apply { - put(userWalletId.stringValue, setOf(prevStatus.toDataModel()!!)) - } - } - - val newStatus = MockNetworkStatusFactory.createVerified(network).copy( - value = MockNetworkStatusFactory.createVerified(network).value.copySealed(source = StatusSource.ACTUAL), - ) - - store.storeSuccess(userWalletId, newStatus) - - val runtimeExpected = mapOf( - userWalletId.stringValue to setOf(newStatus.toSimple()), - ) - - val persistenceExpected = mapOf( - userWalletId.stringValue to setOf(newStatus.toDataModel()!!), - ) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) - } - - @Test - fun `store actual unreachable status if cache store contains verified status`() = runTest { - val prevStatus = MockNetworkStatusFactory.createVerified(network) - - runtimeStore.store( - value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), - ) - - persistenceStore.updateData { - it.toMutableMap().apply { - put(userWalletId.stringValue, setOf(prevStatus.toDataModel()!!)) - } - } - - val newStatus = MockNetworkStatusFactory.createUnreachable(network) - - val actual = runCatching { store.storeSuccess(userWalletId, newStatus) } - - val expectedErrorMessage = "Use storeError method to save unreachable status" - val runtimeExpected = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())) - val persistenceExpected = mapOf(userWalletId.stringValue to setOf(prevStatus.toDataModel()!!)) - - Truth.assertThat(actual.isFailure).isTrue() - Truth.assertThat(actual.exceptionOrNull()).isInstanceOf(IllegalStateException::class.java) - Truth.assertThat(actual.exceptionOrNull()).hasMessageThat().isEqualTo(expectedErrorMessage) - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) - } - - @Test - fun `store error if runtime store is empty`() = runTest { - store.storeError(userWalletId = userWalletId, network = network) - - val runtimeExpected = mapOf( - userWalletId.stringValue to setOf( - SimpleNetworkStatus( - id = SimpleNetworkStatus.Id(network), - value = NetworkStatus.Unreachable(address = null), - ), - ), - ) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) - } - - @Test - fun `store error if runtime store contains status with this network`() = runTest { - val status = MockNetworkStatusFactory.createVerified(network).toSimple() - - runtimeStore.store( - value = mapOf(userWalletId.stringValue to setOf(status)), - ) - - store.storeError(userWalletId = userWalletId, network = network) - - val runtimeExpected = mapOf( - userWalletId.stringValue to setOf( - status.copy(value = status.value.copySealed(source = StatusSource.ONLY_CACHE)), - ), - ) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) - } - - @Test - fun `store error if runtime store is empty and unreachable status was passed`() = runTest { - val status = MockNetworkStatusFactory.createUnreachable(network) - - store.storeError( - userWalletId = userWalletId, - network = network, - value = status.value as NetworkStatus.Unreachable, - ) - - val runtimeExpected = mapOf( - userWalletId.stringValue to setOf(status.toSimple()), - ) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) - } - - @Test - fun `store error if runtime store contains status with this network and unreachable status was passed`() = runTest { - val status = MockNetworkStatusFactory.createVerified(network) - - runtimeStore.store( - value = mapOf(userWalletId.stringValue to setOf(status.toSimple())), - ) - - store.storeError( - userWalletId = userWalletId, - network = network, - value = MockNetworkStatusFactory.createUnreachable(network).value as NetworkStatus.Unreachable, - ) - - val runtimeExpected = mapOf( - userWalletId.stringValue to setOf( - status.copy(value = status.value.copySealed(source = StatusSource.ONLY_CACHE)).toSimple(), - ), - ) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) - } - - @Test - fun `store error for networks if runtime store is empty`() = runTest { - store.storeError(userWalletId = userWalletId, networks = networks) - - val runtimeExpected = mapOf( - userWalletId.stringValue to setOf( - SimpleNetworkStatus( - id = SimpleNetworkStatus.Id(networks.first()), - value = NetworkStatus.Unreachable(address = null), - ), - SimpleNetworkStatus( - id = SimpleNetworkStatus.Id(networks.last()), - value = NetworkStatus.Unreachable(address = null), - ), - ), - ) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) - } - - @Test - fun `store error for networks if runtime store contains status with this network`() = runTest { - val status = MockNetworkStatusFactory.createVerified(networks.first()).toSimple() - - runtimeStore.store( - value = mapOf(userWalletId.stringValue to setOf(status)), - ) - - store.storeError(userWalletId = userWalletId, networks = networks) - - val runtimeExpected = mapOf( - userWalletId.stringValue to setOf( - status.copy(value = status.value.copySealed(source = StatusSource.ONLY_CACHE)), - SimpleNetworkStatus( - id = SimpleNetworkStatus.Id(networks.last()), - value = NetworkStatus.Unreachable(address = null), - ), - ), - ) - - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) - } - - private companion object { - - val userWalletId = UserWalletId(stringValue = "011") - - val network = MockCryptoCurrencyFactory().ethereum.network - - val networks = MockCryptoCurrencyFactory().ethereumAndStellar.mapTo(hashSetOf()) { it.network } - } -} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/NetworksStatusesStoreGetMethodTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/NetworksStatusesStoreGetMethodTest.kt deleted file mode 100644 index 522fcbeacc..0000000000 --- a/data/networks/src/test/java/com/tangem/data/networks/store/NetworksStatusesStoreGetMethodTest.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.tangem.data.networks.store - -import com.google.common.truth.Truth -import com.tangem.common.test.datastore.MockStateDataStore -import com.tangem.common.test.domain.network.MockNetworkStatusFactory -import com.tangem.common.test.utils.getEmittedValues -import com.tangem.data.networks.models.SimpleNetworkStatus -import com.tangem.data.networks.toSimple -import com.tangem.datasource.local.datastore.RuntimeSharedStore -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider -import kotlinx.coroutines.test.runTest -import org.junit.Test - -/** -[REDACTED_AUTHOR] - */ -internal class NetworksStatusesStoreGetMethodTest { - - private val runtimeStore = RuntimeSharedStore() - private val persistenceStore = MockStateDataStore(default = emptyMap()) - - private val store = DefaultNetworksStatusesStoreV2( - runtimeStore = runtimeStore, - persistenceDataStore = persistenceStore, - dispatchers = TestingCoroutineDispatcherProvider(), - ) - - @Test - fun `test get if runtime store is empty`() = runTest { - val actual = store.get(userWalletId = userWalletId) - - val values = getEmittedValues(flow = actual) - - Truth.assertThat(values).isEqualTo(emptyList>()) - } - - @Test - fun `test get if runtime store contains empty map`() = runTest { - runtimeStore.store(value = emptyMap()) - - val actual = store.get(userWalletId = userWalletId) - - val values = getEmittedValues(flow = actual) - - Truth.assertThat(values).isEqualTo(emptyList>()) - } - - @Test - fun `test get if runtime store contains portfolio with empty statuses`() = runTest { - runtimeStore.store( - value = mapOf(userWalletId.stringValue to emptySet()), - ) - - val actual = store.get(userWalletId = userWalletId) - - val values = getEmittedValues(flow = actual) - - Truth.assertThat(values.size).isEqualTo(1) - Truth.assertThat(values).isEqualTo(listOf(emptySet())) - } - - @Test - fun `test get if runtime store is not empty`() = runTest { - val status = MockNetworkStatusFactory.createVerified() - - runtimeStore.store( - value = mapOf(userWalletId.stringValue to setOf(status.toSimple())), - ) - - val actual = store.get(userWalletId = userWalletId) - - val values = getEmittedValues(flow = actual) - - Truth.assertThat(values.size).isEqualTo(1) - Truth.assertThat(values).isEqualTo(listOf(setOf(status.toSimple()))) - } - - private companion object { - - val userWalletId = UserWalletId(stringValue = "011") - } -} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreStatusTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreStatusTest.kt new file mode 100644 index 0000000000..aca2beaf62 --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreStatusTest.kt @@ -0,0 +1,131 @@ +package com.tangem.data.networks.store + +import com.google.common.truth.Truth +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.domain.network.MockNetworkStatusFactory +import com.tangem.data.networks.models.SimpleNetworkStatus +import com.tangem.data.networks.toDataModel +import com.tangem.data.networks.toSimple +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.domain.models.StatusSource +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** +[REDACTED_AUTHOR] + */ +@RunWith(Parameterized::class) +internal class ParameterizedStoreStatusTest(private val model: Model) { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultNetworksStatusesStoreV2( + runtimeStore = runtimeStore, + persistenceDataStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `test store success`() = runTest { + val actual = runCatching { store.storeStatus(userWalletId = userWalletId, status = model.status) } + + Truth.assertThat(actual.isSuccess).isEqualTo(model.isSuccess) + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(model.runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(model.persistenceExpected) + } + + data class Model( + val status: NetworkStatus, + val isSuccess: Boolean, + val runtimeExpected: Map>, + val persistenceExpected: WalletIdWithStatusDM, + ) + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + + @JvmStatic + @Parameterized.Parameters + fun data(): Collection { + return listOf( + // region any network statuses with StatusSource.ACTUAL + MockNetworkStatusFactory.createVerified(source = StatusSource.ACTUAL).let { status -> + Model( + status = status, + isSuccess = true, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = userWalletIdWithData(status), + ) + }, + MockNetworkStatusFactory.createNoAccount(source = StatusSource.ACTUAL).let { status -> + Model( + status = status, + isSuccess = true, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = userWalletIdWithData(status), + ) + }, + MockNetworkStatusFactory.createMissedDerivation().let { status -> + Model( + status = status, + isSuccess = true, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = emptyMap(), + ) + }, + MockNetworkStatusFactory.createUnreachable().let { status -> + Model( + status = status, + isSuccess = true, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = emptyMap(), + ) + }, + // endregion + + // region any status sources + Model( + status = MockNetworkStatusFactory.createVerified(source = StatusSource.CACHE), + isSuccess = false, + runtimeExpected = emptyMap(), + persistenceExpected = emptyMap(), + ), + Model( + status = MockNetworkStatusFactory.createNoAccount(source = StatusSource.CACHE), + isSuccess = false, + runtimeExpected = emptyMap(), + persistenceExpected = emptyMap(), + ), + Model( + status = MockNetworkStatusFactory.createVerified(source = StatusSource.ONLY_CACHE), + isSuccess = false, + runtimeExpected = emptyMap(), + persistenceExpected = emptyMap(), + ), + Model( + status = MockNetworkStatusFactory.createNoAccount(source = StatusSource.ONLY_CACHE), + isSuccess = false, + runtimeExpected = emptyMap(), + persistenceExpected = emptyMap(), + ), + // endregion + ) + } + + fun userWalletIdWithSimple(status: NetworkStatus) = mapOf( + userWalletId.stringValue to setOf(status.toSimple()), + ) + + fun userWalletIdWithData(status: NetworkStatus) = mapOf( + userWalletId.stringValue to setOf(status.toDataModel()!!), + ) + } +} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreSuccessTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreSuccessTest.kt new file mode 100644 index 0000000000..f22b72e0d6 --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreSuccessTest.kt @@ -0,0 +1,129 @@ +package com.tangem.data.networks.store + +import com.google.common.truth.Truth +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.domain.network.MockNetworkStatusFactory +import com.tangem.data.networks.models.SimpleNetworkStatus +import com.tangem.data.networks.toDataModel +import com.tangem.data.networks.toSimple +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.domain.models.StatusSource +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** +[REDACTED_AUTHOR] + */ +@RunWith(Parameterized::class) +internal class ParameterizedStoreSuccessTest(private val model: Model) { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultNetworksStatusesStoreV2( + runtimeStore = runtimeStore, + persistenceDataStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `test store success`() = runTest { + val actual = runCatching { store.storeSuccess(userWalletId = userWalletId, status = model.status) } + + Truth.assertThat(actual.isSuccess).isEqualTo(model.isSuccess) + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(model.runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(model.persistenceExpected) + } + + data class Model( + val status: NetworkStatus, + val isSuccess: Boolean, + val runtimeExpected: Map>, + val persistenceExpected: WalletIdWithStatusDM, + ) + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + + @JvmStatic + @Parameterized.Parameters + fun data(): Collection { + return listOf( + // region any network statuses with StatusSource.ACTUAL + MockNetworkStatusFactory.createVerified(source = StatusSource.ACTUAL).let { status -> + Model( + status = status, + isSuccess = true, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = userWalletIdWithData(status), + ) + }, + MockNetworkStatusFactory.createNoAccount(source = StatusSource.ACTUAL).let { status -> + Model( + status = status, + isSuccess = true, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = userWalletIdWithData(status), + ) + }, + MockNetworkStatusFactory.createMissedDerivation().let { status -> + Model( + status = status, + isSuccess = true, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = emptyMap(), + ) + }, + Model( + status = MockNetworkStatusFactory.createUnreachable(), + isSuccess = false, + runtimeExpected = emptyMap(), + persistenceExpected = emptyMap(), + ), + // endregion + + // region any status sources + Model( + status = MockNetworkStatusFactory.createVerified(source = StatusSource.CACHE), + isSuccess = false, + runtimeExpected = emptyMap(), + persistenceExpected = emptyMap(), + ), + Model( + status = MockNetworkStatusFactory.createNoAccount(source = StatusSource.CACHE), + isSuccess = false, + runtimeExpected = emptyMap(), + persistenceExpected = emptyMap(), + ), + Model( + status = MockNetworkStatusFactory.createVerified(source = StatusSource.ONLY_CACHE), + isSuccess = false, + runtimeExpected = emptyMap(), + persistenceExpected = emptyMap(), + ), + Model( + status = MockNetworkStatusFactory.createNoAccount(source = StatusSource.ONLY_CACHE), + isSuccess = false, + runtimeExpected = emptyMap(), + persistenceExpected = emptyMap(), + ), + // endregion + ) + } + + fun userWalletIdWithSimple(status: NetworkStatus) = mapOf( + userWalletId.stringValue to setOf(status.toSimple()), + ) + + fun userWalletIdWithData(status: NetworkStatus) = mapOf( + userWalletId.stringValue to setOf(status.toDataModel()!!), + ) + } +} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreTest.kt new file mode 100644 index 0000000000..bc68472740 --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/store/ParameterizedStoreTest.kt @@ -0,0 +1,95 @@ +package com.tangem.data.networks.store + +import com.google.common.truth.Truth +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.domain.network.MockNetworkStatusFactory +import com.tangem.data.networks.models.SimpleNetworkStatus +import com.tangem.data.networks.toDataModel +import com.tangem.data.networks.toSimple +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** +[REDACTED_AUTHOR] + */ +@RunWith(Parameterized::class) +internal class ParameterizedStoreTest(private val model: Model) { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultNetworksStatusesStoreV2( + runtimeStore = runtimeStore, + persistenceDataStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `test store method`() = runTest { + store.store(userWalletId = userWalletId, status = model.status) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(model.runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(model.persistenceExpected) + } + + data class Model( + val status: NetworkStatus, + val runtimeExpected: Map>, + val persistenceExpected: WalletIdWithStatusDM, + ) + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + + @JvmStatic + @Parameterized.Parameters + fun data(): Collection { + return listOf( + MockNetworkStatusFactory.createVerified().let { status -> + Model( + status = status, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = userWalletIdWithData(status), + ) + }, + MockNetworkStatusFactory.createNoAccount().let { status -> + Model( + status = status, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = userWalletIdWithData(status), + ) + }, + MockNetworkStatusFactory.createMissedDerivation().let { status -> + Model( + status = status, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = mapOf(), + ) + }, + MockNetworkStatusFactory.createUnreachable().let { status -> + Model( + status = status, + runtimeExpected = userWalletIdWithSimple(status), + persistenceExpected = mapOf(), + ) + }, + ) + } + + fun userWalletIdWithSimple(status: NetworkStatus) = mapOf( + userWalletId.stringValue to setOf(status.toSimple()), + ) + + fun userWalletIdWithData(status: NetworkStatus) = mapOf( + userWalletId.stringValue to setOf(status.toDataModel()!!), + ) + } +} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsCacheTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsCacheTest.kt new file mode 100644 index 0000000000..3043939d1e --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsCacheTest.kt @@ -0,0 +1,154 @@ +package com.tangem.data.networks.store + +import com.google.common.truth.Truth +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.domain.network.MockNetworkStatusFactory +import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.data.networks.toSimple +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.datasource.local.network.entity.NetworkStatusDM +import com.tangem.domain.models.StatusSource +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.junit.Test + +/** +[REDACTED_AUTHOR] + */ +internal class SetSourceAsCacheTest { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultNetworksStatusesStoreV2( + runtimeStore = runtimeStore, + persistenceDataStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `setSourceAsCache the single network if runtime store is empty`() = runTest { + store.setSourceAsCache(userWalletId = userWalletId, network = network) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsCache the single network if runtime store contains empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + + store.setSourceAsCache(userWalletId = userWalletId, network = network) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsCache the single network if runtime store contains portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to emptySet()), + ) + + store.setSourceAsCache(userWalletId = userWalletId, network = network) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsCache the single network if runtime store contains status with this network`() = runTest { + val status = MockNetworkStatusFactory.createVerified(network).toSimple() + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(status)), + ) + + store.setSourceAsCache(userWalletId = userWalletId, network = network) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf( + status.copy(value = status.value.copySealed(source = StatusSource.CACHE)), + ), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsCache the multi networks if runtime store is empty`() = runTest { + store.setSourceAsCache(userWalletId = userWalletId, networks = networks) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsCache the multi networks if runtime store contains empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + + store.setSourceAsCache(userWalletId = userWalletId, networks = networks) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsCache the multi networks if runtime store contains portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to emptySet()), + ) + + store.setSourceAsCache(userWalletId = userWalletId, networks = networks) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsCache the multi networks if runtime store contains status with this network`() = runTest { + val firstStatus = MockNetworkStatusFactory.createVerified(network = networks.first()).toSimple() + val secondStatus = MockNetworkStatusFactory.createVerified(network = networks.last()).toSimple() + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(firstStatus, secondStatus)), + ) + + store.setSourceAsCache(userWalletId = userWalletId, networks = networks) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf( + firstStatus.copy(value = firstStatus.value.copySealed(source = StatusSource.CACHE)), + secondStatus.copy(value = secondStatus.value.copySealed(source = StatusSource.CACHE)), + ), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + + val network = MockCryptoCurrencyFactory().ethereum.network + + val networks = MockCryptoCurrencyFactory().ethereumAndStellar.mapTo(hashSetOf()) { it.network } + } +} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsOnlyCacheTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsOnlyCacheTest.kt new file mode 100644 index 0000000000..6831b3d8be --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/store/SetSourceAsOnlyCacheTest.kt @@ -0,0 +1,214 @@ +package com.tangem.data.networks.store + +import com.google.common.truth.Truth +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.domain.network.MockNetworkStatusFactory +import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.data.networks.toSimple +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.datasource.local.network.entity.NetworkStatusDM +import com.tangem.domain.models.StatusSource +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.junit.Test + +/** +[REDACTED_AUTHOR] + */ +internal class SetSourceAsOnlyCacheTest { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultNetworksStatusesStoreV2( + runtimeStore = runtimeStore, + persistenceDataStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `setSourceAsOnlyCache if runtime store is empty`() = runTest { + store.setSourceAsOnlyCache(userWalletId = userWalletId, network = network) + + val status = MockNetworkStatusFactory.createUnreachable() + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(status.toSimple())) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsOnlyCache with value if runtime store is empty`() = runTest { + val status = MockNetworkStatusFactory.createUnreachable() + + store.setSourceAsOnlyCache( + userWalletId = userWalletId, + network = network, + value = status.value as NetworkStatus.Unreachable, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(status.toSimple())) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsOnlyCache if runtime store contains empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + + store.setSourceAsOnlyCache(userWalletId = userWalletId, network = network) + + val status = MockNetworkStatusFactory.createUnreachable() + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(status.toSimple())) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsOnlyCache with value if runtime store contains empty map`() = runTest { + val status = MockNetworkStatusFactory.createUnreachable() + + runtimeStore.store(value = emptyMap()) + + store.setSourceAsOnlyCache( + userWalletId = userWalletId, + network = network, + value = status.value as NetworkStatus.Unreachable, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(status.toSimple())) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsOnlyCache if runtime store contains portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to emptySet()), + ) + + store.setSourceAsOnlyCache(userWalletId = userWalletId, network = network) + + val status = MockNetworkStatusFactory.createUnreachable() + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(status.toSimple())) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsOnlyCache with value if runtime store contains portfolio with empty statuses`() = runTest { + val status = MockNetworkStatusFactory.createUnreachable() + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to emptySet()), + ) + + store.setSourceAsOnlyCache( + userWalletId = userWalletId, + network = network, + value = status.value as NetworkStatus.Unreachable, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(status.toSimple())) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsOnlyCache the single network if runtime store contains status with this network`() = runTest { + val status = MockNetworkStatusFactory.createVerified(network).toSimple() + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(status)), + ) + + store.setSourceAsOnlyCache(userWalletId = userWalletId, network = network) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf( + status.copy(value = status.value.copySealed(source = StatusSource.ONLY_CACHE)), + ), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsOnlyCache the multi networks if runtime store is empty`() = runTest { + store.setSourceAsOnlyCache(userWalletId = userWalletId, networks = networks) + + val statuses = networks.map { MockNetworkStatusFactory.createUnreachable(it).toSimple() }.toSet() + val runtimeExpected = mapOf(userWalletId.stringValue to statuses) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsOnlyCache the multi networks if runtime store contains empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + + store.setSourceAsOnlyCache(userWalletId = userWalletId, networks = networks) + + val statuses = networks.map { MockNetworkStatusFactory.createUnreachable(it).toSimple() }.toSet() + val runtimeExpected = mapOf(userWalletId.stringValue to statuses) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsOnlyCache the multi networks if runtime store contains portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to emptySet()), + ) + + store.setSourceAsOnlyCache(userWalletId = userWalletId, networks = networks) + + val statuses = networks.map { MockNetworkStatusFactory.createUnreachable(it).toSimple() }.toSet() + val runtimeExpected = mapOf(userWalletId.stringValue to statuses) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `setSourceAsOnlyCache the multi networks if runtime store contains status with this network`() = runTest { + val firstStatus = MockNetworkStatusFactory.createVerified(network = networks.first()).toSimple() + val secondStatus = MockNetworkStatusFactory.createVerified(network = networks.last()).toSimple() + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(firstStatus, secondStatus)), + ) + + store.setSourceAsOnlyCache(userWalletId = userWalletId, networks = networks) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf( + firstStatus.copy(value = firstStatus.value.copySealed(source = StatusSource.ONLY_CACHE)), + secondStatus.copy(value = secondStatus.value.copySealed(source = StatusSource.ONLY_CACHE)), + ), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + + val network = MockCryptoCurrencyFactory().ethereum.network + + val networks = MockCryptoCurrencyFactory().ethereumAndStellar.mapTo(hashSetOf()) { it.network } + } +} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/StoreActualNetworkStatusTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/StoreActualNetworkStatusTest.kt deleted file mode 100644 index 52e6f59b59..0000000000 --- a/data/networks/src/test/java/com/tangem/data/networks/store/StoreActualNetworkStatusTest.kt +++ /dev/null @@ -1,105 +0,0 @@ -package com.tangem.data.networks.store - -import com.google.common.truth.Truth -import com.tangem.common.test.datastore.MockStateDataStore -import com.tangem.common.test.domain.network.MockNetworkStatusFactory -import com.tangem.data.networks.models.SimpleNetworkStatus -import com.tangem.data.networks.toDataModel -import com.tangem.data.networks.toSimple -import com.tangem.datasource.local.datastore.RuntimeSharedStore -import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.test.runTest -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized - -/** -[REDACTED_AUTHOR] - */ -@RunWith(Parameterized::class) -internal class StoreActualNetworkStatusTest(private val model: Model) { - - private val runtimeStore = RuntimeSharedStore() - private val persistenceStore = MockStateDataStore(default = emptyMap()) - - private val store = DefaultNetworksStatusesStoreV2( - runtimeStore = runtimeStore, - persistenceDataStore = persistenceStore, - dispatchers = TestingCoroutineDispatcherProvider(), - ) - - @Test - fun `store actual`() = runTest { - val actual = runCatching { store.storeSuccess(userWalletId = userWalletId, value = model.status) } - - Truth.assertThat(actual.isSuccess).isEqualTo(model.isSuccess) - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(model.runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(model.persistenceExpected) - } - - data class Model( - val status: NetworkStatus, - val isSuccess: Boolean, - val runtimeExpected: Map>, - val persistenceExpected: WalletIdWithStatusDM, - ) - - private companion object { - - val userWalletId = UserWalletId(stringValue = "011") - - @JvmStatic - @Parameterized.Parameters - fun data(): Collection { - return listOf( - Model( - status = MockNetworkStatusFactory.createVerified(), - isSuccess = true, - runtimeExpected = mapOf( - userWalletId.stringValue to setOf( - MockNetworkStatusFactory.createVerified().toSimple(), - ), - ), - persistenceExpected = mapOf( - userWalletId.stringValue to setOf( - MockNetworkStatusFactory.createVerified().toDataModel()!!, - ), - ), - ), - Model( - status = MockNetworkStatusFactory.createNoAccount(), - isSuccess = true, - runtimeExpected = mapOf( - userWalletId.stringValue to setOf( - MockNetworkStatusFactory.createNoAccount().toSimple(), - ), - ), - persistenceExpected = mapOf( - userWalletId.stringValue to setOf( - MockNetworkStatusFactory.createNoAccount().toDataModel()!!, - ), - ), - ), - Model( - status = MockNetworkStatusFactory.createMissedDerivation(), - isSuccess = true, - runtimeExpected = mapOf( - userWalletId.stringValue to setOf( - MockNetworkStatusFactory.createMissedDerivation().toSimple(), - ), - ), - persistenceExpected = mapOf(), - ), - Model( - status = MockNetworkStatusFactory.createUnreachable(), - isSuccess = false, - runtimeExpected = mapOf(), - persistenceExpected = mapOf(), - ), - ) - } - } -} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/StoreStatusTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/StoreStatusTest.kt new file mode 100644 index 0000000000..5e19d72af9 --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/store/StoreStatusTest.kt @@ -0,0 +1,247 @@ +package com.tangem.data.networks.store + +import com.google.common.truth.Truth +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.domain.network.MockNetworkStatusFactory +import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.data.networks.toDataModel +import com.tangem.data.networks.toSimple +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.datasource.local.network.entity.NetworkStatusDM +import com.tangem.domain.models.StatusSource +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.junit.Test + +/** +[REDACTED_AUTHOR] + */ +internal class StoreStatusTest { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultNetworksStatusesStoreV2( + runtimeStore = runtimeStore, + persistenceDataStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `storeStatus if runtime and cache stores are empty`() = runTest { + val newStatus = MockNetworkStatusFactory.createVerified(network) + + store.storeStatus(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `storeStatus if runtime and cache stores contain empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + persistenceStore.updateData { emptyMap() } + + val newStatus = MockNetworkStatusFactory.createVerified(network) + + store.storeStatus(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `storeStatus if runtime and cache stores contain portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf()), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf()) + } + + val newStatus = MockNetworkStatusFactory.createVerified(network) + + store.storeStatus(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `storeStatus if runtime and cache stores contain status with this network`() = runTest { + val prevStatus = MockNetworkStatusFactory.createVerified(network) + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf(prevStatus.toDataModel()!!)) + } + + val newStatus = MockNetworkStatusFactory.createVerified(network).copy( + value = MockNetworkStatusFactory.createVerified(network).value.copySealed(source = StatusSource.ACTUAL), + ) + + store.storeStatus(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `store unreachable status if runtime and cache stores are empty`() = runTest { + val newStatus = MockNetworkStatusFactory.createUnreachable(network) + + store.storeStatus(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = emptyMap() + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `store unreachable status if runtime and cache stores contain empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + persistenceStore.updateData { emptyMap() } + + val newStatus = MockNetworkStatusFactory.createUnreachable(network) + + store.storeStatus(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = emptyMap() + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `store unreachable status if runtime and cache stores contain portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf()), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf()) + } + + val newStatus = MockNetworkStatusFactory.createUnreachable(network) + + store.storeStatus(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `store unreachable status if runtime and cache stores contain verified status with this network`() = runTest { + val prevStatus = MockNetworkStatusFactory.createVerified(network) + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf(prevStatus.toDataModel()!!)) + } + + val newStatus = MockNetworkStatusFactory.createUnreachable(network) + store.storeStatus(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf( + MockNetworkStatusFactory.createVerified(network = network, source = StatusSource.ONLY_CACHE).toSimple(), + ), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(prevStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `store unreachable status if stores contain missed derivation status with this network`() = runTest { + val prevStatus = MockNetworkStatusFactory.createMissedDerivation(network) + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to emptySet()) + } + + val newStatus = MockNetworkStatusFactory.createUnreachable(network) + + store.storeStatus(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + + val network = MockCryptoCurrencyFactory().ethereum.network + } +} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/StoreSuccessTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/StoreSuccessTest.kt new file mode 100644 index 0000000000..5e230e53ca --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/store/StoreSuccessTest.kt @@ -0,0 +1,132 @@ +package com.tangem.data.networks.store + +import com.google.common.truth.Truth +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.domain.network.MockNetworkStatusFactory +import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.data.networks.toDataModel +import com.tangem.data.networks.toSimple +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.domain.models.StatusSource +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.junit.Test + +/** +[REDACTED_AUTHOR] + */ +internal class StoreSuccessTest { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultNetworksStatusesStoreV2( + runtimeStore = runtimeStore, + persistenceDataStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `storeSuccess if runtime and cache stores are empty`() = runTest { + val newStatus = MockNetworkStatusFactory.createVerified(network) + + store.storeSuccess(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `storeSuccess if runtime and cache stores contain empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + persistenceStore.updateData { emptyMap() } + + val newStatus = MockNetworkStatusFactory.createVerified(network) + + store.storeSuccess(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `storeSuccess if runtime and cache stores contain portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf()), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf()) + } + + val newStatus = MockNetworkStatusFactory.createVerified(network) + + store.storeSuccess(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `storeSuccess if runtime and cache stores contain status with this network`() = runTest { + val prevStatus = MockNetworkStatusFactory.createVerified(network) + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf(prevStatus.toDataModel()!!)) + } + + val newStatus = MockNetworkStatusFactory.createVerified(network).copy( + value = MockNetworkStatusFactory.createVerified(network).value.copySealed(source = StatusSource.ACTUAL), + ) + + store.storeSuccess(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + + val network = MockCryptoCurrencyFactory().ethereum.network + } +} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/StoreTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/StoreTest.kt new file mode 100644 index 0000000000..21364e70b0 --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/store/StoreTest.kt @@ -0,0 +1,132 @@ +package com.tangem.data.networks.store + +import com.google.common.truth.Truth +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.domain.network.MockNetworkStatusFactory +import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.data.networks.toDataModel +import com.tangem.data.networks.toSimple +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.domain.models.StatusSource +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.junit.Test + +/** +[REDACTED_AUTHOR] + */ +internal class StoreTest { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultNetworksStatusesStoreV2( + runtimeStore = runtimeStore, + persistenceDataStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `store if runtime and cache stores are empty`() = runTest { + val newStatus = MockNetworkStatusFactory.createVerified(network) + + store.store(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `store if runtime and cache stores contain empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + persistenceStore.updateData { emptyMap() } + + val newStatus = MockNetworkStatusFactory.createVerified(network) + + store.store(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `store if runtime and cache stores contain portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf()), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf()) + } + + val newStatus = MockNetworkStatusFactory.createVerified(network) + + store.store(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `store if runtime and cache stores contain status with this network`() = runTest { + val prevStatus = MockNetworkStatusFactory.createVerified(network) + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf(prevStatus.toDataModel()!!)) + } + + val newStatus = MockNetworkStatusFactory.createVerified(network).copy( + value = MockNetworkStatusFactory.createVerified(network).value.copySealed(source = StatusSource.ACTUAL), + ) + + store.store(userWalletId = userWalletId, status = newStatus) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + + val network = MockCryptoCurrencyFactory().ethereum.network + } +} \ No newline at end of file diff --git a/data/networks/src/test/java/com/tangem/data/networks/store/UpdateStatusSourceTest.kt b/data/networks/src/test/java/com/tangem/data/networks/store/UpdateStatusSourceTest.kt new file mode 100644 index 0000000000..285b7c2ac8 --- /dev/null +++ b/data/networks/src/test/java/com/tangem/data/networks/store/UpdateStatusSourceTest.kt @@ -0,0 +1,477 @@ +package com.tangem.data.networks.store + +import com.google.common.truth.Truth +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.domain.network.MockNetworkStatusFactory +import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.data.networks.models.SimpleNetworkStatus +import com.tangem.data.networks.toDataModel +import com.tangem.data.networks.toSimple +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.datasource.local.network.entity.NetworkStatusDM +import com.tangem.domain.models.StatusSource +import com.tangem.domain.tokens.model.NetworkAddress +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.junit.Test + +/** +[REDACTED_AUTHOR] + */ +internal class UpdateStatusSourceTest { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultNetworksStatusesStoreV2( + runtimeStore = runtimeStore, + persistenceDataStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `updateStatusSource if stores are empty`() = runTest { + store.updateStatusSource( + userWalletId = userWalletId, + network = network, + source = StatusSource.ACTUAL, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + + val persistenceExpected = emptyMap>() + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource with ifNotFound if stores are empty`() = runTest { + val default = MockNetworkStatusFactory.createUnreachable(network).copy( + value = NetworkStatus.Unreachable( + address = NetworkAddress.Single( + defaultAddress = NetworkAddress.Address( + value = "", + type = NetworkAddress.Address.Type.Primary, + ), + ), + ), + ) + + store.updateStatusSource( + userWalletId = userWalletId, + network = network, + source = StatusSource.ACTUAL, + ifNotFound = { default.toSimple() }, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(default.toSimple())) + + val persistenceExpected = emptyMap>() + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource if stores contain empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + persistenceStore.updateData { emptyMap() } + + store.updateStatusSource( + userWalletId = userWalletId, + network = network, + source = StatusSource.ACTUAL, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + + val persistenceExpected = emptyMap>() + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource with ifNotFound if stores contain empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + persistenceStore.updateData { emptyMap() } + + val default = MockNetworkStatusFactory.createUnreachable(network).copy( + value = NetworkStatus.Unreachable( + address = NetworkAddress.Single( + defaultAddress = NetworkAddress.Address( + value = "", + type = NetworkAddress.Address.Type.Primary, + ), + ), + ), + ) + + store.updateStatusSource( + userWalletId = userWalletId, + network = network, + source = StatusSource.ACTUAL, + ifNotFound = { default.toSimple() }, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(default.toSimple())) + + val persistenceExpected = emptyMap>() + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource if stores contain portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf()), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf()) + } + + store.updateStatusSource( + userWalletId = userWalletId, + network = network, + source = StatusSource.ACTUAL, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + val persistenceExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource with ifNotFound if stores contain portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf()), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf()) + } + + val default = MockNetworkStatusFactory.createUnreachable(network).copy( + value = NetworkStatus.Unreachable( + address = NetworkAddress.Single( + defaultAddress = NetworkAddress.Address( + value = "", + type = NetworkAddress.Address.Type.Primary, + ), + ), + ), + ) + + store.updateStatusSource( + userWalletId = userWalletId, + network = network, + source = StatusSource.ACTUAL, + ifNotFound = { default.toSimple() }, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(default.toSimple())) + val persistenceExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource if stores contain status with this network`() = runTest { + val prevStatus = MockNetworkStatusFactory.createVerified(network) + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf(prevStatus.toDataModel()!!)) + } + + store.updateStatusSource( + userWalletId = userWalletId, + network = network, + source = StatusSource.ACTUAL, + ) + + val newStatus = MockNetworkStatusFactory.createVerified(network) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource with ifNotFound if stores contain status with this network`() = runTest { + val prevStatus = MockNetworkStatusFactory.createVerified(network) + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf(prevStatus.toDataModel()!!)) + } + + store.updateStatusSource( + userWalletId = userWalletId, + network = network, + source = StatusSource.ACTUAL, + ifNotFound = { prevStatus.toSimple() }, + ) + + val newStatus = MockNetworkStatusFactory.createVerified(network) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource of networks if stores are empty`() = runTest { + store.updateStatusSource( + userWalletId = userWalletId, + networks = networks, + source = StatusSource.ACTUAL, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + + val persistenceExpected = emptyMap>() + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource of networks with ifNotFound if stores are empty`() = runTest { + val default = MockNetworkStatusFactory.createUnreachable(network).copy( + value = NetworkStatus.Unreachable( + address = NetworkAddress.Single( + defaultAddress = NetworkAddress.Address( + value = "", + type = NetworkAddress.Address.Type.Primary, + ), + ), + ), + ) + + store.updateStatusSource( + userWalletId = userWalletId, + networks = networks, + source = StatusSource.ACTUAL, + ifNotFound = { default.toSimple() }, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(default.toSimple())) + + val persistenceExpected = emptyMap>() + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource of networks if stores contain empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + persistenceStore.updateData { emptyMap() } + + store.updateStatusSource( + userWalletId = userWalletId, + networks = networks, + source = StatusSource.ACTUAL, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + + val persistenceExpected = emptyMap>() + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource of networks with ifNotFound if stores contain empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + persistenceStore.updateData { emptyMap() } + + val default = MockNetworkStatusFactory.createUnreachable(network).copy( + value = NetworkStatus.Unreachable( + address = NetworkAddress.Single( + defaultAddress = NetworkAddress.Address( + value = "", + type = NetworkAddress.Address.Type.Primary, + ), + ), + ), + ) + + store.updateStatusSource( + userWalletId = userWalletId, + networks = networks, + source = StatusSource.ACTUAL, + ifNotFound = { default.toSimple() }, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(default.toSimple())) + + val persistenceExpected = emptyMap>() + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource of networks if stores contain portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf()), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf()) + } + + store.updateStatusSource( + userWalletId = userWalletId, + networks = networks, + source = StatusSource.ACTUAL, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to emptySet()) + val persistenceExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource of networks with ifNotFound if stores contain portfolio with empty statuses`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf()), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf()) + } + + val default = MockNetworkStatusFactory.createUnreachable(network).copy( + value = NetworkStatus.Unreachable( + address = NetworkAddress.Single( + defaultAddress = NetworkAddress.Address( + value = "", + type = NetworkAddress.Address.Type.Primary, + ), + ), + ), + ) + + store.updateStatusSource( + userWalletId = userWalletId, + networks = networks, + source = StatusSource.ACTUAL, + ifNotFound = { default.toSimple() }, + ) + + val runtimeExpected = mapOf(userWalletId.stringValue to setOf(default.toSimple())) + val persistenceExpected = mapOf(userWalletId.stringValue to emptySet()) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource of networks if stores contain status with this network`() = runTest { + val prevStatus = MockNetworkStatusFactory.createVerified(network) + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf(prevStatus.toDataModel()!!)) + } + + store.updateStatusSource( + userWalletId = userWalletId, + networks = networks, + source = StatusSource.ACTUAL, + ) + + val newStatus = MockNetworkStatusFactory.createVerified(network) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `updateStatusSource of networks with ifNotFound if stores contain status with this network`() = runTest { + val prevStatus = MockNetworkStatusFactory.createVerified(network) + + runtimeStore.store( + value = mapOf(userWalletId.stringValue to setOf(prevStatus.toSimple())), + ) + + persistenceStore.updateData { + mapOf(userWalletId.stringValue to setOf(prevStatus.toDataModel()!!)) + } + + store.updateStatusSource( + userWalletId = userWalletId, + networks = networks, + source = StatusSource.ACTUAL, + ifNotFound = { prevStatus.toSimple() }, + ) + + val newStatus = MockNetworkStatusFactory.createVerified(network) + + val runtimeExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toSimple()), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newStatus.toDataModel()!!), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + + val network = MockCryptoCurrencyFactory().ethereum.network + + val networks = MockCryptoCurrencyFactory().ethereumAndStellar.mapTo(hashSetOf()) { it.network } + } +} \ No newline at end of file