Updated on 2026-08-14
This commit is contained in:
parent
6848f91436
commit
b8898092fb
21 changed files with 443 additions and 106 deletions
|
|
@ -9,7 +9,7 @@ import com.tangem.datasource.api.onramp.models.response.OnrampItemResponse
|
|||
*/
|
||||
interface ExpressHistoryRepository {
|
||||
|
||||
suspend fun storeExchanges(ownerAddress: String, items: List<ExchangeItemResponse>)
|
||||
suspend fun storeExchanges(items: List<ExchangeItemResponse>)
|
||||
|
||||
suspend fun storeOnramps(ownerAddress: String, items: List<OnrampItemResponse>)
|
||||
suspend fun storeOnramps(items: List<OnrampItemResponse>)
|
||||
}
|
||||
|
|
@ -174,7 +174,7 @@ internal class DefaultOnrampRepository(
|
|||
.getOrThrow()
|
||||
|
||||
if (txHistoryFeatureToggles.isNewTxHistoryEnabled) {
|
||||
expressHistoryRepository.storeOnramps(ownerAddress = response.payoutAddress, items = listOf(response))
|
||||
expressHistoryRepository.storeOnramps(items = listOf(response))
|
||||
}
|
||||
|
||||
statusConverter.convert(response)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.data.txhistory.repository
|
||||
|
||||
import androidx.room.withTransaction
|
||||
import com.tangem.data.common.txhistory.ExpressHistoryRepository
|
||||
import com.tangem.data.txhistory.repository.converter.toHistoryIndexEntities
|
||||
import com.tangem.data.txhistory.repository.converter.toHistoryIndexEntity
|
||||
import com.tangem.data.txhistory.repository.factory.TokenInfoRepository
|
||||
import com.tangem.data.txhistory.repository.factory.toAssetId
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
|
|
@ -11,8 +14,10 @@ import com.tangem.datasource.api.onramp.models.response.OnrampHistoryDeltaRespon
|
|||
import com.tangem.datasource.api.onramp.models.response.OnrampHistoryResponse
|
||||
import com.tangem.datasource.api.onramp.models.response.OnrampItemResponse
|
||||
import com.tangem.datasource.local.converter.toEntity
|
||||
import com.tangem.datasource.local.txhistory.db.TxHistoryDatabase
|
||||
import com.tangem.datasource.local.txhistory.db.dao.ExpressHistoryDao
|
||||
import com.tangem.datasource.local.txhistory.db.dao.ExpressSyncStateDao
|
||||
import com.tangem.datasource.local.txhistory.db.dao.HistoryIndexDao
|
||||
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressSyncStateEntity
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -25,12 +30,15 @@ import javax.inject.Inject
|
|||
* Fetches express (exchange & onramp) transaction history from the API, persists it into the local database, and
|
||||
* fetches any missing token metadata for the referenced assets.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultExpressHistoryRepository @Inject constructor(
|
||||
private val exchangeApi: TangemExpressApi,
|
||||
private val onrampApi: OnrampApi,
|
||||
private val expressHistoryDao: ExpressHistoryDao,
|
||||
private val historyIndexDao: HistoryIndexDao,
|
||||
private val expressSyncStateDao: ExpressSyncStateDao,
|
||||
private val tokenInfoRepository: TokenInfoRepository,
|
||||
private val database: TxHistoryDatabase,
|
||||
private val appScope: AppCoroutineScope,
|
||||
) : ExpressHistoryRepository {
|
||||
|
||||
|
|
@ -48,7 +56,7 @@ internal class DefaultExpressHistoryRepository @Inject constructor(
|
|||
limit = limit,
|
||||
).getOrThrow()
|
||||
|
||||
storeExchanges(ownerAddress = fromAddress, items = response.items)
|
||||
storeExchanges(items = response.items)
|
||||
persistHistoryState(
|
||||
type = ExpressSyncStateEntity.Type.EXCHANGE,
|
||||
address = fromAddress,
|
||||
|
|
@ -72,7 +80,7 @@ internal class DefaultExpressHistoryRepository @Inject constructor(
|
|||
limit = limit,
|
||||
).getOrThrow()
|
||||
|
||||
storeExchanges(ownerAddress = fromAddress, items = response.items)
|
||||
storeExchanges(items = response.items)
|
||||
persistDeltaState(
|
||||
type = ExpressSyncStateEntity.Type.EXCHANGE,
|
||||
address = fromAddress,
|
||||
|
|
@ -95,7 +103,7 @@ internal class DefaultExpressHistoryRepository @Inject constructor(
|
|||
limit = limit,
|
||||
).getOrThrow()
|
||||
|
||||
storeOnramps(ownerAddress = payoutAddress, items = response.items)
|
||||
storeOnramps(items = response.items)
|
||||
persistHistoryState(
|
||||
type = ExpressSyncStateEntity.Type.ONRAMP,
|
||||
address = payoutAddress,
|
||||
|
|
@ -119,7 +127,7 @@ internal class DefaultExpressHistoryRepository @Inject constructor(
|
|||
limit = limit,
|
||||
).getOrThrow()
|
||||
|
||||
storeOnramps(ownerAddress = payoutAddress, items = response.items)
|
||||
storeOnramps(items = response.items)
|
||||
persistDeltaState(
|
||||
type = ExpressSyncStateEntity.Type.ONRAMP,
|
||||
address = payoutAddress,
|
||||
|
|
@ -128,10 +136,13 @@ internal class DefaultExpressHistoryRepository @Inject constructor(
|
|||
return response
|
||||
}
|
||||
|
||||
override suspend fun storeExchanges(ownerAddress: String, items: List<ExchangeItemResponse>) {
|
||||
if (items.isEmpty()) return
|
||||
val entities = items.map { it.toEntity(ownerAddress) }
|
||||
expressHistoryDao.upsertExchanges(entities)
|
||||
override suspend fun storeExchanges(items: List<ExchangeItemResponse>) {
|
||||
val entities = items.mapNotNull { it.toEntity() }
|
||||
if (entities.isEmpty()) return
|
||||
database.withTransaction {
|
||||
expressHistoryDao.upsertExchanges(entities)
|
||||
historyIndexDao.upsert(entities.flatMap { it.toHistoryIndexEntities() })
|
||||
}
|
||||
fetchMissingTokenInfo(
|
||||
buildSet {
|
||||
entities.forEach { entity ->
|
||||
|
|
@ -142,10 +153,13 @@ internal class DefaultExpressHistoryRepository @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun storeOnramps(ownerAddress: String, items: List<OnrampItemResponse>) {
|
||||
override suspend fun storeOnramps(items: List<OnrampItemResponse>) {
|
||||
if (items.isEmpty()) return
|
||||
val entities = items.map { it.toEntity(ownerAddress) }
|
||||
expressHistoryDao.upsertOnramps(entities)
|
||||
val entities = items.map { it.toEntity() }
|
||||
database.withTransaction {
|
||||
expressHistoryDao.upsertOnramps(entities)
|
||||
historyIndexDao.upsert(entities.map { it.toHistoryIndexEntity() })
|
||||
}
|
||||
fetchMissingTokenInfo(entities.mapTo(mutableSetOf()) { it.to.toAssetId() })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ internal class RefactoredTxHistoryRepository @Inject constructor(
|
|||
|
||||
val flow = combine(
|
||||
flow = expressHistoryDao.observeOutgoingSwaps(
|
||||
ownerAddress = address,
|
||||
fromAddress = address,
|
||||
network = rawNetwork,
|
||||
contract = contract,
|
||||
fromCreatedAtIso = fromCreatedAtIso,
|
||||
|
|
@ -86,7 +86,7 @@ internal class RefactoredTxHistoryRepository @Inject constructor(
|
|||
activeStatuses = ExpressStatusMapper.activeExchangeStatuses,
|
||||
).distinctUntilChanged(),
|
||||
flow3 = expressHistoryDao.observeIncomingOnramps(
|
||||
ownerAddress = address,
|
||||
payoutAddress = address,
|
||||
network = rawNetwork,
|
||||
contract = contract,
|
||||
fromCreatedAtIso = fromCreatedAtIso,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.data.txhistory.repository.converter
|
||||
|
||||
import com.tangem.datasource.local.txhistory.db.entity.HistoryIndexEntity
|
||||
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressExchangeEntity
|
||||
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressOnrampEntity
|
||||
import org.joda.time.DateTime
|
||||
|
||||
/**
|
||||
* Index rows for a swap: it shows on the from-token screen (outgoing, under `from_address`) and, when the payout lands
|
||||
* on another owned token, on that screen too (incoming, under `payout_address`). The set de-duplicates the addresses so
|
||||
* a swap whose from and payout addresses coincide is indexed once.
|
||||
*/
|
||||
internal fun ExpressExchangeEntity.toHistoryIndexEntities(): List<HistoryIndexEntity> {
|
||||
val sortTimeMillis = DateTime.parse(createdAt).millis
|
||||
return setOf(fromAddress, payoutAddress).map { address ->
|
||||
HistoryIndexEntity(
|
||||
type = HistoryIndexEntity.Type.EXCHANGE.value,
|
||||
entityId = txId,
|
||||
address = address,
|
||||
sortTimeMillis = sortTimeMillis,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Onramp is always incoming: a single index row under the payout address. */
|
||||
internal fun ExpressOnrampEntity.toHistoryIndexEntity(): HistoryIndexEntity = HistoryIndexEntity(
|
||||
type = HistoryIndexEntity.Type.ONRAMP.value,
|
||||
entityId = txId,
|
||||
address = payoutAddress,
|
||||
sortTimeMillis = DateTime.parse(createdAt).millis,
|
||||
)
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.data.txhistory.repository
|
||||
|
||||
import androidx.room.withTransaction
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.data.txhistory.repository.converter.toHistoryIndexEntities
|
||||
import com.tangem.data.txhistory.repository.converter.toHistoryIndexEntity
|
||||
import com.tangem.data.txhistory.repository.factory.TokenInfoRepository
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
|
|
@ -15,8 +18,11 @@ import com.tangem.datasource.api.onramp.models.response.OnrampHistoryDeltaRespon
|
|||
import com.tangem.datasource.api.onramp.models.response.OnrampHistoryResponse
|
||||
import com.tangem.datasource.api.onramp.models.response.OnrampItemResponse
|
||||
import com.tangem.datasource.local.converter.toEntity
|
||||
import com.tangem.datasource.local.txhistory.db.TxHistoryDatabase
|
||||
import com.tangem.datasource.local.txhistory.db.dao.ExpressHistoryDao
|
||||
import com.tangem.datasource.local.txhistory.db.dao.ExpressSyncStateDao
|
||||
import com.tangem.datasource.local.txhistory.db.dao.HistoryIndexDao
|
||||
import com.tangem.datasource.local.txhistory.db.entity.HistoryIndexEntity
|
||||
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressSyncStateEntity
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -25,8 +31,12 @@ import io.mockk.clearMocks
|
|||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.slot
|
||||
import io.mockk.unmockkStatic
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
|
@ -37,21 +47,35 @@ internal class DefaultExpressHistoryRepositoryTest {
|
|||
private val exchangeApi: TangemExpressApi = mockk()
|
||||
private val onrampApi: OnrampApi = mockk()
|
||||
private val expressHistoryDao: ExpressHistoryDao = mockk(relaxUnitFun = true)
|
||||
private val historyIndexDao: HistoryIndexDao = mockk(relaxUnitFun = true)
|
||||
private val expressSyncStateDao: ExpressSyncStateDao = mockk(relaxUnitFun = true)
|
||||
private val tokenInfoRepository: TokenInfoRepository = mockk(relaxUnitFun = true)
|
||||
|
||||
private val database: TxHistoryDatabase = mockk()
|
||||
|
||||
private val repository = DefaultExpressHistoryRepository(
|
||||
exchangeApi = exchangeApi,
|
||||
onrampApi = onrampApi,
|
||||
expressHistoryDao = expressHistoryDao,
|
||||
historyIndexDao = historyIndexDao,
|
||||
expressSyncStateDao = expressSyncStateDao,
|
||||
tokenInfoRepository = tokenInfoRepository,
|
||||
database = database,
|
||||
appScope = TestAppCoroutineScope(),
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
clearMocks(exchangeApi, onrampApi, expressHistoryDao, expressSyncStateDao, tokenInfoRepository)
|
||||
clearMocks(exchangeApi, onrampApi, expressHistoryDao, historyIndexDao, expressSyncStateDao, tokenInfoRepository)
|
||||
// Run the withTransaction block inline so the DAO writes inside it actually happen and can be verified.
|
||||
mockkStatic("androidx.room.RoomDatabaseKt")
|
||||
val block = slot<suspend () -> Any?>()
|
||||
coEvery { database.withTransaction(capture(block)) } coAnswers { block.captured.invoke() }
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
unmockkStatic("androidx.room.RoomDatabaseKt")
|
||||
}
|
||||
|
||||
// region exchange history
|
||||
|
|
@ -74,7 +98,7 @@ internal class DefaultExpressHistoryRepositoryTest {
|
|||
coVerify(exactly = 1) {
|
||||
exchangeApi.getHistory(userWalletId = USER_WALLET_ID_VALUE, fromAddress = ADDRESS, cursor = AFTER_CURSOR, limit = DEFAULT_LIMIT)
|
||||
}
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertExchanges(listOf(item.toEntity(ADDRESS))) }
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertExchanges(listOfNotNull(item.toEntity())) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -130,7 +154,7 @@ internal class DefaultExpressHistoryRepositoryTest {
|
|||
coVerify(exactly = 1) {
|
||||
exchangeApi.getHistoryDelta(userWalletId = USER_WALLET_ID_VALUE, fromAddress = ADDRESS, cursor = DELTA_CURSOR, limit = DEFAULT_LIMIT)
|
||||
}
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertExchanges(listOf(item.toEntity(ADDRESS))) }
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertExchanges(listOfNotNull(item.toEntity())) }
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
|
@ -155,7 +179,7 @@ internal class DefaultExpressHistoryRepositoryTest {
|
|||
coVerify(exactly = 1) {
|
||||
onrampApi.getHistory(userWalletId = USER_WALLET_ID_VALUE, payoutAddress = ADDRESS, afterCursor = AFTER_CURSOR, limit = DEFAULT_LIMIT)
|
||||
}
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertOnramps(listOf(item.toEntity(ADDRESS))) }
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertOnramps(listOf(item.toEntity())) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -176,7 +200,7 @@ internal class DefaultExpressHistoryRepositoryTest {
|
|||
coVerify(exactly = 1) {
|
||||
onrampApi.getHistoryDelta(userWalletId = USER_WALLET_ID_VALUE, payoutAddress = ADDRESS, cursor = DELTA_CURSOR, limit = DEFAULT_LIMIT)
|
||||
}
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertOnramps(listOf(item.toEntity(ADDRESS))) }
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertOnramps(listOf(item.toEntity())) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -206,10 +230,11 @@ internal class DefaultExpressHistoryRepositoryTest {
|
|||
val item = createExchangeItem()
|
||||
|
||||
// WHEN
|
||||
repository.storeExchanges(ownerAddress = ADDRESS, items = listOf(item))
|
||||
repository.storeExchanges(items = listOf(item))
|
||||
|
||||
// THEN
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertExchanges(listOf(item.toEntity(ADDRESS))) }
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertExchanges(listOfNotNull(item.toEntity())) }
|
||||
coVerify(exactly = 1) { historyIndexDao.upsert(item.toEntity()!!.toHistoryIndexEntities()) }
|
||||
coVerify(exactly = 1) {
|
||||
tokenInfoRepository.fetchMissing(
|
||||
setOf(
|
||||
|
|
@ -226,10 +251,11 @@ internal class DefaultExpressHistoryRepositoryTest {
|
|||
val item = createOnrampItem()
|
||||
|
||||
// WHEN
|
||||
repository.storeOnramps(ownerAddress = ADDRESS, items = listOf(item))
|
||||
repository.storeOnramps(items = listOf(item))
|
||||
|
||||
// THEN
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertOnramps(listOf(item.toEntity(ADDRESS))) }
|
||||
coVerify(exactly = 1) { expressHistoryDao.upsertOnramps(listOf(item.toEntity())) }
|
||||
coVerify(exactly = 1) { historyIndexDao.upsert(listOf(item.toEntity().toHistoryIndexEntity())) }
|
||||
coVerify(exactly = 1) {
|
||||
tokenInfoRepository.fetchMissing(setOf(ExpressAsset.ID(networkId = "bitcoin", contractAddress = "0xtoContract")))
|
||||
}
|
||||
|
|
@ -238,12 +264,27 @@ internal class DefaultExpressHistoryRepositoryTest {
|
|||
@Test
|
||||
fun `GIVEN empty items WHEN store THEN does nothing`() = runTest {
|
||||
// WHEN
|
||||
repository.storeExchanges(ownerAddress = ADDRESS, items = emptyList())
|
||||
repository.storeOnramps(ownerAddress = ADDRESS, items = emptyList())
|
||||
repository.storeExchanges(items = emptyList())
|
||||
repository.storeOnramps(items = emptyList())
|
||||
|
||||
// THEN
|
||||
coVerify(exactly = 0) { expressHistoryDao.upsertExchanges(any()) }
|
||||
coVerify(exactly = 0) { expressHistoryDao.upsertOnramps(any()) }
|
||||
coVerify(exactly = 0) { historyIndexDao.upsert(any<List<HistoryIndexEntity>>()) }
|
||||
coVerify(exactly = 0) { tokenInfoRepository.fetchMissing(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN exchange with null fromAddress WHEN storeExchanges THEN it is skipped`() = runTest {
|
||||
// GIVEN
|
||||
val item = createExchangeItem().copy(fromAddress = null)
|
||||
|
||||
// WHEN
|
||||
repository.storeExchanges(items = listOf(item))
|
||||
|
||||
// THEN
|
||||
coVerify(exactly = 0) { expressHistoryDao.upsertExchanges(any()) }
|
||||
coVerify(exactly = 0) { historyIndexDao.upsert(any<List<HistoryIndexEntity>>()) }
|
||||
coVerify(exactly = 0) { tokenInfoRepository.fetchMissing(any()) }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ internal class ExpressTxHistoryConverterTest {
|
|||
toActualAmount: String? = null,
|
||||
) = ExpressExchangeEntity(
|
||||
txId = "tx-1",
|
||||
ownerAddress = "owner",
|
||||
providerId = "provider",
|
||||
fromAddress = "owner",
|
||||
payinAddress = "payin-addr",
|
||||
|
|
@ -131,7 +130,6 @@ internal class ExpressTxHistoryConverterTest {
|
|||
status: String = "finished",
|
||||
) = ExpressOnrampEntity(
|
||||
txId = "onramp-1",
|
||||
ownerAddress = "owner",
|
||||
providerId = "provider",
|
||||
payoutAddress = "owner",
|
||||
status = status,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
package com.tangem.data.txhistory.repository.converter
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.datasource.local.txhistory.db.entity.HistoryIndexEntity
|
||||
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressExchangeEntity
|
||||
import com.tangem.datasource.local.txhistory.db.entity.express.ExpressOnrampEntity
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.joda.time.DateTime
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
/**
|
||||
* Verifies the indexing rules that build [HistoryIndexEntity] rows: a swap is indexed under both its from- and
|
||||
|
||||
* into the sort time.
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class HistoryIndexConverterTest {
|
||||
|
||||
@Test
|
||||
fun `GIVEN swap with distinct from and payout WHEN indexed THEN one row per address`() {
|
||||
// Arrange
|
||||
val entity = exchange(txId = "tx1", from = "addrA", payout = "addrB", createdAt = CREATED_AT)
|
||||
|
||||
// Act
|
||||
val rows = entity.toHistoryIndexEntities()
|
||||
|
||||
// Assert
|
||||
assertThat(rows).containsExactly(
|
||||
indexRow(HistoryIndexEntity.Type.EXCHANGE, "tx1", "addrA"),
|
||||
indexRow(HistoryIndexEntity.Type.EXCHANGE, "tx1", "addrB"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN swap with equal from and payout WHEN indexed THEN de-duplicated to one row`() {
|
||||
// Arrange
|
||||
val entity = exchange(txId = "tx1", from = "same", payout = "same", createdAt = CREATED_AT)
|
||||
|
||||
// Act
|
||||
val rows = entity.toHistoryIndexEntities()
|
||||
|
||||
// Assert
|
||||
assertThat(rows).containsExactly(indexRow(HistoryIndexEntity.Type.EXCHANGE, "tx1", "same"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN onramp WHEN indexed THEN single row under payout address`() {
|
||||
// Arrange
|
||||
val entity = onramp(txId = "tx2", payout = "addrP", createdAt = CREATED_AT)
|
||||
|
||||
// Act
|
||||
val row = entity.toHistoryIndexEntity()
|
||||
|
||||
// Assert
|
||||
assertThat(row).isEqualTo(indexRow(HistoryIndexEntity.Type.ONRAMP, "tx2", "addrP"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN ISO-8601 created_at WHEN indexed THEN parsed into sort time millis`() {
|
||||
// Arrange
|
||||
val entity = onramp(txId = "tx2", payout = "addrP", createdAt = "2026-07-03T10:00:00.000Z")
|
||||
|
||||
// Act
|
||||
val row = entity.toHistoryIndexEntity()
|
||||
|
||||
// Assert
|
||||
assertThat(row.sortTimeMillis).isEqualTo(DateTime.parse("2026-07-03T10:00:00.000Z").millis)
|
||||
}
|
||||
|
||||
private fun exchange(txId: String, from: String, payout: String, createdAt: String) =
|
||||
mockk<ExpressExchangeEntity> {
|
||||
every { this@mockk.txId } returns txId
|
||||
every { fromAddress } returns from
|
||||
every { payoutAddress } returns payout
|
||||
every { this@mockk.createdAt } returns createdAt
|
||||
}
|
||||
|
||||
private fun onramp(txId: String, payout: String, createdAt: String) =
|
||||
mockk<ExpressOnrampEntity> {
|
||||
every { this@mockk.txId } returns txId
|
||||
every { payoutAddress } returns payout
|
||||
every { this@mockk.createdAt } returns createdAt
|
||||
}
|
||||
|
||||
private fun indexRow(type: HistoryIndexEntity.Type, txId: String, address: String) = HistoryIndexEntity(
|
||||
type = type.value,
|
||||
entityId = txId,
|
||||
address = address,
|
||||
sortTimeMillis = DateTime.parse(CREATED_AT).millis,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
const val CREATED_AT = "2026-07-03T10:00:00.000Z"
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +88,6 @@ internal class ExpressTransactionAssetFactoryTest {
|
|||
/** A swap from a native coin (empty contract) to an Ethereum token. */
|
||||
private fun coinToTokenSwap() = ExpressExchangeEntity(
|
||||
txId = "tx-1",
|
||||
ownerAddress = "owner",
|
||||
providerId = "provider",
|
||||
fromAddress = "owner",
|
||||
payinAddress = "payin-addr",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue