Test cleanup, deleting outdated tests.
This commit is contained in:
parent
527eb50439
commit
93c01a9f3f
|
@ -1,50 +0,0 @@
|
|||
package cash.z.wallet.sdk.db
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class CacheDbIntegrationTest {
|
||||
@get:Rule
|
||||
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Test
|
||||
fun testDbExists() {
|
||||
assertNotNull(db)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDaoExists() {
|
||||
assertNotNull(dao)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private lateinit var dao: CompactBlockDao
|
||||
private lateinit var db: CompactBlockDb
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setup() {
|
||||
// TODO: put this database in the assets directory and open it from there via .openHelperFactory(new AssetSQLiteOpenHelperFactory()) seen here https://github.com/albertogiunta/sqliteAsset
|
||||
db = Room
|
||||
.databaseBuilder(ApplicationProvider.getApplicationContext(), CompactBlockDb::class.java, "dummy-cache.db")
|
||||
// .databaseBuilder(ApplicationProvider.getApplicationContext(), CompactBlockDb::class.java, "compact-blocks.db")
|
||||
.setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
.apply { dao = complactBlockDao() }
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun close() {
|
||||
db.close()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package cash.z.wallet.sdk.db
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class DerivedDbIntegrationTest {
|
||||
@get:Rule
|
||||
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Test
|
||||
fun testDbExists() {
|
||||
assertNotNull(db)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDaoExists_Transaction() {
|
||||
assertNotNull(transactions)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDaoExists_Block() {
|
||||
assertNotNull(blocks)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCount_Block() {
|
||||
assertEquals(80101, blocks.count())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoteQuery() {
|
||||
val all = transactions.getReceivedTransactions()
|
||||
assertEquals(3, all.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTransactionDaoPrepopulated() {
|
||||
val tran = transactions.findById(1)
|
||||
|
||||
assertEquals(343987, tran?.minedHeight)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private lateinit var transactions: TransactionDao
|
||||
private lateinit var blocks: BlockDao
|
||||
private lateinit var db: DerivedDataDb
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setup() {
|
||||
// TODO: put this database in the assets directory and open it from there via .openHelperFactory(new AssetSQLiteOpenHelperFactory()) seen here https://github.com/albertogiunta/sqliteAsset
|
||||
db = Room
|
||||
.databaseBuilder(ApplicationProvider.getApplicationContext(), DerivedDataDb::class.java, "new-data-glue2.db")
|
||||
.setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
.apply {
|
||||
transactions = transactionDao()
|
||||
blocks = blockDao()
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun close() {
|
||||
db.close()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
package cash.z.wallet.sdk.db
|
||||
|
||||
import android.util.Log
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import cash.z.wallet.sdk.entity.CompactBlockEntity
|
||||
import cash.z.wallet.sdk.jni.RustBackend
|
||||
import cash.z.wallet.sdk.jni.RustBackendWelding
|
||||
import cash.z.wallet.sdk.rpc.CompactTxStreamerGrpc
|
||||
import cash.z.wallet.sdk.rpc.Service
|
||||
import cash.z.wallet.sdk.rpc.Service.BlockID
|
||||
import cash.z.wallet.sdk.rpc.Service.BlockRange
|
||||
import io.grpc.ManagedChannel
|
||||
import io.grpc.ManagedChannelBuilder
|
||||
import org.junit.AfterClass
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class GlueIntegrationTest {
|
||||
@get:Rule
|
||||
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Test
|
||||
fun testDbExists() {
|
||||
Log.e("tezt", "addData")
|
||||
addData()
|
||||
Log.e("tezt", "scanData")
|
||||
scanData()
|
||||
Log.e("tezt", "checkResults")
|
||||
checkResults()
|
||||
}
|
||||
|
||||
private fun checkResults() {
|
||||
Thread.sleep(15000L)
|
||||
}
|
||||
|
||||
private fun addData() {
|
||||
val result = blockingStub.getBlockRange(
|
||||
BlockRange.newBuilder()
|
||||
.setStart(BlockID.newBuilder().setHeight(373070L).build())
|
||||
.setEnd(BlockID.newBuilder().setHeight(373085L).build())
|
||||
.build()
|
||||
)
|
||||
while (result.hasNext()) {
|
||||
val compactBlock = result.next()
|
||||
dao.insert(CompactBlockEntity(compactBlock.height.toInt(), compactBlock.toByteArray()))
|
||||
System.err.println("stored block at height: ${compactBlock.height} with time ${compactBlock.time}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun scanData() {
|
||||
val dbFileName = "/data/user/0/cash.z.wallet.sdk.test/databases/new-data-glue.db"
|
||||
rustBackend.initDataDb()
|
||||
rustBackend.initAccountsTable("dummyseed".toByteArray(), 1)
|
||||
|
||||
|
||||
Log.e("tezt", "scanning blocks...")
|
||||
val result = rustBackend.scanBlocks()
|
||||
System.err.println("done.")
|
||||
}
|
||||
|
||||
fun heightOf(height: Long): Service.BlockID {
|
||||
return BlockID.newBuilder().setHeight(height).build()
|
||||
}
|
||||
|
||||
companion object {
|
||||
// jni
|
||||
val rustBackend: RustBackendWelding = RustBackend
|
||||
|
||||
// db
|
||||
private lateinit var dao: CompactBlockDao
|
||||
private lateinit var db: CompactBlockDb
|
||||
private const val cacheDbName = "new-dummy-cache-glue.db"
|
||||
private const val cacheDbPath = "/data/user/0/cash.z.wallet.sdk.test/databases/$cacheDbName"
|
||||
|
||||
// grpc
|
||||
lateinit var blockingStub: CompactTxStreamerGrpc.CompactTxStreamerBlockingStub
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setup() {
|
||||
rustBackend.create(ApplicationProvider.getApplicationContext())
|
||||
|
||||
val channel = ManagedChannelBuilder.forAddress("10.0.2.2", 9067).usePlaintext().build()
|
||||
blockingStub = CompactTxStreamerGrpc.newBlockingStub(channel)
|
||||
|
||||
db = Room
|
||||
.databaseBuilder(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
CompactBlockDb::class.java,
|
||||
cacheDbName
|
||||
)
|
||||
.setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
.apply { dao = complactBlockDao() }
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun close() {
|
||||
db.close()
|
||||
(blockingStub.channel as ManagedChannel).shutdown().awaitTermination(2000L, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
package cash.z.wallet.sdk.db
|
||||
|
||||
import android.util.Log
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import cash.z.wallet.sdk.entity.CompactBlockEntity
|
||||
import cash.z.wallet.sdk.jni.RustBackend
|
||||
import cash.z.wallet.sdk.jni.RustBackendWelding
|
||||
import cash.z.wallet.sdk.rpc.CompactTxStreamerGrpc
|
||||
import cash.z.wallet.sdk.rpc.Service
|
||||
import cash.z.wallet.sdk.rpc.Service.BlockID
|
||||
import cash.z.wallet.sdk.rpc.Service.BlockRange
|
||||
import io.grpc.ManagedChannel
|
||||
import io.grpc.ManagedChannelBuilder
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class GlueSetupIntegrationTest {
|
||||
@get:Rule
|
||||
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Test
|
||||
fun testDbExists() {
|
||||
assertNotNull(db)
|
||||
// Log.e("tezt", "addData")
|
||||
// addData()
|
||||
// Log.e("tezt", "scanData")
|
||||
// scanData()
|
||||
// Log.e("tezt", "checkResults")
|
||||
// checkResults()
|
||||
}
|
||||
|
||||
private fun checkResults() {
|
||||
Thread.sleep(15000L)
|
||||
}
|
||||
|
||||
private fun addData() {
|
||||
val result = blockingStub.getBlockRange(
|
||||
BlockRange.newBuilder()
|
||||
.setStart(BlockID.newBuilder().setHeight(373070L).build())
|
||||
.setEnd(BlockID.newBuilder().setHeight(373085L).build())
|
||||
.build()
|
||||
)
|
||||
while (result.hasNext()) {
|
||||
val compactBlock = result.next()
|
||||
dao.insert(CompactBlockEntity(compactBlock.height.toInt(), compactBlock.toByteArray()))
|
||||
System.err.println("stored block at height: ${compactBlock.height}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun scanData() {
|
||||
Log.e("tezt", "scanning blocks...")
|
||||
val result = rustBackend.scanBlocks()
|
||||
System.err.println("done.")
|
||||
}
|
||||
|
||||
fun heightOf(height: Long): Service.BlockID {
|
||||
return BlockID.newBuilder().setHeight(height).build()
|
||||
}
|
||||
|
||||
companion object {
|
||||
// jni
|
||||
val rustBackend: RustBackendWelding = RustBackend
|
||||
|
||||
// db
|
||||
private lateinit var dao: CompactBlockDao
|
||||
private lateinit var db: CompactBlockDb
|
||||
private const val cacheDbName = "cache-glue.db"
|
||||
private const val dataDbName = "data-glue.db"
|
||||
|
||||
// grpc
|
||||
lateinit var blockingStub: CompactTxStreamerGrpc.CompactTxStreamerBlockingStub
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setup() {
|
||||
rustBackend.create(ApplicationProvider.getApplicationContext(), cacheDbName, dataDbName)
|
||||
|
||||
val channel = ManagedChannelBuilder.forAddress("10.0.2.2", 9067).usePlaintext().build()
|
||||
blockingStub = CompactTxStreamerGrpc.newBlockingStub(channel)
|
||||
|
||||
db = Room
|
||||
.databaseBuilder(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
CompactBlockDb::class.java,
|
||||
cacheDbName
|
||||
)
|
||||
.setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
.apply { dao = complactBlockDao() }
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun close() {
|
||||
db.close()
|
||||
(blockingStub.channel as ManagedChannel).shutdown().awaitTermination(2000L, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package cash.z.wallet.sdk.db
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import cash.z.wallet.sdk.block.CompactBlockDbStore
|
||||
import cash.z.wallet.sdk.block.CompactBlockDownloader
|
||||
import cash.z.wallet.sdk.block.CompactBlockProcessor
|
||||
//import cash.z.wallet.sdk.transaction.PollingTransactionRepository
|
||||
import cash.z.wallet.sdk.ext.TroubleshootingTwig
|
||||
import cash.z.wallet.sdk.ext.Twig
|
||||
import cash.z.wallet.sdk.ext.SampleSeedProvider
|
||||
import cash.z.wallet.sdk.ext.SampleSpendingKeyProvider
|
||||
import cash.z.wallet.sdk.jni.RustBackend
|
||||
//import cash.z.wallet.sdk.secure.Wallet
|
||||
import cash.z.wallet.sdk.service.LightWalletGrpcService
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
/*
|
||||
TODO:
|
||||
setup a test that we can run and just watch things happen, to give confidence that logging is expressive enough to
|
||||
verify that the SDK is behaving as expected.
|
||||
*/
|
||||
class IntegrationTest {
|
||||
|
||||
private val dataDbName = "IntegrationData41.db"
|
||||
private val cacheDdName = "IntegrationCache41.db"
|
||||
private val context = InstrumentationRegistry.getInstrumentation().context
|
||||
|
||||
private lateinit var downloader: CompactBlockDownloader
|
||||
private lateinit var processor: CompactBlockProcessor
|
||||
// private lateinit var wallet: Wallet
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
deleteDbs()
|
||||
Twig.plant(TroubleshootingTwig())
|
||||
}
|
||||
|
||||
private fun deleteDbs() {
|
||||
// prior to each run, delete the DBs for sanity
|
||||
listOf(cacheDdName, dataDbName).map { context.getDatabasePath(it).absoluteFile }.forEach {
|
||||
println("Deleting ${it.name}")
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 120_000L)
|
||||
fun testSync() = runBlocking<Unit> {
|
||||
// val rustBackend = RustBackend.init(context)
|
||||
|
||||
val lightwalletService = LightWalletGrpcService(context,"192.168.1.134")
|
||||
// val compactBlockStore = CompactBlockDbStore(context)
|
||||
|
||||
// downloader = CompactBlockDownloader(lightwalletService, compactBlockStore)
|
||||
// processor = CompactBlockProcessor(downloader, repository, rustBackend)
|
||||
// repository = PollingTransactionRepository(context, dataDbName, 10_000L)
|
||||
// wallet = Wallet(
|
||||
// context,
|
||||
// rustBackend,
|
||||
// SampleSeedProvider("dummyseed"),
|
||||
// SampleSpendingKeyProvider("dummyseed")
|
||||
// )
|
||||
|
||||
// repository.start(this)
|
||||
// synchronizer = SdkSynchronizer(wallet, repository, , processor)
|
||||
// processor,
|
||||
// repository,
|
||||
// ActiveTransactionManager(repository, lightwalletService, wallet),
|
||||
// wallet,
|
||||
// 1000
|
||||
// ).start(this)
|
||||
//
|
||||
// for(i in synchronizer.progress()) {
|
||||
// twig("made progress: $i")
|
||||
// }
|
||||
}
|
||||
|
||||
companion object {
|
||||
// private lateinit var synchronizer: Synchronizer
|
||||
// private lateinit var repository: PollingTransactionRepository
|
||||
@AfterClass
|
||||
fun tearDown() {
|
||||
// repository.stop()
|
||||
// synchronizer.stop()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
package cash.z.wallet.sdk.db
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class ManualTransactionSender {
|
||||
@get:Rule
|
||||
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Test
|
||||
fun sendTransactionViaTest() {
|
||||
val transaction = transactions.findById(12)
|
||||
val hex = transaction?.raw?.toHex()
|
||||
assertEquals("foo", hex)
|
||||
}
|
||||
|
||||
private fun ByteArray.toHex(): String {
|
||||
val sb = StringBuilder(size * 2)
|
||||
for (b in this)
|
||||
sb.append(String.format("%02x", b))
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private lateinit var transactions: TransactionDao
|
||||
private lateinit var db: DerivedDataDb
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setup() {
|
||||
// TODO: put this database in the assets directory and open it from there via .openHelperFactory(new AssetSQLiteOpenHelperFactory()) seen here https://github.com/albertogiunta/sqliteAsset
|
||||
db = Room
|
||||
.databaseBuilder(ApplicationProvider.getApplicationContext(), DerivedDataDb::class.java, "wallet_data1202.db")
|
||||
.setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
.apply {
|
||||
transactions = transactionDao()
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun close() {
|
||||
db.close()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package cash.z.wallet.sdk.jni
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Test
|
||||
|
||||
class RustBackendTest {
|
||||
|
||||
@Test
|
||||
fun testGetAddress_exists() {
|
||||
assertNotNull(rustBackend.getAddress(0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetAddress_valid() {
|
||||
val address = rustBackend.getAddress(0)
|
||||
val expectedAddress = "ztestsapling1snmqdnfqnc407pvqw7sld8w5zxx6nd0523kvlj4jf39uvxvh7vn0hs3q38n07806dwwecqwke3t"
|
||||
assertEquals("Invalid address", expectedAddress, address)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScanBlocks() {
|
||||
rustBackend.initDataDb()
|
||||
rustBackend.initAccountsTable("dummyseed".toByteArray(), 1)
|
||||
|
||||
// note: for this to work, the db file below must be uploaded to the device. Eventually, this test will be self-contained and remove that requirement.
|
||||
val result = rustBackend.scanBlocks()
|
||||
// Thread.sleep(15 * DateUtils.MINUTE_IN_MILLIS)
|
||||
assertEquals("Invalid number of results", 3, 3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSend() {
|
||||
rustBackend.createToAddress(
|
||||
0,
|
||||
"dummykey",
|
||||
"ztestsapling1fg82ar8y8whjfd52l0xcq0w3n7nn7cask2scp9rp27njeurr72ychvud57s9tu90fdqgwdt07lg",
|
||||
210_000
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val rustBackend: RustBackendWelding = RustBackend.init(ApplicationProvider.getApplicationContext() as Context, "rustTest")
|
||||
}
|
||||
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package cash.z.wallet.sdk.db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import cash.z.wallet.sdk.secure.Wallet
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
|
||||
class WalletTest {
|
||||
val context: Context = InstrumentationRegistry.getInstrumentation().context
|
||||
|
||||
@Test
|
||||
fun testLoadDefaultWallet() {
|
||||
val birthday = Wallet.loadBirthdayFromAssets(context, 280000)
|
||||
assertEquals("Invalid tree", "000000", birthday.tree)
|
||||
assertEquals("Invalid height", 280000, birthday.height)
|
||||
assertEquals("Invalid time", 1535262293, birthday.time)
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ class IntegrationTest {
|
|||
|
||||
@Test
|
||||
fun testLoadBirthday() {
|
||||
val (height, hash, time, tree) = Initializer.loadBirthdayFromAssets(context, ZcashSdk.SAPLING_ACTIVATION_HEIGHT)
|
||||
val (height, hash, time, tree) = Initializer.loadBirthdayFromAssets(context, ZcashSdk.SAPLING_ACTIVATION_HEIGHT + 1)
|
||||
assertEquals(ZcashSdk.SAPLING_ACTIVATION_HEIGHT, height)
|
||||
}
|
||||
|
||||
|
@ -105,6 +105,7 @@ class IntegrationTest {
|
|||
private val synchronizer: Synchronizer = Synchronizer(
|
||||
context,
|
||||
host,
|
||||
443,
|
||||
seed
|
||||
)
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ import kotlin.math.roundToInt
|
|||
* Responsible for processing the compact blocks that are received from the lightwallet server. This class encapsulates
|
||||
* all the business logic required to validate and scan the blockchain and is therefore tightly coupled with
|
||||
* librustzcash.
|
||||
*
|
||||
* @param minimumHeight the lowest height that we could care about. This is mostly used during
|
||||
* reorgs as a backstop to make sure we do not rewind beyond sapling activation. It also is factored
|
||||
* in when considering initial range to download. In most cases, this should be the birthday height
|
||||
* of the current wallet--the height before which we do not need to scan for transactions.
|
||||
*/
|
||||
@OpenForTesting
|
||||
class CompactBlockProcessor(
|
||||
|
@ -76,7 +81,7 @@ class CompactBlockProcessor(
|
|||
consecutiveChainErrors.getAndIncrement()
|
||||
}
|
||||
}
|
||||
} while (isActive && _state.value !is Stopped)
|
||||
} while (isActive && !_state.isClosedForSend && _state.value !is Stopped)
|
||||
twig("processor complete")
|
||||
stop()
|
||||
}
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
package cash.z.wallet.sdk.block
|
||||
|
||||
import cash.z.wallet.sdk.transaction.TransactionRepository
|
||||
import cash.z.wallet.sdk.entity.CompactBlockEntity
|
||||
import cash.z.wallet.sdk.ext.TroubleshootingTwig
|
||||
import cash.z.wallet.sdk.ext.Twig
|
||||
import cash.z.wallet.sdk.entity.CompactBlock
|
||||
import cash.z.wallet.sdk.entity.CompactBlockEntity
|
||||
import cash.z.wallet.sdk.ext.SAPLING_ACTIVATION_HEIGHT
|
||||
import cash.z.wallet.sdk.ext.ZcashSdk.SAPLING_ACTIVATION_HEIGHT
|
||||
import cash.z.wallet.sdk.jni.RustBackendWelding
|
||||
import cash.z.wallet.sdk.ext.twig
|
||||
import cash.z.wallet.sdk.jni.RustBackend
|
||||
import cash.z.wallet.sdk.service.LightWalletService
|
||||
import cash.z.wallet.sdk.transaction.TransactionRepository
|
||||
import com.nhaarman.mockitokotlin2.*
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.Timeout
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.junit.jupiter.MockitoExtension
|
||||
|
@ -25,12 +23,10 @@ import org.mockito.quality.Strictness
|
|||
|
||||
@ExtendWith(MockitoExtension::class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
internal class CompactBlockProcessorTest {
|
||||
|
||||
private val frequency = 5L
|
||||
class CompactBlockProcessorTest {
|
||||
|
||||
// Mocks/Spys
|
||||
@Mock lateinit var rustBackend: RustBackendWelding
|
||||
@Mock lateinit var rustBackend: RustBackend
|
||||
lateinit var processor: CompactBlockProcessor
|
||||
|
||||
// Test variables
|
||||
|
@ -41,14 +37,13 @@ internal class CompactBlockProcessorTest {
|
|||
|
||||
@BeforeEach
|
||||
fun setUp(
|
||||
@Mock lightwalletService: LightWalletService,
|
||||
@Mock compactBlockStore: CompactBlockStore,
|
||||
@Mock repository: TransactionRepository
|
||||
@Mock lightwalletService: LightWalletService,
|
||||
@Mock compactBlockStore: CompactBlockStore,
|
||||
@Mock repository: TransactionRepository
|
||||
) {
|
||||
Twig.plant(TroubleshootingTwig())
|
||||
|
||||
|
||||
lightwalletService.stub {
|
||||
lightwalletService.stub {
|
||||
onBlocking {
|
||||
getBlockRange(any())
|
||||
}.thenAnswer { invocation ->
|
||||
|
@ -90,66 +85,72 @@ internal class CompactBlockProcessorTest {
|
|||
}.thenAnswer { lastScannedHeight }
|
||||
}
|
||||
|
||||
val config = ProcessorConfig(retries = 1, blockPollFrequencyMillis = frequency, downloadBatchSize = 50_000)
|
||||
val downloader = spy(CompactBlockDownloader(lightwalletService, compactBlockStore))
|
||||
processor = spy(CompactBlockProcessor(config, downloader, repository, rustBackend))
|
||||
processor = spy(CompactBlockProcessor(downloader, repository, rustBackend))
|
||||
|
||||
whenever(rustBackend.validateCombinedChain(any(), any())).thenAnswer {
|
||||
whenever(rustBackend.validateCombinedChain()).thenAnswer {
|
||||
errorBlock
|
||||
}
|
||||
|
||||
whenever(rustBackend.scanBlocks(any(), any())).thenAnswer {
|
||||
whenever(rustBackend.scanBlocks()).thenAnswer {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(5)
|
||||
fun `check for OBOE when downloading`() = runBlocking {
|
||||
// if the last block downloaded was 350_000, then we already have that block and should start with 350_001
|
||||
lastDownloadedHeight = 350_000
|
||||
lastDownloadedHeight = 550_000
|
||||
|
||||
processBlocks()
|
||||
verify(processor).downloadNewBlocks(350_001..latestBlockHeight)
|
||||
processBlocksThen {
|
||||
verify(processor).downloadNewBlocks(350_001..latestBlockHeight)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(5)
|
||||
fun `chain error rewinds by expected amount`() = runBlocking {
|
||||
// if the highest block whose prevHash doesn't match happens at block 300_010
|
||||
errorBlock = 300_010
|
||||
errorBlock = 500_010
|
||||
|
||||
// then we should rewind the default (10) blocks
|
||||
val expectedBlock = errorBlock - processor.config.rewindDistance
|
||||
processBlocks(100L)
|
||||
verify(processor.downloader, atLeastOnce()).rewindToHeight(expectedBlock)
|
||||
verify(rustBackend, atLeastOnce()).rewindToHeight("", expectedBlock)
|
||||
assertNotNull(processor)
|
||||
val expectedBlock = errorBlock - 10
|
||||
processBlocksThen {
|
||||
twig("FINISHED PROCESSING!")
|
||||
verify(processor.downloader, atLeastOnce()).rewindToHeight(expectedBlock)
|
||||
verify(rustBackend, atLeastOnce()).rewindToHeight(expectedBlock)
|
||||
assertNotNull(processor)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
// @Timeout(5)
|
||||
fun `chain error downloads expected number of blocks`() = runBlocking {
|
||||
// if the highest block whose prevHash doesn't match happens at block 300_010
|
||||
// and our rewind distance is the default (10), then we want to download exactly ten blocks
|
||||
errorBlock = 300_010
|
||||
errorBlock = 500_010
|
||||
|
||||
// plus 1 because the range is inclusive
|
||||
val expectedRange = (errorBlock - processor.config.rewindDistance + 1)..latestBlockHeight
|
||||
processBlocks(1500L)
|
||||
verify(processor, atLeastOnce()).downloadNewBlocks(expectedRange)
|
||||
}
|
||||
|
||||
private fun processBlocks(delayMillis: Long? = null) = runBlocking {
|
||||
launch { processor.start() }
|
||||
val progressChannel = processor.progress()
|
||||
for (i in progressChannel) {
|
||||
if(i >= 100) {
|
||||
if(delayMillis != null) delay(delayMillis)
|
||||
processor.stop()
|
||||
break
|
||||
}
|
||||
val expectedRange = (errorBlock - 10 + 1)..latestBlockHeight
|
||||
processBlocksThen {
|
||||
verify(processor, atLeastOnce()).downloadNewBlocks(expectedRange)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix the fact that flows cause this not to work as originally coded. With a channel, we can stop observing once we reach 100. A flow makes that more difficult. The SDK behavior is still the same but testing that behavior is a little tricky without some refactors.
|
||||
private suspend fun processBlocksThen(block: suspend () -> Unit) = runBlocking {
|
||||
val scope = this
|
||||
launch {
|
||||
processor.start()
|
||||
}
|
||||
processor.progress.collect { i ->
|
||||
if(i >= 100) {
|
||||
block()
|
||||
processor.stop()
|
||||
}
|
||||
twig("processed $i")
|
||||
}
|
||||
twig("Done processing!")
|
||||
}
|
||||
}
|
|
@ -1,225 +1,225 @@
|
|||
package cash.z.wallet.sdk.transaction
|
||||
|
||||
import cash.z.wallet.sdk.dao.ClearedTransaction
|
||||
import kotlinx.coroutines.*
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextLong
|
||||
import kotlin.system.measureTimeMillis
|
||||
//import cash.z.wallet.sdk.dao.ClearedTransaction
|
||||
//import kotlinx.coroutines.*
|
||||
//import org.junit.jupiter.api.AfterEach
|
||||
//import org.junit.jupiter.api.BeforeEach
|
||||
//import org.junit.jupiter.api.Test
|
||||
//
|
||||
//import org.junit.jupiter.api.Assertions.*
|
||||
//import kotlin.random.Random
|
||||
//import kotlin.random.nextLong
|
||||
//import kotlin.system.measureTimeMillis
|
||||
|
||||
internal class MockSynchronizerTest {
|
||||
|
||||
private val transactionInterval = 200L
|
||||
private val activeTransactionInterval = 200L
|
||||
private val synchronizer = MockSynchronizer(transactionInterval, activeTransactionInterval)
|
||||
private val fastSynchronizer = MockSynchronizer(2L, 2L)
|
||||
private val allTransactionChannel = synchronizer.allTransactions()
|
||||
private val validAddress = "ztestsapling1yu2zy9aane2pje2qvm4qmn4k6q57y2d9ecs5vz0guthxx3m2aq57qm6hkx0520m9u9635xh6ttd"
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
synchronizer.start(CoroutineScope(Dispatchers.IO))
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
synchronizer.stop()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun allTransactions() = runBlocking {
|
||||
var total = 0
|
||||
val duration = measureTimeMillis {
|
||||
repeat(10) {
|
||||
val transactions = allTransactionChannel.receive()
|
||||
total += transactions.size
|
||||
println("received ${transactions.size} transactions")
|
||||
}
|
||||
}
|
||||
assertTrue(total > 0)
|
||||
assertTrue(duration > transactionInterval)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `never calling send yields zero sent transactions`() = runBlocking {
|
||||
val fastChannel = fastSynchronizer.start(fastSynchronizer).allTransactions()
|
||||
var transactions = fastChannel.receive()
|
||||
repeat(10_000) {
|
||||
transactions = fastChannel.receive()
|
||||
}
|
||||
assertTrue(transactions.size > 0, "no transactions created at all")
|
||||
assertTrue(transactions.none { it.isSend })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send - each call to send generates exactly one sent transaction`() = runBlocking {
|
||||
val fastChannel = fastSynchronizer.start(fastSynchronizer).allTransactions()
|
||||
var transactions = fastChannel.receive()
|
||||
repeat(10_000) {
|
||||
if (it.rem(2_000) == 0) {
|
||||
fastSynchronizer.sendToAddress(10, validAddress); println("yep")
|
||||
}
|
||||
transactions = fastChannel.receive()
|
||||
}
|
||||
assertEquals(5, transactions.count { it.isSend })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send - triggers an active transaction`() = runBlocking {
|
||||
synchronizer.sendToAddress(10, validAddress)
|
||||
delay(500L)
|
||||
assertNotNull(synchronizer.activeTransactions().receiveOrNull())
|
||||
synchronizer.stop()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send - results in success`() = runBlocking {
|
||||
synchronizer.sendToAddress(10, validAddress)
|
||||
delay(500L)
|
||||
val result = synchronizer.activeTransactions().receive()
|
||||
assertTrue(result.isNotEmpty(), "result was empty")
|
||||
assertTrue(TransactionState.AwaitingConfirmations(0).order <= result.values.first().order)
|
||||
assertTrue((result.keys.first() as ActiveSendTransaction).transactionId.get() != -1L, "transactionId missing")
|
||||
assertTrue((result.keys.first() as ActiveSendTransaction).height.get() != -1, "height missing")
|
||||
synchronizer.stop()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send - results in mined transaction`() = runBlocking {
|
||||
synchronizer.sendToAddress(10, validAddress)
|
||||
delay(500L)
|
||||
val result = synchronizer.activeTransactions().receive()
|
||||
assertTrue(result.isNotEmpty(), "result was empty")
|
||||
assertTrue(TransactionState.AwaitingConfirmations(0).order <= result.values.first().order)
|
||||
synchronizer.stop()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send - a bad address fails`() = runBlocking {
|
||||
synchronizer.sendToAddress(10, "fail")
|
||||
delay(500L)
|
||||
val result = synchronizer.activeTransactions().receive()
|
||||
assertTrue(result.isNotEmpty(), "result was empty")
|
||||
assertTrue(0 > result.values.first().order)
|
||||
synchronizer.stop()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send - a short address fails`() = runBlocking {
|
||||
// one character too short
|
||||
val toAddress = "ztestsapling1yu2zy9aane2pje2qvm4qmn4k6q57y2d9ecs5vz0guthxx3m2aq57qm6hkx0520m9u9635xh6tt"
|
||||
assertTrue(toAddress.length < 88, "sample address wasn't short enough (${toAddress.length})")
|
||||
|
||||
synchronizer.sendToAddress(10, toAddress)
|
||||
delay(500L)
|
||||
val result = synchronizer.activeTransactions().receive()
|
||||
assertTrue(result.isNotEmpty(), "result was empty")
|
||||
assertTrue(0 > result.values.first().order,
|
||||
"result should have been a failure but was ${result.values.first()::class.simpleName}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send - a non-z prefix address fails`() = runBlocking {
|
||||
// one character too short
|
||||
val toAddress = "atestsapling1yu2zy9aane2pje2qvm4qmn4k6q57y2d9ecs5vz0guthxx3m2aq57qm6hkx0520m9u9635xh6ttd"
|
||||
assertTrue(toAddress.length == 88,
|
||||
"sample address was not the proper length (${toAddress.length}")
|
||||
assertFalse(toAddress.startsWith('z'),
|
||||
"sample address should not start with z")
|
||||
|
||||
synchronizer.sendToAddress(10, toAddress)
|
||||
delay(500L)
|
||||
val result = synchronizer.activeTransactions().receive()
|
||||
assertTrue(result.isNotEmpty(), "result was empty")
|
||||
assertTrue(0 > result.values.first().order,
|
||||
"result should have been a failure but was ${result.values.first()::class.simpleName}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `balance matches transactions without sends`() = runBlocking {
|
||||
val balances = fastSynchronizer.start(fastSynchronizer).balances()
|
||||
var transactions = listOf<ClearedTransaction>()
|
||||
while (transactions.count() < 10) {
|
||||
transactions = fastSynchronizer.allTransactions().receive()
|
||||
println("got ${transactions.count()} transaction(s)")
|
||||
}
|
||||
assertEquals(transactions.fold(0L) { acc, tx -> acc + tx.value }, balances.receive())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `balance matches transactions with sends`() = runBlocking {
|
||||
var transactions = listOf<ClearedTransaction>()
|
||||
val balances = fastSynchronizer.start(fastSynchronizer).balances()
|
||||
val transactionChannel = fastSynchronizer.allTransactions()
|
||||
while (transactions.count() < 10) {
|
||||
fastSynchronizer.sendToAddress(Random.nextLong(1L..10_000_000_000), validAddress)
|
||||
transactions = transactionChannel.receive()
|
||||
println("got ${transactions.count()} transaction(s)")
|
||||
}
|
||||
val transactionsSnapshot = transactionChannel.receive()
|
||||
val balanceSnapshot = balances.receive()
|
||||
|
||||
val positiveValue = transactionsSnapshot.fold(0L) { acc, tx -> acc + (if (tx.isSend) 0 else tx.value) }
|
||||
val negativeValue = transactionsSnapshot.fold(0L) { acc, tx -> acc + (if(!tx.isSend) 0 else tx.value) }
|
||||
assertEquals(positiveValue - negativeValue, balanceSnapshot, "incorrect balance. negative balance: $negativeValue positive balance: $positiveValue")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `progress hits 100`() = runBlocking {
|
||||
var channel = synchronizer.progress()
|
||||
var now = System.currentTimeMillis()
|
||||
var delta = 0L
|
||||
val expectedUpperBounds = transactionInterval * 10
|
||||
while (channel.receive() < 100) {
|
||||
delta = now - System.currentTimeMillis()
|
||||
if (delta > expectedUpperBounds) break
|
||||
}
|
||||
assertTrue(delta < expectedUpperBounds, "progress did not hit 100 within the expected time of $expectedUpperBounds")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `is out of sync about 10% of the time`() = runBlocking {
|
||||
var count = 0
|
||||
repeat(100_000) {
|
||||
if (synchronizer.isStale()) count++
|
||||
}
|
||||
assertTrue(count < 11_000, "a count of $count is too frequent")
|
||||
assertTrue(count > 9_000, "a count of $count is too infrequent")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isFirstRun() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cancelSend() = runBlocking {
|
||||
val activeTransactions = synchronizer.activeTransactions()
|
||||
|
||||
// verify that send creates one transaction
|
||||
launch {
|
||||
synchronizer.sendToAddress(10, validAddress)
|
||||
}
|
||||
println("done sending to address")
|
||||
delay(300L)
|
||||
var actives = activeTransactions.receiveOrNull()
|
||||
assertEquals(1, actives?.size)
|
||||
assertTrue((actives?.values?.first()?.order ?: 0) > -1, "expected positive order but was ${actives?.values?.first()?.order}")
|
||||
val transaction = actives?.keys?.first() as? ActiveSendTransaction
|
||||
assertNotNull(transaction)
|
||||
|
||||
// and then verify that cancel changes its status
|
||||
synchronizer.cancelSend(transaction!!)
|
||||
delay(100L) // look for ignored state change
|
||||
actives = activeTransactions.receiveOrNull()
|
||||
assertNotNull(actives, "cancel changed nothing in 100ms")
|
||||
assertEquals(1, actives!!.size, "unexpected number of active transactions ${actives.size}")
|
||||
val finalState = actives!!.values.first()
|
||||
assertNotNull(finalState as? TransactionState.Cancelled, "transaction was ${finalState::class.simpleName} instead of cancelled for ${actives.keys.first()}")
|
||||
println("donso")
|
||||
synchronizer.stop()
|
||||
}
|
||||
// private val transactionInterval = 200L
|
||||
// private val activeTransactionInterval = 200L
|
||||
// private val synchronizer = MockSynchronizer(transactionInterval, activeTransactionInterval)
|
||||
// private val fastSynchronizer = MockSynchronizer(2L, 2L)
|
||||
// private val allTransactionChannel = synchronizer.allTransactions()
|
||||
// private val validAddress = "ztestsapling1yu2zy9aane2pje2qvm4qmn4k6q57y2d9ecs5vz0guthxx3m2aq57qm6hkx0520m9u9635xh6ttd"
|
||||
//
|
||||
// @BeforeEach
|
||||
// fun setUp() {
|
||||
// synchronizer.start(CoroutineScope(Dispatchers.IO))
|
||||
// }
|
||||
//
|
||||
// @AfterEach
|
||||
// fun tearDown() {
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun allTransactions() = runBlocking {
|
||||
// var total = 0
|
||||
// val duration = measureTimeMillis {
|
||||
// repeat(10) {
|
||||
// val transactions = allTransactionChannel.receive()
|
||||
// total += transactions.size
|
||||
// println("received ${transactions.size} transactions")
|
||||
// }
|
||||
// }
|
||||
// assertTrue(total > 0)
|
||||
// assertTrue(duration > transactionInterval)
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `never calling send yields zero sent transactions`() = runBlocking {
|
||||
// val fastChannel = fastSynchronizer.start(fastSynchronizer).allTransactions()
|
||||
// var transactions = fastChannel.receive()
|
||||
// repeat(10_000) {
|
||||
// transactions = fastChannel.receive()
|
||||
// }
|
||||
// assertTrue(transactions.size > 0, "no transactions created at all")
|
||||
// assertTrue(transactions.none { it.isSend })
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - each call to send generates exactly one sent transaction`() = runBlocking {
|
||||
// val fastChannel = fastSynchronizer.start(fastSynchronizer).allTransactions()
|
||||
// var transactions = fastChannel.receive()
|
||||
// repeat(10_000) {
|
||||
// if (it.rem(2_000) == 0) {
|
||||
// fastSynchronizer.sendToAddress(10, validAddress); println("yep")
|
||||
// }
|
||||
// transactions = fastChannel.receive()
|
||||
// }
|
||||
// assertEquals(5, transactions.count { it.isSend })
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - triggers an active transaction`() = runBlocking {
|
||||
// synchronizer.sendToAddress(10, validAddress)
|
||||
// delay(500L)
|
||||
// assertNotNull(synchronizer.activeTransactions().receiveOrNull())
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - results in success`() = runBlocking {
|
||||
// synchronizer.sendToAddress(10, validAddress)
|
||||
// delay(500L)
|
||||
// val result = synchronizer.activeTransactions().receive()
|
||||
// assertTrue(result.isNotEmpty(), "result was empty")
|
||||
// assertTrue(TransactionState.AwaitingConfirmations(0).order <= result.values.first().order)
|
||||
// assertTrue((result.keys.first() as ActiveSendTransaction).transactionId.get() != -1L, "transactionId missing")
|
||||
// assertTrue((result.keys.first() as ActiveSendTransaction).height.get() != -1, "height missing")
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - results in mined transaction`() = runBlocking {
|
||||
// synchronizer.sendToAddress(10, validAddress)
|
||||
// delay(500L)
|
||||
// val result = synchronizer.activeTransactions().receive()
|
||||
// assertTrue(result.isNotEmpty(), "result was empty")
|
||||
// assertTrue(TransactionState.AwaitingConfirmations(0).order <= result.values.first().order)
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - a bad address fails`() = runBlocking {
|
||||
// synchronizer.sendToAddress(10, "fail")
|
||||
// delay(500L)
|
||||
// val result = synchronizer.activeTransactions().receive()
|
||||
// assertTrue(result.isNotEmpty(), "result was empty")
|
||||
// assertTrue(0 > result.values.first().order)
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - a short address fails`() = runBlocking {
|
||||
// // one character too short
|
||||
// val toAddress = "ztestsapling1yu2zy9aane2pje2qvm4qmn4k6q57y2d9ecs5vz0guthxx3m2aq57qm6hkx0520m9u9635xh6tt"
|
||||
// assertTrue(toAddress.length < 88, "sample address wasn't short enough (${toAddress.length})")
|
||||
//
|
||||
// synchronizer.sendToAddress(10, toAddress)
|
||||
// delay(500L)
|
||||
// val result = synchronizer.activeTransactions().receive()
|
||||
// assertTrue(result.isNotEmpty(), "result was empty")
|
||||
// assertTrue(0 > result.values.first().order,
|
||||
// "result should have been a failure but was ${result.values.first()::class.simpleName}")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - a non-z prefix address fails`() = runBlocking {
|
||||
// // one character too short
|
||||
// val toAddress = "atestsapling1yu2zy9aane2pje2qvm4qmn4k6q57y2d9ecs5vz0guthxx3m2aq57qm6hkx0520m9u9635xh6ttd"
|
||||
// assertTrue(toAddress.length == 88,
|
||||
// "sample address was not the proper length (${toAddress.length}")
|
||||
// assertFalse(toAddress.startsWith('z'),
|
||||
// "sample address should not start with z")
|
||||
//
|
||||
// synchronizer.sendToAddress(10, toAddress)
|
||||
// delay(500L)
|
||||
// val result = synchronizer.activeTransactions().receive()
|
||||
// assertTrue(result.isNotEmpty(), "result was empty")
|
||||
// assertTrue(0 > result.values.first().order,
|
||||
// "result should have been a failure but was ${result.values.first()::class.simpleName}")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `balance matches transactions without sends`() = runBlocking {
|
||||
// val balances = fastSynchronizer.start(fastSynchronizer).balances()
|
||||
// var transactions = listOf<ClearedTransaction>()
|
||||
// while (transactions.count() < 10) {
|
||||
// transactions = fastSynchronizer.allTransactions().receive()
|
||||
// println("got ${transactions.count()} transaction(s)")
|
||||
// }
|
||||
// assertEquals(transactions.fold(0L) { acc, tx -> acc + tx.value }, balances.receive())
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `balance matches transactions with sends`() = runBlocking {
|
||||
// var transactions = listOf<ClearedTransaction>()
|
||||
// val balances = fastSynchronizer.start(fastSynchronizer).balances()
|
||||
// val transactionChannel = fastSynchronizer.allTransactions()
|
||||
// while (transactions.count() < 10) {
|
||||
// fastSynchronizer.sendToAddress(Random.nextLong(1L..10_000_000_000), validAddress)
|
||||
// transactions = transactionChannel.receive()
|
||||
// println("got ${transactions.count()} transaction(s)")
|
||||
// }
|
||||
// val transactionsSnapshot = transactionChannel.receive()
|
||||
// val balanceSnapshot = balances.receive()
|
||||
//
|
||||
// val positiveValue = transactionsSnapshot.fold(0L) { acc, tx -> acc + (if (tx.isSend) 0 else tx.value) }
|
||||
// val negativeValue = transactionsSnapshot.fold(0L) { acc, tx -> acc + (if(!tx.isSend) 0 else tx.value) }
|
||||
// assertEquals(positiveValue - negativeValue, balanceSnapshot, "incorrect balance. negative balance: $negativeValue positive balance: $positiveValue")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `progress hits 100`() = runBlocking {
|
||||
// var channel = synchronizer.progress()
|
||||
// var now = System.currentTimeMillis()
|
||||
// var delta = 0L
|
||||
// val expectedUpperBounds = transactionInterval * 10
|
||||
// while (channel.receive() < 100) {
|
||||
// delta = now - System.currentTimeMillis()
|
||||
// if (delta > expectedUpperBounds) break
|
||||
// }
|
||||
// assertTrue(delta < expectedUpperBounds, "progress did not hit 100 within the expected time of $expectedUpperBounds")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `is out of sync about 10% of the time`() = runBlocking {
|
||||
// var count = 0
|
||||
// repeat(100_000) {
|
||||
// if (synchronizer.isStale()) count++
|
||||
// }
|
||||
// assertTrue(count < 11_000, "a count of $count is too frequent")
|
||||
// assertTrue(count > 9_000, "a count of $count is too infrequent")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun isFirstRun() {
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun cancelSend() = runBlocking {
|
||||
// val activeTransactions = synchronizer.activeTransactions()
|
||||
//
|
||||
// // verify that send creates one transaction
|
||||
// launch {
|
||||
// synchronizer.sendToAddress(10, validAddress)
|
||||
// }
|
||||
// println("done sending to address")
|
||||
// delay(300L)
|
||||
// var actives = activeTransactions.receiveOrNull()
|
||||
// assertEquals(1, actives?.size)
|
||||
// assertTrue((actives?.values?.first()?.order ?: 0) > -1, "expected positive order but was ${actives?.values?.first()?.order}")
|
||||
// val transaction = actives?.keys?.first() as? ActiveSendTransaction
|
||||
// assertNotNull(transaction)
|
||||
//
|
||||
// // and then verify that cancel changes its status
|
||||
// synchronizer.cancelSend(transaction!!)
|
||||
// delay(100L) // look for ignored state change
|
||||
// actives = activeTransactions.receiveOrNull()
|
||||
// assertNotNull(actives, "cancel changed nothing in 100ms")
|
||||
// assertEquals(1, actives!!.size, "unexpected number of active transactions ${actives.size}")
|
||||
// val finalState = actives!!.values.first()
|
||||
// assertNotNull(finalState as? TransactionState.Cancelled, "transaction was ${finalState::class.simpleName} instead of cancelled for ${actives.keys.first()}")
|
||||
// println("donso")
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
}
|
Loading…
Reference in New Issue