Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move things out of ChainUnitTest #448

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,25 +1,25 @@
package org.bitcoins.chain.blockchain

import akka.actor.ActorSystem
import org.bitcoins.chain.util.ChainUnitTest
import org.bitcoins.chain.util.{BitcoindChainHandlerViaZmq, ChainUnitTest}
import org.bitcoins.rpc.util.RpcUtil
import org.scalatest.FutureOutcome

import scala.concurrent.Future

class BitcoindChainHandlerTest extends ChainUnitTest {
class BitcoindChainHandlerViaZmqTest extends ChainUnitTest {

override type FixtureParam = BitcoindChainHandler
override type FixtureParam = BitcoindChainHandlerViaZmq

override implicit val system: ActorSystem = ActorSystem("ChainUnitTest")

override def withFixture(test: OneArgAsyncTest): FutureOutcome =
withBitcoindZmqChainHandler(test)
withBitcoindChainHandlerViaZmq(test)

behavior of "BitcoindChainHandler"
behavior of "BitcoindChainHandlerViaZmq"

it must "peer with bitcoind via zmq and have blockchain info relayed" in {
bitcoindChainHandler: BitcoindChainHandler =>
bitcoindChainHandler: BitcoindChainHandlerViaZmq =>
val bitcoind = bitcoindChainHandler.bitcoindRpc

val chainHandler = bitcoindChainHandler.chainHandler
Expand Down
Expand Up @@ -4,7 +4,7 @@ import akka.actor.ActorSystem
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.chain.db.ChainDbConfig
import org.bitcoins.chain.models.BlockHeaderDAO
import org.bitcoins.chain.util.ChainUnitTest
import org.bitcoins.chain.util.{ChainFixture, ChainFixtureTag, ChainUnitTest}
import org.bitcoins.core.protocol.blockchain.MainNetChainParams
import org.bitcoins.db.NetworkDb
import org.bitcoins.testkit.chain.ChainTestUtil
Expand Down Expand Up @@ -51,7 +51,7 @@ class BitcoinPowTest extends ChainUnitTest {
assert(calculatedWork == expectedNextWork))
}

it must "calculate a GetNextWorkRequired correctly" taggedAs FixtureTag.PopulatedBlockHeaderDAO inFixtured {
it must "calculate a GetNextWorkRequired correctly" taggedAs ChainFixtureTag.PopulatedBlockHeaderDAO inFixtured {
case ChainFixture.PopulatedBlockHeaderDAO(blockHeaderDAO) => succeed
}
}
@@ -0,0 +1,22 @@
package org.bitcoins.chain.util

import org.bitcoins.chain.blockchain.ChainHandler
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.zmq.ZMQSubscriber

/** Represents a bitcoind instance paired with a chain handler via zmq */
case class BitcoindChainHandlerViaZmq(
bitcoindRpc: BitcoindRpcClient,
chainHandler: ChainHandler,
zmqSubscriber: ZMQSubscriber)

object BitcoindChainHandlerViaZmq {

def apply(
bitcoindRpc: BitcoindRpcClient,
pair: (ChainHandler, ZMQSubscriber)): BitcoindChainHandlerViaZmq = {
val (chainHandler, zmqSubscriber) = pair

BitcoindChainHandlerViaZmq(bitcoindRpc, chainHandler, zmqSubscriber)
}
}
@@ -0,0 +1,27 @@
package org.bitcoins.chain.util

import org.bitcoins.chain.blockchain.ChainHandler
import org.bitcoins.chain.models.BlockHeaderDAO

/**
* This ADT represents all Chain test fixtures. If you set this type to be your
* FixtureParam and override withFixture to be withChainFixutre, then simply tag
* tests to specify which fixture that test should receive and then use inFixutred
* which takes a PartialFunction[ChainFixture, Future[Assertion] ] (i.e. just
* specify the relevant case for your expected fixture)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: some markdown syntax works in Scaladoc. So I'd write `withFixture`, `PartialFunction[ChainFixture, Future[Assertion]]` etc. Makes it easier for the lib user to quickly parse the doc:-)

*/
sealed trait ChainFixture

object ChainFixture {
case object Empty extends ChainFixture

case class GenisisBlockHeaderDAO(dao: BlockHeaderDAO) extends ChainFixture

case class PopulatedBlockHeaderDAO(dao: BlockHeaderDAO) extends ChainFixture

case class GenisisChainHandler(chainHandler: ChainHandler)
extends ChainFixture

case class BitcoindZmqChainHandlerWithBlock(bitcoindChainHandler: BitcoindChainHandlerViaZmq)
extends ChainFixture
}
@@ -0,0 +1,35 @@
package org.bitcoins.chain.util

import org.bitcoins.chain.util.ChainFixture.{BitcoindZmqChainHandlerWithBlock, Empty, GenisisBlockHeaderDAO, GenisisChainHandler, PopulatedBlockHeaderDAO}

import scala.concurrent.Future

trait ChainFixtureHelper { this : ChainUnitTest =>


def createFixture(tag: ChainFixtureTag): Future[ChainFixture] = {
tag match {
case ChainFixtureTag.Empty => Future.successful(ChainFixture.Empty)
case ChainFixtureTag.GenisisBlockHeaderDAO =>
createBlockHeaderDAO().map(GenisisBlockHeaderDAO.apply)
case ChainFixtureTag.PopulatedBlockHeaderDAO =>
createPopulatedBlockHeaderDAO().map(PopulatedBlockHeaderDAO.apply)
case ChainFixtureTag.GenisisChainHandler =>
createChainHandler().map(GenisisChainHandler.apply)
case ChainFixtureTag.BitcoindZmqChainHandlerWithBlock =>
createBitcoindChainHandlerViaZmq().map(
BitcoindZmqChainHandlerWithBlock.apply)
}
}

def destroyFixture(fixture: ChainFixture): Future[Any] = {
fixture match {
case Empty => Future.successful(())
case GenisisBlockHeaderDAO(_) => destroyHeaderTable()
case PopulatedBlockHeaderDAO(_) => destroyHeaderTable()
case GenisisChainHandler(_) => destroyHeaderTable()
case BitcoindZmqChainHandlerWithBlock(bitcoindHandler) =>
destroyBitcoindChainHandlerViaZmq(bitcoindHandler)
}
}
}
@@ -0,0 +1,42 @@
package org.bitcoins.chain.util

import org.scalatest.Tag

/**
* If a test file uses ChainFixture as its FixtureParam, then
* using these tags will determine which fixture the test will get.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same nit here above

*
* Simply add taggedAs FixtureTag._ to your test before calling inFixtured.
*/
sealed abstract class ChainFixtureTag(name: String) extends Tag(name)

object ChainFixtureTag {
case object Empty extends ChainFixtureTag("Empty")

case object GenisisBlockHeaderDAO
extends ChainFixtureTag("GenisisBlockHeaderDAO")

case object PopulatedBlockHeaderDAO
extends ChainFixtureTag("PopulatedBlockHeaderDAO")

case object GenisisChainHandler extends ChainFixtureTag("GenisisChainHandler")

case object BitcoindZmqChainHandlerWithBlock
extends ChainFixtureTag("BitcoindZmqChainHandlerWithBlock")

val defaultTag: ChainFixtureTag = ChainFixtureTag.Empty


def from(tag: String): ChainFixtureTag = {
tag match {
case Empty.name => Empty
case GenisisBlockHeaderDAO.name => GenisisBlockHeaderDAO
case PopulatedBlockHeaderDAO.name => PopulatedBlockHeaderDAO
case GenisisChainHandler.name => GenisisChainHandler
case BitcoindZmqChainHandlerWithBlock.name =>
BitcoindZmqChainHandlerWithBlock
case _: String =>
throw new IllegalArgumentException(s"$tag is not a valid tag")
}
}
}
147 changes: 23 additions & 124 deletions chain-test/src/test/scala/org/bitcoins/chain/util/ChainUnitTest.scala
Expand Up @@ -26,6 +26,7 @@ import scala.concurrent.{ExecutionContext, Future}
trait ChainUnitTest
extends fixture.AsyncFlatSpec
with BitcoinSFixture
with ChainFixtureHelper
with MustMatchers
with BitcoinSLogger
with BeforeAndAfter
Expand All @@ -52,111 +53,20 @@ trait ChainUnitTest
implicit def ec: ExecutionContext =
scala.concurrent.ExecutionContext.Implicits.global

/**
* If a test file uses ChainFixture as its FixtureParam, then
* using these tags will determine which fixture the test will get.
*
* Simply add taggedAs FixtureTag._ to your test before calling inFixtured.
*/
sealed abstract class FixtureTag(name: String) extends Tag(name)

object FixtureTag {
case object Empty extends FixtureTag("Empty")

case object GenisisBlockHeaderDAO
extends FixtureTag("GenisisBlockHeaderDAO")

case object PopulatedBlockHeaderDAO
extends FixtureTag("PopulatedBlockHeaderDAO")

case object GenisisChainHandler extends FixtureTag("GenisisChainHandler")

case object BitcoindZmqChainHandlerWithBlock
extends FixtureTag("BitcoindZmqChainHandlerWithBlock")

def from(tag: String): FixtureTag = {
tag match {
case Empty.name => Empty
case GenisisBlockHeaderDAO.name => GenisisBlockHeaderDAO
case PopulatedBlockHeaderDAO.name => PopulatedBlockHeaderDAO
case GenisisChainHandler.name => GenisisChainHandler
case BitcoindZmqChainHandlerWithBlock.name =>
BitcoindZmqChainHandlerWithBlock
case _: String =>
throw new IllegalArgumentException(s"$tag is not a valid tag")
}
}
}

/**
* All untagged tests will be given this tag. Override this if you are using
* ChainFixture and the plurality of tests use some fixture other than Empty.
*/
val defaultTag: FixtureTag = FixtureTag.Empty

/**
* This ADT represents all Chain test fixtures. If you set this type to be your
* FixtureParam and override withFixture to be withChainFixutre, then simply tag
* tests to specify which fixture that test should receive and then use inFixutred
* which takes a PartialFunction[ChainFixture, Future[Assertion] ] (i.e. just
* specify the relevant case for your expected fixture)
*/
sealed trait ChainFixture

object ChainFixture {
case object Empty extends ChainFixture

case class GenisisBlockHeaderDAO(dao: BlockHeaderDAO) extends ChainFixture

case class PopulatedBlockHeaderDAO(dao: BlockHeaderDAO) extends ChainFixture

case class GenisisChainHandler(chainHandler: ChainHandler)
extends ChainFixture

case class BitcoindZmqChainHandlerWithBlock(
bitcoindChainHandler: BitcoindChainHandler)
extends ChainFixture

def create(tag: FixtureTag): Future[ChainFixture] = {
tag match {
case FixtureTag.Empty => Future.successful(ChainFixture.Empty)
case FixtureTag.GenisisBlockHeaderDAO =>
createBlockHeaderDAO().map(GenisisBlockHeaderDAO.apply)
case FixtureTag.PopulatedBlockHeaderDAO =>
createPopulatedBlockHeaderDAO().map(PopulatedBlockHeaderDAO.apply)
case FixtureTag.GenisisChainHandler =>
createChainHandler().map(GenisisChainHandler.apply)
case FixtureTag.BitcoindZmqChainHandlerWithBlock =>
createBitcoindChainHandler().map(
BitcoindZmqChainHandlerWithBlock.apply)
}
}

def destroy(fixture: ChainFixture): Future[Any] = {
fixture match {
case Empty => Future.successful(())
case GenisisBlockHeaderDAO(_) => destroyHeaderTable()
case PopulatedBlockHeaderDAO(_) => destroyHeaderTable()
case GenisisChainHandler(_) => destroyHeaderTable()
case BitcoindZmqChainHandlerWithBlock(bitcoindHandler) =>
destroyBitcoindChainHandler(bitcoindHandler)
}
}
}

def withChainFixture(test: OneArgAsyncTest): FutureOutcome = {
val stringTag = test.tags.headOption.getOrElse(defaultTag.name)
val stringTag = test.tags.headOption.getOrElse(ChainFixtureTag.defaultTag.name)

val fixtureTag: FixtureTag = FixtureTag.from(stringTag)
val fixtureTag: ChainFixtureTag = ChainFixtureTag.from(stringTag)

val fixtureF: Future[ChainFixture] = ChainFixture.create(fixtureTag)
val fixtureF: Future[ChainFixture] = createFixture(fixtureTag)

val outcomeF = fixtureF.flatMap(fixture =>
test(fixture.asInstanceOf[FixtureParam]).toFuture)

val fixtureTakeDownF = outcomeF.flatMap { outcome =>
val destroyedF =
fixtureF.flatMap(fixture => ChainFixture.destroy(fixture))
fixtureF.flatMap(fixture => destroyFixture(fixture))

destroyedF.map(_ => outcome)
}
Expand Down Expand Up @@ -340,23 +250,6 @@ trait ChainUnitTest
genesisHeaderF
}

/** Represents a bitcoind instance paired with a chain handler via zmq */
case class BitcoindChainHandler(
bitcoindRpc: BitcoindRpcClient,
chainHandler: ChainHandler,
zmqSubscriber: ZMQSubscriber)

object BitcoindChainHandler {

def apply(
bitcoindRpc: BitcoindRpcClient,
pair: (ChainHandler, ZMQSubscriber)): BitcoindChainHandler = {
val (chainHandler, zmqSubscriber) = pair

BitcoindChainHandler(bitcoindRpc, chainHandler, zmqSubscriber)
}
}

def createChainHandlerWithBitcoindZmq(
bitcoind: BitcoindRpcClient): Future[(ChainHandler, ZMQSubscriber)] = {
val genesisHeaderF = setupHeaderTableWithGenesisHeader()
Expand Down Expand Up @@ -384,32 +277,38 @@ trait ChainUnitTest
genesisHeaderF.map(_ => (chainHandler, zmqSubscriber))
}

def createBitcoindChainHandler(): Future[BitcoindChainHandler] = {
def createBitcoindChainHandlerViaZmq(): Future[BitcoindChainHandlerViaZmq] = {
composeBuildersAndWrap(createBitcoind,
createChainHandlerWithBitcoindZmq,
BitcoindChainHandler.apply)()
BitcoindChainHandlerViaZmq.apply)()
}

def destroyBitcoindChainHandler(
bitcoindChainHandler: BitcoindChainHandler): Future[Unit] = {
def destroyBitcoindChainHandlerViaZmq(
bitcoindChainHandler: BitcoindChainHandlerViaZmq): Future[Unit] = {
val stopBitcoindF =
BitcoindRpcTestUtil.stopServer(bitcoindChainHandler.bitcoindRpc)
val dropTableF = destroyHeaderTable()

bitcoindChainHandler.zmqSubscriber.stop

stopBitcoindF.flatMap(_ => dropTableF)
}

//BitcoindChainHandler => Future[Assertion]
def withBitcoindZmqChainHandler(test: OneArgAsyncTest)(
/**
* Creates a [[BitcoindRpcClient bitcoind]] that is linked to our [[ChainHandler bitcoin-s chain handler]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: there's a difference between a bitcoind instance and a bitcoind RPC client instance

* via a [[ZMQSubscriber zmq]]. This means messages are passed between bitcoin and our chain handler
* with a zmq pub/sub message passing
* @param test the test to be executed with bitcoind and chain handler via zmq
* @param system
* @return
*/
def withBitcoindChainHandlerViaZmq(test: OneArgAsyncTest)(
implicit system: ActorSystem): FutureOutcome = {
val builder: () => Future[BitcoindChainHandler] = composeBuildersAndWrap(
createBitcoind,
createChainHandlerWithBitcoindZmq,
BitcoindChainHandler.apply)
val builder: () => Future[BitcoindChainHandlerViaZmq] = composeBuildersAndWrap(
builder = createBitcoind,
dependentBuilder = createChainHandlerWithBitcoindZmq,
wrap = BitcoindChainHandlerViaZmq.apply)

makeDependentFixture(builder, destroyBitcoindChainHandler)(test)
makeDependentFixture(builder, destroyBitcoindChainHandlerViaZmq)(test)
}

override def afterAll(): Unit = {
Expand Down