Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-27 21:21:17 +04:00
parent 229a2d7236
commit e9fac0e50e
10 changed files with 101 additions and 39 deletions

View file

@ -1,5 +1,5 @@
import java.util.Properties
import com.tangem.plugin.configuration.configurations.extension.kaptForObfuscatingVariants
import java.util.Properties
plugins {
alias(deps.plugins.android.application)
@ -291,6 +291,7 @@ dependencies {
kaptForObfuscatingVariants(deps.retrofit.response.type.keeper)
/** Testing libraries */
testImplementation(projects.common.test)
testImplementation(deps.test.coroutine)
testImplementation(deps.test.junit)
testImplementation(deps.test.mockk)

View file

@ -4,6 +4,8 @@ import android.annotation.SuppressLint
import com.google.common.truth.Truth
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.CompletionResult
import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.ScanCardException
import com.tangem.domain.common.configs.GenericCardConfig
@ -40,7 +42,7 @@ internal class DefaultDerivationsRepositoryTest {
artworkUrl = "",
cardsInWallet = setOf(),
isMultiCurrency = false,
scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()),
scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()),
hasBackupError = false,
)
@ -77,7 +79,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test
fun `success if currencies is empty`() = runTest {
val userWallet = defaultUserWallet.copy(
scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
)
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
@ -94,7 +96,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test
fun `success if card already has derivations`() = runTest {
val userWallet = defaultUserWallet.copy(
scanResponse = ScanResponseMockFactory.create(
scanResponse = MockScanResponseFactory.create(
cardConfig = MultiWalletCardConfig,
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
),
@ -105,7 +107,7 @@ internal class DefaultDerivationsRepositoryTest {
runCatching {
repository.derivePublicKeys(
userWalletId = defaultUserWalletId,
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum,
currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
)
}
.onSuccess { Truth.assertThat(it) }
@ -119,7 +121,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test
fun `error if tangemSdkManager throws exception`() = runTest {
val userWallet = defaultUserWallet.copy(
scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
)
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } throws ScanCardException.UserCancelled
@ -127,7 +129,7 @@ internal class DefaultDerivationsRepositoryTest {
runCatching {
repository.derivePublicKeys(
userWalletId = defaultUserWalletId,
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum,
currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
)
}
.onSuccess { error("Should throws exception") }
@ -142,7 +144,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test
fun `success case`() = runTest {
val userWallet = defaultUserWallet.copy(
scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
)
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } returns CompletionResult.Success(
@ -153,7 +155,7 @@ internal class DefaultDerivationsRepositoryTest {
runCatching {
repository.derivePublicKeys(
userWalletId = defaultUserWalletId,
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum,
currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
)
}
.onSuccess { Truth.assertThat(it) }

View file

@ -6,6 +6,8 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.derivation.DerivationConfigV2
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.configs.GenericCardConfig
import com.tangem.domain.common.configs.MultiWalletCardConfig
@ -20,7 +22,7 @@ internal class MissedDerivationsFinderTest {
@Test
fun `empty derivations for empty currencies`() {
val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse)
val actual = finder.find(emptyList())
@ -31,10 +33,10 @@ internal class MissedDerivationsFinderTest {
@Test
fun `empty derivations for non supported blockchains`() {
// Bls is not supported
val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).chia
val currencies = MockCryptoCurrencyFactory(scanResponse).chia.let(::listOf)
val actual = finder.find(currencies)
Truth.assertThat(actual).isEmpty()
@ -43,7 +45,7 @@ internal class MissedDerivationsFinderTest {
@Test
fun `derivations ONLY for supported blockchains`() {
// Bls is not supported
val scanResponse = ScanResponseMockFactory.create(
val scanResponse = MockScanResponseFactory.create(
cardConfig = GenericCardConfig(2),
derivedKeys = emptyMap(),
).let {
@ -55,7 +57,7 @@ internal class MissedDerivationsFinderTest {
}
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).chiaAndEthereum
val currencies = MockCryptoCurrencyFactory(scanResponse).chiaAndEthereum
val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly(
@ -66,10 +68,10 @@ internal class MissedDerivationsFinderTest {
@Test
fun `derivations for custom token`() {
val scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).ethereumTokenWithBinanceDerivation
val currencies = MockCryptoCurrencyFactory(scanResponse).ethereumTokenWithBinanceDerivation
val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly(
@ -83,10 +85,10 @@ internal class MissedDerivationsFinderTest {
@Test
fun `derivations for cardano`() {
val scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).cardano
val currencies = MockCryptoCurrencyFactory(scanResponse).cardano.let(::listOf)
val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly(
@ -105,13 +107,13 @@ internal class MissedDerivationsFinderTest {
@Test
fun `empty derivations for already derived currencies`() {
val scanResponse = ScanResponseMockFactory.create(
val scanResponse = MockScanResponseFactory.create(
cardConfig = Wallet2CardConfig,
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
)
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).ethereum
val currencies = MockCryptoCurrencyFactory(scanResponse).ethereum.let(::listOf)
val actual = finder.find(currencies)
Truth.assertThat(actual).isEmpty()
@ -119,13 +121,13 @@ internal class MissedDerivationsFinderTest {
@Test
fun `derivations ONLY for never derived currencies`() {
val scanResponse = ScanResponseMockFactory.create(
val scanResponse = MockScanResponseFactory.create(
cardConfig = MultiWalletCardConfig,
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
)
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).ethereumAndStellar
val currencies = MockCryptoCurrencyFactory(scanResponse).ethereumAndStellar
val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly(

View file

@ -1,10 +1,10 @@
package com.tangem.tap.domain.userWalletList.implementation
import com.google.common.truth.Truth
import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.domain.common.configs.GenericCardConfig
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.card.ScanResponseMockFactory
import io.mockk.mockk
import org.junit.Test
import org.junit.runner.RunWith
@ -198,7 +198,7 @@ internal class BiometricUserWalletsListManagerTest(private val model: Model) {
cardsInWallet = emptySet(),
isMultiCurrency = true,
hasBackupError = false,
scanResponse = ScanResponseMockFactory.create(
scanResponse = MockScanResponseFactory.create(
cardConfig = GenericCardConfig(maxWalletCount = 1),
derivedKeys = emptyMap(),
).let {

1
common/test/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,25 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
id("configuration")
}
android {
namespace = "com.tangem.common.test"
}
dependencies {
implementation(projects.data.common)
implementation(projects.domain.legacy)
implementation(projects.domain.models)
implementation(projects.domain.tokens.models)
implementation(projects.libs.blockchainSdk)
implementation(deps.androidx.datastore)
implementation(deps.test.coroutine)
implementation(tangemDeps.blockchain)
implementation(tangemDeps.card.core)
}

View file

@ -1,4 +1,4 @@
package com.tangem.tap.domain.card
package com.tangem.common.test.domain.card
import com.tangem.common.card.CardWallet
import com.tangem.common.card.FirmwareVersion
@ -14,7 +14,7 @@ import java.util.Date
/**
[REDACTED_AUTHOR]
*/
object ScanResponseMockFactory {
object MockScanResponseFactory {
private val genericFirmwareVersion = CardDTO.FirmwareVersion(
major = 3,

View file

@ -1,13 +1,15 @@
package com.tangem.tap.domain.card
package com.tangem.common.test.domain.token
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.FeePaidCurrency
import com.tangem.blockchain.common.Token
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.data.common.currency.getNetworkDerivationPath
import com.tangem.data.common.currency.getNetworkStandardType
import com.tangem.domain.common.configs.GenericCardConfig
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
@ -16,13 +18,13 @@ import com.tangem.domain.tokens.model.Network
/**
[REDACTED_AUTHOR]
*/
internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) {
class MockCryptoCurrencyFactory(private val scanResponse: ScanResponse = defaultScanResponse) {
private val factory = CryptoCurrencyFactory(excludedBlockchains = ExcludedBlockchains())
val cardano by lazy { listOf(createCoin(blockchain = Blockchain.Cardano)) }
val chia by lazy { listOf(element = createCoin(Blockchain.Chia)) }
val ethereum by lazy { listOf(element = createCoin(Blockchain.Ethereum)) }
val cardano by lazy { createCoin(blockchain = Blockchain.Cardano) }
val chia by lazy { createCoin(Blockchain.Chia) }
val ethereum by lazy { createCoin(Blockchain.Ethereum) }
val chiaAndEthereum by lazy {
listOf(
@ -44,16 +46,16 @@ internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) {
)
}
private fun createCoin(blockchain: Blockchain): CryptoCurrency {
fun createCoin(blockchain: Blockchain): CryptoCurrency {
val network = Network(
id = Network.ID(blockchain.id),
backendId = blockchain.toNetworkId(),
name = blockchain.fullName,
isTestnet = blockchain.isTestnet(),
derivationPath = getNetworkDerivationPath(
blockchain,
blockchain = blockchain,
extraDerivationPath = null,
scanResponse.derivationStyleProvider,
cardDerivationStyleProvider = scanResponse.derivationStyleProvider,
),
currencySymbol = blockchain.currency,
standardType = getNetworkStandardType(blockchain),
@ -66,7 +68,7 @@ internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) {
}
// Impossible to create custom token by CryptoCurrencyFactory because it works with URI under the hood
private fun createCustomToken(blockchain: Blockchain, derivationBlockchain: Blockchain): CryptoCurrency {
fun createCustomToken(blockchain: Blockchain, derivationBlockchain: Blockchain): CryptoCurrency {
return CryptoCurrency.Token(
id = CryptoCurrency.ID(
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
@ -98,7 +100,7 @@ internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) {
)
}
private fun createToken(blockchain: Blockchain): CryptoCurrency {
fun createToken(blockchain: Blockchain): CryptoCurrency {
return factory.createToken(
sdkToken = Token(symbol = "NEVER-MIND", contractAddress = "NEVER-MIND", decimals = 8),
blockchain = blockchain,
@ -106,4 +108,12 @@ internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) {
scanResponse = scanResponse,
)!!
}
private companion object {
val defaultScanResponse = MockScanResponseFactory.create(
cardConfig = GenericCardConfig(2),
derivedKeys = emptyMap(),
)
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.common.test.utils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@OptIn(ExperimentalCoroutinesApi::class)
fun <T> CoroutineScope.getEmittedValues(testScheduler: TestCoroutineScheduler, actual: Flow<T>): List<T> {
val values = mutableListOf<T>()
launch(UnconfinedTestDispatcher(testScheduler)) {
actual.toList(values)
}
return values
}

View file

@ -153,10 +153,11 @@ include(":core:error")
// region Common modules
include(":common")
include(":common:ui-charts")
include(":common:routing")
include(":common:ui")
include(":common:google")
include(":common:routing")
include(":common:test")
include(":common:ui")
include(":common:ui-charts")
// endregion
// region Libs modules