Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-07 22:37:04 +04:00
parent 373880691e
commit cafd4d074b
7 changed files with 378 additions and 126 deletions

View file

@ -5,6 +5,7 @@ import com.tangem.data.account.converter.createGetWalletAccountsResponse
import com.tangem.data.account.converter.createWalletAccountDTO
import com.tangem.data.account.store.AccountsResponseStore
import com.tangem.data.account.store.AccountsResponseStoreFactory
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
import com.tangem.data.common.cache.etag.ETagsStore
import com.tangem.data.common.currency.UserTokensSaver
import com.tangem.datasource.api.common.response.ApiResponse
@ -36,6 +37,7 @@ class DefaultWalletAccountsFetcherTest {
private val userTokensSaver: UserTokensSaver = mockk(relaxUnitFun = true)
private val fetchWalletAccountsErrorHandler: FetchWalletAccountsErrorHandler = mockk(relaxUnitFun = true)
private val defaultWalletAccountsResponseFactory: DefaultWalletAccountsResponseFactory = mockk()
private val eTagsStore: ETagsStore = mockk(relaxUnitFun = true)
private val fetcher: DefaultWalletAccountsFetcher = DefaultWalletAccountsFetcher(
@ -43,6 +45,7 @@ class DefaultWalletAccountsFetcherTest {
accountsResponseStoreFactory = accountsResponseStoreFactory,
userTokensSaver = userTokensSaver,
fetchWalletAccountsErrorHandler = fetchWalletAccountsErrorHandler,
defaultWalletAccountsResponseFactory = defaultWalletAccountsResponseFactory,
eTagsStore = eTagsStore,
dispatchers = TestingCoroutineDispatcherProvider(),
)
@ -205,6 +208,16 @@ class DefaultWalletAccountsFetcherTest {
tangemTechApi.getWalletAccounts(walletId = userWalletId.stringValue, eTag = eTag)
} returns apiError as ApiResponse<GetWalletAccountsResponse>
coEvery {
fetchWalletAccountsErrorHandler.handle(
error = apiError.cause,
userWalletId = userWalletId,
savedAccountsResponse = null,
pushWalletAccounts = any(),
storeWalletAccounts = any(),
)
} returns savedAccountsResponse
// Act
fetcher.fetch(userWalletId)

View file

@ -1,9 +1,7 @@
package com.tangem.data.account.fetcher
import com.tangem.data.account.converter.CryptoPortfolioConverter
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
import com.tangem.data.account.utils.toUserTokensResponse
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
import com.tangem.data.common.currency.UserTokensResponseFactory
import com.tangem.data.common.currency.UserTokensSaver
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.common.response.ApiResponseError.HttpException.Code
@ -11,12 +9,11 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO
import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import io.mockk.*
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@ -29,35 +26,21 @@ import org.junit.jupiter.api.TestInstance
class FetchWalletAccountsErrorHandlerTest {
private val userTokensSaver: UserTokensSaver = mockk(relaxUnitFun = true)
private val userWalletsStore: UserWalletsStore = mockk()
private val userTokensResponseStore: UserTokensResponseStore = mockk(relaxUnitFun = true)
private val cryptoPortfolioCF: CryptoPortfolioConverter.Factory = mockk()
private val cryptoPortfolioConverter = mockk<CryptoPortfolioConverter>()
private val userTokensResponseFactory: UserTokensResponseFactory = mockk()
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory = mockk()
private val defaultWalletAccountsResponseFactory: DefaultWalletAccountsResponseFactory = mockk()
private val handler = FetchWalletAccountsErrorHandler(
userTokensSaver = userTokensSaver,
userWalletsStore = userWalletsStore,
userTokensResponseStore = userTokensResponseStore,
cryptoPortfolioCF = cryptoPortfolioCF,
userTokensResponseFactory = userTokensResponseFactory,
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
defaultWalletAccountsResponseFactory = defaultWalletAccountsResponseFactory,
)
private val userWallet = mockk<UserWallet> {
every { this@mockk.walletId } returns userWalletId
}
@BeforeEach
fun setupEach() {
clearMocks(
userTokensSaver,
userWalletsStore,
userTokensResponseStore,
cryptoPortfolioCF,
cryptoPortfolioConverter,
cardCryptoCurrencyFactory,
defaultWalletAccountsResponseFactory,
)
}
@ -84,12 +67,8 @@ class FetchWalletAccountsErrorHandlerTest {
// Assert
coVerify(inverse = true) {
userWalletsStore.getSyncStrict(key = any())
userTokensResponseStore.getSyncOrNull(userWalletId = any())
userTokensResponseFactory.createUserTokensResponse(any(), any(), any())
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(any())
cryptoPortfolioCF.create(any())
cryptoPortfolioConverter.convertListBack(any())
defaultWalletAccountsResponseFactory.create(userWalletId = any(), userTokensResponse = any())
pushWalletAccounts(any(), any())
userTokensSaver.push(userWalletId = any(), response = any())
storeWalletAccounts(any(), any())
@ -146,12 +125,8 @@ class FetchWalletAccountsErrorHandlerTest {
}
coVerify(inverse = true) {
userWalletsStore.getSyncStrict(key = any())
userTokensResponseStore.getSyncOrNull(userWalletId = any())
userTokensResponseFactory.createUserTokensResponse(any(), any(), any())
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(any())
cryptoPortfolioCF.create(any())
cryptoPortfolioConverter.convertListBack(any())
defaultWalletAccountsResponseFactory.create(userWalletId = any(), userTokensResponse = any())
}
}
@ -160,9 +135,6 @@ class FetchWalletAccountsErrorHandlerTest {
// Arrange
val error = ApiResponseError.TimeoutException()
val accounts = AccountList.empty(userWalletId).accounts
.filterIsInstance<Account.CryptoPortfolio>()
val accountDTO = WalletAccountDTO(
id = "nibh",
name = "Michael Dotson",
@ -186,18 +158,10 @@ class FetchWalletAccountsErrorHandlerTest {
val userTokensResponse = savedAccountsResponse.toUserTokensResponse()
every { userWalletsStore.getSyncStrict(userWalletId) } returns userWallet
every { cryptoPortfolioCF.create(userWallet) } returns cryptoPortfolioConverter
every { cryptoPortfolioConverter.convertListBack(accounts) } returns listOf(accountDTO)
coEvery { userTokensResponseStore.getSyncOrNull(userWalletId) } returns null
every {
userTokensResponseFactory.createUserTokensResponse(
currencies = emptyList(),
isGroupedByNetwork = false,
isSortedByBalance = false,
)
} returns userTokensResponse
every { cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet) } returns emptyList()
coEvery { userTokensResponseStore.getSyncOrNull(userWalletId) } returns userTokensResponse
coEvery {
defaultWalletAccountsResponseFactory.create(userWalletId, userTokensResponse)
} returns savedAccountsResponse
val pushWalletAccounts: suspend (UserWalletId, List<WalletAccountDTO>) -> Unit = mockk(relaxed = true)
val storeWalletAccounts: suspend (UserWalletId, GetWalletAccountsResponse) -> Unit = mockk(relaxed = true)
@ -213,16 +177,8 @@ class FetchWalletAccountsErrorHandlerTest {
// Assert
coVerify {
userWalletsStore.getSyncStrict(userWalletId)
cryptoPortfolioCF.create(userWallet)
cryptoPortfolioConverter.convertListBack(accounts)
userTokensResponseStore.getSyncOrNull(userWalletId)
userTokensResponseFactory.createUserTokensResponse(
currencies = emptyList(),
isGroupedByNetwork = false,
isSortedByBalance = false,
)
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet)
defaultWalletAccountsResponseFactory.create(userWalletId, userTokensResponse)
storeWalletAccounts(userWalletId, any())
}

View file

@ -0,0 +1,240 @@
package com.tangem.data.account.utils
import com.google.common.truth.Truth
import com.tangem.data.account.converter.CryptoPortfolioConverter
import com.tangem.data.account.converter.createWalletAccountDTO
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
import com.tangem.data.common.currency.UserTokensResponseFactory
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import io.mockk.*
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DefaultWalletAccountsResponseFactoryTest {
private val userWalletsListRepository = mockk<UserWalletsListRepository>()
private val cryptoPortfolioCF = mockk<CryptoPortfolioConverter.Factory>()
private val cryptoPortfolioConverter = mockk<CryptoPortfolioConverter>()
private val userTokensResponseFactory = mockk<UserTokensResponseFactory>()
private val cardCryptoCurrencyFactory = mockk<CardCryptoCurrencyFactory>()
private val factory = DefaultWalletAccountsResponseFactory(
userWalletsListRepository = userWalletsListRepository,
cryptoPortfolioCF = cryptoPortfolioCF,
userTokensResponseFactory = userTokensResponseFactory,
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
)
private val userWalletId = UserWalletId("011")
@BeforeEach
fun setUpEach() {
every { cryptoPortfolioCF.create(any()) } returns cryptoPortfolioConverter
}
@AfterEach
fun tearDownEach() {
clearMocks(
userWalletsListRepository,
cryptoPortfolioCF,
cryptoPortfolioConverter,
userTokensResponseFactory,
cardCryptoCurrencyFactory,
)
}
@Test
fun `create returns empty accounts when user wallet not found`() = runTest {
// Arrange
val userTokensResponse = UserTokensResponse(
group = UserTokensResponse.GroupType.NETWORK,
sort = UserTokensResponse.SortType.BALANCE,
tokens = emptyList(),
)
coEvery { userWalletsListRepository.userWalletsSync() } returns emptyList()
every {
userTokensResponseFactory.createUserTokensResponse(
currencies = emptyList(),
isGroupedByNetwork = false,
isSortedByBalance = false,
)
} returns userTokensResponse
// Act
val actual = factory.create(userWalletId = userWalletId, userTokensResponse = null)
// Assert
val expected = GetWalletAccountsResponse(
wallet = GetWalletAccountsResponse.Wallet(
group = UserTokensResponse.GroupType.NETWORK,
sort = UserTokensResponse.SortType.BALANCE,
totalAccounts = 0,
),
accounts = emptyList(),
unassignedTokens = emptyList(),
)
Truth.assertThat(actual).isEqualTo(expected)
coVerifyOrder {
userWalletsListRepository.userWalletsSync()
userTokensResponseFactory.createUserTokensResponse(
currencies = emptyList(),
isGroupedByNetwork = false,
isSortedByBalance = false,
)
}
}
@Test
fun `create returns response with default tokens when userTokensResponse is null`() = runTest {
// Arrange
val userWallet = mockk<UserWallet>(relaxed = true) {
every { walletId } returns userWalletId
}
val defaultCoins = listOf(mockk<CryptoCurrency.Coin>())
coEvery { userWalletsListRepository.userWalletsSync() } returns listOf(userWallet)
every { cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet) } returns defaultCoins
val defaultResponse = UserTokensResponse(
group = UserTokensResponse.GroupType.NETWORK,
sort = UserTokensResponse.SortType.BALANCE,
tokens = listOf(mockk(relaxed = true)),
)
every {
userTokensResponseFactory.createUserTokensResponse(
currencies = defaultCoins,
isGroupedByNetwork = false,
isSortedByBalance = false,
)
} returns defaultResponse
val accounts = AccountList.empty(userWallet.walletId).accounts
.filterIsInstance<Account.CryptoPortfolio>()
val accountsDTO = createWalletAccountDTO(userWalletId)
every { cryptoPortfolioConverter.convertListBack(accounts) } returns listOf(accountsDTO)
// Act
val actual = factory.create(userWalletId, null)
// Assert
val expected = GetWalletAccountsResponse(
wallet = GetWalletAccountsResponse.Wallet(
group = defaultResponse.group,
sort = defaultResponse.sort,
totalAccounts = 1,
),
accounts = listOf(accountsDTO),
unassignedTokens = emptyList(),
)
Truth.assertThat(actual).isEqualTo(expected)
coVerifyOrder {
userWalletsListRepository.userWalletsSync()
cryptoPortfolioConverter.convertListBack(accounts)
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet)
userTokensResponseFactory.createUserTokensResponse(
currencies = defaultCoins,
isGroupedByNetwork = false,
isSortedByBalance = false,
)
}
}
@Test
fun `create returns response with default tokens when userTokensResponse is null and no default coins`() = runTest {
// Arrange
val userWallet = mockk<UserWallet>(relaxed = true) {
every { walletId } returns userWalletId
}
coEvery { userWalletsListRepository.userWalletsSync() } returns listOf(userWallet)
every { cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet) } returns emptyList()
val defaultResponse = UserTokensResponse(
group = UserTokensResponse.GroupType.NETWORK,
sort = UserTokensResponse.SortType.BALANCE,
tokens = emptyList(),
)
every {
userTokensResponseFactory.createUserTokensResponse(
currencies = emptyList(),
isGroupedByNetwork = false,
isSortedByBalance = false,
)
} returns defaultResponse
val accounts = AccountList.empty(userWallet.walletId).accounts
.filterIsInstance<Account.CryptoPortfolio>()
every { cryptoPortfolioConverter.convertListBack(accounts) } returns emptyList()
// Act
val actual = factory.create(userWalletId, null)
// Assert
val expected = GetWalletAccountsResponse(
wallet = GetWalletAccountsResponse.Wallet(
group = defaultResponse.group,
sort = defaultResponse.sort,
totalAccounts = 0,
),
accounts = emptyList(),
unassignedTokens = emptyList(),
)
Truth.assertThat(actual).isEqualTo(expected)
}
@Test
fun `create returns response with assigned tokens`() = runTest {
// Arrange
val userWallet = mockk<UserWallet>(relaxed = true) {
every { walletId } returns userWalletId
}
val assignedTokens = listOf(mockk<CryptoCurrency.Token>(), mockk<CryptoCurrency.Token>())
coEvery { userWalletsListRepository.userWalletsSync() } returns listOf(userWallet)
val userTokensResponse = UserTokensResponse(
group = UserTokensResponse.GroupType.NETWORK,
sort = UserTokensResponse.SortType.BALANCE,
tokens = listOf(mockk(relaxed = true)),
)
every {
userTokensResponseFactory.createUserTokensResponse(
currencies = assignedTokens,
isGroupedByNetwork = false,
isSortedByBalance = false,
)
} returns userTokensResponse
val accounts = AccountList.empty(userWallet.walletId).accounts
.filterIsInstance<Account.CryptoPortfolio>()
val accountsDTO = createWalletAccountDTO(userWalletId)
every { cryptoPortfolioConverter.convertListBack(accounts) } returns listOf(accountsDTO)
// Act
val actual = factory.create(userWalletId, userTokensResponse)
// Assert
val expected = GetWalletAccountsResponse(
wallet = GetWalletAccountsResponse.Wallet(
group = userTokensResponse.group,
sort = userTokensResponse.sort,
totalAccounts = 1,
),
accounts = listOf(accountsDTO),
unassignedTokens = emptyList(),
)
Truth.assertThat(actual).isEqualTo(expected)
}
}