Skip to content

Commit a8d5b00

Browse files
[ETCM-395] faucet unit tests (#816)
* add new unit tests * fix faucet rpc service when dont find the faucet actor * change test name Co-authored-by: Piotr Paradziński <[email protected]>
1 parent 6a4bb70 commit a8d5b00

File tree

2 files changed

+173
-7
lines changed

2 files changed

+173
-7
lines changed

src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import io.iohk.ethereum.jsonrpc.AkkaTaskOps._
1010
import io.iohk.ethereum.jsonrpc.{JsonRpcError, ServiceResponse}
1111
import io.iohk.ethereum.utils.Logger
1212

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

2221
def sendFunds(sendFundsRequest: SendFundsRequest): ServiceResponse[SendFundsResponse] =
23-
faucetHandler().flatMap(handler =>
24-
handler
25-
.askFor[Any](FaucetHandlerMsg.SendFunds(sendFundsRequest.address))
26-
.map(handleSendFundsResponse orElse handleErrors)
27-
)
22+
faucetHandler()
23+
.flatMap(handler =>
24+
handler
25+
.askFor[Any](FaucetHandlerMsg.SendFunds(sendFundsRequest.address))
26+
.map(handleSendFundsResponse orElse handleErrors)
27+
)
28+
.onErrorRecover(handleErrors)
2829

2930
def status(statusRequest: StatusRequest): ServiceResponse[StatusResponse] =
3031
faucetHandler()
3132
.flatMap(handler => handler.askFor[Any](FaucetHandlerMsg.Status))
3233
.map(handleStatusResponse orElse handleErrors)
34+
.onErrorRecover(handleErrors)
3335

3436
private def handleSendFundsResponse: PartialFunction[Any, Either[JsonRpcError, SendFundsResponse]] = {
3537
case FaucetHandlerResponse.TransactionSent(txHash) =>
@@ -44,8 +46,11 @@ class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem)
4446
private def handleErrors[T]: PartialFunction[Any, Either[JsonRpcError, T]] = {
4547
case FaucetHandlerResponse.FaucetIsUnavailable =>
4648
Left(JsonRpcError.LogicError("Faucet is unavailable: Please try again in a few more seconds"))
47-
4849
case FaucetHandlerResponse.WalletRpcClientError(error) =>
4950
Left(JsonRpcError.LogicError(s"Faucet error: $error"))
51+
case other =>
52+
log.error(s"process failure: $other")
53+
Left(JsonRpcError.InternalError)
5054
}
55+
5156
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package io.iohk.ethereum.faucet.jsonrpc
2+
3+
import akka.actor.{ActorRef, ActorSystem}
4+
import akka.testkit.{TestKit, TestProbe}
5+
import akka.util.ByteString
6+
import io.iohk.ethereum.domain.Address
7+
import io.iohk.ethereum.faucet.FaucetHandler.FaucetHandlerMsg
8+
import io.iohk.ethereum.faucet.FaucetHandler.FaucetHandlerResponse.{
9+
FaucetIsUnavailable,
10+
StatusResponse,
11+
TransactionSent,
12+
WalletRpcClientError
13+
}
14+
import io.iohk.ethereum.faucet.FaucetStatus.WalletAvailable
15+
import io.iohk.ethereum.faucet.jsonrpc.FaucetDomain.{SendFundsRequest, StatusRequest}
16+
import io.iohk.ethereum.faucet.{FaucetConfig, SupervisorConfig}
17+
import io.iohk.ethereum.jsonrpc.JsonRpcError
18+
import io.iohk.ethereum.testing.ActorsTesting.simpleAutoPilot
19+
import io.iohk.ethereum.{NormalPatience, WithActorSystemShutDown}
20+
import monix.eval.Task
21+
import monix.execution.Scheduler.Implicits.global
22+
import org.bouncycastle.util.encoders.Hex
23+
import org.scalactic.TypeCheckedTripleEquals
24+
import org.scalamock.scalatest.MockFactory
25+
import org.scalatest.OptionValues
26+
import org.scalatest.concurrent.ScalaFutures
27+
import org.scalatest.flatspec.AnyFlatSpecLike
28+
import org.scalatest.matchers.should.Matchers
29+
30+
import scala.concurrent.duration._
31+
32+
class FaucetRpcServiceSpec
33+
extends TestKit(ActorSystem("ActorSystem_DebugFaucetRpcServiceSpec"))
34+
with AnyFlatSpecLike
35+
with WithActorSystemShutDown
36+
with Matchers
37+
with ScalaFutures
38+
with OptionValues
39+
with MockFactory
40+
with NormalPatience
41+
with TypeCheckedTripleEquals {
42+
43+
"FaucetRpcService" should "answer txHash correctly when the wallet is available and the requested send funds be successfully" in new TestSetup {
44+
val address: Address = Address("0x00")
45+
val request: SendFundsRequest = SendFundsRequest(address)
46+
val txHash: ByteString = ByteString(Hex.decode("112233"))
47+
48+
fHandler.setAutoPilot(simpleAutoPilot { case FaucetHandlerMsg.SendFunds(`address`) =>
49+
TransactionSent(txHash)
50+
})
51+
faucetRpcService.sendFunds(request).runSyncUnsafe(Duration.Inf) match {
52+
case Left(error) => fail(s"failure with error: $error")
53+
case Right(response) => response.txId shouldBe txHash
54+
}
55+
}
56+
57+
it should "answer WalletRpcClientError when the wallet is available and the requested send funds be failure" in new TestSetup {
58+
val address: Address = Address("0x00")
59+
val request: SendFundsRequest = SendFundsRequest(address)
60+
val clientError: String = "Parser error"
61+
62+
fHandler.setAutoPilot(simpleAutoPilot { case FaucetHandlerMsg.SendFunds(`address`) =>
63+
WalletRpcClientError(clientError)
64+
})
65+
faucetRpcService.sendFunds(request).runSyncUnsafe(Duration.Inf) match {
66+
case Right(_) => fail()
67+
case Left(error) => error shouldBe JsonRpcError.LogicError(s"Faucet error: $clientError")
68+
}
69+
}
70+
71+
it should "answer FaucetIsUnavailable when tried to send funds and the wallet is unavailable" in new TestSetup {
72+
val address: Address = Address("0x00")
73+
val request: SendFundsRequest = SendFundsRequest(address)
74+
75+
fHandler.setAutoPilot(simpleAutoPilot { case FaucetHandlerMsg.SendFunds(`address`) =>
76+
FaucetIsUnavailable
77+
})
78+
faucetRpcService.sendFunds(request).runSyncUnsafe(Duration.Inf) match {
79+
case Right(_) => fail()
80+
case Left(error) =>
81+
error shouldBe JsonRpcError.LogicError("Faucet is unavailable: Please try again in a few more seconds")
82+
}
83+
}
84+
85+
it should "answer FaucetIsUnavailable when tried to get status and the wallet is unavailable" in new TestSetup {
86+
fHandler.setAutoPilot(simpleAutoPilot { case FaucetHandlerMsg.Status =>
87+
FaucetIsUnavailable
88+
})
89+
faucetRpcService.status(StatusRequest()).runSyncUnsafe(Duration.Inf) match {
90+
case Right(_) => fail()
91+
case Left(error) =>
92+
error shouldBe JsonRpcError.LogicError("Faucet is unavailable: Please try again in a few more seconds")
93+
}
94+
}
95+
96+
it should "answer WalletAvailable when tried to get status and the wallet is available" in new TestSetup {
97+
fHandler.setAutoPilot(simpleAutoPilot { case FaucetHandlerMsg.Status =>
98+
StatusResponse(WalletAvailable)
99+
})
100+
faucetRpcService.status(StatusRequest()).runSyncUnsafe(Duration.Inf) match {
101+
case Left(error) => fail(s"failure with error: $error")
102+
case Right(response) => response shouldBe FaucetDomain.StatusResponse(WalletAvailable)
103+
}
104+
}
105+
106+
it should "answer internal error when tried to send funds but the Faucet Handler is disable" in new TestSetup {
107+
val address: Address = Address("0x00")
108+
val request: SendFundsRequest = SendFundsRequest(address)
109+
110+
faucetRpcServiceWithoutFaucetHandler.sendFunds(request).runSyncUnsafe(Duration.Inf) match {
111+
case Right(_) => fail()
112+
case Left(error) =>
113+
error shouldBe JsonRpcError.InternalError
114+
}
115+
}
116+
117+
it should "answer internal error when tried to get status but the Faucet Handler is disable" in new TestSetup {
118+
val address: Address = Address("0x00")
119+
val request: SendFundsRequest = SendFundsRequest(address)
120+
121+
faucetRpcServiceWithoutFaucetHandler.status(StatusRequest()).runSyncUnsafe(Duration.Inf) match {
122+
case Right(_) => fail()
123+
case Left(error) =>
124+
error shouldBe JsonRpcError.InternalError
125+
}
126+
}
127+
128+
class TestSetup(implicit system: ActorSystem) {
129+
130+
val config: FaucetConfig = FaucetConfig(
131+
walletAddress = Address("0x99"),
132+
walletPassword = "",
133+
txGasPrice = 10,
134+
txGasLimit = 20,
135+
txValue = 1,
136+
rpcAddress = "",
137+
keyStoreDir = "",
138+
minRequestInterval = 10.seconds,
139+
handlerTimeout = 10.seconds,
140+
responseTimeout = 10.seconds,
141+
supervisor = mock[SupervisorConfig],
142+
shutdownTimeout = 15.seconds
143+
)
144+
145+
val fHandler = TestProbe()
146+
147+
val faucetRpcService: FaucetRpcService = new FaucetRpcService(config) {
148+
149+
override def faucetHandler()(implicit system: ActorSystem): Task[ActorRef] = {
150+
Task(fHandler.ref)
151+
}
152+
}
153+
154+
val faucetRpcServiceWithoutFaucetHandler: FaucetRpcService = new FaucetRpcService(config) {
155+
override def faucetHandler()(implicit system: ActorSystem): Task[ActorRef] = {
156+
Task.raiseError(new RuntimeException("time out"))
157+
}
158+
}
159+
}
160+
161+
}

0 commit comments

Comments
 (0)