Updated on 2026-08-14
This commit is contained in:
parent
0a8e78877d
commit
06263e1193
11 changed files with 360 additions and 436 deletions
|
|
@ -10,12 +10,8 @@ import com.tangem.domain.account.models.AccountList
|
|||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon
|
||||
import com.tangem.domain.models.account.*
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Use case for adding a new crypto portfolio account
|
||||
|
|
@ -42,7 +38,7 @@ class AddCryptoPortfolioUseCase(
|
|||
userWalletId: UserWalletId,
|
||||
accountName: AccountName,
|
||||
icon: CryptoPortfolioIcon,
|
||||
derivationIndex: Int,
|
||||
derivationIndex: DerivationIndex,
|
||||
): Either<Error, Account.CryptoPortfolio> = either {
|
||||
val newAccount = createAccount(userWalletId, accountName, icon, derivationIndex)
|
||||
|
||||
|
|
@ -62,11 +58,10 @@ class AddCryptoPortfolioUseCase(
|
|||
userWalletId: UserWalletId,
|
||||
accountName: AccountName,
|
||||
icon: CryptoPortfolioIcon,
|
||||
derivationIndex: Int,
|
||||
derivationIndex: DerivationIndex,
|
||||
): Account.CryptoPortfolio {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
return Account.CryptoPortfolio(
|
||||
accountId = AccountId(userWalletId = userWalletId, value = UUID.randomUUID().toString()),
|
||||
accountId = AccountId.forCryptoPortfolio(userWalletId = userWalletId, derivationIndex = derivationIndex),
|
||||
accountName = accountName,
|
||||
accountIcon = icon,
|
||||
derivationIndex = derivationIndex,
|
||||
|
|
@ -77,7 +72,6 @@ class AddCryptoPortfolioUseCase(
|
|||
groupType = TokensGroupType.NONE,
|
||||
),
|
||||
)
|
||||
.getOrElse { raise(Error.AccountCreation(it)) }
|
||||
}
|
||||
|
||||
private suspend fun Raise<Error>.getAccountList(userWalletId: UserWalletId): Option<AccountList> {
|
||||
|
|
@ -108,13 +102,6 @@ class AddCryptoPortfolioUseCase(
|
|||
*/
|
||||
sealed interface Error {
|
||||
|
||||
/**
|
||||
* Error indicating that the account creation failed
|
||||
*
|
||||
* @property cause the underlying cause of the failure
|
||||
*/
|
||||
data class AccountCreation(val cause: Account.CryptoPortfolio.Error) : Error
|
||||
|
||||
/**
|
||||
* Error indicating that the account list requirements were not met.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@ package com.tangem.domain.account.models
|
|||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.domain.account.utils.randomAccountId
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.account.utils.createAccount
|
||||
import com.tangem.domain.account.utils.createAccounts
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
|
|
@ -19,7 +18,6 @@ import org.junit.jupiter.api.Test
|
|||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.MethodSource
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -30,7 +28,7 @@ class AccountListTest {
|
|||
@Test
|
||||
fun mainAccount() {
|
||||
// Arrange
|
||||
val mainAccount = createAccount(isMain = true)
|
||||
val mainAccount = Account.CryptoPortfolio.createMainAccount(userWalletId = userWalletId)
|
||||
|
||||
val accountList = AccountList(
|
||||
userWallet = mockk(),
|
||||
|
|
@ -52,13 +50,13 @@ class AccountListTest {
|
|||
// Arrange
|
||||
val accountList = AccountList(
|
||||
userWallet = mockk(),
|
||||
accounts = createAccounts(count = 2),
|
||||
accounts = createAccounts(userWalletId = userWalletId, count = 2),
|
||||
totalAccounts = 2,
|
||||
).getOrNull()!!
|
||||
|
||||
val fullAccountList = AccountList(
|
||||
userWallet = mockk(),
|
||||
accounts = createAccounts(20),
|
||||
accounts = createAccounts(userWalletId = userWalletId, count = 20),
|
||||
totalAccounts = 20,
|
||||
).getOrNull()!!
|
||||
|
||||
|
|
@ -116,50 +114,41 @@ class AccountListTest {
|
|||
expected = AccountList.Error.EmptyAccountsList.left(),
|
||||
),
|
||||
CreateTestModel(
|
||||
accounts = setOf(createAccount(isMain = false)),
|
||||
accounts = setOf(
|
||||
createAccount(userWalletId = userWalletId, derivationIndex = 1),
|
||||
),
|
||||
expected = AccountList.Error.MainAccountNotFound.left(),
|
||||
),
|
||||
CreateTestModel(
|
||||
accounts = setOf(
|
||||
createAccount(isMain = true),
|
||||
createAccount(isMain = true),
|
||||
Account.CryptoPortfolio.createMainAccount(userWalletId),
|
||||
Account.CryptoPortfolio.createMainAccount(userWalletId).copy(
|
||||
accountIcon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
),
|
||||
),
|
||||
expected = AccountList.Error.ExceedsMaxMainAccountsCount.left(),
|
||||
),
|
||||
createAccount(isMain = true).let {
|
||||
CreateTestModel(
|
||||
accounts = setOf(it),
|
||||
expected = AccountList(
|
||||
userWallet = userWallet,
|
||||
accounts = setOf(it),
|
||||
totalAccounts = 1,
|
||||
),
|
||||
)
|
||||
},
|
||||
createAccounts(count = 20).let {
|
||||
createAccounts(userWalletId = userWalletId, count = 1).let {
|
||||
CreateTestModel(
|
||||
accounts = it,
|
||||
expected = AccountList(
|
||||
userWallet = userWallet,
|
||||
accounts = it,
|
||||
totalAccounts = 20,
|
||||
),
|
||||
expected = AccountList(userWallet = userWallet, accounts = it, totalAccounts = 1),
|
||||
)
|
||||
},
|
||||
createAccounts(userWalletId = userWalletId, count = 20).let {
|
||||
CreateTestModel(
|
||||
accounts = it,
|
||||
expected = AccountList(userWallet = userWallet, accounts = it, totalAccounts = 20),
|
||||
)
|
||||
},
|
||||
CreateTestModel(
|
||||
accounts = createAccounts(21),
|
||||
accounts = createAccounts(userWalletId = userWalletId, count = 21),
|
||||
expected = AccountList.Error.ExceedsMaxAccountsCount.left(),
|
||||
),
|
||||
CreateTestModel(
|
||||
accounts = setOf(
|
||||
createAccount(
|
||||
accountId = AccountId(value = "1", userWalletId = mockk()),
|
||||
isMain = true,
|
||||
),
|
||||
createAccount(
|
||||
accountId = AccountId(value = "1", userWalletId = mockk()),
|
||||
isMain = false,
|
||||
),
|
||||
createAccount(userWalletId = userWalletId, derivationIndex = 0),
|
||||
createAccount(userWalletId = userWalletId, derivationIndex = 1),
|
||||
createAccount(userWalletId = userWalletId, derivationIndex = 1),
|
||||
),
|
||||
expected = AccountList.Error.DuplicateAccountIds.left(),
|
||||
),
|
||||
|
|
@ -190,12 +179,8 @@ class AccountListTest {
|
|||
private fun provideTestModels() = listOf(
|
||||
// region Add new account
|
||||
run {
|
||||
val mainAccount = createAccount(
|
||||
accountId = AccountId(value = "1", userWalletId = mockk()),
|
||||
isMain = true,
|
||||
)
|
||||
|
||||
val newAccount = createAccount(isMain = false)
|
||||
val mainAccount = Account.CryptoPortfolio.createMainAccount(userWalletId)
|
||||
val newAccount = createAccount(userWalletId = userWalletId, derivationIndex = 1)
|
||||
|
||||
PlusTestModel(
|
||||
initial = AccountList(
|
||||
|
|
@ -214,11 +199,7 @@ class AccountListTest {
|
|||
// endregion
|
||||
// region Replace existing account
|
||||
run {
|
||||
val mainAccount = createAccount(
|
||||
accountId = AccountId(value = "1", userWalletId = mockk()),
|
||||
isMain = true,
|
||||
)
|
||||
|
||||
val mainAccount = Account.CryptoPortfolio.createMainAccount(userWalletId)
|
||||
val newAccount = mainAccount.copy(accountName = AccountName("New Name").getOrNull()!!)
|
||||
|
||||
PlusTestModel(
|
||||
|
|
@ -239,33 +220,12 @@ class AccountListTest {
|
|||
PlusTestModel(
|
||||
initial = AccountList(
|
||||
userWallet = userWallet,
|
||||
accounts = createAccounts(20),
|
||||
accounts = createAccounts(userWalletId = userWalletId, count = 20),
|
||||
totalAccounts = 20,
|
||||
).getOrNull()!!,
|
||||
toAdd = createAccount(isMain = false),
|
||||
toAdd = createAccount(userWalletId = userWalletId, derivationIndex = 21),
|
||||
expected = AccountList.Error.ExceedsMaxAccountsCount.left(),
|
||||
),
|
||||
PlusTestModel(
|
||||
initial = AccountList(
|
||||
userWallet = userWallet,
|
||||
accounts = setOf(createAccount(isMain = true)),
|
||||
totalAccounts = 1,
|
||||
).getOrNull()!!,
|
||||
toAdd = createAccount(isMain = true),
|
||||
expected = AccountList.Error.ExceedsMaxMainAccountsCount.left(),
|
||||
),
|
||||
PlusTestModel(
|
||||
initial = AccountList(
|
||||
userWallet = userWallet,
|
||||
accounts = setOf(
|
||||
createAccount(isMain = true),
|
||||
createAccount(isMain = false),
|
||||
),
|
||||
totalAccounts = 2,
|
||||
).getOrNull()!!,
|
||||
toAdd = createAccount(isMain = true),
|
||||
expected = AccountList.Error.ExceedsMaxMainAccountsCount.left(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -294,15 +254,8 @@ class AccountListTest {
|
|||
private fun provideTestModels() = listOf(
|
||||
// region Remove existing account
|
||||
run {
|
||||
val mainAccount = createAccount(
|
||||
accountId = AccountId(value = "1", userWalletId = mockk()),
|
||||
isMain = true,
|
||||
)
|
||||
|
||||
val secondaryAccount = createAccount(
|
||||
accountId = AccountId(value = "2", userWalletId = mockk()),
|
||||
isMain = false,
|
||||
)
|
||||
val mainAccount = Account.CryptoPortfolio.createMainAccount(userWalletId)
|
||||
val secondaryAccount = createAccount(userWalletId = userWalletId, derivationIndex = 2)
|
||||
|
||||
MinusTestModel(
|
||||
initial = AccountList(
|
||||
|
|
@ -321,14 +274,9 @@ class AccountListTest {
|
|||
// endregion
|
||||
// region Remove unexisting account
|
||||
run {
|
||||
val mainAccount = createAccount(
|
||||
accountId = AccountId(value = "1", userWalletId = mockk()),
|
||||
isMain = true,
|
||||
)
|
||||
val notInList = createAccount(
|
||||
accountId = AccountId(value = "3", userWalletId = mockk()),
|
||||
isMain = false,
|
||||
)
|
||||
val mainAccount = Account.CryptoPortfolio.createMainAccount(userWalletId)
|
||||
val notInList = createAccount(userWalletId = userWalletId, derivationIndex = 3)
|
||||
|
||||
MinusTestModel(
|
||||
initial = AccountList(
|
||||
userWallet = userWallet,
|
||||
|
|
@ -346,7 +294,7 @@ class AccountListTest {
|
|||
// endregion
|
||||
// region EmptyAccountsList
|
||||
run {
|
||||
val mainAccount = createAccount(isMain = true)
|
||||
val mainAccount = Account.CryptoPortfolio.createMainAccount(userWalletId)
|
||||
|
||||
MinusTestModel(
|
||||
initial = AccountList(
|
||||
|
|
@ -361,14 +309,9 @@ class AccountListTest {
|
|||
// endregion
|
||||
// region MainAccountNotFound
|
||||
run {
|
||||
val mainAccount = createAccount(
|
||||
accountId = AccountId(value = "1", userWalletId = mockk()),
|
||||
isMain = true,
|
||||
)
|
||||
val secondaryAccount = createAccount(
|
||||
accountId = AccountId(value = "2", userWalletId = mockk()),
|
||||
isMain = false,
|
||||
)
|
||||
val mainAccount = Account.CryptoPortfolio.createMainAccount(userWalletId)
|
||||
val secondaryAccount = createAccount(userWalletId = userWalletId, derivationIndex = 2)
|
||||
|
||||
MinusTestModel(
|
||||
initial = AccountList(
|
||||
userWallet = userWallet,
|
||||
|
|
@ -389,32 +332,8 @@ class AccountListTest {
|
|||
val expected: Either<AccountList.Error, AccountList>,
|
||||
)
|
||||
|
||||
private fun createAccounts(count: Int): Set<Account.CryptoPortfolio> {
|
||||
return buildSet {
|
||||
add(createAccount(isMain = true))
|
||||
repeat(count - 1) {
|
||||
add(createAccount(isMain = false))
|
||||
}
|
||||
}
|
||||
}
|
||||
private companion object {
|
||||
|
||||
private fun createAccount(
|
||||
accountId: AccountId = AccountId(value = randomAccountId(length = 5), userWalletId = mockk()),
|
||||
accountIcon: CryptoPortfolioIcon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
isMain: Boolean,
|
||||
): Account.CryptoPortfolio {
|
||||
return Account.CryptoPortfolio(
|
||||
accountId = accountId,
|
||||
name = "Test Account",
|
||||
accountIcon = accountIcon,
|
||||
derivationIndex = if (isMain) 0 else Random.nextInt(1, 21),
|
||||
isArchived = false,
|
||||
cryptoCurrencyList = Account.CryptoPortfolio.CryptoCurrencyList(
|
||||
currencies = emptySet(),
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
),
|
||||
)
|
||||
.getOrNull()!!
|
||||
val userWalletId = UserWalletId("011")
|
||||
}
|
||||
}
|
||||
|
|
@ -7,11 +7,9 @@ import arrow.core.toOption
|
|||
import com.google.common.truth.Truth
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.account.utils.createAccount
|
||||
import com.tangem.domain.account.utils.createAccounts
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -20,8 +18,6 @@ import kotlinx.coroutines.test.runTest
|
|||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import java.util.UUID
|
||||
import kotlin.random.Random
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class AddCryptoPortfolioUseCaseTest {
|
||||
|
|
@ -29,47 +25,21 @@ class AddCryptoPortfolioUseCaseTest {
|
|||
private val crudRepository: AccountsCRUDRepository = mockk(relaxUnitFun = true)
|
||||
private val useCase = AddCryptoPortfolioUseCase(crudRepository)
|
||||
|
||||
private val userWalletId = UserWalletId("011")
|
||||
private val userWallet = mockk<UserWallet>()
|
||||
|
||||
@BeforeEach
|
||||
fun resetMocks() {
|
||||
clearMocks(crudRepository, userWallet)
|
||||
|
||||
every { userWallet.walletId } returns userWalletId
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke should add new crypto portfolio account to existing list`() = runTest {
|
||||
// Arrange
|
||||
val existingAccount = createAccount(
|
||||
name = "Main Account",
|
||||
derivationIndex = 0,
|
||||
icon = CryptoPortfolioIcon.ofMainAccount(userWalletId = userWalletId),
|
||||
)
|
||||
|
||||
val accountList = AccountList(
|
||||
userWallet = userWallet,
|
||||
accounts = setOf(existingAccount),
|
||||
totalAccounts = 1,
|
||||
).getOrNull()!!
|
||||
|
||||
val fakeUUID = UUID.randomUUID()
|
||||
|
||||
mockkStatic(UUID::class)
|
||||
every { UUID.randomUUID() } returns fakeUUID
|
||||
|
||||
val newAccount = createAccount(
|
||||
accountId = AccountId(value = fakeUUID.toString(), userWalletId = userWalletId),
|
||||
name = "New Account",
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = 1,
|
||||
)
|
||||
|
||||
val newAccount = createNewAccount()
|
||||
val accountList = AccountList.createEmpty(userWallet)
|
||||
val updatedAccountList = (accountList + newAccount).getOrNull()!!
|
||||
|
||||
coEvery { crudRepository.getAccounts(userWalletId) } returns accountList.toOption()
|
||||
coEvery { crudRepository.saveAccounts(updatedAccountList) } just Runs
|
||||
|
||||
// Act
|
||||
val actual = useCase(
|
||||
|
|
@ -89,30 +59,16 @@ class AddCryptoPortfolioUseCaseTest {
|
|||
}
|
||||
|
||||
coVerify(inverse = true) { crudRepository.getUserWallet(userWalletId) }
|
||||
|
||||
unmockkStatic(UUID::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke should create new account list if none exists`() = runTest {
|
||||
// Arrange
|
||||
val fakeUUID = UUID.randomUUID()
|
||||
|
||||
mockkStatic(UUID::class)
|
||||
every { UUID.randomUUID() } returns fakeUUID
|
||||
|
||||
val newAccount = createAccount(
|
||||
accountId = AccountId(value = fakeUUID.toString(), userWalletId = userWalletId),
|
||||
name = "New Account",
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = 1,
|
||||
)
|
||||
|
||||
val newAccount = createNewAccount()
|
||||
val newAccountList = (AccountList.createEmpty(userWallet) + newAccount).getOrNull()!!
|
||||
|
||||
coEvery { crudRepository.getAccounts(userWalletId) } returns None
|
||||
coEvery { crudRepository.getUserWallet(userWalletId) } returns userWallet
|
||||
coEvery { crudRepository.saveAccounts(newAccountList) } just Runs
|
||||
|
||||
// Act
|
||||
val actual = useCase(
|
||||
|
|
@ -131,32 +87,6 @@ class AddCryptoPortfolioUseCaseTest {
|
|||
crudRepository.getUserWallet(userWalletId)
|
||||
crudRepository.saveAccounts(newAccountList)
|
||||
}
|
||||
|
||||
unmockkStatic(UUID::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke should return error if account creation fails`() = runTest {
|
||||
// Act
|
||||
val actual = useCase(
|
||||
userWalletId = userWalletId,
|
||||
accountName = AccountName.Main,
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = -1,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = AddCryptoPortfolioUseCase.Error.AccountCreation(
|
||||
cause = Account.CryptoPortfolio.Error.NegativeDerivationIndex,
|
||||
).left()
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerify(inverse = true) {
|
||||
crudRepository.getAccounts(any())
|
||||
crudRepository.getUserWallet(any())
|
||||
crudRepository.saveAccounts(any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -164,15 +94,11 @@ class AddCryptoPortfolioUseCaseTest {
|
|||
// Arrange
|
||||
val accountList = AccountList(
|
||||
userWallet = userWallet,
|
||||
accounts = createAccounts(count = 20),
|
||||
accounts = createAccounts(userWalletId = userWalletId, count = 20),
|
||||
totalAccounts = 20,
|
||||
).getOrNull()!!
|
||||
|
||||
val newAccount = createAccount(
|
||||
name = "New Account",
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = 1,
|
||||
)
|
||||
val newAccount = createNewAccount(derivationIndex = 21)
|
||||
|
||||
coEvery { crudRepository.getAccounts(userWalletId) } returns accountList.toOption()
|
||||
|
||||
|
|
@ -202,12 +128,7 @@ class AddCryptoPortfolioUseCaseTest {
|
|||
@Test
|
||||
fun `invoke should return error if getAccounts throws exception`() = runTest {
|
||||
// Arrange
|
||||
val newAccount = createAccount(
|
||||
name = "New Account",
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = 1,
|
||||
)
|
||||
|
||||
val newAccount = createNewAccount()
|
||||
val exception = IllegalStateException("Test error")
|
||||
|
||||
coEvery { crudRepository.getAccounts(userWalletId) } throws exception
|
||||
|
|
@ -235,30 +156,8 @@ class AddCryptoPortfolioUseCaseTest {
|
|||
@Test
|
||||
fun `invoke should return error if saveAccounts throws exception`() = runTest {
|
||||
// Arrange
|
||||
val existingAccount = createAccount(
|
||||
name = "Main Account",
|
||||
derivationIndex = 0,
|
||||
icon = CryptoPortfolioIcon.ofMainAccount(userWalletId = userWalletId),
|
||||
)
|
||||
|
||||
val accountList = AccountList(
|
||||
userWallet = userWallet,
|
||||
accounts = setOf(existingAccount),
|
||||
totalAccounts = 1,
|
||||
).getOrNull()!!
|
||||
|
||||
val fakeUUID = UUID.randomUUID()
|
||||
|
||||
mockkStatic(UUID::class)
|
||||
every { UUID.randomUUID() } returns fakeUUID
|
||||
|
||||
val newAccount = createAccount(
|
||||
accountId = AccountId(value = fakeUUID.toString(), userWalletId = userWalletId),
|
||||
name = "New Account",
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = 1,
|
||||
)
|
||||
|
||||
val newAccount = createNewAccount()
|
||||
val accountList = AccountList.createEmpty(userWallet)
|
||||
val updatedAccountList = (accountList + newAccount).getOrNull()!!
|
||||
|
||||
val exception = IllegalStateException("Test error")
|
||||
|
|
@ -267,7 +166,7 @@ class AddCryptoPortfolioUseCaseTest {
|
|||
coEvery { crudRepository.saveAccounts(updatedAccountList) } throws exception
|
||||
|
||||
// Act
|
||||
useCase(
|
||||
val actual = useCase(
|
||||
userWalletId = userWalletId,
|
||||
accountName = newAccount.name,
|
||||
icon = newAccount.icon,
|
||||
|
|
@ -275,8 +174,8 @@ class AddCryptoPortfolioUseCaseTest {
|
|||
)
|
||||
|
||||
// Assert
|
||||
// val expected = AddCryptoPortfolioUseCase.Error.DataOperationFailed(cause = exception).left()
|
||||
// Truth.assertThat(actual).isEqualTo(expected)
|
||||
val expected = AddCryptoPortfolioUseCase.Error.DataOperationFailed(cause = exception).left()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
coVerifyOrder {
|
||||
crudRepository.getAccounts(userWalletId)
|
||||
|
|
@ -286,32 +185,17 @@ class AddCryptoPortfolioUseCaseTest {
|
|||
coVerify(inverse = true) { crudRepository.getUserWallet(userWalletId) }
|
||||
}
|
||||
|
||||
private fun createAccounts(count: Int): Set<Account.CryptoPortfolio> {
|
||||
return buildSet {
|
||||
add(createAccount(derivationIndex = 0))
|
||||
repeat(count - 1) {
|
||||
add(createAccount())
|
||||
}
|
||||
private companion object {
|
||||
|
||||
val userWalletId = UserWalletId("011")
|
||||
|
||||
fun createNewAccount(derivationIndex: Int = 1): Account.CryptoPortfolio {
|
||||
return createAccount(
|
||||
userWalletId = userWalletId,
|
||||
name = "New Account",
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = derivationIndex,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAccount(
|
||||
accountId: AccountId? = null,
|
||||
name: String = "Test Account",
|
||||
icon: CryptoPortfolioIcon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex: Int = Random.nextInt(1, 21),
|
||||
): Account.CryptoPortfolio {
|
||||
return Account.CryptoPortfolio(
|
||||
accountId = accountId ?: AccountId(value = UUID.randomUUID().toString(), userWalletId = userWalletId),
|
||||
accountName = AccountName(name).getOrNull()!!,
|
||||
accountIcon = icon,
|
||||
derivationIndex = derivationIndex,
|
||||
isArchived = false,
|
||||
cryptoCurrencyList = Account.CryptoPortfolio.CryptoCurrencyList(
|
||||
currencies = emptySet(),
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
),
|
||||
).getOrNull()!!
|
||||
}
|
||||
}
|
||||
|
|
@ -8,19 +8,17 @@ import com.google.common.truth.Truth
|
|||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.account.usecase.UpdateCryptoPortfolioUseCase.Error
|
||||
import com.tangem.domain.account.utils.randomAccountId
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
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.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -31,25 +29,24 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
private val crudRepository: AccountsCRUDRepository = mockk(relaxUnitFun = true)
|
||||
private val useCase = UpdateCryptoPortfolioUseCase(crudRepository = crudRepository)
|
||||
|
||||
private val userWallet = mockk<UserWallet>()
|
||||
|
||||
@BeforeEach
|
||||
fun resetMocks() {
|
||||
clearMocks(crudRepository)
|
||||
clearMocks(crudRepository, userWallet)
|
||||
|
||||
every { userWallet.walletId } returns userWalletId
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke should update crypto portfolio account with new name`() = runTest {
|
||||
// Arrange
|
||||
val accountId = AccountId(value = "test-account-id", userWalletId = mockk())
|
||||
val userWalletId = accountId.userWalletId
|
||||
val account = createAccount(accountId = accountId, isMain = true)
|
||||
val accountList = AccountList(
|
||||
userWallet = mockk(),
|
||||
accounts = setOf(account),
|
||||
totalAccounts = 1,
|
||||
)
|
||||
.getOrNull()!!
|
||||
val accountList = AccountList.createEmpty(userWallet = userWallet)
|
||||
val accountId = accountList.mainAccount.accountId
|
||||
|
||||
val newAccountName = AccountName("New name").getOrNull()!!
|
||||
val updatedAccount = accountList.mainAccount.copy(accountName = newAccountName)
|
||||
val updatedAccountList = (accountList + updatedAccount).getOrNull()!!
|
||||
|
||||
coEvery { crudRepository.getAccounts(userWalletId = userWalletId) } returns accountList.toOption()
|
||||
|
||||
|
|
@ -57,11 +54,9 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
val actual = useCase(accountId = accountId, accountName = newAccountName)
|
||||
|
||||
// Assert
|
||||
val updatedAccount = account.copy(accountName = newAccountName)
|
||||
val expected = updatedAccount.right()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
val updatedAccountList = (accountList + updatedAccount).getOrNull()!!
|
||||
coVerifyOrder {
|
||||
crudRepository.getAccounts(userWalletId = userWalletId)
|
||||
crudRepository.saveAccounts(accountList = updatedAccountList)
|
||||
|
|
@ -71,20 +66,15 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
@Test
|
||||
fun `invoke should update crypto portfolio account with new icon`() = runTest {
|
||||
// Arrange
|
||||
val accountId = AccountId(value = "test-account-id", userWalletId = mockk())
|
||||
val userWalletId = accountId.userWalletId
|
||||
val account = createAccount(accountId = accountId, isMain = true)
|
||||
val accountList = AccountList(
|
||||
userWallet = mockk(),
|
||||
accounts = setOf(account),
|
||||
totalAccounts = 1,
|
||||
)
|
||||
.getOrNull()!!
|
||||
val accountList = AccountList.createEmpty(userWallet = userWallet)
|
||||
val accountId = accountList.mainAccount.accountId
|
||||
|
||||
val newAccountIcon = CryptoPortfolioIcon.ofCustomAccount(
|
||||
value = CryptoPortfolioIcon.Icon.Star,
|
||||
color = CryptoPortfolioIcon.Color.CaribbeanBlue,
|
||||
)
|
||||
val updatedAccount = accountList.mainAccount.copy(accountIcon = newAccountIcon)
|
||||
val updatedAccountList = (accountList + updatedAccount).getOrNull()!!
|
||||
|
||||
coEvery { crudRepository.getAccounts(userWalletId = userWalletId) } returns accountList.toOption()
|
||||
|
||||
|
|
@ -92,11 +82,9 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
val actual = useCase(accountId = accountId, icon = newAccountIcon)
|
||||
|
||||
// Assert
|
||||
val updatedAccount = account.copy(accountIcon = newAccountIcon)
|
||||
val expected = updatedAccount.right()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
val updatedAccountList = (accountList + updatedAccount).getOrNull()!!
|
||||
coVerifyOrder {
|
||||
crudRepository.getAccounts(userWalletId = userWalletId)
|
||||
crudRepository.saveAccounts(accountList = updatedAccountList)
|
||||
|
|
@ -106,21 +94,16 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
@Test
|
||||
fun `invoke should update crypto portfolio account with new name and icon`() = runTest {
|
||||
// Arrange
|
||||
val accountId = AccountId(value = "test-account-id", userWalletId = mockk())
|
||||
val userWalletId = accountId.userWalletId
|
||||
val account = createAccount(accountId = accountId, isMain = true)
|
||||
val accountList = AccountList(
|
||||
userWallet = mockk(),
|
||||
accounts = setOf(account),
|
||||
totalAccounts = 1,
|
||||
)
|
||||
.getOrNull()!!
|
||||
val accountList = AccountList.createEmpty(userWallet = userWallet)
|
||||
val accountId = accountList.mainAccount.accountId
|
||||
|
||||
val newAccountName = AccountName("New name").getOrNull()!!
|
||||
val newAccountIcon = CryptoPortfolioIcon.ofCustomAccount(
|
||||
value = CryptoPortfolioIcon.Icon.Star,
|
||||
color = CryptoPortfolioIcon.Color.CaribbeanBlue,
|
||||
)
|
||||
val updatedAccount = accountList.mainAccount.copy(accountName = newAccountName, accountIcon = newAccountIcon)
|
||||
val updatedAccountList = (accountList + updatedAccount).getOrNull()!!
|
||||
|
||||
coEvery { crudRepository.getAccounts(userWalletId = userWalletId) } returns accountList.toOption()
|
||||
|
||||
|
|
@ -128,11 +111,9 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
val actual = useCase(accountId = accountId, accountName = newAccountName, icon = newAccountIcon)
|
||||
|
||||
// Assert
|
||||
val updatedAccount = account.copy(accountName = newAccountName, accountIcon = newAccountIcon)
|
||||
val expected = updatedAccount.right()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
|
||||
val updatedAccountList = (accountList + updatedAccount).getOrNull()!!
|
||||
coVerifyOrder {
|
||||
crudRepository.getAccounts(userWalletId = userWalletId)
|
||||
crudRepository.saveAccounts(accountList = updatedAccountList)
|
||||
|
|
@ -142,15 +123,8 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
@Test
|
||||
fun `invoke if name and icon are null`() = runTest {
|
||||
// Arrange
|
||||
val accountId = AccountId(value = "test-account-id", userWalletId = mockk())
|
||||
val userWalletId = accountId.userWalletId
|
||||
val account = createAccount(accountId = accountId, isMain = true)
|
||||
val accountList = AccountList(
|
||||
userWallet = mockk(),
|
||||
accounts = setOf(account),
|
||||
totalAccounts = 1,
|
||||
)
|
||||
.getOrNull()!!
|
||||
val accountList = AccountList.createEmpty(userWallet = userWallet)
|
||||
val accountId = accountList.mainAccount.accountId
|
||||
|
||||
coEvery { crudRepository.getAccounts(userWalletId = userWalletId) } returns accountList.toOption()
|
||||
|
||||
|
|
@ -170,10 +144,11 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
@Test
|
||||
fun `invoke if getAccounts throws exception`() = runTest {
|
||||
// Arrange
|
||||
val accountId = AccountId(value = "test-account-id", userWalletId = mockk())
|
||||
val userWalletId = accountId.userWalletId
|
||||
val accountList = AccountList.createEmpty(userWallet = userWallet)
|
||||
val accountId = accountList.mainAccount.accountId
|
||||
|
||||
val newAccountName = AccountName("New name").getOrNull()!!
|
||||
|
||||
val exception = IllegalStateException("Test exception")
|
||||
|
||||
coEvery { crudRepository.getAccounts(userWalletId = userWalletId) } throws exception
|
||||
|
|
@ -192,8 +167,10 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
@Test
|
||||
fun `invoke if getAccounts returns None`() = runTest {
|
||||
// Arrange
|
||||
val accountId = AccountId(value = "test-account-id", userWalletId = mockk())
|
||||
val userWalletId = accountId.userWalletId
|
||||
val accountId = AccountId.forCryptoPortfolio(
|
||||
userWalletId = userWalletId,
|
||||
derivationIndex = DerivationIndex.Main,
|
||||
)
|
||||
val accountList = None
|
||||
|
||||
val newAccountName = AccountName("New name").getOrNull()!!
|
||||
|
|
@ -214,18 +191,11 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
@Test
|
||||
fun `invoke if getAccounts does not contain accountId`() = runTest {
|
||||
// Arrange
|
||||
val accountId = AccountId(value = "test-account-id", userWalletId = mockk())
|
||||
val userWalletId = accountId.userWalletId
|
||||
val account = createAccount(
|
||||
accountId = AccountId(value = "another-account-id", userWalletId = mockk()),
|
||||
isMain = true,
|
||||
val accountList = AccountList.createEmpty(userWallet = userWallet)
|
||||
val accountId = AccountId.forCryptoPortfolio(
|
||||
userWalletId = userWalletId,
|
||||
derivationIndex = DerivationIndex(1).getOrNull()!!,
|
||||
)
|
||||
val accountList = AccountList(
|
||||
userWallet = mockk(),
|
||||
accounts = setOf(account),
|
||||
totalAccounts = 1,
|
||||
)
|
||||
.getOrNull()!!
|
||||
|
||||
val newAccountName = AccountName("New name").getOrNull()!!
|
||||
|
||||
|
|
@ -245,17 +215,11 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
@Test
|
||||
fun `invoke if saveAccounts throws exception`() = runTest {
|
||||
// Arrange
|
||||
val accountId = AccountId(value = "test-account-id", userWalletId = mockk())
|
||||
val userWalletId = accountId.userWalletId
|
||||
val account = createAccount(accountId = accountId, isMain = true)
|
||||
val accountList = AccountList(
|
||||
userWallet = mockk(),
|
||||
accounts = setOf(account),
|
||||
totalAccounts = 1,
|
||||
).getOrNull()!!
|
||||
val accountList = AccountList.createEmpty(userWallet = userWallet)
|
||||
val accountId = accountList.mainAccount.accountId
|
||||
|
||||
val newAccountName = AccountName("New name").getOrNull()!!
|
||||
val updatedAccount = account.copy(accountName = newAccountName)
|
||||
val updatedAccount = accountList.mainAccount.copy(accountName = newAccountName)
|
||||
val updatedAccountList = (accountList + updatedAccount).getOrNull()!!
|
||||
|
||||
val exception = IllegalStateException("Save failed")
|
||||
|
|
@ -276,23 +240,8 @@ class UpdateCryptoPortfolioUseCaseTest {
|
|||
}
|
||||
}
|
||||
|
||||
private fun createAccount(
|
||||
accountId: AccountId = AccountId(value = randomAccountId(length = 5), userWalletId = mockk()),
|
||||
accountIcon: CryptoPortfolioIcon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
isMain: Boolean,
|
||||
): Account.CryptoPortfolio {
|
||||
return Account.CryptoPortfolio(
|
||||
accountId = accountId,
|
||||
name = "Test Account",
|
||||
accountIcon = accountIcon,
|
||||
derivationIndex = if (isMain) 0 else Random.nextInt(1, 21),
|
||||
isArchived = false,
|
||||
cryptoCurrencyList = Account.CryptoPortfolio.CryptoCurrencyList(
|
||||
currencies = emptySet(),
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
),
|
||||
)
|
||||
.getOrNull()!!
|
||||
private companion object {
|
||||
|
||||
val userWalletId = UserWalletId("011")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,41 @@
|
|||
package com.tangem.domain.account.utils
|
||||
|
||||
fun randomAccountId(length: Int): String {
|
||||
val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
return (1..length)
|
||||
.map { chars.random() }
|
||||
.joinToString("")
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.account.*
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlin.random.Random
|
||||
|
||||
fun createAccounts(userWalletId: UserWalletId, count: Int): Set<Account.CryptoPortfolio> {
|
||||
return buildSet {
|
||||
add(Account.CryptoPortfolio.createMainAccount(userWalletId))
|
||||
|
||||
repeat(count - 1) {
|
||||
val account = createAccount(userWalletId = userWalletId, derivationIndex = it + 1)
|
||||
|
||||
add(account)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createAccount(
|
||||
userWalletId: UserWalletId,
|
||||
name: String = "Test Account",
|
||||
icon: CryptoPortfolioIcon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex: Int = Random.nextInt(1, 21),
|
||||
): Account.CryptoPortfolio {
|
||||
val derivationIndex = DerivationIndex(derivationIndex).getOrNull()!!
|
||||
|
||||
return Account.CryptoPortfolio(
|
||||
accountId = AccountId.forCryptoPortfolio(userWalletId = userWalletId, derivationIndex = derivationIndex),
|
||||
accountName = AccountName(name).getOrNull()!!,
|
||||
accountIcon = icon,
|
||||
derivationIndex = derivationIndex,
|
||||
isArchived = false,
|
||||
cryptoCurrencyList = Account.CryptoPortfolio.CryptoCurrencyList(
|
||||
currencies = emptySet(),
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -2,10 +2,10 @@ package com.tangem.domain.models.account
|
|||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.raise.ensure
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.account.Account.CryptoPortfolio.Error.AccountNameError
|
||||
import com.tangem.domain.models.account.Account.CryptoPortfolio.Error.DerivationIndexError
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
|
@ -43,14 +43,14 @@ sealed interface Account {
|
|||
override val accountId: AccountId,
|
||||
override val name: AccountName,
|
||||
val icon: CryptoPortfolioIcon,
|
||||
val derivationIndex: Int,
|
||||
val derivationIndex: DerivationIndex,
|
||||
val isArchived: Boolean,
|
||||
val cryptoCurrencyList: CryptoCurrencyList,
|
||||
) : Account {
|
||||
|
||||
/** Indicates if the account is the main account */
|
||||
val isMainAccount: Boolean
|
||||
get() = derivationIndex == 0
|
||||
get() = derivationIndex.isMain
|
||||
|
||||
/** Number of tokens in the account */
|
||||
val tokensCount: Int
|
||||
|
|
@ -97,15 +97,11 @@ sealed interface Account {
|
|||
|
||||
/** Error indicating that the account name is blank */
|
||||
@Serializable
|
||||
data class AccountNameError(val cause: AccountName.Error) : Error {
|
||||
override fun toString(): String = cause.toString()
|
||||
}
|
||||
data class AccountNameError(val cause: AccountName.Error) : Error
|
||||
|
||||
/** Error indicating that the derivation index is negative */
|
||||
@Serializable
|
||||
data object NegativeDerivationIndex : Error {
|
||||
override fun toString(): String = "${this::class.simpleName}: Derivation index must be non-negative"
|
||||
}
|
||||
data class DerivationIndexError(val cause: DerivationIndex.Error) : Error
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
@ -130,7 +126,8 @@ sealed interface Account {
|
|||
cryptoCurrencyList: CryptoCurrencyList,
|
||||
): Either<Error, CryptoPortfolio> {
|
||||
return either {
|
||||
val accountName = AccountName(name).mapLeft(::AccountNameError).bind()
|
||||
val accountName = AccountName(value = name).mapLeft(::AccountNameError).bind()
|
||||
val derivationIndex = DerivationIndex(derivationIndex).mapLeft(::DerivationIndexError).bind()
|
||||
|
||||
invoke(
|
||||
accountId = accountId,
|
||||
|
|
@ -140,7 +137,6 @@ sealed interface Account {
|
|||
isArchived = isArchived,
|
||||
cryptoCurrencyList = cryptoCurrencyList,
|
||||
)
|
||||
.bind()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -159,22 +155,18 @@ sealed interface Account {
|
|||
accountId: AccountId,
|
||||
accountName: AccountName,
|
||||
accountIcon: CryptoPortfolioIcon,
|
||||
derivationIndex: Int,
|
||||
derivationIndex: DerivationIndex,
|
||||
isArchived: Boolean,
|
||||
cryptoCurrencyList: CryptoCurrencyList,
|
||||
): Either<Error, CryptoPortfolio> {
|
||||
return either {
|
||||
ensure(derivationIndex >= 0) { Error.NegativeDerivationIndex }
|
||||
|
||||
CryptoPortfolio(
|
||||
accountId = accountId,
|
||||
name = accountName,
|
||||
icon = accountIcon,
|
||||
derivationIndex = derivationIndex,
|
||||
isArchived = isArchived,
|
||||
cryptoCurrencyList = cryptoCurrencyList,
|
||||
)
|
||||
}
|
||||
): CryptoPortfolio {
|
||||
return CryptoPortfolio(
|
||||
accountId = accountId,
|
||||
name = accountName,
|
||||
icon = accountIcon,
|
||||
derivationIndex = derivationIndex,
|
||||
isArchived = isArchived,
|
||||
cryptoCurrencyList = cryptoCurrencyList,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -183,12 +175,16 @@ sealed interface Account {
|
|||
* @param userWalletId the ID of the user wallet
|
||||
*/
|
||||
fun createMainAccount(userWalletId: UserWalletId): CryptoPortfolio {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
val derivationIndex = DerivationIndex.Main
|
||||
|
||||
return CryptoPortfolio(
|
||||
accountId = AccountId(userWalletId = userWalletId, value = "main_account"),
|
||||
accountId = AccountId.forCryptoPortfolio(
|
||||
userWalletId = userWalletId,
|
||||
derivationIndex = derivationIndex,
|
||||
),
|
||||
name = AccountName.Main,
|
||||
icon = CryptoPortfolioIcon.ofMainAccount(userWalletId),
|
||||
derivationIndex = 0,
|
||||
derivationIndex = derivationIndex,
|
||||
isArchived = false,
|
||||
cryptoCurrencyList = CryptoCurrencyList(
|
||||
currencies = emptySet(),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
package com.tangem.domain.models.account
|
||||
|
||||
import com.tangem.common.extensions.toByteArray
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.utils.extensions.toHexString
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.security.MessageDigest
|
||||
|
||||
/**
|
||||
* Represents a unique identifier for an account
|
||||
|
|
@ -10,7 +13,26 @@ import kotlinx.serialization.Serializable
|
|||
* @property userWalletId the identifier of the user wallet associated with the account
|
||||
*/
|
||||
@Serializable
|
||||
data class AccountId(
|
||||
data class AccountId private constructor(
|
||||
val value: String,
|
||||
val userWalletId: UserWalletId,
|
||||
)
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
private val sha256Digest: MessageDigest by lazy { MessageDigest.getInstance("SHA-256") }
|
||||
|
||||
/**
|
||||
* Creates a unique account identifier for a crypto portfolio
|
||||
*
|
||||
* @param userWalletId the identifier of the user wallet
|
||||
* @param derivationIndex the derivation index used to generate the identifier
|
||||
*/
|
||||
fun forCryptoPortfolio(userWalletId: UserWalletId, derivationIndex: DerivationIndex): AccountId {
|
||||
val input = userWalletId.value + derivationIndex.value.toByteArray()
|
||||
val value = sha256Digest.digest(input).toHexString()
|
||||
|
||||
return AccountId(value = value, userWalletId = userWalletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package com.tangem.domain.models.account
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.raise.ensure
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Represents a derivation index for accounts, ensuring validity and providing utility methods
|
||||
*
|
||||
* @property value the integer value of the derivation index
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Serializable
|
||||
data class DerivationIndex private constructor(
|
||||
val value: Int,
|
||||
) {
|
||||
|
||||
/** Checks if the derivation index corresponds to the main account */
|
||||
val isMain: Boolean
|
||||
get() = value == MAIN_ACCOUNT_DERIVATION_INDEX
|
||||
|
||||
/**
|
||||
* Represents possible errors that can occur when creating a [DerivationIndex]
|
||||
*/
|
||||
@Serializable
|
||||
sealed interface Error {
|
||||
|
||||
/** Error indicating that the provided derivation index [derivationIndex] is invalid */
|
||||
@Serializable
|
||||
data class NegativeDerivationIndex(val derivationIndex: Int) : Error {
|
||||
override fun toString(): String {
|
||||
return "${this::class.simpleName}: Derivation index cannot be negative: $derivationIndex"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val MAIN_ACCOUNT_DERIVATION_INDEX = 0
|
||||
|
||||
/** Predefined instance of [DerivationIndex] for the main account */
|
||||
val Main: DerivationIndex = DerivationIndex(value = MAIN_ACCOUNT_DERIVATION_INDEX)
|
||||
|
||||
/**
|
||||
* Factory method to create a [DerivationIndex] instance
|
||||
*
|
||||
* @param value the integer value of the derivation index
|
||||
*
|
||||
* @return Either an error if the value is invalid, or a valid [DerivationIndex] instance
|
||||
*/
|
||||
operator fun invoke(value: Int): Either<Error, DerivationIndex> = either {
|
||||
ensure(value >= 0) { Error.NegativeDerivationIndex(derivationIndex = value) }
|
||||
|
||||
DerivationIndex(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.domain.models.account
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.MethodSource
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class AccountIdTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestModels")
|
||||
fun forCryptoPortfolio(model: ForCryptoPortfolioModel) {
|
||||
// Arrange
|
||||
val userWalletId = UserWalletId("27163F47405CE73110837F24DF82607FF11C7AF9D78C93F409E4FEAFF3400C8F")
|
||||
|
||||
// Act
|
||||
val actual = AccountId.forCryptoPortfolio(userWalletId = userWalletId, derivationIndex = model.derivationIndex)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual.value).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
ForCryptoPortfolioModel(
|
||||
derivationIndex = DerivationIndex.Main,
|
||||
expected = "4E39B13EA11E3B35339664A10BEF48F4AF752A1CC2200F79D23CB0FB3396C63F",
|
||||
),
|
||||
ForCryptoPortfolioModel(
|
||||
derivationIndex = DerivationIndex(1).getOrNull()!!,
|
||||
expected = "7F22E71F8106783F0F2DAFCDE525E2F2A2281E864DDBE2FE668FA09329D563A2",
|
||||
),
|
||||
ForCryptoPortfolioModel(
|
||||
derivationIndex = DerivationIndex(42).getOrNull()!!,
|
||||
expected = "555C1E17A302659446C97393453B7C2B3246AF4DA082C56C28FB6EDD1A6606A4",
|
||||
),
|
||||
)
|
||||
|
||||
data class ForCryptoPortfolioModel(
|
||||
val derivationIndex: DerivationIndex,
|
||||
val expected: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -115,38 +115,18 @@ class AccountTest {
|
|||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke returns NegativeDerivationIndex`() {
|
||||
// Arrange
|
||||
val derivationIndex = -1
|
||||
|
||||
// Act
|
||||
val actual = CryptoPortfolio(
|
||||
accountId = mockk(),
|
||||
name = "Test Account",
|
||||
accountIcon = mockk(),
|
||||
derivationIndex = derivationIndex,
|
||||
isArchived = false,
|
||||
cryptoCurrencyList = mockk(),
|
||||
)
|
||||
.leftOrNull()!!
|
||||
|
||||
// Assert
|
||||
val expected = CryptoPortfolio.Error.NegativeDerivationIndex
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke returns CryptoPortfolio`() {
|
||||
// Act
|
||||
val derivationIndex = DerivationIndex.Main
|
||||
val actual = CryptoPortfolio(
|
||||
accountId = AccountId(
|
||||
value = "value",
|
||||
accountId = AccountId.forCryptoPortfolio(
|
||||
userWalletId = UserWalletId("011"),
|
||||
derivationIndex = derivationIndex,
|
||||
),
|
||||
name = "Test Account",
|
||||
accountIcon = CryptoPortfolioIcon.ofMainAccount(userWalletId = UserWalletId("011")),
|
||||
derivationIndex = 0,
|
||||
derivationIndex = derivationIndex.value,
|
||||
isArchived = false,
|
||||
cryptoCurrencyList = CryptoCurrencyList(
|
||||
currencies = emptySet(),
|
||||
|
|
@ -165,24 +145,27 @@ class AccountTest {
|
|||
fun createMainAccount() {
|
||||
// Arrange
|
||||
val userWalletId = UserWalletId("011")
|
||||
val derivationIndex = DerivationIndex.Main
|
||||
|
||||
// Act
|
||||
val actual = CryptoPortfolio.createMainAccount(userWalletId = userWalletId)
|
||||
|
||||
// Assert
|
||||
// TODO: [REDACTED_JIRA]
|
||||
val expected = CryptoPortfolio(
|
||||
accountId = AccountId(userWalletId = userWalletId, value = "main_account"),
|
||||
accountId = AccountId.forCryptoPortfolio(
|
||||
userWalletId = userWalletId,
|
||||
derivationIndex = derivationIndex,
|
||||
),
|
||||
accountName = AccountName.Main,
|
||||
accountIcon = CryptoPortfolioIcon.ofMainAccount(userWalletId),
|
||||
derivationIndex = 0,
|
||||
derivationIndex = derivationIndex,
|
||||
isArchived = false,
|
||||
cryptoCurrencyList = CryptoCurrencyList(
|
||||
currencies = emptySet(),
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
),
|
||||
).getOrNull()
|
||||
)
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
|
@ -194,11 +177,10 @@ class AccountTest {
|
|||
derivationIndex: Int = 0,
|
||||
currencies: Set<CryptoCurrency> = emptySet(),
|
||||
): CryptoPortfolio {
|
||||
val accountIndex = DerivationIndex(value = derivationIndex).getOrNull()!!
|
||||
|
||||
return CryptoPortfolio.invoke(
|
||||
accountId = AccountId(
|
||||
value = "value",
|
||||
userWalletId = userWalletId,
|
||||
),
|
||||
accountId = AccountId.forCryptoPortfolio(userWalletId = userWalletId, derivationIndex = accountIndex),
|
||||
name = name,
|
||||
accountIcon = CryptoPortfolioIcon.ofMainAccount(userWalletId),
|
||||
derivationIndex = derivationIndex,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package com.tangem.domain.models.account
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.google.common.truth.Truth
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.MethodSource
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class DerivationIndexTest {
|
||||
|
||||
@Test
|
||||
fun `isMain returns true only for main derivation index`() {
|
||||
// Arrange
|
||||
val main = DerivationIndex.Main
|
||||
val notMain = DerivationIndex(1).getOrNull()!!
|
||||
|
||||
// Act & Assert
|
||||
Truth.assertThat(main.isMain).isTrue()
|
||||
Truth.assertThat(notMain.isMain).isFalse()
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestModels")
|
||||
fun invoke(model: InvokeTestModel) {
|
||||
// Act
|
||||
val actual = DerivationIndex(model.index)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(model.expected)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
InvokeTestModel(index = 0, expected = DerivationIndex.Main.right()),
|
||||
InvokeTestModel(index = 5, expected = DerivationIndex(5).getOrNull()!!.right()),
|
||||
InvokeTestModel(index = -1, expected = DerivationIndex.Error.NegativeDerivationIndex(-1).left()),
|
||||
)
|
||||
|
||||
data class InvokeTestModel(
|
||||
val index: Int,
|
||||
val expected: Either<DerivationIndex.Error, DerivationIndex>,
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue