Updated on 2026-08-14
This commit is contained in:
parent
926b74d491
commit
a9333c10a7
2 changed files with 263 additions and 92 deletions
|
|
@ -16,6 +16,8 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
|||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.lib.crypto.derivation.AccountNodeRecognizer
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
|
||||
/**
|
||||
* Use case to retrieve the status of a specific cryptocurrency associated with an account.
|
||||
|
|
@ -35,11 +37,8 @@ class GetAccountCurrencyStatusUseCase(
|
|||
* @param currency the cryptocurrency for which the status is to be retrieved.
|
||||
* @return an [Option] containing [AccountCryptoCurrencyStatus] if found, otherwise None.
|
||||
*/
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): Option<AccountCryptoCurrencyStatus> {
|
||||
return invoke(userWalletId = userWalletId, currencyId = currency.id, network = currency.network)
|
||||
suspend fun invokeSync(userWalletId: UserWalletId, currency: CryptoCurrency): Option<AccountCryptoCurrencyStatus> {
|
||||
return invokeSync(userWalletId = userWalletId, currencyId = currency.id, network = currency.network)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -51,7 +50,7 @@ class GetAccountCurrencyStatusUseCase(
|
|||
* @param network the network associated with the cryptocurrency, can be null.
|
||||
* @return an [Option] containing [AccountCryptoCurrencyStatus] if found, otherwise None.
|
||||
*/
|
||||
suspend operator fun invoke(
|
||||
suspend fun invokeSync(
|
||||
userWalletId: UserWalletId,
|
||||
currencyId: CryptoCurrency.ID,
|
||||
network: Network?,
|
||||
|
|
@ -60,7 +59,48 @@ class GetAccountCurrencyStatusUseCase(
|
|||
params = SingleAccountStatusListProducer.Params(userWalletId),
|
||||
) ?: return none()
|
||||
|
||||
return accountStatusList.getExpectedAccountStatuses(network)
|
||||
return accountStatusList
|
||||
.toAccountCryptoCurrencyStatus(currencyId, network)
|
||||
.toOption()
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the status of a specific cryptocurrency for a given user wallet as a [Flow].
|
||||
*
|
||||
* @param userWalletId The ID of the user wallet.
|
||||
* @param currency The cryptocurrency for which the status is to be retrieved.
|
||||
* @return A [Flow] emitting [AccountCryptoCurrencyStatus] if found.
|
||||
*/
|
||||
operator fun invoke(userWalletId: UserWalletId, currency: CryptoCurrency): Flow<AccountCryptoCurrencyStatus> {
|
||||
return invoke(userWalletId = userWalletId, currencyId = currency.id, network = currency.network)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the status of a specific cryptocurrency by its ID for a given user wallet and network as a [Flow].
|
||||
*
|
||||
* @param userWalletId The ID of the user wallet.
|
||||
* @param currencyId The ID of the cryptocurrency.
|
||||
* @param network The network associated with the cryptocurrency, can be null.
|
||||
* @return A [Flow] emitting [AccountCryptoCurrencyStatus] if found.
|
||||
*/
|
||||
operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
currencyId: CryptoCurrency.ID,
|
||||
network: Network?,
|
||||
): Flow<AccountCryptoCurrencyStatus> {
|
||||
return singleAccountStatusListSupplier(
|
||||
params = SingleAccountStatusListProducer.Params(userWalletId),
|
||||
)
|
||||
.mapNotNull { accountStatusList ->
|
||||
accountStatusList.toAccountCryptoCurrencyStatus(currencyId, network)
|
||||
}
|
||||
}
|
||||
|
||||
private fun AccountStatusList.toAccountCryptoCurrencyStatus(
|
||||
currencyId: CryptoCurrency.ID,
|
||||
network: Network?,
|
||||
): AccountCryptoCurrencyStatus? {
|
||||
return getExpectedAccountStatuses(network)
|
||||
.asSequence()
|
||||
.filterIsInstance<AccountStatus.CryptoPortfolio>()
|
||||
.mapNotNull { accountStatus ->
|
||||
|
|
@ -70,7 +110,6 @@ class GetAccountCurrencyStatusUseCase(
|
|||
AccountCryptoCurrencyStatus(account = accountStatus.account, status = status)
|
||||
}
|
||||
.firstOrNull()
|
||||
.toOption()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.domain.account.status.usecase
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.common.test.utils.assertNone
|
||||
import com.tangem.common.test.utils.assertSome
|
||||
import com.tangem.common.test.utils.getEmittedValues
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
|
|
@ -18,8 +20,11 @@ import com.tangem.domain.models.network.Network
|
|||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
|
|
@ -47,111 +52,238 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
clearMocks(supplier)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke returns None when supplier returns null`() = runTest {
|
||||
// Arrange
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns null
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class InvokeSync {
|
||||
|
||||
// Act
|
||||
val actual = useCase(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
@Test
|
||||
fun `invokeSync returns None when supplier returns null`() = runTest {
|
||||
// Arrange
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns null
|
||||
|
||||
// Assert
|
||||
assertNone(actual)
|
||||
coVerifyOrder { supplier.getSyncOrNull(supplierParams) }
|
||||
// Act
|
||||
val actual = useCase.invokeSync(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
|
||||
// Assert
|
||||
assertNone(actual)
|
||||
coVerifyOrder { supplier.getSyncOrNull(supplierParams) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invokeSync returns None when AccountList does not contain required currency id`() = runTest {
|
||||
// Arrange
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = Account.CryptoPortfolio.createMainAccount(userWalletId),
|
||||
tokenList = TokenList.Empty,
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns setOf(accountStatus)
|
||||
}
|
||||
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns accountStatusList
|
||||
|
||||
// Act
|
||||
val actual = useCase.invokeSync(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
|
||||
// Assert
|
||||
assertNone(actual)
|
||||
coVerifyOrder { supplier.getSyncOrNull(supplierParams) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invokeSync returns Some if network is not null`() = runTest {
|
||||
// Arrange
|
||||
val mainAccountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = Account.CryptoPortfolio.createMainAccount(userWalletId),
|
||||
tokenList = TokenList.Empty,
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val account = mockk<Account.CryptoPortfolio>(relaxed = true) {
|
||||
every { this@mockk.derivationIndex } returns DerivationIndex(1).getOrNull()!!
|
||||
every { this@mockk.cryptoCurrencies } returns setOf(currency)
|
||||
}
|
||||
val currencyStatus = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = listOf(currencyStatus),
|
||||
),
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns setOf(mainAccountStatus, accountStatus, mockk())
|
||||
}
|
||||
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns accountStatusList
|
||||
|
||||
// Act
|
||||
val actual = useCase.invokeSync(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = AccountCryptoCurrencyStatus(account = accountStatus.account, status = currencyStatus)
|
||||
assertSome(actual, expected)
|
||||
|
||||
coVerifyOrder { supplier.getSyncOrNull(supplierParams) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invokeSync returns Some if network is null`() = runTest {
|
||||
// Arrange
|
||||
val account = mockk<Account.CryptoPortfolio>(relaxed = true) {
|
||||
every { this@mockk.cryptoCurrencies } returns setOf(currency)
|
||||
}
|
||||
val currencyStatus = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = listOf(currencyStatus),
|
||||
),
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns setOf(accountStatus)
|
||||
}
|
||||
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns accountStatusList
|
||||
|
||||
// Act
|
||||
val actual = useCase.invokeSync(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
|
||||
// Assert
|
||||
val expected = AccountCryptoCurrencyStatus(account = accountStatus.account, status = currencyStatus)
|
||||
assertSome(actual, expected)
|
||||
coVerifyOrder { supplier.getSyncOrNull(supplierParams) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invoke returns None when AccountList does not contain required currency id`() = runTest {
|
||||
// Arrange
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = Account.CryptoPortfolio.createMainAccount(userWalletId),
|
||||
tokenList = TokenList.Empty,
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
@Suppress("UnusedFlow")
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class Invoke {
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns setOf(accountStatus)
|
||||
@Test
|
||||
fun `invoke returns empty flow when supplier returns empty flow`() = runTest {
|
||||
// Arrange
|
||||
coEvery { supplier(supplierParams) } returns emptyFlow()
|
||||
|
||||
// Act
|
||||
val actual = useCase(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
.let(::getEmittedValues)
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEmpty()
|
||||
coVerifyOrder { supplier(supplierParams) }
|
||||
}
|
||||
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns accountStatusList
|
||||
@Test
|
||||
fun `invoke returns empty flow when AccountList does not contain required currency id`() = runTest {
|
||||
// Arrange
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = Account.CryptoPortfolio.createMainAccount(userWalletId),
|
||||
tokenList = TokenList.Empty,
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
// Act
|
||||
val actual = useCase(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns setOf(accountStatus)
|
||||
}
|
||||
|
||||
// Assert
|
||||
assertNone(actual)
|
||||
coVerifyOrder { supplier.getSyncOrNull(supplierParams) }
|
||||
}
|
||||
coEvery { supplier(supplierParams) } returns flowOf(accountStatusList)
|
||||
|
||||
@Test
|
||||
fun `invoke returns Some if network is not null`() = runTest {
|
||||
// Arrange
|
||||
val mainAccountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = Account.CryptoPortfolio.createMainAccount(userWalletId),
|
||||
tokenList = TokenList.Empty,
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
// Act
|
||||
val actual = useCase(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
.let(::getEmittedValues)
|
||||
|
||||
val account = mockk<Account.CryptoPortfolio>(relaxed = true) {
|
||||
every { this@mockk.derivationIndex } returns DerivationIndex(1).getOrNull()!!
|
||||
every { this@mockk.cryptoCurrencies } returns setOf(currency)
|
||||
}
|
||||
val currencyStatus = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = listOf(currencyStatus),
|
||||
),
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns setOf(mainAccountStatus, accountStatus, mockk())
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEmpty()
|
||||
coVerifyOrder { supplier(supplierParams) }
|
||||
}
|
||||
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns accountStatusList
|
||||
@Test
|
||||
fun `invoke returns data if network is not null`() = runTest {
|
||||
// Arrange
|
||||
val mainAccountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = Account.CryptoPortfolio.createMainAccount(userWalletId),
|
||||
tokenList = TokenList.Empty,
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
// Act
|
||||
val actual = useCase(userWalletId = userWalletId, currencyId = currency.id, network = currency.network)
|
||||
val account = mockk<Account.CryptoPortfolio>(relaxed = true) {
|
||||
every { this@mockk.derivationIndex } returns DerivationIndex(1).getOrNull()!!
|
||||
every { this@mockk.cryptoCurrencies } returns setOf(currency)
|
||||
}
|
||||
val currencyStatus = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = listOf(currencyStatus),
|
||||
),
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = AccountCryptoCurrencyStatus(account = accountStatus.account, status = currencyStatus)
|
||||
assertSome(actual, expected)
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns setOf(mainAccountStatus, accountStatus, mockk())
|
||||
}
|
||||
|
||||
coVerifyOrder { supplier.getSyncOrNull(supplierParams) }
|
||||
}
|
||||
coEvery { supplier(supplierParams) } returns flowOf(accountStatusList)
|
||||
|
||||
@Test
|
||||
fun `invoke returns Some if network is null`() = runTest {
|
||||
// Arrange
|
||||
val account = mockk<Account.CryptoPortfolio>(relaxed = true) {
|
||||
every { this@mockk.cryptoCurrencies } returns setOf(currency)
|
||||
}
|
||||
val currencyStatus = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = listOf(currencyStatus),
|
||||
),
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
// Act
|
||||
val actual = useCase(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
.let(::getEmittedValues)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns setOf(accountStatus)
|
||||
// Assert
|
||||
val expected = AccountCryptoCurrencyStatus(account = accountStatus.account, status = currencyStatus)
|
||||
Truth.assertThat(actual).containsExactly(expected)
|
||||
|
||||
coVerifyOrder { supplier(supplierParams) }
|
||||
}
|
||||
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns accountStatusList
|
||||
@Test
|
||||
fun `invoke returns data if network is null`() = runTest {
|
||||
// Arrange
|
||||
val account = mockk<Account.CryptoPortfolio>(relaxed = true) {
|
||||
every { this@mockk.cryptoCurrencies } returns setOf(currency)
|
||||
}
|
||||
val currencyStatus = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = listOf(currencyStatus),
|
||||
),
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
// Act
|
||||
val actual = useCase(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns setOf(accountStatus)
|
||||
}
|
||||
|
||||
// Assert
|
||||
val expected = AccountCryptoCurrencyStatus(account = accountStatus.account, status = currencyStatus)
|
||||
assertSome(actual, expected)
|
||||
coVerifyOrder { supplier.getSyncOrNull(supplierParams) }
|
||||
coEvery { supplier(supplierParams) } returns flowOf(accountStatusList)
|
||||
|
||||
// Act
|
||||
val actual = useCase(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
.let(::getEmittedValues)
|
||||
|
||||
// Assert
|
||||
val expected = AccountCryptoCurrencyStatus(account = accountStatus.account, status = currencyStatus)
|
||||
Truth.assertThat(actual).containsExactly(expected)
|
||||
coVerifyOrder { supplier(supplierParams) }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue