Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import io.iohk.ethereum.jsonrpc.AkkaTaskOps._
import io.iohk.ethereum.jsonrpc.{JsonRpcError, ServiceResponse}
import io.iohk.ethereum.utils.Logger

//TODO: Add unit tests - task: ETCM-395
class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem)
extends FaucetConfigBuilder
with RetrySupport
Expand All @@ -20,16 +19,19 @@ class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem)
implicit lazy val actorTimeout: Timeout = Timeout(config.responseTimeout)

def sendFunds(sendFundsRequest: SendFundsRequest): ServiceResponse[SendFundsResponse] =
faucetHandler().flatMap(handler =>
handler
.askFor[Any](FaucetHandlerMsg.SendFunds(sendFundsRequest.address))
.map(handleSendFundsResponse orElse handleErrors)
)
faucetHandler()
.flatMap(handler =>
handler
.askFor[Any](FaucetHandlerMsg.SendFunds(sendFundsRequest.address))
.map(handleSendFundsResponse orElse handleErrors)
)
.onErrorRecover(handleErrors)

def status(statusRequest: StatusRequest): ServiceResponse[StatusResponse] =
faucetHandler()
.flatMap(handler => handler.askFor[Any](FaucetHandlerMsg.Status))
.map(handleStatusResponse orElse handleErrors)
.onErrorRecover(handleErrors)

private def handleSendFundsResponse: PartialFunction[Any, Either[JsonRpcError, SendFundsResponse]] = {
case FaucetHandlerResponse.TransactionSent(txHash) =>
Expand All @@ -44,8 +46,11 @@ class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem)
private def handleErrors[T]: PartialFunction[Any, Either[JsonRpcError, T]] = {
case FaucetHandlerResponse.FaucetIsUnavailable =>
Left(JsonRpcError.LogicError("Faucet is unavailable: Please try again in a few more seconds"))

case FaucetHandlerResponse.WalletRpcClientError(error) =>
Left(JsonRpcError.LogicError(s"Faucet error: $error"))
case other =>
log.error(s"process failure: $other")
Left(JsonRpcError.InternalError)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package io.iohk.ethereum.faucet.jsonrpc

import akka.actor.{ActorRef, ActorSystem}
import akka.testkit.{TestKit, TestProbe}
import akka.util.ByteString
import io.iohk.ethereum.domain.Address
import io.iohk.ethereum.faucet.FaucetHandler.FaucetHandlerMsg
import io.iohk.ethereum.faucet.FaucetHandler.FaucetHandlerResponse.{
FaucetIsUnavailable,
StatusResponse,
TransactionSent,
WalletRpcClientError
}
import io.iohk.ethereum.faucet.FaucetStatus.WalletAvailable
import io.iohk.ethereum.faucet.jsonrpc.FaucetDomain.{SendFundsRequest, StatusRequest}
import io.iohk.ethereum.faucet.{FaucetConfig, SupervisorConfig}
import io.iohk.ethereum.jsonrpc.JsonRpcError
import io.iohk.ethereum.testing.ActorsTesting.simpleAutoPilot
import io.iohk.ethereum.{NormalPatience, WithActorSystemShutDown}
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.bouncycastle.util.encoders.Hex
import org.scalactic.TypeCheckedTripleEquals
import org.scalamock.scalatest.MockFactory
import org.scalatest.OptionValues
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.flatspec.AnyFlatSpecLike
import org.scalatest.matchers.should.Matchers

import scala.concurrent.duration._

class FaucetRpcServiceSpec
extends TestKit(ActorSystem("ActorSystem_DebugFaucetRpcServiceSpec"))
with AnyFlatSpecLike
with WithActorSystemShutDown
with Matchers
with ScalaFutures
with OptionValues
with MockFactory
with NormalPatience
with TypeCheckedTripleEquals {

"FaucetRpcService" should "answer txHash correctly when the wallet is available and the requested send funds be successfully" in new TestSetup {
val address: Address = Address("0x00")
val request: SendFundsRequest = SendFundsRequest(address)
val txHash: ByteString = ByteString(Hex.decode("112233"))

fHandler.setAutoPilot(simpleAutoPilot { case FaucetHandlerMsg.SendFunds(`address`) =>
TransactionSent(txHash)
})
faucetRpcService.sendFunds(request).runSyncUnsafe(Duration.Inf) match {
case Left(error) => fail(s"failure with error: $error")
case Right(response) => response.txId shouldBe txHash
}
}

it should "answer WalletRpcClientError when the wallet is available and the requested send funds be failure" in new TestSetup {
val address: Address = Address("0x00")
val request: SendFundsRequest = SendFundsRequest(address)
val clientError: String = "Parser error"

fHandler.setAutoPilot(simpleAutoPilot { case FaucetHandlerMsg.SendFunds(`address`) =>
WalletRpcClientError(clientError)
})
faucetRpcService.sendFunds(request).runSyncUnsafe(Duration.Inf) match {
case Right(_) => fail()
case Left(error) => error shouldBe JsonRpcError.LogicError(s"Faucet error: $clientError")
}
}

it should "answer FaucetIsUnavailable when tried to send funds and the wallet is unavailable" in new TestSetup {
val address: Address = Address("0x00")
val request: SendFundsRequest = SendFundsRequest(address)

fHandler.setAutoPilot(simpleAutoPilot { case FaucetHandlerMsg.SendFunds(`address`) =>
FaucetIsUnavailable
})
faucetRpcService.sendFunds(request).runSyncUnsafe(Duration.Inf) match {
case Right(_) => fail()
case Left(error) =>
error shouldBe JsonRpcError.LogicError("Faucet is unavailable: Please try again in a few more seconds")
}
}

it should "answer FaucetIsUnavailable when tried to get status and the wallet is unavailable" in new TestSetup {
fHandler.setAutoPilot(simpleAutoPilot { case FaucetHandlerMsg.Status =>
FaucetIsUnavailable
})
faucetRpcService.status(StatusRequest()).runSyncUnsafe(Duration.Inf) match {
case Right(_) => fail()
case Left(error) =>
error shouldBe JsonRpcError.LogicError("Faucet is unavailable: Please try again in a few more seconds")
}
}

it should "answer WalletAvailable when tried to get status and the wallet is available" in new TestSetup {
fHandler.setAutoPilot(simpleAutoPilot { case FaucetHandlerMsg.Status =>
StatusResponse(WalletAvailable)
})
faucetRpcService.status(StatusRequest()).runSyncUnsafe(Duration.Inf) match {
case Left(error) => fail(s"failure with error: $error")
case Right(response) => response shouldBe FaucetDomain.StatusResponse(WalletAvailable)
}
}

it should "answer internal error when tried to send funds but the Faucet Handler is disable" in new TestSetup {
val address: Address = Address("0x00")
val request: SendFundsRequest = SendFundsRequest(address)

faucetRpcServiceWithoutFaucetHandler.sendFunds(request).runSyncUnsafe(Duration.Inf) match {
case Right(_) => fail()
case Left(error) =>
error shouldBe JsonRpcError.InternalError
}
}

it should "answer internal error when tried to get status but the Faucet Handler is disable" in new TestSetup {
val address: Address = Address("0x00")
val request: SendFundsRequest = SendFundsRequest(address)

faucetRpcServiceWithoutFaucetHandler.status(StatusRequest()).runSyncUnsafe(Duration.Inf) match {
case Right(_) => fail()
case Left(error) =>
error shouldBe JsonRpcError.InternalError
}
}

class TestSetup(implicit system: ActorSystem) {

val config: FaucetConfig = FaucetConfig(
walletAddress = Address("0x99"),
walletPassword = "",
txGasPrice = 10,
txGasLimit = 20,
txValue = 1,
rpcAddress = "",
keyStoreDir = "",
minRequestInterval = 10.seconds,
handlerTimeout = 10.seconds,
responseTimeout = 10.seconds,
supervisor = mock[SupervisorConfig],
shutdownTimeout = 15.seconds
)

val fHandler = TestProbe()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
val fHandler = TestProbe()
val faucetHandler = TestProbe()


val faucetRpcService: FaucetRpcService = new FaucetRpcService(config) {

override def faucetHandler()(implicit system: ActorSystem): Task[ActorRef] = {
Task(fHandler.ref)
}
}

val faucetRpcServiceWithoutFaucetHandler: FaucetRpcService = new FaucetRpcService(config) {
override def faucetHandler()(implicit system: ActorSystem): Task[ActorRef] = {
Task.raiseError(new RuntimeException("time out"))
}
}
}

}