Stream responses from node (#361)
This commit is contained in:
@@ -62,7 +62,7 @@ class BaseHandlerSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||
call.items.add(request)
|
||||
call.ids[0] = 5
|
||||
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, null, null)
|
||||
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, null, null, null)
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM__MAINNET, call, requestHandler, false))
|
||||
.collectList()
|
||||
@@ -85,7 +85,7 @@ class BaseHandlerSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.BATCH)
|
||||
call.items.add(request)
|
||||
call.ids[0] = 5
|
||||
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, null, null)
|
||||
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, null, null, null)
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM__MAINNET, call, requestHandler, false))
|
||||
.collectList()
|
||||
@@ -116,8 +116,8 @@ class BaseHandlerSpec extends Specification {
|
||||
call.items.add(request2)
|
||||
call.ids[1] = 6
|
||||
def response = [
|
||||
new NativeCall.CallResult(1, null, '{"foo": 2}'.bytes, null, null, null, null),
|
||||
new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, null, null)
|
||||
new NativeCall.CallResult(1, null, '{"foo": 2}'.bytes, null, null, null, null, null),
|
||||
new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, null, null, null)
|
||||
]
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM__MAINNET, call, requestHandler, true))
|
||||
@@ -149,8 +149,8 @@ class BaseHandlerSpec extends Specification {
|
||||
call.items.add(request2)
|
||||
call.ids[1] = 6
|
||||
def response = [
|
||||
new NativeCall.CallResult(1, null, '{"foo": 2}'.bytes, null, null, null, null),
|
||||
new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, null, null)
|
||||
new NativeCall.CallResult(1, null, '{"foo": 2}'.bytes, null, null, null, null, null),
|
||||
new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, null, null, null)
|
||||
]
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM__MAINNET, call, requestHandler, true))
|
||||
@@ -189,8 +189,8 @@ class BaseHandlerSpec extends Specification {
|
||||
|
||||
// note there is only 2 responses
|
||||
def response = [
|
||||
new NativeCall.CallResult(1, null, '{"foo": 2}'.bytes, null, null, null, null),
|
||||
new NativeCall.CallResult(2, null, '{"foo": 3}'.bytes, null, null, null, null)
|
||||
new NativeCall.CallResult(1, null, '{"foo": 2}'.bytes, null, null, null, null, null),
|
||||
new NativeCall.CallResult(2, null, '{"foo": 3}'.bytes, null, null, null, null, null)
|
||||
]
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM__MAINNET, call, requestHandler, true))
|
||||
|
||||
@@ -42,7 +42,7 @@ class HttpHandlerSpec extends Specification {
|
||||
.setMethod("test_test")
|
||||
.setPayload(ByteString.copyFromUtf8("[]"))
|
||||
.build()
|
||||
def respItem = new NativeCall.CallResult(1, null, "100".bytes, null, null, null, null)
|
||||
def respItem = new NativeCall.CallResult(1, null, "100".bytes, null, null, null, null, null)
|
||||
def req = BlockchainOuterClass.NativeCallRequest.newBuilder()
|
||||
.setChain(Common.ChainRef.CHAIN_ETHEREUM__MAINNET)
|
||||
.addItems(reqItem)
|
||||
@@ -128,7 +128,7 @@ class HttpHandlerSpec extends Specification {
|
||||
def act = handler.execute(Chain.ETHEREUM__MAINNET, call, new AccessHandlerHttp.NoOpHandler(), false)
|
||||
|
||||
then:
|
||||
1 * nativeCall.nativeCallResult(_) >> Flux.just(new NativeCall.CallResult(1, null, "".bytes, null, null, null, null))
|
||||
1 * nativeCall.nativeCallResult(_) >> Flux.just(new NativeCall.CallResult(1, null, "".bytes, null, null, null, null, null))
|
||||
StepVerifier.create(act)
|
||||
.expectNext("hello")
|
||||
.expectComplete()
|
||||
|
||||
@@ -85,7 +85,7 @@ class WebsocketHandlerSpec extends Specification {
|
||||
|
||||
def "Respond to a single call"() {
|
||||
setup:
|
||||
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, "test", null)
|
||||
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, "test", null, null)
|
||||
|
||||
def nativeCall = Mock(NativeCall) {
|
||||
1 * it.nativeCallResult(_) >> Flux.fromIterable([response])
|
||||
|
||||
@@ -85,7 +85,7 @@ class WriteRpcJsonSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||
call.ids[1] = 105
|
||||
def data = [
|
||||
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null, null, null)
|
||||
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null, null, null, null)
|
||||
]
|
||||
when:
|
||||
def act = writer.toJson(call, data[0])
|
||||
@@ -98,7 +98,7 @@ class WriteRpcJsonSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||
call.ids[1] = 1
|
||||
def data = [
|
||||
new NativeCall.CallResult(1, null, null, new NativeCall.CallError(1, "Internal Error", null, null, null), null, null, null)
|
||||
new NativeCall.CallResult(1, null, null, new NativeCall.CallError(1, "Internal Error", null, null, null), null, null, null, null)
|
||||
]
|
||||
when:
|
||||
def act = writer.toJson(call, data[0])
|
||||
@@ -111,7 +111,7 @@ class WriteRpcJsonSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||
call.ids[1] = "aaa"
|
||||
def data = [
|
||||
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null, null, null)
|
||||
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null, null, null, null)
|
||||
]
|
||||
when:
|
||||
def act = writer.toJson(call, data[0])
|
||||
@@ -126,9 +126,9 @@ class WriteRpcJsonSpec extends Specification {
|
||||
call.ids[2] = 11
|
||||
call.ids[3] = 15
|
||||
def data = [
|
||||
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null, null, null),
|
||||
new NativeCall.CallResult(2, null, null, new NativeCall.CallError(2, "oops", null, null, null), null, null, null),
|
||||
new NativeCall.CallResult(3, null, '{"hash": "0x2484f459dc"}'.bytes, null, null, null, null),
|
||||
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null, null, null, null),
|
||||
new NativeCall.CallResult(2, null, null, new NativeCall.CallError(2, "oops", null, null, null), null, null, null, null),
|
||||
new NativeCall.CallResult(3, null, '{"hash": "0x2484f459dc"}'.bytes, null, null, null, null, null),
|
||||
]
|
||||
when:
|
||||
def act = Flux.fromIterable(data)
|
||||
@@ -154,7 +154,7 @@ class WriteRpcJsonSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||
call.ids[1] = 10
|
||||
def data = [
|
||||
new NativeCall.CallResult(1, null, '"0x1"'.bytes, null, null, null, null),
|
||||
new NativeCall.CallResult(1, null, '"0x1"'.bytes, null, null, null, null, null),
|
||||
]
|
||||
when:
|
||||
def act = Flux.fromIterable(data)
|
||||
|
||||
@@ -17,6 +17,7 @@ package io.emeraldpay.dshackle.quorum
|
||||
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||
import spock.lang.Specification
|
||||
|
||||
@@ -42,10 +43,10 @@ class AlwaysQuorumSpec extends Specification {
|
||||
def quorum = new AlwaysQuorum()
|
||||
def up = Stub(Upstream)
|
||||
when:
|
||||
quorum.record("123".bytes, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
quorum.record(new JsonRpcResponse("123".bytes, null), new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
then:
|
||||
quorum.isResolved()
|
||||
quorum.getResult() == "123".bytes
|
||||
quorum.getResponse().getResult() == "123".bytes
|
||||
quorum.signature == new ResponseSigner.Signature("sig1".bytes, "test", 100)
|
||||
!quorum.isFailed()
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ package io.emeraldpay.dshackle.quorum
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import io.emeraldpay.dshackle.Global
|
||||
import io.emeraldpay.dshackle.upstream.Head
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import spock.lang.Specification
|
||||
|
||||
class BroadcastQuorumSpec extends Specification {
|
||||
@@ -35,20 +35,20 @@ class BroadcastQuorumSpec extends Specification {
|
||||
def upstream3 = Stub(Upstream)
|
||||
|
||||
when:
|
||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream1)
|
||||
q.record(new JsonRpcResponse('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null), null, upstream1)
|
||||
then:
|
||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _)
|
||||
|
||||
when:
|
||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream2)
|
||||
q.record(new JsonRpcResponse('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null), null, upstream2)
|
||||
then:
|
||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _)
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Nonce too low"), null, upstream3)
|
||||
then:
|
||||
1 * q.recordError(_, _, _, _)
|
||||
objectMapper.readValue(q.result, Object) == "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"
|
||||
1 * q.recordError(_, _, _)
|
||||
objectMapper.readValue(q.response.getResult(), Object) == "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"
|
||||
}
|
||||
|
||||
def "Remembers first response"() {
|
||||
@@ -61,19 +61,19 @@ class BroadcastQuorumSpec extends Specification {
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Internal error"), null, upstream1)
|
||||
then:
|
||||
1 * q.recordError(_, _, _, _)
|
||||
1 * q.recordError(_, _, _)
|
||||
|
||||
when:
|
||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream2)
|
||||
q.record(new JsonRpcResponse('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null), null, upstream2)
|
||||
then:
|
||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _)
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Nonce too low"), null, upstream3)
|
||||
then:
|
||||
1 * q.recordError(_, _, _, _)
|
||||
1 * q.recordError(_, _, _)
|
||||
q.isResolved()
|
||||
objectMapper.readValue(q.result, Object) == "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"
|
||||
objectMapper.readValue(q.response.result, Object) == "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"
|
||||
}
|
||||
|
||||
def "Failed if error received 3+ times"() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.emeraldpay.dshackle.quorum
|
||||
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import spock.lang.Specification
|
||||
|
||||
class MaximumValueQuorumSpec extends Specification {
|
||||
@@ -18,11 +19,11 @@ class MaximumValueQuorumSpec extends Specification {
|
||||
}
|
||||
when:
|
||||
def quorum = new MaximumValueQuorum()
|
||||
quorum.record('"0x137"'.bytes, null, up)
|
||||
quorum.record('"0x138"'.bytes, null, up1)
|
||||
quorum.record('"0x139"'.bytes, null, up2)
|
||||
quorum.record(new JsonRpcResponse('"0x137"'.bytes, null), null, up)
|
||||
quorum.record(new JsonRpcResponse('"0x138"'.bytes, null), null, up1)
|
||||
quorum.record(new JsonRpcResponse('"0x139"'.bytes, null), null, up2)
|
||||
then:
|
||||
quorum.result == '"0x139"'.bytes
|
||||
quorum.response.result == '"0x139"'.bytes
|
||||
quorum.resolvedBy.size() == 1
|
||||
quorum.isResolved()
|
||||
quorum.resolvedBy.contains(up2)
|
||||
@@ -41,11 +42,11 @@ class MaximumValueQuorumSpec extends Specification {
|
||||
}
|
||||
when:
|
||||
def quorum = new MaximumValueQuorum()
|
||||
quorum.record('"0x137"'.bytes, null, up)
|
||||
quorum.record('"0x138"'.bytes, null, up1)
|
||||
quorum.record(new JsonRpcResponse('"0x137"'.bytes, null), null, up)
|
||||
quorum.record(new JsonRpcResponse('"0x138"'.bytes, null), null, up1)
|
||||
quorum.record(new JsonRpcException(10, "error"), null, up2)
|
||||
then:
|
||||
quorum.result == '"0x138"'.bytes
|
||||
quorum.response.result == '"0x138"'.bytes
|
||||
quorum.isResolved()
|
||||
quorum.resolvedBy.size() == 1
|
||||
quorum.resolvedBy.contains(up1)
|
||||
|
||||
@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.quorum
|
||||
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||
import spock.lang.Specification
|
||||
|
||||
@@ -30,12 +31,12 @@ class NotLaggingQuorumSpec extends Specification {
|
||||
def quorum = new NotLaggingQuorum(1)
|
||||
|
||||
when:
|
||||
quorum.record(value, null, up)
|
||||
quorum.record(new JsonRpcResponse(value, null), null, up)
|
||||
then:
|
||||
1 * up.getLag() >> 0
|
||||
quorum.isResolved()
|
||||
!quorum.isFailed()
|
||||
quorum.result == value
|
||||
quorum.response.result == value
|
||||
}
|
||||
|
||||
def "Keeps signature and upstream"() {
|
||||
@@ -45,12 +46,12 @@ class NotLaggingQuorumSpec extends Specification {
|
||||
def quorum = new NotLaggingQuorum(1)
|
||||
|
||||
when:
|
||||
quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
quorum.record(new JsonRpcResponse(value, null), new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
then:
|
||||
1 * up.getLag() >> 0
|
||||
quorum.isResolved()
|
||||
!quorum.isFailed()
|
||||
quorum.result == value
|
||||
quorum.response.result == value
|
||||
quorum.signature == new ResponseSigner.Signature("sig1".bytes, "test", 100)
|
||||
}
|
||||
|
||||
@@ -61,12 +62,12 @@ class NotLaggingQuorumSpec extends Specification {
|
||||
def quorum = new NotLaggingQuorum(1)
|
||||
|
||||
when:
|
||||
quorum.record(value, null, up)
|
||||
quorum.record(new JsonRpcResponse(value, null), null, up)
|
||||
then:
|
||||
1 * up.getLag() >> 1
|
||||
quorum.isResolved()
|
||||
!quorum.isFailed()
|
||||
quorum.result == value
|
||||
quorum.response.result == value
|
||||
}
|
||||
|
||||
def "Ignores if lags"() {
|
||||
@@ -76,7 +77,7 @@ class NotLaggingQuorumSpec extends Specification {
|
||||
def quorum = new NotLaggingQuorum(1)
|
||||
|
||||
when:
|
||||
quorum.record(value, null, up)
|
||||
quorum.record(new JsonRpcResponse(value, null), null, up)
|
||||
then:
|
||||
1 * up.getLag() >> 2
|
||||
!quorum.isResolved()
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.emeraldpay.dshackle.quorum
|
||||
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||
import spock.lang.Specification
|
||||
|
||||
@@ -19,19 +20,20 @@ class NotNullQuorumSpec extends Specification {
|
||||
1 * getId() >> "id2"
|
||||
}
|
||||
def value = "null".getBytes()
|
||||
def response = new JsonRpcResponse(value, null)
|
||||
def quorum = new NotNullQuorum()
|
||||
|
||||
when:
|
||||
def res = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
def res1 = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up1)
|
||||
def res2 = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up2)
|
||||
def res3 = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
def res = quorum.record(response, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
def res1 = quorum.record(response, new ResponseSigner.Signature("sig1".bytes, "test", 100), up1)
|
||||
def res2 = quorum.record(response, new ResponseSigner.Signature("sig1".bytes, "test", 100), up2)
|
||||
def res3 = quorum.record(response, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
then:
|
||||
!res
|
||||
!res1
|
||||
!res2
|
||||
res3
|
||||
quorum.result == value
|
||||
quorum.response.result == value
|
||||
!quorum.isFailed()
|
||||
quorum.isResolved()
|
||||
quorum.signature == new ResponseSigner.Signature("sig1".bytes, "test", 100)
|
||||
@@ -77,7 +79,7 @@ class NotNullQuorumSpec extends Specification {
|
||||
def quorum = new NotNullQuorum()
|
||||
|
||||
when:
|
||||
def res = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
def res = quorum.record(new JsonRpcResponse(value, null), new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
quorum.record(new JsonRpcException(10, "error"), new ResponseSigner.Signature("sig1".bytes, "test", 100), up1)
|
||||
quorum.record(new JsonRpcException(10, "error"), new ResponseSigner.Signature("sig1".bytes, "test", 100), up2)
|
||||
quorum.record(new JsonRpcException(10, "error"), new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
@@ -86,7 +88,7 @@ class NotNullQuorumSpec extends Specification {
|
||||
!res
|
||||
quorum.isResolved()
|
||||
!quorum.isFailed()
|
||||
quorum.result == value
|
||||
quorum.response.result == value
|
||||
quorum.signature == new ResponseSigner.Signature("sig1".bytes, "test", 100)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
package io.emeraldpay.dshackle.quorum
|
||||
|
||||
import io.emeraldpay.dshackle.upstream.Head
|
||||
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.jetbrains.annotations.Nullable
|
||||
@@ -66,14 +67,14 @@ class ValueAwareQuorumSpec extends Specification {
|
||||
}
|
||||
|
||||
@Override
|
||||
void recordValue(@NotNull byte[] response, @Nullable Object responseValue, @Nullable ResponseSigner.Signature signature, @NotNull Upstream upstream) {
|
||||
void recordValue(@NotNull JsonRpcResponse response, @Nullable Object responseValue, @Nullable ResponseSigner.Signature signature, @NotNull Upstream upstream) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
void recordError(@Nullable byte[] response, @Nullable String errorMessage, @Nullable ResponseSigner.Signature signature, @NotNull Upstream upstream) {
|
||||
void recordError(@Nullable String errorMessage, @Nullable ResponseSigner.Signature signature, @NotNull Upstream upstream) {
|
||||
|
||||
}
|
||||
|
||||
@@ -88,8 +89,8 @@ class ValueAwareQuorumSpec extends Specification {
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] getResult() {
|
||||
return new byte[0]
|
||||
JsonRpcResponse getResponse() {
|
||||
return new JsonRpcResponse(null, null)
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -181,7 +181,7 @@ class BroadcastReaderSpec extends Specification {
|
||||
when:
|
||||
def act = reader
|
||||
.read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"]))
|
||||
.switchIfEmpty(Mono.just(new RpcReader.Result(new byte[0], null, 0, null)))
|
||||
.switchIfEmpty(Mono.just(new RpcReader.Result(new byte[0], null, 0, null, null)))
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
.expectErrorMessage("Unhandled Upstream error")
|
||||
|
||||
@@ -130,7 +130,7 @@ class NativeCallSpec extends Specification {
|
||||
def nativeCall = nativeCall()
|
||||
nativeCall.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(_) >> Mono.just(new RpcReader.Result("\"foo\"".bytes, null, 1, ups))
|
||||
1 * read(_) >> Mono.just(new RpcReader.Result("\"foo\"".bytes, null, 1, ups, null))
|
||||
}
|
||||
}
|
||||
def call = new NativeCall.ValidCallContext(1, 10, TestingCommons.multistream(TestingCommons.api()), Selector.empty, quorum,
|
||||
@@ -238,7 +238,7 @@ class NativeCallSpec extends Specification {
|
||||
|
||||
when:
|
||||
def resp = nativeCall.buildResponse(
|
||||
new NativeCall.CallResult(1561, 10, objectMapper.writeValueAsBytes(json), null, null, null, null)
|
||||
new NativeCall.CallResult(1561, 10, objectMapper.writeValueAsBytes(json), null, null, null, null, null)
|
||||
)
|
||||
then:
|
||||
resp.id == 1561
|
||||
@@ -253,7 +253,7 @@ class NativeCallSpec extends Specification {
|
||||
|
||||
when:
|
||||
def resp = nativeCall.buildResponse(
|
||||
new NativeCall.CallResult(1561, 10, objectMapper.writeValueAsBytes(json), null, new ResponseSigner.Signature("sig1".bytes, "test", 100), "test", null)
|
||||
new NativeCall.CallResult(1561, 10, objectMapper.writeValueAsBytes(json), null, new ResponseSigner.Signature("sig1".bytes, "test", 100), "test", null, null)
|
||||
)
|
||||
then:
|
||||
resp.id == 1561
|
||||
@@ -584,7 +584,7 @@ class NativeCallSpec extends Specification {
|
||||
def nativeCall = nativeCall()
|
||||
def ctx = new NativeCall.ValidCallContext(1, null, Stub(Multistream), Selector.empty, new AlwaysQuorum(),
|
||||
new NativeCall.RawCallDetails("eth_getFilterUpdates", '["0xabcd"]'),
|
||||
new NativeCall.WithFilterIdDecorator(), new NativeCall.NoneResultDecorator(), null, "reqId", 1)
|
||||
new NativeCall.WithFilterIdDecorator(), new NativeCall.NoneResultDecorator(), null, false, "reqId", 1)
|
||||
when:
|
||||
def act = nativeCall.parseParams(ctx)
|
||||
then:
|
||||
@@ -614,12 +614,12 @@ class NativeCallSpec extends Specification {
|
||||
def nativeCall = nativeCall(multistreamHolder)
|
||||
nativeCall.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(_) >> Mono.just(new RpcReader.Result("\"0xab\"".bytes, null, 1, ups))
|
||||
1 * read(_) >> Mono.just(new RpcReader.Result("\"0xab\"".bytes, null, 1, ups, null))
|
||||
}
|
||||
}
|
||||
def call = new NativeCall.ValidCallContext(1, 10, multistream, Selector.empty, quorum,
|
||||
new NativeCall.ParsedCallDetails("eth_getFilterChanges", []),
|
||||
new NativeCall.WithFilterIdDecorator(), new NativeCall.CreateFilterDecorator(), null, "reqId", 1)
|
||||
new NativeCall.WithFilterIdDecorator(), new NativeCall.CreateFilterDecorator(), null, false, "reqId", 1)
|
||||
|
||||
when:
|
||||
def resp = nativeCall.executeOnRemote(call).block(Duration.ofSeconds(1))
|
||||
@@ -650,12 +650,12 @@ class NativeCallSpec extends Specification {
|
||||
def nativeCall = nativeCall(multistreamHolder)
|
||||
nativeCall.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(_) >> Mono.just(new RpcReader.Result("\"0xab\"".bytes, null, 1, ups))
|
||||
1 * read(_) >> Mono.just(new RpcReader.Result("\"0xab\"".bytes, null, 1, ups, null))
|
||||
}
|
||||
}
|
||||
def call = new NativeCall.ValidCallContext(1, 10, multistream, Selector.empty, quorum,
|
||||
new NativeCall.ParsedCallDetails("eth_getFilterChanges", []),
|
||||
new NativeCall.WithFilterIdDecorator(), new NativeCall.CreateFilterDecorator(), null, "reqId", 1)
|
||||
new NativeCall.WithFilterIdDecorator(), new NativeCall.CreateFilterDecorator(), null, false, "reqId", 1)
|
||||
|
||||
when:
|
||||
def resp = nativeCall.executeOnRemote(call).block(Duration.ofSeconds(1))
|
||||
|
||||
@@ -108,7 +108,7 @@ class ApiReaderMock implements Reader<JsonRpcRequest, JsonRpcResponse> {
|
||||
}
|
||||
error = new JsonRpcError(-32601, "Method ${request.method} with ${request.params} is not mocked")
|
||||
}
|
||||
return new JsonRpcResponse(result, error, JsonRpcResponse.Id.from(request.id), null, null)
|
||||
return new JsonRpcResponse(result, error, JsonRpcResponse.Id.from(request.id), null, null, null)
|
||||
} as Callable<JsonRpcResponse>
|
||||
return Mono.fromCallable(call)
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver
|
||||
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver, null
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -114,7 +114,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >> Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -146,7 +146,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(new JsonRpcRequest("eth_getLogs", [Map.of("blockHash", hash1)])) >> Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes([json]), null, 1, resolver
|
||||
Global.objectMapper.writeValueAsBytes([json]), null, 1, resolver, null
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -179,7 +179,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -212,7 +212,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(new JsonRpcRequest("eth_getTransactionReceipt", [hash1])) >> Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -246,7 +246,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(new JsonRpcRequest("eth_getTransactionReceipt", [hash1])) >> Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -271,7 +271,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver
|
||||
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver, null
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -301,7 +301,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "latest"])) >> Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver
|
||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver, null
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -332,7 +332,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
1 * create(_) >> Mock(RpcReader) {
|
||||
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "0xa8c9bb"])) >> Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver
|
||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver, null
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -361,7 +361,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
}
|
||||
def result = Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null)
|
||||
)
|
||||
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
||||
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||
@@ -401,7 +401,7 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
}
|
||||
def result = Mono.just(
|
||||
new RpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null)
|
||||
)
|
||||
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
||||
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||
|
||||
@@ -335,7 +335,7 @@ class GenericWsHeadSpec extends Specification {
|
||||
1 * it.subscribe(_) >> new WsSubscriptions.SubscribeData(
|
||||
Flux.error(new RuntimeException()), "id", new AtomicReference<String>(subId)
|
||||
)
|
||||
1 * it.unsubscribe(new JsonRpcRequest("eth_unsubscribe", List.of(subId), 2, null, null)) >>
|
||||
1 * it.unsubscribe(new JsonRpcRequest("eth_unsubscribe", List.of(subId), 2, null, null, false)) >>
|
||||
Mono.just(new JsonRpcResponse("".bytes, null))
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class WsConnectionImplSpec extends Specification {
|
||||
|
||||
when:
|
||||
Flux.from(ws.handle(wsApiMock.inbound, wsApiMock.outbound)).subscribe()
|
||||
def act = ws.callRpc(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15, null, null))
|
||||
def act = ws.callRpc(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15, null, null, false))
|
||||
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
@@ -90,7 +90,7 @@ class WsConnectionImplSpec extends Specification {
|
||||
|
||||
when:
|
||||
Flux.from(ws.handle(wsApiMock.inbound, wsApiMock.outbound)).subscribe()
|
||||
def act = ws.callRpc(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15, null, null))
|
||||
def act = ws.callRpc(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15, null, null, false))
|
||||
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
@@ -124,7 +124,7 @@ class WsConnectionImplSpec extends Specification {
|
||||
|
||||
when:
|
||||
Flux.from(ws.handle(wsApiMock.inbound, wsApiMock.outbound)).subscribe()
|
||||
def act = ws.callRpc(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15, null, null))
|
||||
def act = ws.callRpc(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15, null, null, false))
|
||||
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package io.emeraldpay.dshackle.upstream.rpcclient
|
||||
|
||||
import io.emeraldpay.dshackle.config.AuthConfig
|
||||
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.etherjar.rpc.RpcResponseError
|
||||
import io.micrometer.core.instrument.Counter
|
||||
@@ -23,9 +23,7 @@ import io.micrometer.core.instrument.Timer
|
||||
import org.mockserver.integration.ClientAndServer
|
||||
import org.mockserver.model.HttpRequest
|
||||
import org.mockserver.model.HttpResponse
|
||||
import org.mockserver.model.MediaType
|
||||
import org.springframework.util.SocketUtils
|
||||
import reactor.test.StepVerifier
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.time.Duration
|
||||
@@ -69,35 +67,6 @@ class JsonRpcHttpClientSpec extends Specification {
|
||||
new String(act.result) == '"0x98de45"'
|
||||
}
|
||||
|
||||
def "Make request with basic auth"() {
|
||||
setup:
|
||||
def auth = new AuthConfig.ClientBasicAuth("user", "passwd")
|
||||
def client = new JsonRpcHttpClient("localhost:${port}", metrics, auth, null)
|
||||
|
||||
mockServer.when(
|
||||
HttpRequest.request()
|
||||
.withMethod("POST")
|
||||
.withBody("ping")
|
||||
).respond(
|
||||
HttpResponse.response()
|
||||
.withBody("pong")
|
||||
)
|
||||
when:
|
||||
def act = client.execute("ping".bytes).map { new String(it.t2) }
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
.expectNext("pong")
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
mockServer.verify(
|
||||
HttpRequest.request()
|
||||
.withMethod("POST")
|
||||
.withBody("ping")
|
||||
.withContentType(MediaType.APPLICATION_JSON)
|
||||
.withHeader("authorization", "Basic dXNlcjpwYXNzd2Q=")
|
||||
)
|
||||
}
|
||||
|
||||
def "Produces RPC Exception on error status code"() {
|
||||
setup:
|
||||
def client = new JsonRpcHttpClient("localhost:${port}", metrics, null, null)
|
||||
|
||||
@@ -63,7 +63,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize int id and null result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.NumberId(1), null, null)
|
||||
def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.NumberId(1), null, null, null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -72,7 +72,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize int id and string result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse('"Hello World"'.bytes, null, new JsonRpcResponse.NumberId(10), null, null)
|
||||
def json = new JsonRpcResponse('"Hello World"'.bytes, null, new JsonRpcResponse.NumberId(10), null, null, null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -81,7 +81,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize int id and object result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse('{"foo": "Hello World", "bar": 1}'.bytes, null, new JsonRpcResponse.NumberId(101), null, null)
|
||||
def json = new JsonRpcResponse('{"foo": "Hello World", "bar": 1}'.bytes, null, new JsonRpcResponse.NumberId(101), null, null, null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -90,7 +90,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize int id and error"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse(null, new JsonRpcError(-32041, "Oooops"), new JsonRpcResponse.NumberId(101), null, null)
|
||||
def json = new JsonRpcResponse(null, new JsonRpcError(-32041, "Oooops"), new JsonRpcResponse.NumberId(101), null, null, null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -99,7 +99,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize string id and null result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.StringId("asf01t1gg"), null, null)
|
||||
def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.StringId("asf01t1gg"), null, null, null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -108,7 +108,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize string id and string result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse('"Hello World"'.bytes, null, new JsonRpcResponse.StringId("10"), null, null)
|
||||
def json = new JsonRpcResponse('"Hello World"'.bytes, null, new JsonRpcResponse.StringId("10"), null, null, null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -117,7 +117,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize string id and object result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse('{"foo": "Hello World", "bar": 1}'.bytes, null, new JsonRpcResponse.StringId("g8gk19g"), null, null)
|
||||
def json = new JsonRpcResponse('{"foo": "Hello World", "bar": 1}'.bytes, null, new JsonRpcResponse.StringId("g8gk19g"), null, null, null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -128,7 +128,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
setup:
|
||||
def json = new JsonRpcResponse(null,
|
||||
new JsonRpcError(-32041, "Oooops"),
|
||||
new JsonRpcResponse.StringId("9kbo29gkaasf"), null, null)
|
||||
new JsonRpcResponse.StringId("9kbo29gkaasf"), null, null, null )
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
package io.emeraldpay.dshackle.rpc
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.google.protobuf.ByteString
|
||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||
import io.emeraldpay.api.proto.BlockchainOuterClass.NativeCallRequest
|
||||
import io.emeraldpay.dshackle.Global
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.springframework.util.ResourceUtils
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.test.StepVerifier
|
||||
import java.time.Duration
|
||||
|
||||
class NativeCallStreamTest {
|
||||
private val upstreamId = "upstreamId"
|
||||
private val mapper = Global.objectMapper
|
||||
|
||||
@Test
|
||||
fun `streaming response is equal to the original response`() {
|
||||
val responseFile = ResourceUtils.getFile("classpath:responses/get-by-number-response.json")
|
||||
val response = mapper.writeValueAsBytes(mapper.readValue(responseFile, JsonNode::class.java))
|
||||
val nativeCallResponse = BlockchainOuterClass.NativeCallReplyItem.newBuilder()
|
||||
.setId(1)
|
||||
.setSucceed(true)
|
||||
.setUpstreamId(upstreamId)
|
||||
.setPayload(ByteString.copyFrom(response))
|
||||
.build()
|
||||
val nativeCallMock = mock<NativeCall> {
|
||||
on { nativeCall(any()) } doReturn Flux.just(nativeCallResponse)
|
||||
}
|
||||
val nativeCallStream = NativeCallStream(nativeCallMock)
|
||||
val req = Mono.just(
|
||||
NativeCallRequest.newBuilder()
|
||||
.setChunkSize(1000)
|
||||
.build(),
|
||||
)
|
||||
|
||||
val result = nativeCallStream.nativeCall(req)
|
||||
.collectList()
|
||||
.block()!!
|
||||
.map { it.payload.toByteArray() }
|
||||
.reduce { acc, bytes -> acc.plus(bytes) }
|
||||
|
||||
assertTrue(response.contentEquals(result))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `streaming responses is correct`() {
|
||||
val response = "\"0x1126938\"".toByteArray()
|
||||
val nativeCallResponse = BlockchainOuterClass.NativeCallReplyItem.newBuilder()
|
||||
.setId(15)
|
||||
.setSucceed(true)
|
||||
.setUpstreamId(upstreamId)
|
||||
.setPayload(ByteString.copyFrom(response))
|
||||
.build()
|
||||
val nativeCallMock = mock<NativeCall> {
|
||||
on { nativeCall(any()) } doReturn Flux.just(nativeCallResponse)
|
||||
}
|
||||
val nativeCallStream = NativeCallStream(nativeCallMock)
|
||||
val req = Mono.just(
|
||||
NativeCallRequest.newBuilder()
|
||||
.setChunkSize(5)
|
||||
.build(),
|
||||
)
|
||||
|
||||
val chunkResponse: (Int) -> BlockchainOuterClass.NativeCallReplyItem.Builder = { id ->
|
||||
BlockchainOuterClass.NativeCallReplyItem.newBuilder()
|
||||
.setId(id)
|
||||
.setChunked(true)
|
||||
.setSucceed(true)
|
||||
.setUpstreamId(upstreamId)
|
||||
}
|
||||
|
||||
val result = nativeCallStream.nativeCall(req)
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext(
|
||||
chunkResponse(15)
|
||||
.setPayload(ByteString.copyFrom("\"0x11".toByteArray()))
|
||||
.build(),
|
||||
)
|
||||
.expectNext(
|
||||
chunkResponse(15)
|
||||
.setPayload(ByteString.copyFrom("26938".toByteArray()))
|
||||
.build(),
|
||||
)
|
||||
.expectNext(
|
||||
chunkResponse(15)
|
||||
.setFinalChunk(true)
|
||||
.setPayload(ByteString.copyFrom("\"".toByteArray()))
|
||||
.build(),
|
||||
)
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(3))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no streaming if response is too small`() {
|
||||
val response = "\"0x1\"".toByteArray()
|
||||
val nativeCallResponse = BlockchainOuterClass.NativeCallReplyItem.newBuilder()
|
||||
.setId(15)
|
||||
.setSucceed(true)
|
||||
.setUpstreamId(upstreamId)
|
||||
.setPayload(ByteString.copyFrom(response))
|
||||
.build()
|
||||
val nativeCallMock = mock<NativeCall> {
|
||||
on { nativeCall(any()) } doReturn Flux.just(nativeCallResponse)
|
||||
}
|
||||
val nativeCallStream = NativeCallStream(nativeCallMock)
|
||||
val req = Mono.just(
|
||||
NativeCallRequest.newBuilder()
|
||||
.setChunkSize(1000)
|
||||
.build(),
|
||||
)
|
||||
|
||||
val result = nativeCallStream.nativeCall(req)
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext(
|
||||
nativeCallResponse,
|
||||
)
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(3))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sort responses by request id is correct`() {
|
||||
val response = "\"0x1\"".toByteArray()
|
||||
val response2 = "\"0x2\"".toByteArray()
|
||||
val response3 = "\"0x3\"".toByteArray()
|
||||
|
||||
val nativeCallResponse: (Int, ByteArray) -> BlockchainOuterClass.NativeCallReplyItem = { id, resp ->
|
||||
BlockchainOuterClass.NativeCallReplyItem.newBuilder()
|
||||
.setId(id)
|
||||
.setChunked(true)
|
||||
.setSucceed(true)
|
||||
.setUpstreamId(upstreamId)
|
||||
.setPayload(ByteString.copyFrom(resp))
|
||||
.build()
|
||||
}
|
||||
val nativeCallMock = mock<NativeCall> {
|
||||
on { nativeCall(any()) } doReturn Flux.just(
|
||||
nativeCallResponse(1, response),
|
||||
nativeCallResponse(2, response2),
|
||||
nativeCallResponse(3, response3),
|
||||
).flatMap {
|
||||
when (it.id) {
|
||||
1 -> Mono.just(it).delayElement(Duration.ofMillis(200))
|
||||
2 -> Mono.just(it).delayElement(Duration.ofMillis(100))
|
||||
else -> Mono.just(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
val nativeCallStream = NativeCallStream(nativeCallMock)
|
||||
val req = Mono.just(
|
||||
NativeCallRequest.newBuilder()
|
||||
.setSorted(true)
|
||||
.build(),
|
||||
)
|
||||
|
||||
val result = nativeCallStream.nativeCall(req)
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextMatches { it.payload.toByteArray().contentEquals(response) }
|
||||
.expectNextMatches { it.payload.toByteArray().contentEquals(response2) }
|
||||
.expectNextMatches { it.payload.toByteArray().contentEquals(response3) }
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(3))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package io.emeraldpay.dshackle.upstream.rpcclient.stream
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.Arguments
|
||||
import org.junit.jupiter.params.provider.MethodSource
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.test.StepVerifier
|
||||
import java.time.Duration
|
||||
|
||||
class JsonRpcStreamParserTest {
|
||||
private val streamParser = JsonRpcStreamParser()
|
||||
|
||||
@Test
|
||||
fun `if first part couldn't be parsed then aggregate response`() {
|
||||
val statusCode = 200
|
||||
val bytes = "{\"strangeResponse\": 2}".toByteArray()
|
||||
val stream: Flux<ByteArray> = Flux.just(bytes)
|
||||
|
||||
StepVerifier.create(streamParser.streamParse(statusCode, stream))
|
||||
.expectNext(AggregateResponse(bytes, statusCode))
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
fun `if first part has result field then single response`(
|
||||
response: ByteArray,
|
||||
result: ByteArray,
|
||||
) {
|
||||
val statusCode = 200
|
||||
val stream: Flux<ByteArray> = Flux.just(response)
|
||||
|
||||
StepVerifier.create(streamParser.streamParse(statusCode, stream))
|
||||
.expectNext(SingleResponse(result, null))
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("dataStream")
|
||||
fun `if big result then stream response`(
|
||||
response: List<ByteArray>,
|
||||
chunks: List<Chunk>,
|
||||
) {
|
||||
val statusCode = 200
|
||||
val stream: Flux<ByteArray> = Flux.fromIterable(response)
|
||||
|
||||
val result = streamParser.streamParse(statusCode, stream).block()
|
||||
assertTrue(result is StreamResponse)
|
||||
assertNotNull(result)
|
||||
|
||||
StepVerifier.create((result as StreamResponse).stream)
|
||||
.expectNextSequence(chunks)
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(5))
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun data(): List<Arguments> = listOf(
|
||||
Arguments.of("{\"id\": 2,\"result\": \"0x12\"}".toByteArray(), "\"0x12\"".toByteArray()),
|
||||
Arguments.of("{\"id\": 2,\"result\": 11}".toByteArray(), "11".toByteArray()),
|
||||
Arguments.of("{\"id\": 2,\"result\": false}".toByteArray(), "false".toByteArray()),
|
||||
Arguments.of("{\"id\": 2,\"result\": null}".toByteArray(), "null".toByteArray()),
|
||||
Arguments.of("{\"id\": 2,\"result\": {\"name\": \"value\"}".toByteArray(), "{\"name\": \"value\"}".toByteArray()),
|
||||
Arguments.of("{\"id\": 2,\"result\": [{\"name\": \"value\"}]".toByteArray(), "[{\"name\": \"value\"}]".toByteArray()),
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun dataStream(): List<Arguments> = listOf(
|
||||
Arguments.of(
|
||||
listOf("{\"id\": 2,\"result\": \"0x12".toByteArray(), "222\"}".toByteArray()),
|
||||
listOf(
|
||||
Chunk("\"0x12".toByteArray(), false),
|
||||
Chunk("222\"".toByteArray(), true),
|
||||
),
|
||||
),
|
||||
Arguments.of(
|
||||
listOf(
|
||||
"{\"id\": 2,\"result\": \"0x12".toByteArray(),
|
||||
"123\\\"".toByteArray(),
|
||||
"222\"}".toByteArray(),
|
||||
),
|
||||
listOf(
|
||||
Chunk("\"0x12".toByteArray(), false),
|
||||
Chunk("123\\\"".toByteArray(), false),
|
||||
Chunk("222\"".toByteArray(), true),
|
||||
),
|
||||
),
|
||||
Arguments.of(
|
||||
listOf(
|
||||
"{\"id\": 2,\"result\": \"0x12".toByteArray(),
|
||||
"1\\n23\\\"".toByteArray(),
|
||||
"456\\".toByteArray(),
|
||||
"\\222\\\\\\\\\"}".toByteArray(),
|
||||
),
|
||||
listOf(
|
||||
Chunk("\"0x12".toByteArray(), false),
|
||||
Chunk("1\\n23\\\"".toByteArray(), false),
|
||||
Chunk("456\\".toByteArray(), false),
|
||||
Chunk("\\222\\\\\\\\\"".toByteArray(), true),
|
||||
),
|
||||
),
|
||||
Arguments.of(
|
||||
listOf("{\"id\": 2,\"result\": {\"name\": ".toByteArray(), "\"bigName\"".toByteArray(), "}".toByteArray()),
|
||||
listOf(
|
||||
Chunk("{\"name\": ".toByteArray(), false),
|
||||
Chunk("\"bigName\"".toByteArray(), false),
|
||||
Chunk("}".toByteArray(), true),
|
||||
),
|
||||
),
|
||||
Arguments.of(
|
||||
listOf(
|
||||
"{\"id\": 2,\"result\": [{\"name\": ".toByteArray(),
|
||||
"\"bigName\"".toByteArray(),
|
||||
"}],".toByteArray(),
|
||||
"\"field\": \"value\"}".toByteArray(),
|
||||
),
|
||||
listOf(
|
||||
Chunk("[{\"name\": ".toByteArray(), false),
|
||||
Chunk("\"bigName\"".toByteArray(), false),
|
||||
Chunk("}]".toByteArray(), true),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user