Updated on 2026-08-14
This commit is contained in:
parent
1edf995ab0
commit
2dadb60975
26 changed files with 1110 additions and 48 deletions
|
|
@ -1,10 +1,14 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.common.wallets.UserWalletDataCleaner
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.error.DeleteWalletError
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.coroutines.runSuspendCatching
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Use case for deleting user wallet
|
||||
|
|
@ -15,6 +19,8 @@ import com.tangem.utils.logging.TangemLogger
|
|||
*/
|
||||
class DeleteWalletUseCase(
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val userWalletDataCleaners: Set<UserWalletDataCleaner>,
|
||||
private val appCoroutineScope: AppCoroutineScope,
|
||||
) {
|
||||
|
||||
/**
|
||||
|
|
@ -25,10 +31,22 @@ class DeleteWalletUseCase(
|
|||
* @return [Either] with [com.tangem.domain.common.wallets.error.DeleteWalletError] or [Boolean] which indicates that there are still saved wallets.
|
||||
* */
|
||||
suspend operator fun invoke(userWalletId: UserWalletId): Either<DeleteWalletError, Boolean> {
|
||||
return userWalletsListRepository.delete(userWalletIds = listOf(userWalletId))
|
||||
val userWalletIds = listOf(userWalletId)
|
||||
return userWalletsListRepository.delete(userWalletIds = userWalletIds)
|
||||
.onRight { clearWalletDataInBackground(userWalletIds) }
|
||||
.map { userWalletsListRepository.selectedUserWallet.value != null }
|
||||
.onLeft {
|
||||
TangemLogger.e("Failed to delete wallet with id ${userWalletId.value}: $it")
|
||||
TangemLogger.e("Failed to delete wallet with id ${userWalletId.stringValue}: $it")
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearWalletDataInBackground(userWalletIds: List<UserWalletId>) {
|
||||
if (userWalletDataCleaners.isEmpty()) return
|
||||
appCoroutineScope.launch {
|
||||
userWalletDataCleaners.forEach { cleaner ->
|
||||
runSuspendCatching { cleaner.clear(userWalletIds) }
|
||||
.onFailure { TangemLogger.e("Failed to clear data for wallets $userWalletIds", it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
|
||||
import com.tangem.domain.common.wallets.UserWalletDataCleaner
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.error.DeleteWalletError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.test.core.TestAppCoroutineScope
|
||||
import io.mockk.Runs
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class DeleteWalletUseCaseTest {
|
||||
|
||||
private val userWalletsListRepository: UserWalletsListRepository = mockk(relaxed = true)
|
||||
private val firstCleaner: UserWalletDataCleaner = mockk()
|
||||
private val secondCleaner: UserWalletDataCleaner = mockk()
|
||||
|
||||
@BeforeEach
|
||||
fun resetMocks() {
|
||||
clearMocks(userWalletsListRepository, firstCleaner, secondCleaner)
|
||||
coEvery { userWalletsListRepository.delete(any()) } returns Unit.right()
|
||||
every { userWalletsListRepository.selectedUserWallet } returns MutableStateFlow<UserWallet?>(null)
|
||||
coEvery { firstCleaner.clear(any()) } just Runs
|
||||
coEvery { secondCleaner.clear(any()) } just Runs
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN delete succeeds WHEN invoke THEN cleanup runs in background after invoke returns`() = runTest {
|
||||
// Arrange
|
||||
val useCase = createUseCase()
|
||||
|
||||
// Act
|
||||
useCase(WALLET_ID)
|
||||
|
||||
// Assert — cleanup is fire-and-forget: not run yet when invoke returns
|
||||
coVerify(exactly = 0) { firstCleaner.clear(any()) }
|
||||
coVerify(exactly = 0) { secondCleaner.clear(any()) }
|
||||
|
||||
// ...and completes for every wallet once the app scope is drained
|
||||
advanceUntilIdle()
|
||||
coVerify(exactly = 1) { firstCleaner.clear(listOf(WALLET_ID)) }
|
||||
coVerify(exactly = 1) { secondCleaner.clear(listOf(WALLET_ID)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN delete fails WHEN invoke THEN cleaners are not run and error returned`() = runTest {
|
||||
// Arrange
|
||||
coEvery { userWalletsListRepository.delete(any()) } returns DeleteWalletError.UnableToDelete.left()
|
||||
val useCase = createUseCase()
|
||||
|
||||
// Act
|
||||
val result = useCase(WALLET_ID)
|
||||
advanceUntilIdle()
|
||||
|
||||
// Assert
|
||||
assertThat(result).isEqualTo(DeleteWalletError.UnableToDelete.left())
|
||||
coVerify(exactly = 0) { firstCleaner.clear(any()) }
|
||||
coVerify(exactly = 0) { secondCleaner.clear(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a wallet still selected WHEN invoke THEN returns right true`() = runTest {
|
||||
// Arrange
|
||||
every { userWalletsListRepository.selectedUserWallet } returns MutableStateFlow(wallet)
|
||||
val useCase = createUseCase()
|
||||
|
||||
// Act
|
||||
val result = useCase(WALLET_ID)
|
||||
|
||||
// Assert
|
||||
assertThat(result).isEqualTo(true.right())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN no wallet selected WHEN invoke THEN returns right false`() = runTest {
|
||||
// Arrange
|
||||
val useCase = createUseCase()
|
||||
|
||||
// Act
|
||||
val result = useCase(WALLET_ID)
|
||||
|
||||
// Assert
|
||||
assertThat(result).isEqualTo(false.right())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN one cleaner throws WHEN invoke THEN other cleaners still run and result is right`() = runTest {
|
||||
// Arrange
|
||||
coEvery { firstCleaner.clear(any()) } throws IllegalStateException("boom")
|
||||
val useCase = createUseCase()
|
||||
|
||||
// Act
|
||||
val result = useCase(WALLET_ID)
|
||||
advanceUntilIdle()
|
||||
|
||||
// Assert
|
||||
assertThat(result).isEqualTo(false.right())
|
||||
coVerify(exactly = 1) { secondCleaner.clear(listOf(WALLET_ID)) }
|
||||
}
|
||||
|
||||
private fun TestScope.createUseCase() = DeleteWalletUseCase(
|
||||
userWalletsListRepository = userWalletsListRepository,
|
||||
userWalletDataCleaners = setOf(firstCleaner, secondCleaner),
|
||||
appCoroutineScope = TestAppCoroutineScope(this),
|
||||
)
|
||||
|
||||
private companion object {
|
||||
val WALLET_ID = UserWalletId("0011")
|
||||
val wallet: UserWallet = MockUserWalletFactory.create()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue