solution: JSON RPC client that treats result as raw
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2020 EmeraldPay, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.upstream.rpcclient
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import io.emeraldpay.dshackle.config.AuthConfig
|
||||
import io.netty.buffer.Unpooled
|
||||
import io.netty.handler.codec.http.HttpHeaderNames
|
||||
import io.netty.handler.codec.http.HttpHeaders
|
||||
import org.slf4j.LoggerFactory
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.netty.http.client.HttpClient
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
|
||||
/**
|
||||
* JSON RPC client
|
||||
*/
|
||||
class JsonRpcClient(
|
||||
private val target: String,
|
||||
private val objectMapper: ObjectMapper,
|
||||
basicAuth: AuthConfig.ClientBasicAuth?
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(JsonRpcClient::class.java)
|
||||
}
|
||||
|
||||
private val parser = JsonRpcParser()
|
||||
private val httpClient: HttpClient
|
||||
|
||||
init {
|
||||
var build = HttpClient.create()
|
||||
|
||||
build = build.headers { h ->
|
||||
h.add(HttpHeaderNames.CONTENT_TYPE, "application/json")
|
||||
}
|
||||
|
||||
basicAuth?.let { basicAuth ->
|
||||
val authString: String = basicAuth.username + ":" + basicAuth.password
|
||||
val authBase64 = Base64.getEncoder().encodeToString(authString.toByteArray())
|
||||
val auth = "Basic $authBase64"
|
||||
val headers = Consumer { h: HttpHeaders -> h.add(HttpHeaderNames.AUTHORIZATION, auth) }
|
||||
build = build.headers(headers)
|
||||
}
|
||||
|
||||
this.httpClient = build
|
||||
}
|
||||
|
||||
fun execute(request: JsonRpcRequest): Mono<JsonRpcResponse> {
|
||||
return Mono.just(request)
|
||||
.map { it.toJson(objectMapper) }
|
||||
.flatMap(this@JsonRpcClient::execute)
|
||||
.map(parser::parse)
|
||||
}
|
||||
|
||||
fun execute(request: ByteArray): Mono<ByteArray> {
|
||||
val response = httpClient
|
||||
.post()
|
||||
.uri(target)
|
||||
.send(Mono.just(request).map { Unpooled.wrappedBuffer(it) })
|
||||
|
||||
return response.responseContent()
|
||||
.aggregate()
|
||||
.asByteArray()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2020 EmeraldPay, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.upstream.rpcclient
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.core.JsonToken
|
||||
import io.infinitape.etherjar.rpc.RpcResponseError
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
class JsonRpcParser() {
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(JsonRpcParser::class.java)
|
||||
}
|
||||
|
||||
private val jsonFactory = JsonFactory()
|
||||
|
||||
fun parse(json: ByteArray): JsonRpcResponse {
|
||||
val parser: JsonParser = jsonFactory.createParser(json)
|
||||
parser.nextToken()
|
||||
if (parser.currentToken != JsonToken.START_OBJECT) {
|
||||
println("token ${parser.currentToken}")
|
||||
return JsonRpcResponse(null, JsonRpcResponse.ResponseError(RpcResponseError.CODE_UPSTREAM_INVALID_RESPONSE, "Invalid JSON"))
|
||||
}
|
||||
var nullResponse: JsonRpcResponse? = null
|
||||
while (parser.nextToken() != JsonToken.END_OBJECT) {
|
||||
val field = parser.currentName
|
||||
if (field == "jsonrpc" || field == "id") {
|
||||
if (!parser.nextToken().isScalarValue) {
|
||||
return JsonRpcResponse(null, JsonRpcResponse.ResponseError(RpcResponseError.CODE_UPSTREAM_INVALID_RESPONSE, "Invalid JSON (id/type)"))
|
||||
}
|
||||
// just skip the field
|
||||
} else if (field == "result") {
|
||||
val value = parser.nextToken()
|
||||
val start = parser.tokenLocation
|
||||
if (value.isScalarValue) {
|
||||
val text = parser.text
|
||||
if (value == JsonToken.VALUE_STRING) {
|
||||
return JsonRpcResponse(("\"" + text + "\"").toByteArray(), null)
|
||||
} else if (value == JsonToken.VALUE_NULL) {
|
||||
//if null we should check if error is present
|
||||
nullResponse = JsonRpcResponse(text.toByteArray(), null)
|
||||
} else {
|
||||
return JsonRpcResponse(text.toByteArray(), null)
|
||||
}
|
||||
} else if (value == JsonToken.START_OBJECT || value == JsonToken.START_ARRAY) {
|
||||
parser.skipChildren()
|
||||
val end = parser.currentLocation.byteOffset.toInt()
|
||||
val copy = ByteArray((end - start.byteOffset).toInt())
|
||||
System.arraycopy(json, start.byteOffset.toInt(), copy, 0, copy.size)
|
||||
return JsonRpcResponse(copy, null)
|
||||
}
|
||||
} else if (field == "error") {
|
||||
val err = readError(parser)
|
||||
if (err != null) {
|
||||
return JsonRpcResponse(null, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nullResponse != null) {
|
||||
return nullResponse
|
||||
}
|
||||
return JsonRpcResponse(null, JsonRpcResponse.ResponseError(RpcResponseError.CODE_UPSTREAM_INVALID_RESPONSE, "Invalid JSON structure"))
|
||||
}
|
||||
|
||||
fun readError(parser: JsonParser): JsonRpcResponse.ResponseError? {
|
||||
var code = 0
|
||||
var message = ""
|
||||
|
||||
while (parser.nextToken() != JsonToken.END_OBJECT) {
|
||||
if (parser.currentToken() == JsonToken.VALUE_NULL) {
|
||||
// error is just null
|
||||
return null
|
||||
}
|
||||
val field = parser.currentName()
|
||||
if (field == "code" && parser.currentToken == JsonToken.VALUE_NUMBER_INT) {
|
||||
code = parser.intValue
|
||||
} else if (field == "message" && parser.currentToken == JsonToken.VALUE_STRING) {
|
||||
message = parser.valueAsString
|
||||
}
|
||||
}
|
||||
|
||||
return JsonRpcResponse.ResponseError(code, message)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2020 EmeraldPay, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.upstream.rpcclient
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
|
||||
class JsonRpcRequest(
|
||||
val method: String,
|
||||
val params: List<Any>
|
||||
) {
|
||||
|
||||
fun toJson(objectMapper: ObjectMapper): ByteArray {
|
||||
val json = mapOf(
|
||||
"jsonrpc" to "2.0",
|
||||
"id" to 1,
|
||||
"method" to method,
|
||||
"params" to params
|
||||
)
|
||||
return objectMapper.writeValueAsBytes(json)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is JsonRpcRequest) return false
|
||||
|
||||
if (method != other.method) return false
|
||||
if (params != other.params) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = method.hashCode()
|
||||
result = 31 * result + params.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2020 EmeraldPay, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.upstream.rpcclient
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.databind.JsonSerializer
|
||||
import com.fasterxml.jackson.databind.SerializerProvider
|
||||
|
||||
class JsonRpcResponse(
|
||||
val result: ByteArray?,
|
||||
val error: ResponseError?
|
||||
) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is JsonRpcResponse) return false
|
||||
|
||||
if (result != null) {
|
||||
if (other.result == null) return false
|
||||
if (!result.contentEquals(other.result)) return false
|
||||
} else if (other.result != null) return false
|
||||
if (error != other.error) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result1 = result?.contentHashCode() ?: 0
|
||||
result1 = 31 * result1 + (error?.hashCode() ?: 0)
|
||||
return result1
|
||||
}
|
||||
|
||||
class ResponseError(val code: Int, val message: String)
|
||||
|
||||
class ResponseJsonSerializer : JsonSerializer<JsonRpcResponse>() {
|
||||
override fun serialize(value: JsonRpcResponse, gen: JsonGenerator, serializers: SerializerProvider) {
|
||||
gen.writeStartObject()
|
||||
gen.writeStringField("jsonrpc", "2.0")
|
||||
gen.writeNumberField("id", 0)
|
||||
if (value.error != null) {
|
||||
gen.writeObjectFieldStart("error")
|
||||
gen.writeNumberField("code", value.error.code)
|
||||
gen.writeStringField("message", value.error.message)
|
||||
gen.writeEndObject()
|
||||
} else {
|
||||
if (value.result == null) {
|
||||
throw IllegalStateException("No result set")
|
||||
}
|
||||
gen.writeRawUTF8String(value.result, 0, value.result.size)
|
||||
}
|
||||
gen.writeEndObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2020 EmeraldPay, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.upstream.rpcclient
|
||||
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import org.mockserver.integration.ClientAndServer
|
||||
import org.mockserver.model.HttpRequest
|
||||
import org.mockserver.model.HttpResponse
|
||||
import spock.lang.Specification
|
||||
|
||||
class JsonRpcClientSpec extends Specification {
|
||||
|
||||
ClientAndServer mockServer
|
||||
JsonRpcClient client
|
||||
|
||||
def setup() {
|
||||
mockServer = ClientAndServer.startClientAndServer(18332);
|
||||
client = new JsonRpcClient("localhost:18332", TestingCommons.objectMapper(), null)
|
||||
}
|
||||
|
||||
def cleanup() {
|
||||
mockServer.stop()
|
||||
}
|
||||
|
||||
|
||||
def "Make a request"() {
|
||||
setup:
|
||||
def resp = '{' +
|
||||
' "jsonrpc": "2.0",' +
|
||||
' "result": "0x98de45",' +
|
||||
' "error": null,' +
|
||||
' "id": 15' +
|
||||
'}'
|
||||
mockServer.when(
|
||||
HttpRequest.request()
|
||||
).respond(
|
||||
HttpResponse.response(resp)
|
||||
)
|
||||
when:
|
||||
def act = client.execute(new JsonRpcRequest("test", [])).block()
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '"0x98de45"'
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Copyright (c) 2020 EmeraldPay, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.upstream.rpcclient
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
class JsonRpcParserSpec extends Specification {
|
||||
|
||||
JsonRpcParser parser = new JsonRpcParser()
|
||||
|
||||
def "Parse just result"() {
|
||||
setup:
|
||||
def json = '{"result": "Hello world!"}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '"Hello world!"'
|
||||
}
|
||||
|
||||
def "Parse string response"() {
|
||||
setup:
|
||||
// 0 8 16 32 35
|
||||
def json = '{"jsonrpc": "2.0", "id": 1, "result": "Hello world!"}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '"Hello world!"'
|
||||
}
|
||||
|
||||
def "Parse string response when result starts first"() {
|
||||
setup:
|
||||
// 0 8 16 32 35
|
||||
def json = '{"result": "Hello world!", "jsonrpc": "2.0", "id": 1}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '"Hello world!"'
|
||||
}
|
||||
|
||||
def "Parse bool response"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "id": 1, "result": false}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == 'false'
|
||||
}
|
||||
|
||||
def "Parse bool response when id is last"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "result": false, "id": 1}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == 'false'
|
||||
}
|
||||
|
||||
def "Parse int response"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "id": 1, "result": 100}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '100'
|
||||
}
|
||||
|
||||
def "Parse null response"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "id": 1, "result": null}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == 'null'
|
||||
}
|
||||
|
||||
def "Parse object response"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "id": 1, "result": {"hash": "0x00000", "foo": false, "bar": 1}}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '{"hash": "0x00000", "foo": false, "bar": 1}'
|
||||
}
|
||||
|
||||
def "Parse object response with null error"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "id": 1, "result": {"hash": "0x00000", "foo": false, "bar": 1}, "error": null}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '{"hash": "0x00000", "foo": false, "bar": 1}'
|
||||
}
|
||||
|
||||
def "Parse object response if null error comes first"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "id": 1, "error": null, "result": {"hash": "0x00000", "foo": false, "bar": 1}}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '{"hash": "0x00000", "foo": false, "bar": 1}'
|
||||
}
|
||||
|
||||
def "Parse object response with extra spaces"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "result" : {"hash": "0x00000", "foo": false , "bar":1} , "id": 1}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '{"hash": "0x00000", "foo": false , "bar":1}'
|
||||
}
|
||||
|
||||
def "Parse complex object response"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "id": 1, "result": {"hash": "0x00000", "foo": {"bar": 1, "baz": 2}}}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '{"hash": "0x00000", "foo": {"bar": 1, "baz": 2}}'
|
||||
}
|
||||
|
||||
def "Parse array response"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "id": 1, "result": [1, 2, false]}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error == null
|
||||
new String(act.result) == '[1, 2, false]'
|
||||
}
|
||||
|
||||
def "Parse error"() {
|
||||
setup:
|
||||
// 0 8 16 32
|
||||
def json = '{"jsonrpc": "2.0", "id": 1, "result": null, "error": {"code": -1111, "message": "test"}}'
|
||||
when:
|
||||
def act = parser.parse(json.getBytes())
|
||||
then:
|
||||
act.error != null
|
||||
act.error.code == -1111
|
||||
act.error.message == "test"
|
||||
act.result == null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2020 EmeraldPay, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.upstream.rpcclient
|
||||
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import spock.lang.Specification
|
||||
|
||||
class JsonRpcRequestSpec extends Specification {
|
||||
|
||||
def "Serialize empty params"() {
|
||||
setup:
|
||||
def req = new JsonRpcRequest("test_foo", [])
|
||||
when:
|
||||
def act = req.toJson(TestingCommons.objectMapper())
|
||||
then:
|
||||
new String(act) == '{"jsonrpc":"2.0","id":1,"method":"test_foo","params":[]}'
|
||||
}
|
||||
|
||||
def "Serialize single param"() {
|
||||
setup:
|
||||
def req = new JsonRpcRequest("test_foo", ["0x0000"])
|
||||
when:
|
||||
def act = req.toJson(TestingCommons.objectMapper())
|
||||
then:
|
||||
new String(act) == '{"jsonrpc":"2.0","id":1,"method":"test_foo","params":["0x0000"]}'
|
||||
}
|
||||
|
||||
def "Serialize two params"() {
|
||||
setup:
|
||||
def req = new JsonRpcRequest("test_foo", ["0x0000", false])
|
||||
when:
|
||||
def act = req.toJson(TestingCommons.objectMapper())
|
||||
then:
|
||||
new String(act) == '{"jsonrpc":"2.0","id":1,"method":"test_foo","params":["0x0000",false]}'
|
||||
}
|
||||
|
||||
def "Same requests are equal"() {
|
||||
setup:
|
||||
def req1 = new JsonRpcRequest("test_foo", ["0x0000", false])
|
||||
def req2 = new JsonRpcRequest("test_foo", ["0x0000", false])
|
||||
when:
|
||||
def act = req1.equals(req2)
|
||||
then:
|
||||
act == true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2020 EmeraldPay, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.upstream.rpcclient
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Same responses are equal"() {
|
||||
setup:
|
||||
def resp1 = new JsonRpcResponse("\"hello\"".bytes, null)
|
||||
def resp2 = new JsonRpcResponse("\"hello\"".bytes, null)
|
||||
when:
|
||||
def act = resp1.equals(resp2)
|
||||
then:
|
||||
act == true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user