From 08f26e764bd8a11db84ee6d597d2aaf0c8d7f716 Mon Sep 17 00:00:00 2001 From: Alexis Hernandez Date: Sat, 26 Jan 2019 22:16:24 -0700 Subject: [PATCH] server: Reduce boilerplate on the BlocksControllerSpec --- .../helpers/FileBasedXSNService.scala | 17 +- .../controllers/BlocksControllerSpec.scala | 213 +++--------------- 2 files changed, 51 insertions(+), 179 deletions(-) diff --git a/server/test/com/xsn/explorer/helpers/FileBasedXSNService.scala b/server/test/com/xsn/explorer/helpers/FileBasedXSNService.scala index 9388b404..736d6f03 100644 --- a/server/test/com/xsn/explorer/helpers/FileBasedXSNService.scala +++ b/server/test/com/xsn/explorer/helpers/FileBasedXSNService.scala @@ -3,8 +3,9 @@ package com.xsn.explorer.helpers import com.alexitc.playsonify.core.FutureApplicationResult import com.xsn.explorer.errors.{BlockNotFoundError, TransactionNotFoundError} import com.xsn.explorer.models.rpc.{Block, Transaction} -import com.xsn.explorer.models.{Blockhash, TransactionId} +import com.xsn.explorer.models.{Blockhash, Height, TransactionId} import org.scalactic.{Good, One, Or} +import play.api.libs.json.JsValue import scala.concurrent.Future @@ -19,6 +20,20 @@ class FileBasedXSNService extends DummyXSNService { Future.successful(result) } + override def getRawBlock(blockhash: Blockhash): FutureApplicationResult[JsValue] = { + val result = BlockLoader.json(blockhash.string) + Future.successful(Good(result)) + } + + override def getBlockhash(height: Height): FutureApplicationResult[Blockhash] = { + val maybe = blockMap.collectFirst { + case (_, block) if block.height == height => block.hash + } + + val result = Or.from(maybe, One(BlockNotFoundError)) + Future.successful(result) + } + override def getLatestBlock(): FutureApplicationResult[Block] = { val block = blockMap.values.maxBy(_.height.int) Future.successful(Good(block)) diff --git a/server/test/controllers/BlocksControllerSpec.scala b/server/test/controllers/BlocksControllerSpec.scala index ef300a1a..8d7e1604 100644 --- a/server/test/controllers/BlocksControllerSpec.scala +++ b/server/test/controllers/BlocksControllerSpec.scala @@ -1,114 +1,27 @@ package controllers -import com.alexitc.playsonify.core.{ApplicationResult, FutureApplicationResult} +import com.alexitc.playsonify.core.ApplicationResult import com.alexitc.playsonify.models.ordering.FieldOrdering import com.alexitc.playsonify.models.pagination.{Count, PaginatedQuery, PaginatedResult} import com.alexitc.playsonify.play.PublicErrorRenderer import com.xsn.explorer.data.TransactionBlockingDataHandler -import com.xsn.explorer.errors.{BlockNotFoundError, TransactionNotFoundError} -import com.xsn.explorer.helpers.{BlockLoader, DummyXSNService, TransactionDummyDataHandler, TransactionLoader} +import com.xsn.explorer.helpers._ import com.xsn.explorer.models._ import com.xsn.explorer.models.fields.TransactionField -import com.xsn.explorer.models.rpc.{Block, Transaction} +import com.xsn.explorer.models.rpc.Block import com.xsn.explorer.services.XSNService import controllers.common.MyAPISpec -import org.scalactic.{Bad, Good} +import org.scalactic.Good import play.api.inject.bind import play.api.libs.json.JsValue import play.api.test.Helpers._ -import scala.concurrent.Future - class BlocksControllerSpec extends MyAPISpec { // PoS block val posBlock = BlockLoader.get("1ca318b7a26ed67ca7c8c9b5069d653ba224bf86989125d1dfbb0973b7d6a5e0") - val posBlockCoinstakeTx = TransactionLoader.get("9165692aa53f0db6be61521daf8bece68e005396880376260872352db0c005c1") - val posBlockCoinstakeTxInput = TransactionLoader.get("0c0f595a004eab5cf62ea70570f175701d120a0da31c8222d2d99fc60bf96577") - - // PoS block with rounding error - val posBlockRoundingError = BlockLoader.get("25762bf01143f7fe34912c926e0b95528b082c6323de35516de0fc321f5d8058") - val posBlockRoundingErrorCoinstakeTx = TransactionLoader.get("0b761343c7be39116d5429953e0cfbf51bfe83400ab27d61084222451045116c") - val posBlockRoundingErrorCoinstakeTxInput = TransactionLoader.get("1860288a5a87c79e617f743af44600e050c28ddb7d929d93d43a9148e2ba6638") - - // TPoS - val tposBlock = BlockLoader.get("19f320185015d146237efe757852b21c5e08b88b2f4de9d3fa9517d8463e472b") - val tposBlockContractTx = TransactionLoader.get("7f2b5f25b0ae24a417633e4214827f930a69802c1c43d1fb2ff7b7075b2d1701") - val tposBlockCoinstakeTx = TransactionLoader.get("8c7feafc18576b89bf87faf8aa89feaac1a3fad7d5da77d1fe773219a0e9d864") - val tposBlockCoinstakeTxInput = TransactionLoader.get("9ecf10916467dccc8c8f3a87d869dc5aceb57d5d1c2117036fe60f31369a284e") - - val tposBlock2 = BlockLoader.get("a3a9fb111a3f85c3d920c2dc58ce14d541a65763834247ef958aa3b4d665ef9c") - val tposBlock2ContractTx = TransactionLoader.get(tposBlock2.tposContract.get.string) - val tposBlock2CoinstakeTx = TransactionLoader.get(tposBlock2.transactions(1).string) - - // PoW - val powBlock = BlockLoader.get("000004645e2717b556682e3c642a4c6e473bf25c653ff8e8c114a3006040ffb8") - val powBlockPreviousTx = TransactionLoader.get("67aa0bd8b9297ca6ee25a1e5c2e3a8dbbcc1e20eab76b6d1bdf9d69f8a5356b8") - - val customXSNService = new DummyXSNService { - val blocks = Map( - posBlock.hash -> posBlock, - posBlockRoundingError.hash -> posBlockRoundingError, - tposBlock.hash -> tposBlock, - tposBlock2.hash -> tposBlock2, - powBlock.hash -> powBlock - ) - - override def getBlock(blockhash: Blockhash): FutureApplicationResult[Block] = { - val result = blocks.get(blockhash) - .map(Good(_)) - .getOrElse { - Bad(BlockNotFoundError).accumulating - } - Future.successful(result) - } - - override def getRawBlock(blockhash: Blockhash): FutureApplicationResult[JsValue] = { - val result = blocks.get(blockhash) - .map { _ => BlockLoader.json(blockhash.string) } - .map(Good(_)) - .getOrElse { - Bad(BlockNotFoundError).accumulating - } - - Future.successful(result) - } - - override def getBlockhash(height: Height): FutureApplicationResult[Blockhash] = { - val result = blocks - .values - .find(_.height == height) - .map(_.hash) - .map(Good(_)) - .getOrElse(Bad(BlockNotFoundError).accumulating) - - Future.successful(result) - } - - val txs = Map( - posBlockCoinstakeTx.id -> posBlockCoinstakeTx, - posBlockCoinstakeTxInput.id -> posBlockCoinstakeTxInput, - posBlockRoundingErrorCoinstakeTx.id -> posBlockRoundingErrorCoinstakeTx, - posBlockRoundingErrorCoinstakeTxInput.id -> posBlockRoundingErrorCoinstakeTxInput, - powBlockPreviousTx.id -> powBlockPreviousTx, - tposBlockContractTx.id -> tposBlockContractTx, - tposBlockCoinstakeTx.id -> tposBlockCoinstakeTx, - tposBlockCoinstakeTxInput.id -> tposBlockCoinstakeTxInput, - tposBlock2CoinstakeTx.id -> tposBlock2CoinstakeTx, - tposBlock2ContractTx.id -> tposBlock2ContractTx - ) - - override def getTransaction(txid: TransactionId): FutureApplicationResult[Transaction] = { - val result = txs.get(txid) - .map(Good(_)) - .getOrElse { - Bad(TransactionNotFoundError).accumulating - } - - Future.successful(result) - } - } + val customXSNService = new FileBasedXSNService val transactionDataHandler = new TransactionDummyDataHandler { // TODO: Handle ordering @@ -157,20 +70,7 @@ class BlocksControllerSpec extends MyAPISpec { val jsonBlock = (json \ "block").as[JsValue] val jsonRewards = (json \ "rewards").as[JsValue] - (jsonBlock \ "hash").as[Blockhash] mustEqual block.hash - (jsonBlock \ "size").as[Size] mustEqual block.size - (jsonBlock \ "bits").as[String] mustEqual block.bits - (jsonBlock \ "chainwork").as[String] mustEqual block.chainwork - (jsonBlock \ "difficulty").as[BigDecimal] mustEqual block.difficulty - (jsonBlock \ "confirmations").as[Confirmations] mustEqual block.confirmations - (jsonBlock \ "height").as[Height] mustEqual block.height - (jsonBlock \ "medianTime").as[Long] mustEqual block.medianTime - (jsonBlock \ "time").as[Long] mustEqual block.time - (jsonBlock \ "merkleRoot").as[Blockhash] mustEqual block.merkleRoot - (jsonBlock \ "version").as[Long] mustEqual block.version - (jsonBlock \ "nonce").as[Int] mustEqual block.nonce - (jsonBlock \ "previousBlockhash").asOpt[Blockhash] mustEqual block.previousBlockhash - (jsonBlock \ "nextBlockhash").asOpt[Blockhash] mustEqual block.nextBlockhash + matchBlock(block, jsonBlock) val jsonCoinstake = (jsonRewards \ "coinstake").as[JsValue] (jsonCoinstake \ "address").as[String] mustEqual "XiHW7SR56UPHeXKwcpeVsE4nUfkHv5RqE3" @@ -182,6 +82,7 @@ class BlocksControllerSpec extends MyAPISpec { } "retrieve a PoS block having a rounding error" in { + val posBlockRoundingError = BlockLoader.get("25762bf01143f7fe34912c926e0b95528b082c6323de35516de0fc321f5d8058") val block = posBlockRoundingError val response = GET(url(block.hash.string)) @@ -191,20 +92,7 @@ class BlocksControllerSpec extends MyAPISpec { val jsonBlock = (json \ "block").as[JsValue] val jsonRewards = (json \ "rewards").as[JsValue] - (jsonBlock \ "hash").as[Blockhash] mustEqual block.hash - (jsonBlock \ "size").as[Size] mustEqual block.size - (jsonBlock \ "bits").as[String] mustEqual block.bits - (jsonBlock \ "chainwork").as[String] mustEqual block.chainwork - (jsonBlock \ "difficulty").as[BigDecimal] mustEqual block.difficulty - (jsonBlock \ "confirmations").as[Confirmations] mustEqual block.confirmations - (jsonBlock \ "height").as[Height] mustEqual block.height - (jsonBlock \ "medianTime").as[Long] mustEqual block.medianTime - (jsonBlock \ "time").as[Long] mustEqual block.time - (jsonBlock \ "merkleRoot").as[Blockhash] mustEqual block.merkleRoot - (jsonBlock \ "version").as[Long] mustEqual block.version - (jsonBlock \ "nonce").as[Int] mustEqual block.nonce - (jsonBlock \ "previousBlockhash").asOpt[Blockhash] mustEqual block.previousBlockhash - (jsonBlock \ "nextBlockhash").asOpt[Blockhash] mustEqual block.nextBlockhash + matchBlock(block, jsonBlock) val jsonCoinstake = (jsonRewards \ "coinstake").as[JsValue] (jsonCoinstake \ "address").as[String] mustEqual "XgEGH3y7RfeKEdn2hkYEvBnrnmGBr7zvjL" @@ -215,6 +103,7 @@ class BlocksControllerSpec extends MyAPISpec { } "retrieve a PoW block" in { + val powBlock = BlockLoader.get("000004645e2717b556682e3c642a4c6e473bf25c653ff8e8c114a3006040ffb8") val block = powBlock val response = GET(url(block.hash.string)) @@ -224,20 +113,7 @@ class BlocksControllerSpec extends MyAPISpec { val jsonBlock = (json \ "block").as[JsValue] val jsonRewards = (json \ "rewards").as[JsValue] - (jsonBlock \ "hash").as[Blockhash] mustEqual block.hash - (jsonBlock \ "size").as[Size] mustEqual block.size - (jsonBlock \ "bits").as[String] mustEqual block.bits - (jsonBlock \ "chainwork").as[String] mustEqual block.chainwork - (jsonBlock \ "difficulty").as[BigDecimal] mustEqual block.difficulty - (jsonBlock \ "confirmations").as[Confirmations] mustEqual block.confirmations - (jsonBlock \ "height").as[Height] mustEqual block.height - (jsonBlock \ "medianTime").as[Long] mustEqual block.medianTime - (jsonBlock \ "time").as[Long] mustEqual block.time - (jsonBlock \ "merkleRoot").as[Blockhash] mustEqual block.merkleRoot - (jsonBlock \ "version").as[Long] mustEqual block.version - (jsonBlock \ "nonce").as[Int] mustEqual block.nonce - (jsonBlock \ "previousBlockhash").asOpt[Blockhash] mustEqual block.previousBlockhash - (jsonBlock \ "nextBlockhash").asOpt[Blockhash] mustEqual block.nextBlockhash + matchBlock(block, jsonBlock) val jsonReward = (jsonRewards \ "reward").as[JsValue] (jsonReward \ "address").as[String] mustEqual "XdJnCKYNwzCz8ATv8Eu75gonaHyfr9qXg9" @@ -245,6 +121,7 @@ class BlocksControllerSpec extends MyAPISpec { } "retrieve TPoS block" in { + val tposBlock = BlockLoader.get("19f320185015d146237efe757852b21c5e08b88b2f4de9d3fa9517d8463e472b") val block = tposBlock val response = GET(url(block.hash.string)) @@ -254,21 +131,7 @@ class BlocksControllerSpec extends MyAPISpec { val jsonBlock = (json \ "block").as[JsValue] val jsonRewards = (json \ "rewards").as[JsValue] - (jsonBlock \ "hash").as[Blockhash] mustEqual block.hash - (jsonBlock \ "size").as[Size] mustEqual block.size - (jsonBlock \ "bits").as[String] mustEqual block.bits - (jsonBlock \ "chainwork").as[String] mustEqual block.chainwork - (jsonBlock \ "difficulty").as[BigDecimal] mustEqual block.difficulty - (jsonBlock \ "confirmations").as[Confirmations] mustEqual block.confirmations - (jsonBlock \ "height").as[Height] mustEqual block.height - (jsonBlock \ "medianTime").as[Long] mustEqual block.medianTime - (jsonBlock \ "time").as[Long] mustEqual block.time - (jsonBlock \ "merkleRoot").as[Blockhash] mustEqual block.merkleRoot - (jsonBlock \ "version").as[Long] mustEqual block.version - (jsonBlock \ "nonce").as[Int] mustEqual block.nonce - (jsonBlock \ "previousBlockhash").asOpt[Blockhash] mustEqual block.previousBlockhash - (jsonBlock \ "nextBlockhash").asOpt[Blockhash] mustEqual block.nextBlockhash - (jsonBlock \ "tposContract").as[String] mustEqual block.tposContract.get.string + matchBlock(block, jsonBlock) val jsonOwner = (jsonRewards \ "owner").as[JsValue] (jsonOwner \ "address").as[String] mustEqual "Xi3sQfMQsy2CzMZTrnKW6HFGp1VqFThdLw" @@ -284,6 +147,7 @@ class BlocksControllerSpec extends MyAPISpec { } "retrieve TPoS block with coinsplit" in { + val tposBlock2 = BlockLoader.get("a3a9fb111a3f85c3d920c2dc58ce14d541a65763834247ef958aa3b4d665ef9c") val block = tposBlock2 val response = GET(url(block.hash.string)) @@ -293,21 +157,7 @@ class BlocksControllerSpec extends MyAPISpec { val jsonBlock = (json \ "block").as[JsValue] val jsonRewards = (json \ "rewards").as[JsValue] - (jsonBlock \ "hash").as[Blockhash] mustEqual block.hash - (jsonBlock \ "size").as[Size] mustEqual block.size - (jsonBlock \ "bits").as[String] mustEqual block.bits - (jsonBlock \ "chainwork").as[String] mustEqual block.chainwork - (jsonBlock \ "difficulty").as[BigDecimal] mustEqual block.difficulty - (jsonBlock \ "confirmations").as[Confirmations] mustEqual block.confirmations - (jsonBlock \ "height").as[Height] mustEqual block.height - (jsonBlock \ "medianTime").as[Long] mustEqual block.medianTime - (jsonBlock \ "time").as[Long] mustEqual block.time - (jsonBlock \ "merkleRoot").as[Blockhash] mustEqual block.merkleRoot - (jsonBlock \ "version").as[Long] mustEqual block.version - (jsonBlock \ "nonce").as[Int] mustEqual block.nonce - (jsonBlock \ "previousBlockhash").asOpt[Blockhash] mustEqual block.previousBlockhash - (jsonBlock \ "nextBlockhash").asOpt[Blockhash] mustEqual block.nextBlockhash - (jsonBlock \ "tposContract").as[String] mustEqual block.tposContract.get.string + matchBlock(block, jsonBlock) val jsonOwner = (jsonRewards \ "owner").as[JsValue] (jsonOwner \ "address").as[String] mustEqual "Xu5UkgRL8YRqoW6uEW8SxMLDkJwbjFVfge" @@ -332,20 +182,7 @@ class BlocksControllerSpec extends MyAPISpec { val jsonBlock = (json \ "block").as[JsValue] val jsonRewards = (json \ "rewards").as[JsValue] - (jsonBlock \ "hash").as[Blockhash] mustEqual block.hash - (jsonBlock \ "size").as[Size] mustEqual block.size - (jsonBlock \ "bits").as[String] mustEqual block.bits - (jsonBlock \ "chainwork").as[String] mustEqual block.chainwork - (jsonBlock \ "difficulty").as[BigDecimal] mustEqual block.difficulty - (jsonBlock \ "confirmations").as[Confirmations] mustEqual block.confirmations - (jsonBlock \ "height").as[Height] mustEqual block.height - (jsonBlock \ "medianTime").as[Long] mustEqual block.medianTime - (jsonBlock \ "time").as[Long] mustEqual block.time - (jsonBlock \ "merkleRoot").as[Blockhash] mustEqual block.merkleRoot - (jsonBlock \ "version").as[Long] mustEqual block.version - (jsonBlock \ "nonce").as[Int] mustEqual block.nonce - (jsonBlock \ "previousBlockhash").asOpt[Blockhash] mustEqual block.previousBlockhash - (jsonBlock \ "nextBlockhash").asOpt[Blockhash] mustEqual block.nextBlockhash + matchBlock(block, jsonBlock) val jsonCoinstake = (jsonRewards \ "coinstake").as[JsValue] (jsonCoinstake \ "address").as[String] mustEqual "XiHW7SR56UPHeXKwcpeVsE4nUfkHv5RqE3" @@ -445,4 +282,24 @@ class BlocksControllerSpec extends MyAPISpec { data.size mustEqual 1 } } + + private def matchBlock(expected: Block, actual: JsValue) = { + val jsonBlock = actual + val block = expected + + (jsonBlock \ "hash").as[Blockhash] mustEqual block.hash + (jsonBlock \ "size").as[Size] mustEqual block.size + (jsonBlock \ "bits").as[String] mustEqual block.bits + (jsonBlock \ "chainwork").as[String] mustEqual block.chainwork + (jsonBlock \ "difficulty").as[BigDecimal] mustEqual block.difficulty + (jsonBlock \ "confirmations").as[Confirmations] mustEqual block.confirmations + (jsonBlock \ "height").as[Height] mustEqual block.height + (jsonBlock \ "medianTime").as[Long] mustEqual block.medianTime + (jsonBlock \ "time").as[Long] mustEqual block.time + (jsonBlock \ "merkleRoot").as[Blockhash] mustEqual block.merkleRoot + (jsonBlock \ "version").as[Long] mustEqual block.version + (jsonBlock \ "nonce").as[Int] mustEqual block.nonce + (jsonBlock \ "previousBlockhash").asOpt[Blockhash] mustEqual block.previousBlockhash + (jsonBlock \ "nextBlockhash").asOpt[Blockhash] mustEqual block.nextBlockhash + } }