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

View file

@ -4,6 +4,8 @@ import android.annotation.SuppressLint
import com.google.common.truth.Truth import com.google.common.truth.Truth
import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.CompletionResult 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.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.ScanCardException import com.tangem.domain.card.ScanCardException
import com.tangem.domain.common.configs.GenericCardConfig import com.tangem.domain.common.configs.GenericCardConfig
@ -40,7 +42,7 @@ internal class DefaultDerivationsRepositoryTest {
artworkUrl = "", artworkUrl = "",
cardsInWallet = setOf(), cardsInWallet = setOf(),
isMultiCurrency = false, isMultiCurrency = false,
scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()), scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()),
hasBackupError = false, hasBackupError = false,
) )
@ -77,7 +79,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test @Test
fun `success if currencies is empty`() = runTest { fun `success if currencies is empty`() = runTest {
val userWallet = defaultUserWallet.copy( 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 { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
@ -94,7 +96,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test @Test
fun `success if card already has derivations`() = runTest { fun `success if card already has derivations`() = runTest {
val userWallet = defaultUserWallet.copy( val userWallet = defaultUserWallet.copy(
scanResponse = ScanResponseMockFactory.create( scanResponse = MockScanResponseFactory.create(
cardConfig = MultiWalletCardConfig, cardConfig = MultiWalletCardConfig,
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys, derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
), ),
@ -105,7 +107,7 @@ internal class DefaultDerivationsRepositoryTest {
runCatching { runCatching {
repository.derivePublicKeys( repository.derivePublicKeys(
userWalletId = defaultUserWalletId, userWalletId = defaultUserWalletId,
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum, currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
) )
} }
.onSuccess { Truth.assertThat(it) } .onSuccess { Truth.assertThat(it) }
@ -119,7 +121,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test @Test
fun `error if tangemSdkManager throws exception`() = runTest { fun `error if tangemSdkManager throws exception`() = runTest {
val userWallet = defaultUserWallet.copy( 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 { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } throws ScanCardException.UserCancelled coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } throws ScanCardException.UserCancelled
@ -127,7 +129,7 @@ internal class DefaultDerivationsRepositoryTest {
runCatching { runCatching {
repository.derivePublicKeys( repository.derivePublicKeys(
userWalletId = defaultUserWalletId, userWalletId = defaultUserWalletId,
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum, currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
) )
} }
.onSuccess { error("Should throws exception") } .onSuccess { error("Should throws exception") }
@ -142,7 +144,7 @@ internal class DefaultDerivationsRepositoryTest {
@Test @Test
fun `success case`() = runTest { fun `success case`() = runTest {
val userWallet = defaultUserWallet.copy( 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 { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } returns CompletionResult.Success( coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } returns CompletionResult.Success(
@ -153,7 +155,7 @@ internal class DefaultDerivationsRepositoryTest {
runCatching { runCatching {
repository.derivePublicKeys( repository.derivePublicKeys(
userWalletId = defaultUserWalletId, userWalletId = defaultUserWalletId,
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum, currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
) )
} }
.onSuccess { Truth.assertThat(it) } .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.blockchain.common.derivation.DerivationConfigV2
import com.tangem.common.card.EllipticCurve import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.ByteArrayKey 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.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.configs.GenericCardConfig import com.tangem.domain.common.configs.GenericCardConfig
import com.tangem.domain.common.configs.MultiWalletCardConfig import com.tangem.domain.common.configs.MultiWalletCardConfig
@ -20,7 +22,7 @@ internal class MissedDerivationsFinderTest {
@Test @Test
fun `empty derivations for empty currencies`() { 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 finder = MissedDerivationsFinder(scanResponse)
val actual = finder.find(emptyList()) val actual = finder.find(emptyList())
@ -31,10 +33,10 @@ internal class MissedDerivationsFinderTest {
@Test @Test
fun `empty derivations for non supported blockchains`() { fun `empty derivations for non supported blockchains`() {
// Bls is not supported // 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 finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).chia val currencies = MockCryptoCurrencyFactory(scanResponse).chia.let(::listOf)
val actual = finder.find(currencies) val actual = finder.find(currencies)
Truth.assertThat(actual).isEmpty() Truth.assertThat(actual).isEmpty()
@ -43,7 +45,7 @@ internal class MissedDerivationsFinderTest {
@Test @Test
fun `derivations ONLY for supported blockchains`() { fun `derivations ONLY for supported blockchains`() {
// Bls is not supported // Bls is not supported
val scanResponse = ScanResponseMockFactory.create( val scanResponse = MockScanResponseFactory.create(
cardConfig = GenericCardConfig(2), cardConfig = GenericCardConfig(2),
derivedKeys = emptyMap(), derivedKeys = emptyMap(),
).let { ).let {
@ -55,7 +57,7 @@ internal class MissedDerivationsFinderTest {
} }
val finder = MissedDerivationsFinder(scanResponse) val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).chiaAndEthereum val currencies = MockCryptoCurrencyFactory(scanResponse).chiaAndEthereum
val actual = finder.find(currencies) val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly( Truth.assertThat(actual).containsExactly(
@ -66,10 +68,10 @@ internal class MissedDerivationsFinderTest {
@Test @Test
fun `derivations for custom token`() { 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 finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).ethereumTokenWithBinanceDerivation val currencies = MockCryptoCurrencyFactory(scanResponse).ethereumTokenWithBinanceDerivation
val actual = finder.find(currencies) val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly( Truth.assertThat(actual).containsExactly(
@ -83,10 +85,10 @@ internal class MissedDerivationsFinderTest {
@Test @Test
fun `derivations for cardano`() { 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 finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).cardano val currencies = MockCryptoCurrencyFactory(scanResponse).cardano.let(::listOf)
val actual = finder.find(currencies) val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly( Truth.assertThat(actual).containsExactly(
@ -105,13 +107,13 @@ internal class MissedDerivationsFinderTest {
@Test @Test
fun `empty derivations for already derived currencies`() { fun `empty derivations for already derived currencies`() {
val scanResponse = ScanResponseMockFactory.create( val scanResponse = MockScanResponseFactory.create(
cardConfig = Wallet2CardConfig, cardConfig = Wallet2CardConfig,
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys, derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
) )
val finder = MissedDerivationsFinder(scanResponse) val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).ethereum val currencies = MockCryptoCurrencyFactory(scanResponse).ethereum.let(::listOf)
val actual = finder.find(currencies) val actual = finder.find(currencies)
Truth.assertThat(actual).isEmpty() Truth.assertThat(actual).isEmpty()
@ -119,13 +121,13 @@ internal class MissedDerivationsFinderTest {
@Test @Test
fun `derivations ONLY for never derived currencies`() { fun `derivations ONLY for never derived currencies`() {
val scanResponse = ScanResponseMockFactory.create( val scanResponse = MockScanResponseFactory.create(
cardConfig = MultiWalletCardConfig, cardConfig = MultiWalletCardConfig,
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys, derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
) )
val finder = MissedDerivationsFinder(scanResponse) val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).ethereumAndStellar val currencies = MockCryptoCurrencyFactory(scanResponse).ethereumAndStellar
val actual = finder.find(currencies) val actual = finder.find(currencies)
Truth.assertThat(actual).containsExactly( Truth.assertThat(actual).containsExactly(

View file

@ -1,10 +1,10 @@
package com.tangem.tap.domain.userWalletList.implementation package com.tangem.tap.domain.userWalletList.implementation
import com.google.common.truth.Truth 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.common.configs.GenericCardConfig
import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.card.ScanResponseMockFactory
import io.mockk.mockk import io.mockk.mockk
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -198,7 +198,7 @@ internal class BiometricUserWalletsListManagerTest(private val model: Model) {
cardsInWallet = emptySet(), cardsInWallet = emptySet(),
isMultiCurrency = true, isMultiCurrency = true,
hasBackupError = false, hasBackupError = false,
scanResponse = ScanResponseMockFactory.create( scanResponse = MockScanResponseFactory.create(
cardConfig = GenericCardConfig(maxWalletCount = 1), cardConfig = GenericCardConfig(maxWalletCount = 1),
derivedKeys = emptyMap(), derivedKeys = emptyMap(),
).let { ).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.CardWallet
import com.tangem.common.card.FirmwareVersion import com.tangem.common.card.FirmwareVersion
@ -14,7 +14,7 @@ import java.util.Date
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
object ScanResponseMockFactory { object MockScanResponseFactory {
private val genericFirmwareVersion = CardDTO.FirmwareVersion( private val genericFirmwareVersion = CardDTO.FirmwareVersion(
major = 3, 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.Blockchain
import com.tangem.blockchain.common.FeePaidCurrency import com.tangem.blockchain.common.FeePaidCurrency
import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.Token
import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.toNetworkId 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.CryptoCurrencyFactory
import com.tangem.data.common.currency.getNetworkDerivationPath import com.tangem.data.common.currency.getNetworkDerivationPath
import com.tangem.data.common.currency.getNetworkStandardType 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.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrency
@ -16,13 +18,13 @@ import com.tangem.domain.tokens.model.Network
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) { class MockCryptoCurrencyFactory(private val scanResponse: ScanResponse = defaultScanResponse) {
private val factory = CryptoCurrencyFactory(excludedBlockchains = ExcludedBlockchains()) private val factory = CryptoCurrencyFactory(excludedBlockchains = ExcludedBlockchains())
val cardano by lazy { listOf(createCoin(blockchain = Blockchain.Cardano)) } val cardano by lazy { createCoin(blockchain = Blockchain.Cardano) }
val chia by lazy { listOf(element = createCoin(Blockchain.Chia)) } val chia by lazy { createCoin(Blockchain.Chia) }
val ethereum by lazy { listOf(element = createCoin(Blockchain.Ethereum)) } val ethereum by lazy { createCoin(Blockchain.Ethereum) }
val chiaAndEthereum by lazy { val chiaAndEthereum by lazy {
listOf( 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( val network = Network(
id = Network.ID(blockchain.id), id = Network.ID(blockchain.id),
backendId = blockchain.toNetworkId(), backendId = blockchain.toNetworkId(),
name = blockchain.fullName, name = blockchain.fullName,
isTestnet = blockchain.isTestnet(), isTestnet = blockchain.isTestnet(),
derivationPath = getNetworkDerivationPath( derivationPath = getNetworkDerivationPath(
blockchain, blockchain = blockchain,
extraDerivationPath = null, extraDerivationPath = null,
scanResponse.derivationStyleProvider, cardDerivationStyleProvider = scanResponse.derivationStyleProvider,
), ),
currencySymbol = blockchain.currency, currencySymbol = blockchain.currency,
standardType = getNetworkStandardType(blockchain), 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 // 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( return CryptoCurrency.Token(
id = CryptoCurrency.ID( id = CryptoCurrency.ID(
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX, 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( return factory.createToken(
sdkToken = Token(symbol = "NEVER-MIND", contractAddress = "NEVER-MIND", decimals = 8), sdkToken = Token(symbol = "NEVER-MIND", contractAddress = "NEVER-MIND", decimals = 8),
blockchain = blockchain, blockchain = blockchain,
@ -106,4 +108,12 @@ internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) {
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 // region Common modules
include(":common") include(":common")
include(":common:ui-charts")
include(":common:routing")
include(":common:ui")
include(":common:google") include(":common:google")
include(":common:routing")
include(":common:test")
include(":common:ui")
include(":common:ui-charts")
// endregion // endregion
// region Libs modules // region Libs modules