Updated on 2026-08-14
This commit is contained in:
parent
7bbab0a40b
commit
46dd1333ff
5 changed files with 306 additions and 2 deletions
|
|
@ -108,10 +108,16 @@ internal object AccountDataModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideMainAccountTokensMigration(
|
fun provideMainAccountTokensMigration(
|
||||||
|
defaultMainAccountTokensMigration: DefaultMainAccountTokensMigration,
|
||||||
|
): MainAccountTokensMigration = defaultMainAccountTokensMigration
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideDefaultMainAccountTokensMigration(
|
||||||
accountsResponseStoreFactory: AccountsResponseStoreFactory,
|
accountsResponseStoreFactory: AccountsResponseStoreFactory,
|
||||||
userTokensSaver: UserTokensSaver,
|
userTokensSaver: UserTokensSaver,
|
||||||
accountTokenMigrationStore: AccountTokenMigrationStore,
|
accountTokenMigrationStore: AccountTokenMigrationStore,
|
||||||
): MainAccountTokensMigration {
|
): DefaultMainAccountTokensMigration {
|
||||||
return DefaultMainAccountTokensMigration(
|
return DefaultMainAccountTokensMigration(
|
||||||
accountsResponseStoreFactory = accountsResponseStoreFactory,
|
accountsResponseStoreFactory = accountsResponseStoreFactory,
|
||||||
accountTokenMigrationStore = accountTokenMigrationStore,
|
accountTokenMigrationStore = accountTokenMigrationStore,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.data.account.fetcher
|
||||||
|
|
||||||
import com.tangem.data.account.store.AccountsResponseStore
|
import com.tangem.data.account.store.AccountsResponseStore
|
||||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||||
|
import com.tangem.data.account.tokens.DefaultMainAccountTokensMigration
|
||||||
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
||||||
import com.tangem.data.account.utils.assignTokens
|
import com.tangem.data.account.utils.assignTokens
|
||||||
import com.tangem.data.common.account.WalletAccountsFetcher
|
import com.tangem.data.common.account.WalletAccountsFetcher
|
||||||
|
|
@ -50,6 +51,7 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
||||||
private val defaultWalletAccountsResponseFactory: DefaultWalletAccountsResponseFactory,
|
private val defaultWalletAccountsResponseFactory: DefaultWalletAccountsResponseFactory,
|
||||||
private val eTagsStore: ETagsStore,
|
private val eTagsStore: ETagsStore,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
private val mainAccountTokensMigration: DefaultMainAccountTokensMigration,
|
||||||
) : WalletAccountsFetcher, WalletAccountsSaver {
|
) : WalletAccountsFetcher, WalletAccountsSaver {
|
||||||
|
|
||||||
override suspend fun fetch(userWalletId: UserWalletId): GetWalletAccountsResponse {
|
override suspend fun fetch(userWalletId: UserWalletId): GetWalletAccountsResponse {
|
||||||
|
|
@ -71,7 +73,8 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
|
||||||
throw fetchResult.error
|
throw fetchResult.error
|
||||||
}
|
}
|
||||||
|
|
||||||
return updatedResponse
|
val migratedResponse = mainAccountTokensMigration.migrate(userWalletId).getOrNull() ?: updatedResponse
|
||||||
|
return migratedResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getSaved(userWalletId: UserWalletId): GetWalletAccountsResponse? {
|
override suspend fun getSaved(userWalletId: UserWalletId): GetWalletAccountsResponse? {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import arrow.core.raise.ensureNotNull
|
||||||
import arrow.core.toNonEmptyListOrNull
|
import arrow.core.toNonEmptyListOrNull
|
||||||
import com.tangem.blockchain.common.Blockchain
|
import com.tangem.blockchain.common.Blockchain
|
||||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||||
|
import com.tangem.data.account.converter.toDerivationIndex
|
||||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||||
import com.tangem.data.account.utils.assignTokens
|
import com.tangem.data.account.utils.assignTokens
|
||||||
import com.tangem.data.common.currency.UserTokensSaver
|
import com.tangem.data.common.currency.UserTokensSaver
|
||||||
|
|
@ -37,6 +38,67 @@ internal class DefaultMainAccountTokensMigration(
|
||||||
private val userTokensSaver: UserTokensSaver,
|
private val userTokensSaver: UserTokensSaver,
|
||||||
) : MainAccountTokensMigration {
|
) : MainAccountTokensMigration {
|
||||||
|
|
||||||
|
internal suspend fun migrate(userWalletId: UserWalletId): Either<Throwable, GetWalletAccountsResponse> = either {
|
||||||
|
val store = accountsResponseStoreFactory.create(userWalletId)
|
||||||
|
val response = store.getSyncOrNull()
|
||||||
|
ensureNotNull(response) {
|
||||||
|
val exception = IllegalStateException("No cached accounts response found")
|
||||||
|
Timber.e(exception)
|
||||||
|
exception
|
||||||
|
}
|
||||||
|
val mainAccount = findAccount(response = response, derivationIndex = DerivationIndex.Main)
|
||||||
|
val notMainAccounts = response.accounts
|
||||||
|
.filterNot { accountDTO -> accountDTO.derivationIndex.toDerivationIndex().isMain }
|
||||||
|
|
||||||
|
if (notMainAccounts.isEmpty()) {
|
||||||
|
Timber.i("There is only the Main account. Nothing to migrate")
|
||||||
|
return@either response
|
||||||
|
}
|
||||||
|
|
||||||
|
val unassignedTokens = mainAccount.groupUnassignedTokens()
|
||||||
|
|
||||||
|
if (unassignedTokens.isEmpty()) {
|
||||||
|
Timber.i("No unassigned tokens found for migration")
|
||||||
|
return@either response
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedMainAccount = mainAccount
|
||||||
|
val assignedTokensAccounts = notMainAccounts.mapNotNull { accountDTO ->
|
||||||
|
val derivationIndex = accountDTO.derivationIndex.toDerivationIndex()
|
||||||
|
val tokensForAccount = unassignedTokens[derivationIndex]
|
||||||
|
if (tokensForAccount.isNullOrEmpty()) return@mapNotNull null
|
||||||
|
updatedMainAccount = updatedMainAccount.copy(
|
||||||
|
tokens = updatedMainAccount.tokens.orEmpty() - tokensForAccount,
|
||||||
|
)
|
||||||
|
accountDTO.assignTokens(userWalletId, tokensForAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
val updatedResponse = response.copy(
|
||||||
|
accounts = response.accounts.map { account ->
|
||||||
|
val assignAccount = assignedTokensAccounts.find { it.id == account.id }
|
||||||
|
when {
|
||||||
|
account.id == mainAccount.id -> updatedMainAccount
|
||||||
|
assignAccount != null -> assignAccount
|
||||||
|
else -> account
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
store.updateData { updatedResponse }
|
||||||
|
|
||||||
|
val userTokensResponse = updatedResponse.toUserTokensResponse()
|
||||||
|
userTokensSaver.pushWithRetryer(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
response = userTokensResponse,
|
||||||
|
onFailSend = {
|
||||||
|
val exception = IllegalStateException("Failed to push updated tokens after migration")
|
||||||
|
Timber.e(exception)
|
||||||
|
raise(exception)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return@either updatedResponse
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun migrate(
|
override suspend fun migrate(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
derivationIndex: DerivationIndex,
|
derivationIndex: DerivationIndex,
|
||||||
|
|
@ -113,6 +175,22 @@ internal class DefaultMainAccountTokensMigration(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun WalletAccountDTO.groupUnassignedTokens(): Map<DerivationIndex, List<UserTokensResponse.Token>> =
|
||||||
|
this.tokens
|
||||||
|
.orEmpty()
|
||||||
|
.mapNotNull { token ->
|
||||||
|
val blockchain = Blockchain.fromNetworkId(token.networkId) ?: return@mapNotNull null
|
||||||
|
val derivationPath = token.derivationPath ?: return@mapNotNull null
|
||||||
|
val accountNode = AccountNodeRecognizer(blockchain)
|
||||||
|
.recognize(derivationPath)
|
||||||
|
?: return@mapNotNull null
|
||||||
|
|
||||||
|
if (accountNode == DerivationIndex.Main.value.toLong()) return@mapNotNull null
|
||||||
|
val derivationIndex = DerivationIndex(accountNode.toInt()).getOrNull() ?: return@mapNotNull null
|
||||||
|
derivationIndex to token.copy(accountId = null)
|
||||||
|
}
|
||||||
|
.groupBy({ it.first }, { it.second })
|
||||||
|
|
||||||
private fun WalletAccountDTO.findUnassignedTokens(
|
private fun WalletAccountDTO.findUnassignedTokens(
|
||||||
derivationIndex: DerivationIndex,
|
derivationIndex: DerivationIndex,
|
||||||
): List<UserTokensResponse.Token>? {
|
): List<UserTokensResponse.Token>? {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
package com.tangem.data.account.fetcher
|
package com.tangem.data.account.fetcher
|
||||||
|
|
||||||
|
import arrow.core.right
|
||||||
import com.google.common.truth.Truth
|
import com.google.common.truth.Truth
|
||||||
import com.tangem.data.account.converter.createGetWalletAccountsResponse
|
import com.tangem.data.account.converter.createGetWalletAccountsResponse
|
||||||
import com.tangem.data.account.converter.createWalletAccountDTO
|
import com.tangem.data.account.converter.createWalletAccountDTO
|
||||||
import com.tangem.data.account.fetcher.DefaultWalletAccountsFetcher.FetchResult
|
import com.tangem.data.account.fetcher.DefaultWalletAccountsFetcher.FetchResult
|
||||||
import com.tangem.data.account.store.AccountsResponseStore
|
import com.tangem.data.account.store.AccountsResponseStore
|
||||||
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
import com.tangem.data.account.store.AccountsResponseStoreFactory
|
||||||
|
import com.tangem.data.account.tokens.DefaultMainAccountTokensMigration
|
||||||
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
|
||||||
import com.tangem.data.common.cache.etag.ETagsStore
|
import com.tangem.data.common.cache.etag.ETagsStore
|
||||||
import com.tangem.data.common.currency.UserTokensSaver
|
import com.tangem.data.common.currency.UserTokensSaver
|
||||||
|
|
@ -35,6 +37,7 @@ class DefaultWalletAccountsFetcherTest {
|
||||||
private val accountsResponseStoreFactory: AccountsResponseStoreFactory = mockk()
|
private val accountsResponseStoreFactory: AccountsResponseStoreFactory = mockk()
|
||||||
private val accountsResponseStore: AccountsResponseStore = mockk()
|
private val accountsResponseStore: AccountsResponseStore = mockk()
|
||||||
private val accountsResponseStoreFlow = MutableStateFlow<GetWalletAccountsResponse?>(value = null)
|
private val accountsResponseStoreFlow = MutableStateFlow<GetWalletAccountsResponse?>(value = null)
|
||||||
|
private val tokensMigration: DefaultMainAccountTokensMigration = mockk(relaxed = true)
|
||||||
|
|
||||||
private val userTokensSaver: UserTokensSaver = mockk(relaxUnitFun = true)
|
private val userTokensSaver: UserTokensSaver = mockk(relaxUnitFun = true)
|
||||||
private val fetchWalletAccountsErrorHandler: FetchWalletAccountsErrorHandler = mockk(relaxUnitFun = true)
|
private val fetchWalletAccountsErrorHandler: FetchWalletAccountsErrorHandler = mockk(relaxUnitFun = true)
|
||||||
|
|
@ -49,16 +52,19 @@ class DefaultWalletAccountsFetcherTest {
|
||||||
defaultWalletAccountsResponseFactory = defaultWalletAccountsResponseFactory,
|
defaultWalletAccountsResponseFactory = defaultWalletAccountsResponseFactory,
|
||||||
eTagsStore = eTagsStore,
|
eTagsStore = eTagsStore,
|
||||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||||
|
mainAccountTokensMigration = tokensMigration,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val userWalletId = UserWalletId("011")
|
private val userWalletId = UserWalletId("011")
|
||||||
private val eTag = "etag"
|
private val eTag = "etag"
|
||||||
|
private val migratedAccountsResponse = createGetWalletAccountsResponse(userWalletId)
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
every { accountsResponseStoreFactory.create(userWalletId) } returns accountsResponseStore
|
every { accountsResponseStoreFactory.create(userWalletId) } returns accountsResponseStore
|
||||||
every { accountsResponseStore.data } returns accountsResponseStoreFlow
|
every { accountsResponseStore.data } returns accountsResponseStoreFlow
|
||||||
|
|
||||||
|
coEvery { tokensMigration.migrate(userWalletId) } returns migratedAccountsResponse.right()
|
||||||
coEvery { eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts) } returns eTag
|
coEvery { eTagsStore.getSyncOrNull(userWalletId, ETagsStore.Key.WalletAccounts) } returns eTag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,6 +140,7 @@ class DefaultWalletAccountsFetcherTest {
|
||||||
accountsResponseStore.updateData(any())
|
accountsResponseStore.updateData(any())
|
||||||
accountsResponseStoreFactory.create(userWalletId = userWalletId)
|
accountsResponseStoreFactory.create(userWalletId = userWalletId)
|
||||||
accountsResponseStore.updateData(any())
|
accountsResponseStore.updateData(any())
|
||||||
|
tokensMigration.migrate(userWalletId)
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(inverse = true) {
|
coVerify(inverse = true) {
|
||||||
|
|
@ -181,6 +188,7 @@ class DefaultWalletAccountsFetcherTest {
|
||||||
eTagsStore.store(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts, value = newETag)
|
eTagsStore.store(userWalletId = userWalletId, key = ETagsStore.Key.WalletAccounts, value = newETag)
|
||||||
accountsResponseStoreFactory.create(userWalletId = userWalletId)
|
accountsResponseStoreFactory.create(userWalletId = userWalletId)
|
||||||
accountsResponseStore.updateData(any())
|
accountsResponseStore.updateData(any())
|
||||||
|
tokensMigration.migrate(userWalletId)
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(inverse = true) {
|
coVerify(inverse = true) {
|
||||||
|
|
@ -236,6 +244,7 @@ class DefaultWalletAccountsFetcherTest {
|
||||||
pushWalletAccounts = any(),
|
pushWalletAccounts = any(),
|
||||||
storeWalletAccounts = any(),
|
storeWalletAccounts = any(),
|
||||||
)
|
)
|
||||||
|
tokensMigration.migrate(userWalletId)
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(inverse = true) {
|
coVerify(inverse = true) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.data.account.token
|
package com.tangem.data.account.token
|
||||||
|
|
||||||
|
import arrow.core.right
|
||||||
import com.tangem.data.account.converter.createGetWalletAccountsResponse
|
import com.tangem.data.account.converter.createGetWalletAccountsResponse
|
||||||
import com.tangem.data.account.converter.createWalletAccountDTO
|
import com.tangem.data.account.converter.createWalletAccountDTO
|
||||||
import com.tangem.data.account.store.AccountsResponseStore
|
import com.tangem.data.account.store.AccountsResponseStore
|
||||||
|
|
@ -13,6 +14,7 @@ import com.tangem.datasource.local.accounts.AccountTokenMigrationStore
|
||||||
import com.tangem.domain.models.account.AccountId
|
import com.tangem.domain.models.account.AccountId
|
||||||
import com.tangem.domain.models.account.DerivationIndex
|
import com.tangem.domain.models.account.DerivationIndex
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.test.core.assertEither
|
||||||
import com.tangem.test.core.assertEitherLeft
|
import com.tangem.test.core.assertEitherLeft
|
||||||
import com.tangem.test.core.assertEitherRight
|
import com.tangem.test.core.assertEitherRight
|
||||||
import io.mockk.*
|
import io.mockk.*
|
||||||
|
|
@ -214,6 +216,212 @@ class DefaultMainAccountTokensMigrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `migrate updates tokens for all accounts`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val unassignedToken1 = createBitcoin(accountIndex = 1)
|
||||||
|
val unassignedToken2 = createBitcoin(accountIndex = 2)
|
||||||
|
|
||||||
|
val derivationIndex1 = DerivationIndex(1).getOrNull()!!
|
||||||
|
val derivationIndex2 = DerivationIndex(2).getOrNull()!!
|
||||||
|
|
||||||
|
val mainAccount = createWalletAccountDTO(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
accountId = AccountId.forCryptoPortfolio(userWalletId, DerivationIndex.Main).value,
|
||||||
|
derivationIndex = DerivationIndex.Main.value,
|
||||||
|
tokens = listOf(
|
||||||
|
createBitcoin(accountIndex = 0),
|
||||||
|
unassignedToken1,
|
||||||
|
unassignedToken2,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val account1 = createWalletAccountDTO(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
accountId = AccountId.forCryptoPortfolio(userWalletId, derivationIndex1).value,
|
||||||
|
derivationIndex = derivationIndex1.value,
|
||||||
|
tokens = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val account2 = createWalletAccountDTO(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
accountId = AccountId.forCryptoPortfolio(userWalletId, derivationIndex2).value,
|
||||||
|
derivationIndex = derivationIndex2.value,
|
||||||
|
tokens = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val response = GetWalletAccountsResponse(
|
||||||
|
wallet = GetWalletAccountsResponse.Wallet(
|
||||||
|
group = UserTokensResponse.GroupType.NONE,
|
||||||
|
sort = UserTokensResponse.SortType.MANUAL,
|
||||||
|
totalAccounts = 3,
|
||||||
|
totalArchivedAccounts = 0,
|
||||||
|
),
|
||||||
|
accounts = listOf(mainAccount, account1, account2),
|
||||||
|
unassignedTokens = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
accountsResponseStoreFlow.value = response
|
||||||
|
|
||||||
|
coEvery { accountsResponseStore.updateData(any()) } returns mockk()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = migration.migrate(userWalletId)
|
||||||
|
|
||||||
|
val migratedResponse = response.copy(
|
||||||
|
accounts = listOf(
|
||||||
|
mainAccount.copy(tokens = mainAccount.tokens!! - unassignedToken1 - unassignedToken2),
|
||||||
|
account1.copy(tokens = listOf(unassignedToken1)),
|
||||||
|
account2.copy(tokens = listOf(unassignedToken2)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEither(actual, migratedResponse.right())
|
||||||
|
|
||||||
|
coVerifySequence {
|
||||||
|
accountsResponseStoreFactory.create(userWalletId)
|
||||||
|
accountsResponseStore.data
|
||||||
|
accountsResponseStore.updateData(any())
|
||||||
|
userTokensSaver.pushWithRetryer(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
response = migratedResponse.toUserTokensResponse(),
|
||||||
|
onFailSend = any(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `migrate updates tokens only for one account`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val unassignedToken1 = createBitcoin(accountIndex = 1)
|
||||||
|
|
||||||
|
val derivationIndex1 = DerivationIndex(1).getOrNull()!!
|
||||||
|
val derivationIndex2 = DerivationIndex(2).getOrNull()!!
|
||||||
|
|
||||||
|
val mainAccount = createWalletAccountDTO(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
accountId = AccountId.forCryptoPortfolio(userWalletId, DerivationIndex.Main).value,
|
||||||
|
derivationIndex = DerivationIndex.Main.value,
|
||||||
|
tokens = listOf(
|
||||||
|
createBitcoin(accountIndex = 0),
|
||||||
|
unassignedToken1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val account1 = createWalletAccountDTO(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
accountId = AccountId.forCryptoPortfolio(userWalletId, derivationIndex1).value,
|
||||||
|
derivationIndex = derivationIndex1.value,
|
||||||
|
tokens = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val account2 = createWalletAccountDTO(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
accountId = AccountId.forCryptoPortfolio(userWalletId, derivationIndex2).value,
|
||||||
|
derivationIndex = derivationIndex2.value,
|
||||||
|
tokens = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val response = GetWalletAccountsResponse(
|
||||||
|
wallet = GetWalletAccountsResponse.Wallet(
|
||||||
|
group = UserTokensResponse.GroupType.NONE,
|
||||||
|
sort = UserTokensResponse.SortType.MANUAL,
|
||||||
|
totalAccounts = 3,
|
||||||
|
totalArchivedAccounts = 0,
|
||||||
|
),
|
||||||
|
accounts = listOf(mainAccount, account1, account2),
|
||||||
|
unassignedTokens = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
accountsResponseStoreFlow.value = response
|
||||||
|
|
||||||
|
coEvery { accountsResponseStore.updateData(any()) } returns mockk()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = migration.migrate(userWalletId)
|
||||||
|
|
||||||
|
val migratedResponse = response.copy(
|
||||||
|
accounts = listOf(
|
||||||
|
mainAccount.copy(tokens = mainAccount.tokens!! - unassignedToken1),
|
||||||
|
account1.copy(tokens = listOf(unassignedToken1)),
|
||||||
|
account2,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEither(actual, migratedResponse.right())
|
||||||
|
|
||||||
|
coVerifySequence {
|
||||||
|
accountsResponseStoreFactory.create(userWalletId)
|
||||||
|
accountsResponseStore.data
|
||||||
|
accountsResponseStore.updateData(any())
|
||||||
|
userTokensSaver.pushWithRetryer(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
response = migratedResponse.toUserTokensResponse(),
|
||||||
|
onFailSend = any(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `migrate all skips when no unassigned tokens`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val derivationIndex1 = DerivationIndex(1).getOrNull()!!
|
||||||
|
val derivationIndex2 = DerivationIndex(2).getOrNull()!!
|
||||||
|
|
||||||
|
val mainAccount = createWalletAccountDTO(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
accountId = AccountId.forCryptoPortfolio(userWalletId, DerivationIndex.Main).value,
|
||||||
|
derivationIndex = DerivationIndex.Main.value,
|
||||||
|
tokens = listOf(
|
||||||
|
createBitcoin(accountIndex = 0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val account1 = createWalletAccountDTO(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
accountId = AccountId.forCryptoPortfolio(userWalletId, derivationIndex1).value,
|
||||||
|
derivationIndex = derivationIndex1.value,
|
||||||
|
tokens = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val account2 = createWalletAccountDTO(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
accountId = AccountId.forCryptoPortfolio(userWalletId, derivationIndex2).value,
|
||||||
|
derivationIndex = derivationIndex2.value,
|
||||||
|
tokens = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val response = GetWalletAccountsResponse(
|
||||||
|
wallet = GetWalletAccountsResponse.Wallet(
|
||||||
|
group = UserTokensResponse.GroupType.NONE,
|
||||||
|
sort = UserTokensResponse.SortType.MANUAL,
|
||||||
|
totalAccounts = 3,
|
||||||
|
totalArchivedAccounts = 0,
|
||||||
|
),
|
||||||
|
accounts = listOf(mainAccount, account1, account2),
|
||||||
|
unassignedTokens = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
accountsResponseStoreFlow.value = response
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actual = migration.migrate(userWalletId)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEither(actual, response.right())
|
||||||
|
|
||||||
|
coVerifySequence {
|
||||||
|
accountsResponseStoreFactory.create(userWalletId)
|
||||||
|
accountsResponseStore.data
|
||||||
|
}
|
||||||
|
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
userTokensSaver.pushWithRetryer(userWalletId = any(), response = any(), onFailSend = any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createBitcoin(accountIndex: Int): UserTokensResponse.Token {
|
private fun createBitcoin(accountIndex: Int): UserTokensResponse.Token {
|
||||||
return UserTokensResponse.Token(
|
return UserTokensResponse.Token(
|
||||||
id = "ne",
|
id = "ne",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue