solution: use caching for WS head
This commit is contained in:
@@ -32,6 +32,8 @@ import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
class EthereumApiMock extends DirectEthereumApi {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(this)
|
||||
@@ -55,26 +57,30 @@ class EthereumApiMock extends DirectEthereumApi {
|
||||
|
||||
@Override
|
||||
Mono<byte[]> execute(int id, @NotNull String method, @NotNull List<?> params) {
|
||||
def predefined = predefined.find { it.isSame(id, method, params) }
|
||||
ResponseJson json = new ResponseJson<Object, Integer>(id: id)
|
||||
if (predefined != null) {
|
||||
if (predefined.exception != null) {
|
||||
Callable<byte[]> call = {
|
||||
def predefined = predefined.find { it.isSame(id, method, params) }
|
||||
ResponseJson json = new ResponseJson<Object, Integer>(id: id)
|
||||
if (predefined != null) {
|
||||
if (predefined.exception != null) {
|
||||
predefined.onCalled()
|
||||
predefined.print()
|
||||
throw predefined.exception
|
||||
}
|
||||
if (predefined.result instanceof RpcResponseError) {
|
||||
json.error = predefined.result
|
||||
} else {
|
||||
json.result = predefined.result
|
||||
}
|
||||
predefined.onCalled()
|
||||
predefined.print()
|
||||
return Mono.error(predefined.exception)
|
||||
}
|
||||
if (predefined.result instanceof RpcResponseError) {
|
||||
json.error = predefined.result
|
||||
} else {
|
||||
json.result = predefined.result
|
||||
log.error("Method ${method} with ${params} is not mocked")
|
||||
json.error = new RpcResponseError(-32601, "Method ${method} with ${params} is not mocked")
|
||||
}
|
||||
} else {
|
||||
log.error("Method ${method} with ${params} is not mocked")
|
||||
json.error = new RpcResponseError(-32601, "Method ${method} with ${params} is not mocked")
|
||||
}
|
||||
predefined.onCalled()
|
||||
predefined.print()
|
||||
return Mono.just(objectMapper.writeValueAsBytes(json))
|
||||
byte[] result = objectMapper.writeValueAsBytes(json)
|
||||
return result
|
||||
} as Callable<byte[]>
|
||||
return Mono.fromCallable(call)
|
||||
}
|
||||
|
||||
def nativeCall(BlockchainOuterClass.NativeCallRequest request, StreamObserver<BlockchainOuterClass.NativeCallReplyItem> responseObserver) {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package io.emeraldpay.dshackle.upstream.ethereum
|
||||
|
||||
import io.emeraldpay.dshackle.cache.BlocksMemCache
|
||||
import io.emeraldpay.dshackle.cache.Caches
|
||||
import io.emeraldpay.dshackle.cache.HeightCache
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.infinitape.etherjar.domain.BlockHash
|
||||
import io.infinitape.etherjar.rpc.ReactorRpcClient
|
||||
import io.infinitape.etherjar.rpc.json.BlockJson
|
||||
import io.infinitape.etherjar.rpc.json.TransactionRefJson
|
||||
import io.infinitape.etherjar.rpc.ws.WebsocketClient
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.test.StepVerifier
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
class EthereumWsSpec extends Specification {
|
||||
|
||||
def "Uses cache to fetch block"() {
|
||||
setup:
|
||||
ReactorRpcClient rpcClient = Stub(ReactorRpcClient)
|
||||
def apiMock = TestingCommons.api(rpcClient)
|
||||
def ws = new EthereumWs(new URI("http://localhost"), new URI("http://localhost"), apiMock)
|
||||
def blocksCache = Mock(BlocksMemCache)
|
||||
def caches = Caches.newBuilder().setBlockByHash(blocksCache).build()
|
||||
ws.setCaches(caches)
|
||||
|
||||
def block = new BlockJson<TransactionRefJson>()
|
||||
block.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200")
|
||||
block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
|
||||
|
||||
when:
|
||||
ws.onNewBlock(block)
|
||||
|
||||
then:
|
||||
1 * blocksCache.read(BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200")) >> Mono.just(block)
|
||||
StepVerifier.create(ws.flux.take(1))
|
||||
.expectNext(block)
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
}
|
||||
|
||||
def "Fetch block if cache is empty"() {
|
||||
setup:
|
||||
ReactorRpcClient rpcClient = Stub(ReactorRpcClient)
|
||||
def apiMock = TestingCommons.api(rpcClient)
|
||||
def ws = new EthereumWs(new URI("http://localhost"), new URI("http://localhost"), apiMock)
|
||||
def blocksCache = Mock(BlocksMemCache)
|
||||
def caches = Caches.newBuilder().setBlockByHash(blocksCache).build()
|
||||
ws.setCaches(caches)
|
||||
|
||||
def block = new BlockJson<TransactionRefJson>()
|
||||
block.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200")
|
||||
block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
|
||||
block.transactions = []
|
||||
block.uncles = []
|
||||
|
||||
apiMock.answerOnce("eth_getBlockByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200", false], block)
|
||||
|
||||
when:
|
||||
ws.onNewBlock(block)
|
||||
|
||||
then:
|
||||
1 * blocksCache.read(_) >> Mono.empty()
|
||||
StepVerifier.create(ws.flux.take(1))
|
||||
.expectNext(block)
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user