mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-07-22 13:45:48 +00:00
test: expand full-architecture test to cover the whole Pezkuwi ecosystem
Was HEZ-on-Asset-Hub-only. Now data-driven from wallet-utils' new pezkuwi_assets_for_testBalance.json (TEST_ASSETS_URL), asserting every asset - HEZ/PEZ/USDT/DOT/ETH/BTC across Pezkuwi's 3 chains - gets an assets DB row written via a single shared BalancesUpdateSystem run, not just one asset on one chain. Failure message lists exactly which assets never synced, by name and chain.
This commit is contained in:
+37
-28
@@ -2,27 +2,40 @@ package io.novafoundation.nova.balances
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.google.gson.Gson
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.fromJson
|
||||
import io.novafoundation.nova.core.model.CryptoType
|
||||
import io.novafoundation.nova.core_db.dao.AssetDao
|
||||
import io.novafoundation.nova.core_db.dao.MetaAccountDao
|
||||
import io.novafoundation.nova.core_db.di.DbApi
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
|
||||
import io.novafoundation.nova.feature_assets.di.AssetsFeatureApi
|
||||
import io.novafoundation.nova.runtime.BuildConfig.TEST_ASSETS_URL
|
||||
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import java.net.URL
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
private data class AssetFixture(val chainId: String, val chainName: String, val assetId: Int, val symbol: String)
|
||||
private data class AssetsFixtureFile(val account: String, val assets: List<AssetFixture>)
|
||||
|
||||
/**
|
||||
* Exercises the ACTUAL production balance-sync pipeline (BalancesUpdateSystem -> AssetCache/AssetDao) end to
|
||||
* end, unlike [BalancesIntegrationTest] which bypasses it entirely via a direct low-level storage query. This
|
||||
* is meant to answer one question with hard evidence, not speculation: does the app's real, running background
|
||||
* sync ever write an `assets` cache row for HEZ on the Pezkuwi Asset Hub chain, for a real, well-funded account?
|
||||
* is meant to answer one question with hard evidence, not speculation: for a real, well-funded mainnet Founder
|
||||
* account, does the app's real, running background sync ever write an `assets` cache row for every asset in
|
||||
* wallet-utils' pezkuwi_assets_for_testBalance.json (HEZ/PEZ/USDT/DOT/ETH/BTC across Pezkuwi's chains) - not
|
||||
* just the native balance on one chain, which is all the older [BalancesIntegrationTest] fixture covers.
|
||||
*
|
||||
* A single watch-only account is created and selected once, so a single BalancesUpdateSystem run has to
|
||||
* successfully sync every asset in the fixture - this is what actually caught the 2026-07-09 HEZ-on-Asset-Hub
|
||||
* silent sync failure (5 of 6 assets on that chain synced fine; only HEZ silently never did).
|
||||
*
|
||||
* BalancesUpdateSystem.start() is a cold flow - in production it's only ever collected by RootInteractor,
|
||||
* which is wired to the root Activity/ViewModel lifecycle. A bare instrumented test never launches that
|
||||
@@ -30,17 +43,11 @@ import kotlin.time.Duration.Companion.seconds
|
||||
* uses, instead of relying on app UI lifecycle to start it.
|
||||
*
|
||||
* If this test fails, the standard per-updater error logs already wired into BalancesUpdateSystem/
|
||||
* FullSyncPaymentUpdater/NativeAssetBalance show exactly which decision branch or exception is responsible -
|
||||
* not another layer of inference from silence.
|
||||
* FullSyncPaymentUpdater/NativeAssetBalance/StatemineAssetBalance show exactly which decision branch or
|
||||
* exception is responsible - not another layer of inference from silence.
|
||||
*/
|
||||
class PezkuwiFullArchitectureBalancesTest {
|
||||
|
||||
// Mainnet Founder account (SS58, generic substrate prefix) - verified live via @pezkuwi/api on 2026-07-09
|
||||
// to hold a substantial non-zero, non-frozen free HEZ balance on Pezkuwi Asset Hub (180,297.80 HEZ).
|
||||
private val founderSubstrateAddress = "5CyuFfbF95rzBxru7c9yEsX4XmQXUxpLUcbj9RLg9K1cGiiF"
|
||||
private val pezkuwiAssetHubChainId = "e7c15092dcbe3f320260ddbbc685bfceed9125a3b3d8436db2766201dec3b949"
|
||||
private val hezAssetId = 0
|
||||
|
||||
private val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
|
||||
private val dbApi = FeatureUtils.getFeature<DbApi>(context, DbApi::class.java)
|
||||
@@ -50,36 +57,38 @@ class PezkuwiFullArchitectureBalancesTest {
|
||||
private val assetsFeatureApi = FeatureUtils.getFeature<AssetsFeatureApi>(context, AssetsFeatureApi::class.java)
|
||||
|
||||
@Test
|
||||
fun testPezkuwiAssetHubHezBalanceActuallySyncs() = runBlocking {
|
||||
fun testPezkuwiEcosystemAssetsActuallySync() = runBlocking {
|
||||
val fixture: AssetsFixtureFile = Gson().fromJson(URL(TEST_ASSETS_URL).readText())
|
||||
|
||||
val updateSystemJob = launch { assetsFeatureApi.updateSystem.start().collect {} }
|
||||
|
||||
try {
|
||||
val metaId = insertAndSelectFounderWatchAccount(metaAccountDao)
|
||||
val metaId = insertAndSelectWatchAccount(metaAccountDao, fixture.account)
|
||||
|
||||
val assetRow = withTimeoutOrNull(90.seconds) {
|
||||
while (true) {
|
||||
val asset = assetDao.getAsset(metaId, pezkuwiAssetHubChainId, hezAssetId)
|
||||
if (asset != null) return@withTimeoutOrNull asset
|
||||
|
||||
delay(2.seconds)
|
||||
val stillMissing = fixture.assets.toMutableList()
|
||||
withTimeoutOrNull(120.seconds) {
|
||||
while (stillMissing.isNotEmpty()) {
|
||||
stillMissing.removeAll { asset ->
|
||||
assetDao.getAsset(metaId, asset.chainId, asset.assetId) != null
|
||||
}
|
||||
if (stillMissing.isNotEmpty()) delay(2.seconds)
|
||||
}
|
||||
@Suppress("UNREACHABLE_CODE")
|
||||
null
|
||||
}
|
||||
|
||||
assertNotNull(
|
||||
"No `assets` row was ever written for HEZ on Pezkuwi Asset Hub (metaId=$metaId) within 90s. " +
|
||||
"The real BalancesUpdateSystem pipeline never completed a sync for this asset - check the " +
|
||||
"standard FullSyncPaymentUpdater/NativeAssetBalance error logs in logcat.",
|
||||
assetRow
|
||||
assertTrue(
|
||||
"No `assets` row was ever written for: ${stillMissing.joinToString { "${it.symbol} on ${it.chainName}" }} " +
|
||||
"(metaId=$metaId) within 120s, out of ${fixture.assets.size} total. The real BalancesUpdateSystem " +
|
||||
"pipeline never completed a sync for these - check the standard FullSyncPaymentUpdater/" +
|
||||
"NativeAssetBalance/StatemineAssetBalance error logs in logcat.",
|
||||
stillMissing.isEmpty()
|
||||
)
|
||||
} finally {
|
||||
updateSystemJob.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun insertAndSelectFounderWatchAccount(dao: MetaAccountDao): Long {
|
||||
val accountId = founderSubstrateAddress.toAccountId()
|
||||
private suspend fun insertAndSelectWatchAccount(dao: MetaAccountDao, substrateAddress: String): Long {
|
||||
val accountId = substrateAddress.toAccountId()
|
||||
|
||||
val metaAccount = MetaAccountLocal(
|
||||
substratePublicKey = accountId,
|
||||
|
||||
@@ -13,6 +13,7 @@ android {
|
||||
buildConfigField "String", "PRE_CONFIGURED_CHAIN_DETAILS_URL", "\"https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/chains/v22/preConfigured/details\""
|
||||
|
||||
buildConfigField "String", "TEST_CHAINS_URL", "\"https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/tests/chains_for_testBalance.json\""
|
||||
buildConfigField "String", "TEST_ASSETS_URL", "\"https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/tests/pezkuwi_assets_for_testBalance.json\""
|
||||
|
||||
buildConfigField "String", "INFURA_API_KEY", readStringSecret("INFURA_API_KEY")
|
||||
buildConfigField "String", "DWELLIR_API_KEY", readStringSecret("DWELLIR_API_KEY")
|
||||
|
||||
Reference in New Issue
Block a user