Browse Source

server: Refactor the TransactionPostgresDataHandlerSpec

prometheus-integration
Alexis Hernandez 6 years ago
parent
commit
72b66c0768
  1. 3
      server/app/com/xsn/explorer/models/Blockhash.scala
  2. 2
      server/app/com/xsn/explorer/models/TransactionId.scala
  3. 368
      server/test/com/xsn/explorer/data/TransactionPostgresDataHandlerSpec.scala
  4. 138
      server/test/com/xsn/explorer/helpers/DataGenerator.scala
  5. 30
      server/test/com/xsn/explorer/helpers/DataHandlerObjects.scala

3
server/app/com/xsn/explorer/models/Blockhash.scala

@ -6,6 +6,9 @@ import play.api.libs.json._
class Blockhash private (val string: String) extends AnyVal with WrappedString
object Blockhash {
val Length = 64
private val pattern = "^[a-f0-9]{64}$".r.pattern
def from(string: String): Option[Blockhash] = {

2
server/app/com/xsn/explorer/models/TransactionId.scala

@ -7,6 +7,8 @@ class TransactionId private (val string: String) extends AnyVal with WrappedStri
object TransactionId {
val Length = 64
private val pattern = "^[a-f0-9]{64}$".r.pattern
def from(string: String): Option[TransactionId] = {

368
server/test/com/xsn/explorer/data/TransactionPostgresDataHandlerSpec.scala

@ -2,87 +2,29 @@ package com.xsn.explorer.data
import com.alexitc.playsonify.models.ordering.{FieldOrdering, OrderingCondition}
import com.alexitc.playsonify.models.pagination._
import com.alexitc.playsonify.sql.FieldOrderingSQLInterpreter
import com.xsn.explorer.data.anorm.dao.{AggregatedAmountPostgresDAO, BalancePostgresDAO, BlockPostgresDAO, TransactionPostgresDAO}
import com.xsn.explorer.data.anorm.{BlockPostgresDataHandler, LedgerPostgresDataHandler, TransactionPostgresDataHandler}
import com.xsn.explorer.data.anorm.TransactionPostgresDataHandler
import com.xsn.explorer.data.common.PostgresDataHandlerSpec
import com.xsn.explorer.errors.{BlockNotFoundError, TransactionNotFoundError}
import com.xsn.explorer.helpers.DataHandlerObjects._
import com.xsn.explorer.helpers.DataHelper._
import com.xsn.explorer.helpers.{BlockLoader, TransactionLoader}
import com.xsn.explorer.helpers.{BlockLoader, DataGenerator, TransactionLoader}
import com.xsn.explorer.models._
import com.xsn.explorer.models.fields.TransactionField
import com.xsn.explorer.models.rpc.Block
import org.scalactic.{Every, Good, One, Or}
import org.scalactic.{Good, One, Or}
import org.scalatest.BeforeAndAfter
class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with BeforeAndAfter {
lazy val transactionPostgresDAO = new TransactionPostgresDAO(new FieldOrderingSQLInterpreter)
lazy val dataHandler = new TransactionPostgresDataHandler(database, transactionPostgresDAO)
lazy val ledgerDataHandler = new LedgerPostgresDataHandler(
database,
new BlockPostgresDAO(new FieldOrderingSQLInterpreter),
transactionPostgresDAO,
new BalancePostgresDAO(new FieldOrderingSQLInterpreter),
new AggregatedAmountPostgresDAO)
lazy val blockDataHandler = new BlockPostgresDataHandler(database, new BlockPostgresDAO(new FieldOrderingSQLInterpreter))
val defaultOrdering = FieldOrdering(TransactionField.Time, OrderingCondition.DescendingOrder)
import DataGenerator._
val block = Block(
hash = createBlockhash("ad92f0dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"),
previousBlockhash = None,
nextBlockhash = None,
merkleRoot = createBlockhash("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"),
transactions = List.empty,
confirmations = Confirmations(0),
size = Size(10),
height = Height(0),
version = 0,
time = 0,
medianTime = 0,
nonce = 0,
bits = "abcdef",
chainwork = "abcdef",
difficulty = 12.2,
tposContract = None
)
val dummyTransaction = Transaction(
createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"),
block.hash,
12312312L,
Size(1000),
List.empty,
List(
Transaction.Output(createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"), 0, 1000, createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7Dkpss"), HexString.from("00").get, None, None),
Transaction.Output(createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"), 1, 1000, createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7Dkpss"), HexString.from("00").get, None, None)
)
)
val inputs = List(
Transaction.Input(dummyTransaction.id, 0, 1, BigDecimal(100), createAddress("XxQ7j37LfuXgsLd5DZAwFKhT3s2ZMkW85F"))
)
lazy val dataHandler = new TransactionPostgresDataHandler(database, transactionPostgresDAO)
lazy val ledgerDataHandler = createLedgerDataHandler(database)
lazy val blockDataHandler = createBlockDataHandler(database)
val outputs = List(
Transaction.Output(createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"), 0, BigDecimal(50), createAddress("XxQ7j37LfuXgsLd5DZAwFKhT3s2ZMkW85F"), HexString.from("00").get, None, None),
Transaction.Output(
createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"),
1,
BigDecimal(150),
createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7DkpcF"),
HexString.from("00").get,
Some(createAddress("XfAATXtkRgCdMTrj2fxHvLsKLLmqAjhEAt")),
Some(createAddress("XjfNeGJhLgW3egmsZqdbpCNGfysPs7jTNm")))
)
val defaultOrdering = FieldOrdering(TransactionField.Time, OrderingCondition.DescendingOrder)
val transaction = Transaction(
createTransactionId("99c51e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
block.hash,
12312312L,
Size(1000),
inputs,
outputs.map(_.copy(txid = createTransactionId("99c51e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"))))
val block = DataGenerator.randomBlock()
val blockList = List(
BlockLoader.get("00000c822abdbb23e28f79a49d29b41429737c6c7e15df40d1b1f1b35907ae34"),
@ -93,33 +35,7 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be
BlockLoader.get("00000267225f7dba55d9a3493740e7f0dde0f28a371d2c3b42e7676b5728d020")
)
private def prepareBlock(block: Block) = {
val dao = new BlockPostgresDAO(new FieldOrderingSQLInterpreter)
try {
database.withConnection { implicit conn =>
val maybe = dao.insert(block)
Or.from(maybe, One(BlockNotFoundError))
}
} catch {
case _ => ()
}
}
private def prepareTransaction(transaction: Transaction) = {
try {
upsertTransaction(transaction)
} catch {
case _ => ()
}
}
private def upsertTransaction(transaction: Transaction) = {
val dao = transactionPostgresDAO
database.withConnection { implicit conn =>
val maybe = dao.upsert(1, transaction)
Or.from(maybe, One(TransactionNotFoundError))
}
}
val dummyTransaction = randomTransaction(blockhash = block.hash, utxos = List.empty)
before {
clearDatabase()
@ -128,49 +44,31 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be
}
"getBy address" should {
val address = createAddress("XxQ7j37LfuXgsLd5DZAwFKhT3s2ZMkW86F")
val inputs = List(
Transaction.Input(dummyTransaction.id, 0, 1, 100, address),
Transaction.Input(dummyTransaction.id, 1, 2, 200, createAddress("XxQ7j37LfuXgsLD5DZAwFKhT3s2ZMkW86F"))
)
val outputs = List(
Transaction.Output(createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"), 0, BigDecimal(50), address, HexString.from("00").get, None, None),
Transaction.Output(
createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"),
1,
BigDecimal(250),
createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7DkpcF"),
HexString.from("00").get,
None, None)
)
val transaction = Transaction(
createTransactionId("92c51e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
block.hash,
12312312L,
Size(1000),
inputs,
outputs)
val address = randomAddress
val partialTransaction = randomTransaction(blockhash = block.hash, utxos = dummyTransaction.outputs)
val outputsForAddress = partialTransaction.outputs.map { _.copy(address = address) }
val transaction = partialTransaction.copy(outputs = outputsForAddress)
val query = PaginatedQuery(Offset(0), Limit(10))
"find no results" in {
val expected = PaginatedResult(query.offset, query.limit, Count(0), List.empty)
val result = dataHandler.getBy(address, query, defaultOrdering)
val result = dataHandler.getBy(randomAddress, query, defaultOrdering)
result mustEqual Good(expected)
}
"find the right values" in {
upsertTransaction(transaction)
val transactionWithValues = TransactionWithValues(
transaction.id, transaction.blockhash, transaction.time, transaction.size,
sent = 100,
received = 50)
transaction.id,
transaction.blockhash,
transaction.time,
transaction.size,
received = transaction.outputs.filter(_.address == address).map(_.value).sum,
sent = transaction.inputs.filter(_.address == address).map(_.value).sum)
val expected = PaginatedResult(query.offset, query.limit, Count(1), List(transactionWithValues))
upsertTransaction(transaction).isGood mustEqual true
val result = dataHandler.getBy(address, query, defaultOrdering)
result mustEqual Good(expected)
}
@ -193,61 +91,24 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be
)
val result = dataHandler.getUnspentOutputs(expected.address).get
result.size mustEqual 1
result mustEqual List(expected)
}
}
"getByBlockhash" should {
val blockhash = createBlockhash("0000000000bdbb23e28f79a49d29b41429737c6c7e15df40d1b1f1b35907ae34")
val inputs = List(
Transaction.Input(dummyTransaction.id, 0, 1, 100, createAddress("XxQ7j37LfuXgsLd5DZAwFKhT3s2ZMkW86F")),
Transaction.Input(dummyTransaction.id, 1, 2, 200, createAddress("XxQ7j37LfuXgsLD5DZAwFKhT3s2ZMkW86F"))
)
val outputs = List(
Transaction.Output(createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"), 0, BigDecimal(50), createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7DkpcF"), HexString.from("00").get, None, None),
Transaction.Output(
createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"),
1,
BigDecimal(250),
createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7DkpcF"),
HexString.from("00").get,
None, None)
)
val blockhash = randomBlockhash
val transactions = List(
Transaction(
createTransactionId("00051e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
blockhash,
12312312L,
Size(1000),
inputs,
outputs),
Transaction(
createTransactionId("02c51e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
blockhash,
12312302L,
Size(900),
inputs.map(x => x.copy(value = x.value * 2)),
outputs.map(x => x.copy(value = x.value * 2))),
Transaction(
createTransactionId("00c51e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
blockhash,
12312310L,
Size(100),
inputs.map(x => x.copy(value = x.value / 2)),
outputs.map(x => x.copy(value = x.value / 2)))
randomTransaction(blockhash = blockhash, utxos = dummyTransaction.outputs),
randomTransaction(blockhash = blockhash, utxos = dummyTransaction.outputs),
randomTransaction(blockhash = blockhash, utxos = dummyTransaction.outputs)
)
val block = this.block.copy(
hash = blockhash,
height = Height(10),
transactions = transactions.map(_.id))
val block = randomBlock(blockhash = blockhash).copy(transactions = transactions.map(_.id))
"find no results" in {
val blockhash = createBlockhash("021d335a910f6780bdf48f9efd751b162074367eeb6740ac205223496430260f")
val blockhash = randomBlockhash
val query = PaginatedQuery(Offset(0), Limit(10))
val expected = PaginatedResult(query.offset, query.limit, Count(0), List.empty)
val result = dataHandler.getByBlockhash(blockhash, query, defaultOrdering)
@ -267,81 +128,105 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be
result.data.size mustEqual transactions.size
}
def testOrdering[B](field: TransactionField)(sortBy: Transaction => B)(implicit order: Ordering[B]) = {
def testOrdering[B](field: TransactionField, orderingCondition: OrderingCondition)(lt: (Transaction, Transaction) => Boolean) = {
createBlock(block, transactions)
val ordering = FieldOrdering(field, OrderingCondition.AscendingOrder)
val ordering = FieldOrdering(field, orderingCondition)
val query = PaginatedQuery(Offset(0), Limit(10))
val expected = transactions.sortBy(sortBy)(order).map(_.id)
val expected = transactions.sortWith(lt).map(_.id)
val result = dataHandler.getByBlockhash(blockhash, query, ordering).get.data
result.map(_.id) mustEqual expected
val expectedReverse = expected.reverse
val resultReverse = dataHandler.getByBlockhash(blockhash, query, ordering.copy(orderingCondition = OrderingCondition.DescendingOrder)).get.data
resultReverse.map(_.id) mustEqual expectedReverse
result.map(_.id) mustEqual expected
}
"allow to sort by txid" in {
testOrdering(TransactionField.TransactionId)(_.id.string)
testOrdering(TransactionField.TransactionId, OrderingCondition.AscendingOrder) { case (a, b) => a.id.string.compareTo(b.id.string) < 0 }
}
"allow to sort by txid - descending" in {
testOrdering(TransactionField.TransactionId, OrderingCondition.DescendingOrder) { case (a, b) => a.id.string.compareTo(b.id.string) > 0 }
}
"allow to sort by time" in {
testOrdering(TransactionField.Time)(_.time)
testOrdering(TransactionField.Time, OrderingCondition.AscendingOrder) { case (a, b) =>
if (a.time < b.time) true
else if (a.time > b.time) false
else a.id.string.compareTo(b.id.string) < 0
}
}
"allow to sort by time - descending" in {
testOrdering(TransactionField.Time, OrderingCondition.DescendingOrder) { case (a, b) =>
if (a.time < b.time) false
else if (a.time > b.time) true
else a.id.string.compareTo(b.id.string) < 0
}
}
"allow to sort by sent" in {
testOrdering(TransactionField.Sent)(_.inputs.map(_.value).sum)
testOrdering(TransactionField.Sent, OrderingCondition.AscendingOrder) { case (a, b) =>
if (a.inputs.map(_.value).sum < b.inputs.map(_.value).sum) true
else if (a.inputs.map(_.value).sum > b.inputs.map(_.value).sum) false
else a.id.string.compareTo(b.id.string) < 0
}
}
"allow to sort by sent - descending" in {
testOrdering(TransactionField.Sent, OrderingCondition.DescendingOrder) { case (a, b) =>
if (a.inputs.map(_.value).sum < b.inputs.map(_.value).sum) false
else if (a.inputs.map(_.value).sum > b.inputs.map(_.value).sum) true
else a.id.string.compareTo(b.id.string) < 0
}
}
"allow to sort by received" in {
testOrdering(TransactionField.Received)(_.outputs.map(_.value).sum)
testOrdering(TransactionField.Received, OrderingCondition.AscendingOrder) { case (a, b) =>
if (a.outputs.map(_.value).sum < b.outputs.map(_.value).sum) true
else if (a.outputs.map(_.value).sum > b.outputs.map(_.value).sum) false
else a.id.string.compareTo(b.id.string) < 0
}
}
"allow to sort by received - descending" in {
testOrdering(TransactionField.Received, OrderingCondition.DescendingOrder) { case (a, b) =>
if (a.outputs.map(_.value).sum < b.outputs.map(_.value).sum) false
else if (a.outputs.map(_.value).sum > b.outputs.map(_.value).sum) true
else a.id.string.compareTo(b.id.string) < 0
}
}
}
"getBy with scroll" should {
val address = createAddress("XxQ7j37LfuXgsLD5DZAwFKhT3s2ZMkW86F")
val blockhash = createBlockhash("0000000000bdbb23e28f79a49d29b41429737c6c7e15df40d1b1f1b35907ae34")
val address = randomAddress
val blockhash = randomBlockhash
val inputs = List(
Transaction.Input(dummyTransaction.id, 0, 1, 100, address),
Transaction.Input(dummyTransaction.id, 1, 2, 200, address)
)
val outputs = List(
Transaction.Output(createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"), 0, BigDecimal(50), createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7DkpcF"), HexString.from("00").get, None, None),
Transaction.Output(randomTransactionId, 0, BigDecimal(50), randomAddress, randomHexString(), None, None),
Transaction.Output(
createTransactionId("ad9320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"),
randomTransactionId,
1,
BigDecimal(250),
createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7DkpcF"),
randomAddress,
HexString.from("00").get,
None, None)
)
val transaction = Transaction(
createTransactionId("00051e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
val transactions = List.fill(4)(randomTransactionId).zip(List(321L, 320L, 319L, 319L)).map { case (txid, time) =>
Transaction(
txid,
blockhash,
321,
time,
Size(1000),
inputs,
outputs)
outputs.map(_.copy(txid = txid)))
}
val transactions = List(
transaction,
transaction.copy(
id = createTransactionId("00041e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
time = 320),
transaction.copy(
id = createTransactionId("00c51e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
time = 319),
transaction.copy(
id = createTransactionId("02c51e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
time = 319))
val block = this.block.copy(
hash = blockhash,
height = Height(10),
transactions = transactions.map(_.id))
val block = randomBlock(blockhash = blockhash).copy(transactions = transactions.map(_.id))
def prepare() = {
createBlock(block, transactions)
@ -354,8 +239,7 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be
.sortWith { case (a, b) =>
if (a.time < b.time) true
else if (a.time > b.time) false
else if (a.id.string < b.id.string) true
else false
else a.id.string.compareTo(b.id.string) < 0
}
case OrderingCondition.DescendingOrder =>
@ -363,16 +247,20 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be
.sortWith { case (a, b) =>
if (a.time > b.time) true
else if (a.time < b.time) false
else if (a.id.string < b.id.string) true
else false
else a.id.string.compareTo(b.id.string) < 0
}
}
def matchOnlyData(expected: Transaction, actual: Transaction) = {
actual.copy(inputs = List.empty, outputs = List.empty) mustEqual expected.copy(inputs = List.empty, outputs = List.empty)
}
s"[$tag] return the first elements" in {
prepare()
val expected = sorted.head
val result = dataHandler.getBy(address, Limit(1), None, condition).get
result.map(_.copy(inputs = List.empty, outputs = List.empty)) mustEqual List(expected.copy(inputs = List.empty, outputs = List.empty))
matchOnlyData(expected, result.head)
}
s"[$tag] return the next elements given the last seen tx" in {
@ -381,7 +269,7 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be
val lastSeenTxid = sorted.head.id
val expected = sorted(1)
val result = dataHandler.getBy(address, Limit(1), Option(lastSeenTxid), condition).get
result.map(_.copy(inputs = List.empty, outputs = List.empty)) mustEqual List(expected.copy(inputs = List.empty, outputs = List.empty))
matchOnlyData(expected, result.head)
}
s"[$tag] return the element with the same time breaking ties by txid" in {
@ -390,7 +278,7 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be
val lastSeenTxid = sorted(2).id
val expected = sorted(3)
val result = dataHandler.getBy(address, Limit(1), Option(lastSeenTxid), condition).get
result.map(_.copy(inputs = List.empty, outputs = List.empty)) mustEqual List(expected.copy(inputs = List.empty, outputs = List.empty))
matchOnlyData(expected, result.head)
}
s"[$tag] return no elements on unknown lastSeenTransaction" in {
@ -406,42 +294,46 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be
"spending an output" should {
"use the right values" in {
val address = createAddress("XxQ7j37LfuXgsLD5DZAwFKhT3s2ZMkW86F")
val blockhash = createBlockhash("0000000000bdbb23e28f79a49d29b41429737c6c7e15df40d1b1f1b35907ae34")
val address = randomAddress
val blockhash = randomBlockhash
val inputs = List(
Transaction.Input(dummyTransaction.id, 0, 1, 100, address),
Transaction.Input(dummyTransaction.id, 1, 2, 200, address)
)
val newTxid = randomTransactionId
val outputs = List(
Transaction.Output(createTransactionId("ad1320dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"), 0, BigDecimal(50), createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7DkpcF"), HexString.from("00").get, None, None),
Transaction.Output(newTxid, 0, BigDecimal(50), randomAddress, randomHexString(), None, None),
Transaction.Output(
createTransactionId("ad9330dcea2fdaa357aac6eab00695cf07b487e34113598909f625c24629c981"),
newTxid,
1,
BigDecimal(250),
createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7DkpcF"),
HexString.from("00").get,
randomAddress,
randomHexString(),
None, None)
)
val transaction = Transaction(
createTransactionId("00051e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
newTxid,
blockhash,
321,
Size(1000),
inputs,
outputs)
val newAddress = createAddress("Xbh5pJdBNm8J9PxnEmwVcuQKRmZZ7Dkpcx")
val transactions = List(
transaction,
transaction.copy(
id = createTransactionId("00041e4fe89466faa734d6207a7ef6115fa1dd33f7156b006fafc6bb85a79eb8"),
val newTxid2 = randomTransactionId
val newAddress = randomAddress
val transaction2 = transaction.copy(
id = newTxid2,
inputs = List(
Transaction.Input(fromTxid = transaction.id, fromOutputIndex = 0, index = 0, value = transaction.outputs(0).value, address = newAddress),
Transaction.Input(fromTxid = transaction.id, fromOutputIndex = 1, index = 1, value = transaction.outputs(1).value, address = newAddress)
),
outputs = transaction.outputs.map(_.copy(txid = newTxid2))
)
))
val transactions = List(
transaction, transaction2)
val block = this.block.copy(
hash = blockhash,
@ -503,4 +395,30 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be
result.isGood mustEqual true
}
private def prepareBlock(block: Block) = {
try {
database.withConnection { implicit conn =>
val maybe = blockPostgresDAO.insert(block)
Or.from(maybe, One(BlockNotFoundError))
}
} catch {
case _: Throwable => ()
}
}
private def prepareTransaction(transaction: Transaction) = {
try {
upsertTransaction(transaction)
} catch {
case _: Throwable => ()
}
}
private def upsertTransaction(transaction: Transaction) = {
database.withConnection { implicit conn =>
val maybe = transactionPostgresDAO.upsert(1, transaction)
Or.from(maybe, One(TransactionNotFoundError))
}.isGood must be(true)
}
}

138
server/test/com/xsn/explorer/helpers/DataGenerator.scala

@ -0,0 +1,138 @@
package com.xsn.explorer.helpers
import com.xsn.explorer.models.rpc.Block
import com.xsn.explorer.models.{Address, Blockhash, Confirmations, Height, HexString, Size, Transaction, TransactionId}
trait DataGenerator {
import scala.util.Random._
val hexCharacters = ((0 to 9) ++ ('a' to 'f')).mkString("")
def randomItem[T](items: List[T]): T = {
val index = scala.util.Random.nextInt(items.size)
items(index)
}
def randomItems[T](items: List[T], n: Int): List[T] = {
(0 until n).map(_ => randomItem(items)).toList
}
def randomHexString(length: Int = 8): HexString = {
val string = randomItems(hexCharacters.toList, length)
HexString.from(string.mkString("")).get
}
def randomBlockhash: Blockhash = {
val hex = randomHexString(Blockhash.Length)
Blockhash.from(hex.string).get
}
def randomTransactionId: TransactionId = {
val hex = randomHexString(TransactionId.Length)
TransactionId.from(hex.string).get
}
def randomAddress: Address = {
val hex = randomHexString(34)
Address.from(hex.string).get
}
def randomBlock(
blockhash: Blockhash = randomBlockhash,
previousBlockhash: Option[Blockhash] = None,
nextBlockhash: Option[Blockhash] = None): Block = {
Block(
hash = blockhash,
previousBlockhash = previousBlockhash,
nextBlockhash = nextBlockhash,
merkleRoot = randomBlockhash,
transactions = List.empty,
confirmations = Confirmations(0),
size = Size(10),
height = Height(nextInt(10000)),
version = 0,
time = 0,
medianTime = 0,
nonce = 0,
bits = "abcdef",
chainwork = "abcdef",
difficulty = 12.2,
tposContract = None
)
}
def randomOutput: Transaction.Output = {
Transaction.Output(
txid = randomTransactionId,
index = nextInt(100),
value = nextInt(1000000),
address = randomAddress,
script = randomHexString(8),
None, None)
}
def randomOutputs(n: Int = nextInt(5) + 1) : List[Transaction.Output] = {
(0 until n).map(x => randomOutput.copy(index = x)).toList
}
def randomInput(utxos: List[Transaction.Output]): Transaction.Input = {
val utxo = randomItem(utxos)
Transaction.Input(
fromTxid = utxo.txid,
fromOutputIndex = utxo.index,
index = scala.util.Random.nextInt(100),
value = utxo.value,
address = randomAddress
)
}
def randomInput(utxo: Transaction.Output): Transaction.Input = {
Transaction.Input(
fromTxid = utxo.txid,
fromOutputIndex = utxo.index,
index = scala.util.Random.nextInt(100),
value = utxo.value,
address = randomAddress
)
}
def randomInputs(utxos: List[Transaction.Output]): List[Transaction.Input] = utxos match {
case Nil => List.empty
case _ =>
randomItems(utxos, scala.util.Random.nextInt(utxos.size))
.map(randomInput)
}
/**
* Generate a random transaction spending the given utxos
*/
def randomTransaction(
blockhash: Blockhash,
id: TransactionId = randomTransactionId,
utxos: List[Transaction.Output]): Transaction = {
Transaction(
id = id,
blockhash = blockhash,
time = java.lang.System.currentTimeMillis(),
Size(1000),
createInputs(utxos),
randomOutputs().map(_.copy(txid = id))
)
}
def createInputs(outputs: List[Transaction.Output]): List[Transaction.Input] = {
outputs.zipWithIndex.map { case (output, index) =>
Transaction.Input(
fromTxid = output.txid,
fromOutputIndex = output.index,
index = index,
value = output.value,
address = randomAddress)
}
}
}
object DataGenerator extends DataGenerator

30
server/test/com/xsn/explorer/helpers/DataHandlerObjects.scala

@ -0,0 +1,30 @@
package com.xsn.explorer.helpers
import com.alexitc.playsonify.sql.FieldOrderingSQLInterpreter
import com.xsn.explorer.data.anorm.dao.{AggregatedAmountPostgresDAO, BalancePostgresDAO, BlockPostgresDAO, TransactionPostgresDAO}
import com.xsn.explorer.data.anorm.{BlockPostgresDataHandler, LedgerPostgresDataHandler}
import play.api.db.Database
trait DataHandlerObjects {
lazy val fieldOrderingSQLInterpreter = new FieldOrderingSQLInterpreter
lazy val transactionPostgresDAO = new TransactionPostgresDAO(fieldOrderingSQLInterpreter)
lazy val blockPostgresDAO = new BlockPostgresDAO(fieldOrderingSQLInterpreter)
lazy val balancePostgresDAO = new BalancePostgresDAO(fieldOrderingSQLInterpreter)
lazy val aggregatedAmountPostgresDAO = new AggregatedAmountPostgresDAO
def createLedgerDataHandler(database: Database) = {
new LedgerPostgresDataHandler(
database,
blockPostgresDAO,
transactionPostgresDAO,
balancePostgresDAO,
aggregatedAmountPostgresDAO)
}
def createBlockDataHandler(database: Database) = {
new BlockPostgresDataHandler(database, blockPostgresDAO)
}
}
object DataHandlerObjects extends DataHandlerObjects
Loading…
Cancel
Save