package com.xsn.explorer.data import com.alexitc.playsonify.models.ordering.{FieldOrdering, OrderingCondition} import com.alexitc.playsonify.models.pagination.{Count, Limit, Offset, PaginatedQuery} import com.alexitc.playsonify.sql.FieldOrderingSQLInterpreter import com.xsn.explorer.data.anorm.BlockPostgresDataHandler import com.xsn.explorer.data.anorm.dao.BlockPostgresDAO import com.xsn.explorer.data.common.PostgresDataHandlerSpec import com.xsn.explorer.errors.BlockNotFoundError import com.xsn.explorer.helpers.BlockLoader import com.xsn.explorer.models.Blockhash import com.xsn.explorer.models.fields.BlockField import com.xsn.explorer.models.rpc.Block import org.scalactic.{Bad, One, Or} import org.scalatest.BeforeAndAfter class BlockPostgresDataHandlerSpec extends PostgresDataHandlerSpec with BeforeAndAfter { before { clearDatabase() } val defaultOrdering = FieldOrdering(BlockField.Height, OrderingCondition.AscendingOrder) lazy val dao = new BlockPostgresDAO(new FieldOrderingSQLInterpreter) lazy val dataHandler = new BlockPostgresDataHandler(database, dao) def insert(block: Block) = { database.withConnection { implicit conn => val maybe = dao.insert(block) Or.from(maybe, One(BlockNotFoundError)) } } "getBy blockhash" should { "return a block" in { val block = BlockLoader.get("1ca318b7a26ed67ca7c8c9b5069d653ba224bf86989125d1dfbb0973b7d6a5e0") .copy(previousBlockhash = None, nextBlockhash = None) insert(block).isGood mustEqual true val result = dataHandler.getBy(block.hash) result.isGood mustEqual true matches(block, result.get) } "fail on block not found" in { val blockhash = Blockhash.from("b858d38a3552c83aea58f66fe00ae220352a235e33fcf1f3af04507a61a9dc32").get val result = dataHandler.getBy(blockhash) result mustEqual Bad(BlockNotFoundError).accumulating } } "getBy height" should { "return a block" in { val block = BlockLoader.get("1ca318b7a26ed67ca7c8c9b5069d653ba224bf86989125d1dfbb0973b7d6a5e0") .copy(previousBlockhash = None, nextBlockhash = None) insert(block).isGood mustEqual true val result = dataHandler.getBy(block.height) result.isGood mustEqual true matches(block, result.get) } "fail on block not found" in { val block = BlockLoader.get("1ca318b7a26ed67ca7c8c9b5069d653ba224bf86989125d1dfbb0973b7d6a5e0") val result = dataHandler.getBy(block.height) result mustEqual Bad(BlockNotFoundError).accumulating } } "getBy" should { "paginate the results" in { val block0 = BlockLoader.get("00000c822abdbb23e28f79a49d29b41429737c6c7e15df40d1b1f1b35907ae34") .copy(previousBlockhash = None, nextBlockhash = None) val block1 = BlockLoader.get("000003fb382f6892ae96594b81aa916a8923c70701de4e7054aac556c7271ef7") .copy(nextBlockhash = None) val block2 = BlockLoader.get("000004645e2717b556682e3c642a4c6e473bf25c653ff8e8c114a3006040ffb8") .copy(nextBlockhash = None) List(block0, block1, block2).map(insert).foreach(_.isGood mustEqual true) val query = PaginatedQuery(Offset(1), Limit(3)) val expected = List(block1, block2) val result = dataHandler.getBy(query, defaultOrdering).get result.total mustEqual Count(3) result.offset mustEqual query.offset result.limit mustEqual query.limit result.data.size mustEqual expected.size val data = result.data matches(data(0), expected(0)) matches(data(1), expected(1)) } def testOrdering[B](field: BlockField)(sortBy: Block => B)(implicit order: Ordering[B]) = { val block0 = BlockLoader.get("00000c822abdbb23e28f79a49d29b41429737c6c7e15df40d1b1f1b35907ae34") .copy(previousBlockhash = None, nextBlockhash = None) val block1 = BlockLoader.get("000003fb382f6892ae96594b81aa916a8923c70701de4e7054aac556c7271ef7") .copy(nextBlockhash = None) val block2 = BlockLoader.get("000004645e2717b556682e3c642a4c6e473bf25c653ff8e8c114a3006040ffb8") .copy(nextBlockhash = None) val blocks = List(block0, block1, block2) blocks.map(insert).foreach(_.isGood mustEqual true) val ordering = FieldOrdering(field, OrderingCondition.AscendingOrder) val query = PaginatedQuery(Offset(0), Limit(10)) val expected = blocks.sortBy(sortBy)(order).map(_.hash) val result = dataHandler.getBy(query, ordering).get.data result.map(_.hash) mustEqual expected val expectedReverse = expected.reverse val resultReverse = dataHandler.getBy(query, ordering.copy(orderingCondition = OrderingCondition.DescendingOrder)).get.data resultReverse.map(_.hash) mustEqual expectedReverse } "allow to sort by txid" in { testOrdering(BlockField.Height)(_.height.int) } "allow to sort by time" in { testOrdering(BlockField.Time)(_.time) } } "delete" should { "delete a block" in { val block = BlockLoader.get("1ca318b7a26ed67ca7c8c9b5069d653ba224bf86989125d1dfbb0973b7d6a5e0") .copy(previousBlockhash = None, nextBlockhash = None) insert(block).isGood mustEqual true val result = dataHandler.delete(block.hash) result.isGood mustEqual true matches(block, result.get) } "fail on block not found" in { val blockhash = Blockhash.from("b858d38a3552c83aea58f66fe00ae220352a235e33fcf1f3af04507a61a9dc32").get val result = dataHandler.delete(blockhash) result mustEqual Bad(BlockNotFoundError).accumulating } } "getLatestBlock" should { "return the block" in { clearDatabase() val block0 = BlockLoader.get("00000c822abdbb23e28f79a49d29b41429737c6c7e15df40d1b1f1b35907ae34") .copy(previousBlockhash = None, nextBlockhash = None) val block1 = BlockLoader.get("000003fb382f6892ae96594b81aa916a8923c70701de4e7054aac556c7271ef7") .copy(nextBlockhash = None) val block2 = BlockLoader.get("000004645e2717b556682e3c642a4c6e473bf25c653ff8e8c114a3006040ffb8") .copy(nextBlockhash = None) List(block0, block1, block2).map(insert).foreach(_.isGood mustEqual true) val result = dataHandler.getLatestBlock() result.isGood mustEqual true matches(block2, result.get) } "fail on no blocks" in { clearDatabase() val result = dataHandler.getLatestBlock() result mustEqual Bad(BlockNotFoundError).accumulating } } "getFirstBlock" should { "return the block" in { clearDatabase() val block0 = BlockLoader.get("00000c822abdbb23e28f79a49d29b41429737c6c7e15df40d1b1f1b35907ae34") .copy(previousBlockhash = None, nextBlockhash = None) val block1 = BlockLoader.get("000003fb382f6892ae96594b81aa916a8923c70701de4e7054aac556c7271ef7") .copy(nextBlockhash = None) val block2 = BlockLoader.get("000004645e2717b556682e3c642a4c6e473bf25c653ff8e8c114a3006040ffb8") .copy(nextBlockhash = None) List(block0, block1, block2).map(insert).foreach(_.isGood mustEqual true) val result = dataHandler.getFirstBlock() result.isGood mustEqual true matches(block0, result.get) } "fail on no blocks" in { clearDatabase() val result = dataHandler.getLatestBlock() result mustEqual Bad(BlockNotFoundError).accumulating } } private def matches(expected: Block, result: Block) = { // NOTE: transactions and confirmations are not matched intentionally result.hash mustEqual expected.hash result.tposContract mustEqual expected.tposContract result.nextBlockhash mustEqual expected.nextBlockhash result.previousBlockhash mustEqual expected.previousBlockhash result.merkleRoot mustEqual expected.merkleRoot result.size mustEqual expected.size result.height mustEqual expected.height result.version mustEqual expected.version result.medianTime mustEqual expected.medianTime result.time mustEqual expected.time result.bits mustEqual expected.bits result.chainwork mustEqual expected.chainwork result.difficulty mustEqual expected.difficulty result.nonce mustEqual expected.nonce } }