Updated on 2026-08-14
This commit is contained in:
parent
603f522cf9
commit
86d8877629
6 changed files with 315 additions and 4 deletions
|
|
@ -4,6 +4,10 @@ plugins {
|
|||
id("configuration")
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
// region DI
|
||||
|
|
@ -18,4 +22,9 @@ dependencies {
|
|||
// region Time dependencies
|
||||
implementation(deps.jodatime)
|
||||
// endregion
|
||||
|
||||
testImplementation(deps.test.junit5)
|
||||
testRuntimeOnly(deps.test.junit5.engine)
|
||||
testImplementation(deps.test.truth)
|
||||
testImplementation(deps.test.mockk)
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package com.tangem.utils.extensions
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class ByteArrayExtTest {
|
||||
|
||||
@Test
|
||||
fun `toHexString converts byte array to hexadecimal string`() {
|
||||
// Arrange
|
||||
val byteArray = byteArrayOf(0x0F, 0xA5.toByte(), 0xFF.toByte())
|
||||
|
||||
// Act
|
||||
val actual = byteArray.toHexString()
|
||||
|
||||
// Assert
|
||||
val expected = "0FA5FF"
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hexToBytes converts hexadecimal string to byte array`() {
|
||||
// Arrange
|
||||
val hexString = "0FA5FF"
|
||||
|
||||
// Act
|
||||
val actual = hexString.hexToBytes()
|
||||
|
||||
// Assert
|
||||
val expected = byteArrayOf(0x0F, 0xA5.toByte(), 0xFF.toByte())
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hexToBytes handles hex string with prefix`() {
|
||||
// Arrange
|
||||
val hexString = "0x0FA5FF"
|
||||
|
||||
// Act
|
||||
val actual = hexString.hexToBytes()
|
||||
|
||||
// Assert
|
||||
val expected = byteArrayOf(0x0F, 0xA5.toByte(), 0xFF.toByte())
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hexToBytes throws exception for invalid hex string`() {
|
||||
// Arrange
|
||||
val invalidHexString = "GHIJKL"
|
||||
|
||||
// Act
|
||||
val actual = runCatching { invalidHexString.hexToBytes() }.exceptionOrNull()
|
||||
|
||||
// Assert
|
||||
val expected = NumberFormatException()
|
||||
Truth.assertThat(actual).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual).hasMessageThat().isEqualTo("For input string: \"GH\" under radix 16")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hexToBytes handles empty string`() {
|
||||
// Arrange
|
||||
val emptyHexString = ""
|
||||
|
||||
// Act
|
||||
val actual = emptyHexString.hexToBytes()
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isEqualTo(byteArrayOf())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hexToBytesOrNull returns byte array for valid hex string`() {
|
||||
// Arrange
|
||||
val hexString = "0FA5FF"
|
||||
|
||||
// Act
|
||||
val actual = hexString.hexToBytesOrNull()
|
||||
|
||||
// Assert
|
||||
val expected = byteArrayOf(0x0F, 0xA5.toByte(), 0xFF.toByte())
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hexToBytesOrNull returns null for invalid hex string`() {
|
||||
// Arrange
|
||||
val invalidHexString = "GHIJKL"
|
||||
|
||||
// Act
|
||||
val actual = invalidHexString.hexToBytesOrNull()
|
||||
|
||||
// Assert
|
||||
Truth.assertThat(actual).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hexToBytesOrNullHandlesEmptyString() {
|
||||
// Arrange
|
||||
val emptyHexString = ""
|
||||
|
||||
// Act
|
||||
val actual = emptyHexString.hexToBytesOrNull()
|
||||
|
||||
// Assert
|
||||
val expected = byteArrayOf()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,10 @@ plugins {
|
|||
id("configuration")
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.domain.visa.models)
|
||||
api(projects.core.utils)
|
||||
|
|
@ -17,4 +21,9 @@ dependencies {
|
|||
implementation(deps.kotlin.serialization)
|
||||
ksp(deps.moshi.kotlin.codegen)
|
||||
implementation(deps.arrow.core)
|
||||
|
||||
testImplementation(deps.test.junit5)
|
||||
testRuntimeOnly(deps.test.junit5.engine)
|
||||
testImplementation(deps.test.truth)
|
||||
testImplementation(deps.test.mockk)
|
||||
}
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
package com.tangem.domain.models.account
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon.*
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.unmockkObject
|
||||
import io.mockk.verify
|
||||
import org.junit.jupiter.api.Nested
|
||||
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]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class CryptoPortfolioIconTest {
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class OfMainAccount {
|
||||
|
||||
@Test
|
||||
fun `ofMainAccount with empty exclude`() {
|
||||
// Act
|
||||
val actual = CryptoPortfolioIcon.ofMainAccount(exclude = emptySet())
|
||||
|
||||
// Assert
|
||||
val expectedColor = Color.Azure
|
||||
Truth.assertThat(actual.color).isEqualTo(expectedColor)
|
||||
|
||||
val expectedType = Type.Icon(value = Icon.Star)
|
||||
Truth.assertThat(actual.type).isEqualTo(expectedType)
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestModels")
|
||||
fun ofMainAccount(model: OfMainAccountModel) {
|
||||
// Arrange
|
||||
mockkObject(Random.Default)
|
||||
|
||||
val size = (Color.entries.size - model.exclude.size).takeIf { it > 0 } ?: Color.entries.size
|
||||
every { Random.nextInt(size) } returns model.randomNextInt
|
||||
|
||||
// Act
|
||||
val actual = CryptoPortfolioIcon.ofMainAccount(exclude = model.exclude)
|
||||
|
||||
// Assert
|
||||
val expectedColor = model.expectedColor
|
||||
Truth.assertThat(actual.color).isEqualTo(expectedColor)
|
||||
|
||||
val expectedType = Type.Icon(value = Icon.Star)
|
||||
Truth.assertThat(actual.type).isEqualTo(expectedType)
|
||||
|
||||
verify(exactly = 1) { Random.nextInt(size) }
|
||||
|
||||
unmockkObject(Random.Default)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
// If the default color is already occupied (present in the exclude set), a random color from the
|
||||
// remaining available colors will be selected for the main account icon.
|
||||
OfMainAccountModel(
|
||||
exclude = setOf(Color.Azure),
|
||||
randomNextInt = 0,
|
||||
expectedColor = Color.entries[1],
|
||||
),
|
||||
OfMainAccountModel(
|
||||
exclude = setOf(Color.Azure, Color.CaribbeanBlue),
|
||||
randomNextInt = 0,
|
||||
expectedColor = Color.entries[2],
|
||||
),
|
||||
// If all colors are already occupied, a random one will be selected.
|
||||
OfMainAccountModel(
|
||||
exclude = Color.entries.toSet(),
|
||||
randomNextInt = 1,
|
||||
expectedColor = Color.entries[1],
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
data class OfMainAccountModel(
|
||||
val exclude: Set<Color>,
|
||||
val randomNextInt: Int,
|
||||
val expectedColor: Color,
|
||||
)
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class OfCustomAccountBasedOnName {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestModels")
|
||||
fun ofCustomAccount(model: OfCustomAccountModel.BasedOnName) {
|
||||
// Arrange
|
||||
mockkObject(Random.Default)
|
||||
|
||||
every { Random.nextInt(until = Color.entries.size) } returns model.randomNextInt
|
||||
|
||||
// Act
|
||||
val actual = CryptoPortfolioIcon.ofCustomAccount(accountName = model.accountName)
|
||||
|
||||
// Assert
|
||||
val expectedColor = model.expectedColor
|
||||
Truth.assertThat(actual.color).isEqualTo(expectedColor)
|
||||
|
||||
val expectedType = Type.Symbol(value = model.accountName.first())
|
||||
Truth.assertThat(actual.type).isEqualTo(expectedType)
|
||||
|
||||
verify(exactly = 1) { Random.nextInt(until = Color.entries.size) }
|
||||
|
||||
unmockkObject(Random.Default)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
OfCustomAccountModel.BasedOnName(
|
||||
accountName = "New account",
|
||||
randomNextInt = 0,
|
||||
expectedColor = Color.entries[0],
|
||||
),
|
||||
OfCustomAccountModel.BasedOnName(
|
||||
accountName = "Awesome",
|
||||
randomNextInt = Color.entries.lastIndex,
|
||||
expectedColor = Color.entries.last(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class OfCustomAccountWithTypeAndColor {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestModels")
|
||||
fun ofCustomAccount(model: OfCustomAccountModel.WithTypeAndColor) {
|
||||
// Act
|
||||
val actual = CryptoPortfolioIcon.ofCustomAccount(type = model.type, color = model.color)
|
||||
|
||||
// Assert
|
||||
val expectedColor = model.expectedColor
|
||||
Truth.assertThat(actual.color).isEqualTo(expectedColor)
|
||||
|
||||
val expectedType = model.expectedType
|
||||
Truth.assertThat(actual.type).isEqualTo(expectedType)
|
||||
}
|
||||
|
||||
private fun provideTestModels() = listOf(
|
||||
OfCustomAccountModel.WithTypeAndColor(
|
||||
type = Type.Icon(value = Icon.User),
|
||||
color = Color.CaribbeanBlue,
|
||||
expectedType = Type.Icon(value = Icon.User),
|
||||
expectedColor = Color.CaribbeanBlue,
|
||||
),
|
||||
OfCustomAccountModel.WithTypeAndColor(
|
||||
type = Type.Symbol(value = 'A'),
|
||||
color = Color.DullLavender,
|
||||
expectedType = Type.Symbol(value = 'A'),
|
||||
expectedColor = Color.DullLavender,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
sealed interface OfCustomAccountModel {
|
||||
|
||||
data class BasedOnName(
|
||||
val accountName: String,
|
||||
val randomNextInt: Int,
|
||||
val expectedColor: Color,
|
||||
) : OfCustomAccountModel
|
||||
|
||||
data class WithTypeAndColor(
|
||||
val type: Type,
|
||||
val color: Color,
|
||||
val expectedType: Type,
|
||||
val expectedColor: Color,
|
||||
) : OfCustomAccountModel
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,8 @@ import com.ihsanbal.logging.LoggingInterceptor
|
|||
import com.tangem.lib.visa.model.VisaContractInfo
|
||||
import com.tangem.lib.visa.utils.Constants
|
||||
import com.tangem.lib.visa.utils.Constants.NETWORK_LOGS_TAG
|
||||
import com.tangem.lib.visa.utils.toHexString
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.toHexString
|
||||
import okhttp3.OkHttpClient
|
||||
import org.web3j.crypto.Credentials
|
||||
import org.web3j.protocol.Web3j
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
package com.tangem.lib.visa.utils
|
||||
|
||||
internal fun ByteArray.toHexString(): String = joinToString("") { "%02X".format(it) }
|
||||
Loading…
Add table
Add a link
Reference in a new issue