@@ -202,4 +202,40 @@ class UpstreamsConfigReaderSpec extends Specification {
|
||||
where:
|
||||
id << ["test", "test_test", "test-test", "test123", "test1test", "foo_bar_12"]
|
||||
}
|
||||
|
||||
def "Parse config without fallback role"() {
|
||||
setup:
|
||||
def config = this.class.getClassLoader().getResourceAsStream("upstreams-basic.yaml")
|
||||
when:
|
||||
def act = reader.read(config)
|
||||
then:
|
||||
act != null
|
||||
act.upstreams.size() == 2
|
||||
act.upstreams.get(0).role == UpstreamsConfig.UpstreamRole.STANDARD
|
||||
act.upstreams.get(1).role == UpstreamsConfig.UpstreamRole.STANDARD
|
||||
}
|
||||
|
||||
def "Parse config with fallback role"() {
|
||||
setup:
|
||||
def config = this.class.getClassLoader().getResourceAsStream("upstreams-roles.yaml")
|
||||
when:
|
||||
def act = reader.read(config)
|
||||
then:
|
||||
act != null
|
||||
act.upstreams.size() == 2
|
||||
act.upstreams.get(0).role == UpstreamsConfig.UpstreamRole.STANDARD
|
||||
act.upstreams.get(1).role == UpstreamsConfig.UpstreamRole.FALLBACK
|
||||
}
|
||||
|
||||
def "Parse config with invalid role"() {
|
||||
setup:
|
||||
def config = this.class.getClassLoader().getResourceAsStream("upstreams-roles-invalid.yaml")
|
||||
when:
|
||||
def act = reader.read(config)
|
||||
then:
|
||||
act != null
|
||||
act.upstreams.size() == 2
|
||||
act.upstreams.get(0).role == UpstreamsConfig.UpstreamRole.STANDARD
|
||||
act.upstreams.get(1).role == UpstreamsConfig.UpstreamRole.STANDARD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package io.emeraldpay.dshackle.quorum
|
||||
|
||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.FilteredApis
|
||||
@@ -34,6 +35,7 @@ class QuorumRpcReaderSpec extends Specification {
|
||||
setup:
|
||||
def up = Mock(Upstream) {
|
||||
_ * isAvailable() >> true
|
||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.STANDARD
|
||||
1 * getApi() >> Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_test", [])) >> Mono.just(JsonRpcResponse.ok("1"))
|
||||
}
|
||||
@@ -60,6 +62,7 @@ class QuorumRpcReaderSpec extends Specification {
|
||||
setup:
|
||||
def up = Mock(Upstream) {
|
||||
_ * isAvailable() >> true
|
||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.STANDARD
|
||||
_ * getApi() >> Mock(Reader) {
|
||||
2 * read(new JsonRpcRequest("eth_test", [])) >>> [
|
||||
Mono.just(JsonRpcResponse.error(1, "test")),
|
||||
@@ -89,6 +92,7 @@ class QuorumRpcReaderSpec extends Specification {
|
||||
setup:
|
||||
def up = Mock(Upstream) {
|
||||
_ * isAvailable() >> true
|
||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.STANDARD
|
||||
_ * getApi() >> Mock(Reader) {
|
||||
2 * read(new JsonRpcRequest("eth_test", [])) >>> [
|
||||
Mono.just(JsonRpcResponse.ok("null")),
|
||||
@@ -119,6 +123,7 @@ class QuorumRpcReaderSpec extends Specification {
|
||||
setup:
|
||||
def up = Mock(Upstream) {
|
||||
_ * isAvailable() >> true
|
||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.STANDARD
|
||||
_ * getApi() >> Mock(Reader) {
|
||||
2 * read(new JsonRpcRequest("eth_test", [])) >>> [
|
||||
Mono.just(JsonRpcResponse.error(1, "test")),
|
||||
@@ -148,6 +153,7 @@ class QuorumRpcReaderSpec extends Specification {
|
||||
setup:
|
||||
def up = Mock(Upstream) {
|
||||
_ * isAvailable() >> true
|
||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.STANDARD
|
||||
_ * getApi() >> Mock(Reader) {
|
||||
3 * read(new JsonRpcRequest("eth_test", [])) >>> [
|
||||
Mono.just(JsonRpcResponse.ok("null")),
|
||||
@@ -178,6 +184,7 @@ class QuorumRpcReaderSpec extends Specification {
|
||||
setup:
|
||||
def up = Mock(Upstream) {
|
||||
_ * isAvailable() >> true
|
||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.STANDARD
|
||||
_ * getApi() >> Mock(Reader) {
|
||||
3 * read(new JsonRpcRequest("eth_test", [])) >>> [
|
||||
Mono.just(JsonRpcResponse.ok("null")),
|
||||
|
||||
@@ -61,7 +61,9 @@ class EthereumUpstreamMock extends EthereumUpstream {
|
||||
|
||||
EthereumUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) {
|
||||
super(id, chain, api, null,
|
||||
UpstreamsConfig.Options.getDefaults(), new QuorumForLabels.QuorumItem(1, new UpstreamsConfig.Labels()),
|
||||
UpstreamsConfig.Options.getDefaults(),
|
||||
UpstreamsConfig.UpstreamRole.STANDARD,
|
||||
new QuorumForLabels.QuorumItem(1, new UpstreamsConfig.Labels()),
|
||||
methods)
|
||||
setLag(0)
|
||||
setStatus(UpstreamAvailability.OK)
|
||||
@@ -85,4 +87,9 @@ class EthereumUpstreamMock extends EthereumUpstream {
|
||||
Head getHead() {
|
||||
return ethereumHeadMock
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
return "Upstream mock ${getId()}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ class TestingCommons {
|
||||
return new EthereumApiMock()
|
||||
}
|
||||
|
||||
static EthereumUpstreamMock upstream(String id, Reader<JsonRpcRequest, JsonRpcResponse> api) {
|
||||
return new EthereumUpstreamMock(id, Chain.ETHEREUM, api)
|
||||
}
|
||||
|
||||
static EthereumUpstreamMock upstream(Reader<JsonRpcRequest, JsonRpcResponse> api) {
|
||||
return new EthereumUpstreamMock(Chain.ETHEREUM, api)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumWsFactory
|
||||
import io.emeraldpay.grpc.Chain
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.test.StepVerifier
|
||||
import spock.lang.Retry
|
||||
import spock.lang.Specification
|
||||
@@ -50,6 +51,7 @@ class FilteredApisSpec extends Specification {
|
||||
TestingCommons.api().tap { it.id = "${i++}" },
|
||||
(EthereumWsFactory) null,
|
||||
new UpstreamsConfig.Options(),
|
||||
UpstreamsConfig.UpstreamRole.STANDARD,
|
||||
new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(it)),
|
||||
ethereumTargets
|
||||
)
|
||||
@@ -175,7 +177,52 @@ class FilteredApisSpec extends Specification {
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
.expectNext(ups[2], ups[3], ups[4], ups[5], ups[0], ups[1])
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
}
|
||||
|
||||
def "Start with offset"() {
|
||||
expect:
|
||||
FilteredApis.startFrom([0, 1, 2, 3, 4], pos) == exp
|
||||
where:
|
||||
pos | exp
|
||||
0 | [0, 1, 2, 3, 4]
|
||||
1 | [1, 2, 3, 4, 0]
|
||||
2 | [2, 3, 4, 0, 1]
|
||||
3 | [3, 4, 0, 1, 2]
|
||||
4 | [4, 0, 1, 2, 3]
|
||||
5 | [0, 1, 2, 3, 4]
|
||||
6 | [1, 2, 3, 4, 0]
|
||||
}
|
||||
|
||||
def "Starts with standard"() {
|
||||
setup:
|
||||
List<Upstream> standard = (0..1).collect {
|
||||
TestingCommons.upstream(
|
||||
it.toString(),
|
||||
new EthereumApiStub(it)
|
||||
)
|
||||
}
|
||||
def fallback = [
|
||||
Mock(Upstream) {
|
||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.FALLBACK
|
||||
_ * isAvailable() >> true
|
||||
}
|
||||
]
|
||||
when:
|
||||
def act = new FilteredApis([] + fallback + standard,
|
||||
Selector.empty, 0, 3, 0)
|
||||
act.request(10)
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
.expectNext(standard[0], standard[1]).as("Initial requests")
|
||||
|
||||
.expectNext(standard[0], standard[1]).as("Retry with standard")
|
||||
.expectNext(fallback[0]).as("Retry with fallback")
|
||||
|
||||
.expectNext(standard[0], standard[1]).as("Second retry with standard")
|
||||
.expectNext(fallback[0]).as("Second retry with fallback")
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
}
|
||||
}
|
||||
|
||||
31
src/test/resources/upstreams-roles-invalid.yaml
Normal file
31
src/test/resources/upstreams-roles-invalid.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
version: v1
|
||||
|
||||
defaults:
|
||||
- chains:
|
||||
- ethereum
|
||||
options:
|
||||
min-peers: 3
|
||||
|
||||
upstreams:
|
||||
- id: local
|
||||
chain: ethereum
|
||||
connection:
|
||||
ethereum:
|
||||
rpc:
|
||||
url: "http://localhost:8545"
|
||||
ws:
|
||||
url: "ws://localhost:8546"
|
||||
origin: "http://localhost"
|
||||
basic-auth:
|
||||
username: 9c199ad8f281f20154fc258fe41a6814
|
||||
password: 258fe4149c199ad8f2811a68f20154fc
|
||||
- id: infura
|
||||
chain: ethereum
|
||||
role: fallbackZ
|
||||
connection:
|
||||
ethereum:
|
||||
rpc:
|
||||
url: "https://mainnet.infura.io/v3/fa28c968191849c1aff541ad1d8511f2"
|
||||
basic-auth:
|
||||
username: 4fc258fe41a68149c199ad8f281f2015
|
||||
password: 1a68f20154fc258fe4149c199ad8f281
|
||||
31
src/test/resources/upstreams-roles.yaml
Normal file
31
src/test/resources/upstreams-roles.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
version: v1
|
||||
|
||||
defaults:
|
||||
- chains:
|
||||
- ethereum
|
||||
options:
|
||||
min-peers: 3
|
||||
|
||||
upstreams:
|
||||
- id: local
|
||||
chain: ethereum
|
||||
connection:
|
||||
ethereum:
|
||||
rpc:
|
||||
url: "http://localhost:8545"
|
||||
ws:
|
||||
url: "ws://localhost:8546"
|
||||
origin: "http://localhost"
|
||||
basic-auth:
|
||||
username: 9c199ad8f281f20154fc258fe41a6814
|
||||
password: 258fe4149c199ad8f2811a68f20154fc
|
||||
- id: infura
|
||||
chain: ethereum
|
||||
role: fallback
|
||||
connection:
|
||||
ethereum:
|
||||
rpc:
|
||||
url: "https://mainnet.infura.io/v3/fa28c968191849c1aff541ad1d8511f2"
|
||||
basic-auth:
|
||||
username: 4fc258fe41a68149c199ad8f281f2015
|
||||
password: 1a68f20154fc258fe4149c199ad8f281
|
||||
Reference in New Issue
Block a user