From 17f14987aa5a8b74c04e4be06e2f4b792ec40a1d Mon Sep 17 00:00:00 2001 From: Igor Artamonov Date: Wed, 24 Mar 2021 22:52:25 -0400 Subject: [PATCH] solution: integration test with two upstreams --- .../dshackle/upstream/FilteredApisSpec.groovy | 16 +++++++- testing/README.adoc | 5 +++ testing/dshackle/dshackle.yaml | 8 ++++ .../groovy/testing/InternalHandler.groovy | 35 ++++++++++++++++ .../groovy/testing/PingPongHandler.groovy | 12 ++++++ .../main/groovy/testing/SimpleUpstream.groovy | 14 +++++-- .../dshackle/testing/trial/ProxyClient.groovy | 4 ++ .../testing/trial/proxy/DispatchSpec.groovy | 40 +++++++++++++++++++ 8 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 testing/simple-upstream/src/main/groovy/testing/InternalHandler.groovy create mode 100644 testing/simple-upstream/src/main/groovy/testing/PingPongHandler.groovy create mode 100644 testing/trial/src/test/groovy/io/emeraldpay/dshackle/testing/trial/proxy/DispatchSpec.groovy diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/FilteredApisSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/FilteredApisSpec.groovy index 3d809631..0364daba 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/FilteredApisSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/FilteredApisSpec.groovy @@ -182,7 +182,7 @@ class FilteredApisSpec extends Specification { .verify(Duration.ofSeconds(1)) } - def "Start with offset"() { + def "Start with offset - 5 items"() { expect: FilteredApis.startFrom([0, 1, 2, 3, 4], pos) == exp where: @@ -196,6 +196,20 @@ class FilteredApisSpec extends Specification { 6 | [1, 2, 3, 4, 0] } + def "Start with offset - 2 items"() { + expect: + FilteredApis.startFrom([0, 1], pos) == exp + where: + pos | exp + 0 | [0, 1] + 1 | [1, 0] + 2 | [0, 1] + 3 | [1, 0] + 4 | [0, 1] + 5 | [1, 0] + 6 | [0, 1] + } + def "Starts with standard"() { setup: List standard = (0..1).collect { diff --git a/testing/README.adoc b/testing/README.adoc index e92380f2..bc5b866c 100644 --- a/testing/README.adoc +++ b/testing/README.adoc @@ -18,6 +18,11 @@ NOTE: Run commands in project's root dir cd testing/simple-upstream && ./gradlew run ---- +[source,bash] +---- +cd testing/simple-upstream && DSHACKLE_TESTUP_PORT=18546 ./gradlew run +---- + === Run Dshackle [source,bash] diff --git a/testing/dshackle/dshackle.yaml b/testing/dshackle/dshackle.yaml index ae58c0fb..549d3784 100644 --- a/testing/dshackle/dshackle.yaml +++ b/testing/dshackle/dshackle.yaml @@ -18,6 +18,14 @@ cluster: ethereum: rpc: url: "http://localhost:18545" + - id: test-2 + chain: ethereum + options: + disable-validation: true + connection: + ethereum: + rpc: + url: "http://localhost:18546" cache: redis: diff --git a/testing/simple-upstream/src/main/groovy/testing/InternalHandler.groovy b/testing/simple-upstream/src/main/groovy/testing/InternalHandler.groovy new file mode 100644 index 00000000..9fe926aa --- /dev/null +++ b/testing/simple-upstream/src/main/groovy/testing/InternalHandler.groovy @@ -0,0 +1,35 @@ +package testing + +import java.time.Instant +import java.util.concurrent.atomic.AtomicInteger + +class InternalHandler implements CallHandler { + + private AtomicInteger ids = new AtomicInteger() + private List requests = new ArrayList() + + def record(String json) { + requests << new Item(ids.getAndIncrement(), json) + } + + @Override + Result handle(String method, List params) { + if (method == "eth_call" + && params[0].to?.toLowerCase() == "0x0123456789abcdef0123456789abcdef00000002".toLowerCase()) { + return Result.ok(Collections.unmodifiableList(requests)) + } + return null + } + + class Item { + Integer id + String timestamp + String json + + Item(Integer id, String json) { + this.id = id + this.timestamp = Instant.now().toString() + this.json = json + } + } +} diff --git a/testing/simple-upstream/src/main/groovy/testing/PingPongHandler.groovy b/testing/simple-upstream/src/main/groovy/testing/PingPongHandler.groovy new file mode 100644 index 00000000..9babfbd6 --- /dev/null +++ b/testing/simple-upstream/src/main/groovy/testing/PingPongHandler.groovy @@ -0,0 +1,12 @@ +package testing + +class PingPongHandler implements CallHandler { + @Override + Result handle(String method, List params) { + if (method == "eth_call" + && params[0].to?.toLowerCase() == "0x0123456789abcdef0123456789abcdef00000001".toLowerCase()) { + return Result.ok([data: params[0].data]) + } + return null + } +} diff --git a/testing/simple-upstream/src/main/groovy/testing/SimpleUpstream.groovy b/testing/simple-upstream/src/main/groovy/testing/SimpleUpstream.groovy index 31ff7cce..2c20b9be 100644 --- a/testing/simple-upstream/src/main/groovy/testing/SimpleUpstream.groovy +++ b/testing/simple-upstream/src/main/groovy/testing/SimpleUpstream.groovy @@ -5,17 +5,23 @@ import spark.Spark class SimpleUpstream { - private int port = 18545 + private int port = System.getenv("DSHACKLE_TESTUP_PORT")?.toInteger() ?: 18545 private ObjectMapper objectMapper private List handlers = [] + private InternalHandler internalHandler void prepare() { objectMapper = new ObjectMapper() + internalHandler = new InternalHandler() + handlers << new TestcaseHandler(objectMapper) handlers << new CommonHandlers() handlers << new BlocksHandler(objectMapper) + handlers << new PingPongHandler() + handlers << internalHandler + handlers << new InvalidCallHandler() } @@ -23,9 +29,11 @@ class SimpleUpstream { println("Starting upstream on 0.0.0.0:$port") Spark.port(port) Spark.post("/") { req, resp -> - println("request") try { - Map json = objectMapper.readValue(req.body(), Map) + def requestBody = req.body() + println("request: $requestBody") + internalHandler.record(requestBody) + Map json = objectMapper.readValue(requestBody, Map) def id = json["id"] String method = json["method"].toString() List params = json.containsKey("params") ? json["params"] as List : [] diff --git a/testing/trial/src/main/groovy/io/emeraldpay/dshackle/testing/trial/ProxyClient.groovy b/testing/trial/src/main/groovy/io/emeraldpay/dshackle/testing/trial/ProxyClient.groovy index a6c4a1a2..859b9c66 100644 --- a/testing/trial/src/main/groovy/io/emeraldpay/dshackle/testing/trial/ProxyClient.groovy +++ b/testing/trial/src/main/groovy/io/emeraldpay/dshackle/testing/trial/ProxyClient.groovy @@ -28,6 +28,10 @@ class ProxyClient { return new ProxyClient("http://127.0.0.1:18080/$prefix") } + static ProxyClient forOriginal(int port) { + return new ProxyClient("http://127.0.0.1:$port") + } + Map execute(String method, List params) { return execute(sequence++, method, params) } diff --git a/testing/trial/src/test/groovy/io/emeraldpay/dshackle/testing/trial/proxy/DispatchSpec.groovy b/testing/trial/src/test/groovy/io/emeraldpay/dshackle/testing/trial/proxy/DispatchSpec.groovy new file mode 100644 index 00000000..abae0430 --- /dev/null +++ b/testing/trial/src/test/groovy/io/emeraldpay/dshackle/testing/trial/proxy/DispatchSpec.groovy @@ -0,0 +1,40 @@ +package io.emeraldpay.dshackle.testing.trial.proxy + +import io.emeraldpay.dshackle.testing.trial.ProxyClient +import spock.lang.Specification + +class DispatchSpec extends Specification { + + def client = ProxyClient.forPrefix("eth") + + def "multiple calls routed roughly equal to upstreams"() { + when: + def calls1before = ProxyClient.forOriginal(18545).execute("eth_call", [[to: "0x0123456789abcdef0123456789abcdef00000002"]]).result as List + def calls2before = ProxyClient.forOriginal(18546).execute("eth_call", [[to: "0x0123456789abcdef0123456789abcdef00000002"]]).result as List + + 100.times { + client.execute("eth_call", [[to: "0x0123456789abcdef0123456789abcdef00000001", data: "0x00000000" + Integer.toString(it, 16)]]) + } + + def calls1after = ProxyClient.forOriginal(18545).execute("eth_call", [[to: "0x0123456789abcdef0123456789abcdef00000002"]]).result as List + def calls2after = ProxyClient.forOriginal(18546).execute("eth_call", [[to: "0x0123456789abcdef0123456789abcdef00000002"]]).result as List + + def calls1 = onlyNew(calls1before, calls1after) + def calls2 = onlyNew(calls2before, calls2after) + + then: + calls1.size() >= 48 + calls2.size() >= 48 + calls1.size() + calls2.size() == 100 + } + + private List onlyNew(List before, List after) { + return after.findAll { a -> + !before.any { b -> + b.id == a.id + } + }.findAll { + (it.json as String).contains("0x0123456789abcdef0123456789abcdef00000001") + } + } +}