From 06263e1193cc6f7d0d070133732d0eb089eabf87 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 7 Aug 2025 18:58:42 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../usecase/AddCryptoPortfolioUseCase.kt | 21 +-- .../domain/account/models/AccountListTest.kt | 163 +++++------------ .../usecase/AddCryptoPortfolioUseCaseTest.kt | 164 +++--------------- .../UpdateCryptoPortfolioUseCaseTest.kt | 129 +++++--------- .../tangem/domain/account/utils/AccountExt.kt | 43 ++++- .../tangem/domain/models/account/Account.kt | 52 +++--- .../tangem/domain/models/account/AccountId.kt | 26 ++- .../domain/models/account/DerivationIndex.kt | 59 +++++++ .../domain/models/account/AccountIdTest.kt | 44 +++++ .../domain/models/account/AccountTest.kt | 46 ++--- .../models/account/DerivationIndexTest.kt | 49 ++++++ 11 files changed, 360 insertions(+), 436 deletions(-) create mode 100644 domain/models/src/main/kotlin/com/tangem/domain/models/account/DerivationIndex.kt create mode 100644 domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountIdTest.kt create mode 100644 domain/models/src/test/kotlin/com/tangem/domain/models/account/DerivationIndexTest.kt diff --git a/domain/account/src/main/java/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCase.kt b/domain/account/src/main/java/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCase.kt index 4e68e70e3a..8c9d43c18f 100644 --- a/domain/account/src/main/java/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCase.kt +++ b/domain/account/src/main/java/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCase.kt @@ -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 = 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.getAccountList(userWalletId: UserWalletId): Option { @@ -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. * diff --git a/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt b/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt index ca1a2d397b..69e846a06a 100644 --- a/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt +++ b/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt @@ -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, ) - private fun createAccounts(count: Int): Set { - 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") } } \ No newline at end of file diff --git a/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCaseTest.kt b/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCaseTest.kt index 9dc84f5b4d..1e7d026c11 100644 --- a/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCaseTest.kt +++ b/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCaseTest.kt @@ -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() @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 { - 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()!! - } } \ No newline at end of file diff --git a/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/UpdateCryptoPortfolioUseCaseTest.kt b/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/UpdateCryptoPortfolioUseCaseTest.kt index 6588216bc8..073c68e1d6 100644 --- a/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/UpdateCryptoPortfolioUseCaseTest.kt +++ b/domain/account/src/test/kotlin/com/tangem/domain/account/usecase/UpdateCryptoPortfolioUseCaseTest.kt @@ -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() + @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") } } \ No newline at end of file diff --git a/domain/account/src/test/kotlin/com/tangem/domain/account/utils/AccountExt.kt b/domain/account/src/test/kotlin/com/tangem/domain/account/utils/AccountExt.kt index 1186faab17..6246176a50 100644 --- a/domain/account/src/test/kotlin/com/tangem/domain/account/utils/AccountExt.kt +++ b/domain/account/src/test/kotlin/com/tangem/domain/account/utils/AccountExt.kt @@ -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 { + 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, + ), + ) } \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt index 73789d0c65..db4a5b164c 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt @@ -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 { 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 { - 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(), diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt index 725f7143e9..a9cf874078 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt @@ -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, -) \ No newline at end of file +) { + + 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) + } + } +} \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/DerivationIndex.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/DerivationIndex.kt new file mode 100644 index 0000000000..5ffa4e2ecc --- /dev/null +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/DerivationIndex.kt @@ -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 = either { + ensure(value >= 0) { Error.NegativeDerivationIndex(derivationIndex = value) } + + DerivationIndex(value) + } + } +} \ No newline at end of file diff --git a/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountIdTest.kt b/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountIdTest.kt new file mode 100644 index 0000000000..e988ec9fc1 --- /dev/null +++ b/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountIdTest.kt @@ -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, + ) +} \ No newline at end of file diff --git a/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountTest.kt b/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountTest.kt index dac8fdac45..eabb287672 100644 --- a/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountTest.kt +++ b/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountTest.kt @@ -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 = 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, diff --git a/domain/models/src/test/kotlin/com/tangem/domain/models/account/DerivationIndexTest.kt b/domain/models/src/test/kotlin/com/tangem/domain/models/account/DerivationIndexTest.kt new file mode 100644 index 0000000000..f7daa8a9b2 --- /dev/null +++ b/domain/models/src/test/kotlin/com/tangem/domain/models/account/DerivationIndexTest.kt @@ -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, + ) +} \ No newline at end of file