Fix unsubscribe requests (#721)

This commit is contained in:
KirillPamPam
2025-09-10 15:18:54 +04:00
committed by GitHub
parent 650fa5cf0e
commit 0ed77f1cfd
26 changed files with 125 additions and 62 deletions

View File

@@ -37,6 +37,7 @@ import reactor.core.publisher.Mono
import reactor.core.publisher.Sinks
import reactor.core.scheduler.Schedulers
import reactor.test.StepVerifier
import reactor.util.function.Tuples
import spock.lang.Specification
import java.time.Duration
@@ -77,7 +78,7 @@ class GenericWsHeadSpec extends Specification {
def client = new JsonRpcWsClient(pool)
1 * ws.subscribe(_) >> new WsSubscriptions.SubscribeData(
Flux.fromIterable([headBlock]), "id", new AtomicReference<String>("")
Mono.just(Tuples.of("", Flux.fromIterable([headBlock]))), "id", new AtomicReference<String>("")
)
def head = new GenericWsHead(
@@ -130,8 +131,8 @@ class GenericWsHeadSpec extends Specification {
def ws = Mock(WsSubscriptions) {
1 * it.connectionInfoFlux() >> connectionInfoSink.asFlux()
2 * subscribe(_) >>> [
new WsSubscriptions.SubscribeData(Flux.error(new RuntimeException()), "id", new AtomicReference<String>("")),
new WsSubscriptions.SubscribeData(Flux.fromIterable([secondHeadBlock]), "id", new AtomicReference<String>(""))
new WsSubscriptions.SubscribeData(Mono.just(Tuples.of("", Flux.error(new RuntimeException()))), "id", new AtomicReference<String>("")),
new WsSubscriptions.SubscribeData(Mono.just(Tuples.of("", Flux.fromIterable([secondHeadBlock]))), "id", new AtomicReference<String>(""))
]
1 * it.unsubscribe(new ChainRequest("eth_unsubscribe", new ListParams(""), 2, null, null, false, Selector.UpstreamFilter.default)) >>
Mono.just(new ChainResponse("".bytes, null))
@@ -202,8 +203,8 @@ class GenericWsHeadSpec extends Specification {
def ws = Mock(WsSubscriptions) {
1 * it.connectionInfoFlux() >> connectionInfoSink.asFlux()
2 * subscribe(_) >>> [
new WsSubscriptions.SubscribeData(Flux.fromIterable([firstHeadBlock]), "id", new AtomicReference<String>("")),
new WsSubscriptions.SubscribeData(Flux.fromIterable([secondHeadBlock]), "id", new AtomicReference<String>(""))
new WsSubscriptions.SubscribeData(Mono.just(Tuples.of("", Flux.fromIterable([firstHeadBlock]))), "id", new AtomicReference<String>("")),
new WsSubscriptions.SubscribeData(Mono.just(Tuples.of("", Flux.fromIterable([secondHeadBlock]))), "id", new AtomicReference<String>(""))
]
}
@@ -259,7 +260,7 @@ class GenericWsHeadSpec extends Specification {
def ws = Mock(WsSubscriptions) {
1 * it.connectionInfoFlux() >> connectionInfoSink.asFlux()
1 * subscribe(_) >>> [
new WsSubscriptions.SubscribeData(Flux.fromIterable([firstHeadBlock]), "id", new AtomicReference<String>("")),
new WsSubscriptions.SubscribeData(Mono.just(Tuples.of("", Flux.fromIterable([firstHeadBlock]))), "id", new AtomicReference<String>("")),
]
}
@@ -314,7 +315,7 @@ class GenericWsHeadSpec extends Specification {
def ws = Mock(WsSubscriptions) {
1 * it.connectionInfoFlux() >> connectionInfoSink.asFlux()
1 * subscribe(_) >>> [
new WsSubscriptions.SubscribeData(Flux.fromIterable([firstHeadBlock]), "id", new AtomicReference<String>("")),
new WsSubscriptions.SubscribeData(Mono.just(Tuples.of("", Flux.fromIterable([firstHeadBlock]))), "id", new AtomicReference<String>("")),
]
}
@@ -382,8 +383,8 @@ class GenericWsHeadSpec extends Specification {
def ws = Mock(WsSubscriptions) {
1 * it.connectionInfoFlux() >> connectionInfoSink.asFlux()
2 * subscribe(_) >>> [
new WsSubscriptions.SubscribeData(Flux.fromIterable([firstHeadBlock]), "id", new AtomicReference<String>("")),
new WsSubscriptions.SubscribeData(Flux.fromIterable([secondHeadBlock]), "id", new AtomicReference<String>("")),
new WsSubscriptions.SubscribeData(Mono.just(Tuples.of("", Flux.fromIterable([firstHeadBlock]))), "id", new AtomicReference<String>("")),
new WsSubscriptions.SubscribeData(Mono.just(Tuples.of("", Flux.fromIterable([secondHeadBlock]))), "id", new AtomicReference<String>("")),
]
}
@@ -451,7 +452,7 @@ class GenericWsHeadSpec extends Specification {
def ws = Mock(WsSubscriptions) {
1 * it.connectionInfoFlux() >> Flux.empty()
1 * it.subscribe(_) >> new WsSubscriptions.SubscribeData(
Flux.error(new RuntimeException()), "id", new AtomicReference<String>(subId)
Mono.just(Tuples.of(subId, Flux.error(new RuntimeException()))), "id", new AtomicReference<String>(subId)
)
1 * it.unsubscribe(new ChainRequest("eth_unsubscribe", new ListParams(subId), 2, null, null, false, Selector.UpstreamFilter.default)) >>
Mono.just(new ChainResponse("".bytes, null))
@@ -506,7 +507,7 @@ class GenericWsHeadSpec extends Specification {
def ws = Mock(WsSubscriptions) {
1 * it.connectionInfoFlux() >> connectionInfoSink.asFlux()
1 * subscribe(_) >>> [
new WsSubscriptions.SubscribeData(Flux.fromIterable([secondHeadBlock]), "id", new AtomicReference<String>(""))
new WsSubscriptions.SubscribeData(Mono.just(Tuples.of("", Flux.fromIterable([secondHeadBlock]))), "id", new AtomicReference<String>(""))
]
}

View File

@@ -47,7 +47,7 @@ class WsSubscriptionsImplSpec extends Specification {
when:
def act = ws.subscribe(new ChainRequest("eth_subscribe", new ListParams(["foo_bar"])))
.data
.data.flatMapMany { it.getT2() }
.map { new String(it) }
.take(3)
.collectList().block(Duration.ofSeconds(1))
@@ -85,7 +85,7 @@ class WsSubscriptionsImplSpec extends Specification {
when:
def act = ws.subscribe(new ChainRequest("eth_subscribe", new ListParams(["foo_bar"])))
.data
.data.flatMapMany { it.getT2() }
.map { new String(it) }
.take(3)
.collectList().block(Duration.ofSeconds(1))

View File

@@ -16,10 +16,12 @@
package io.emeraldpay.dshackle.upstream.ethereum.subscribe
import io.emeraldpay.dshackle.upstream.ChainRequest
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import io.emeraldpay.dshackle.upstream.Selector
import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import reactor.util.function.Tuples
import spock.lang.Specification
import java.time.Duration
@@ -44,7 +46,7 @@ class WebsocketPendingTxesSpec extends Specification {
then:
1 * ws.subscribe(new ChainRequest("eth_subscribe", new ListParams(["newPendingTransactions"]))) >> new WsSubscriptions.SubscribeData(
Flux.fromIterable(responses), "id", new AtomicReference<String>("")
Mono.just(Tuples.of("", Flux.fromIterable(responses))), "id", new AtomicReference<String>("")
)
txes.collect {it.toHex() } == [
"0xa61bab14fc9720ea8725622688c2f964666d7c2afdae38af7dad53f12f242d5c",

View File

@@ -26,6 +26,7 @@ import reactor.core.publisher.Mono
import reactor.core.publisher.Sinks
import reactor.core.scheduler.Schedulers
import reactor.test.StepVerifier
import reactor.util.function.Tuples
import java.math.BigInteger
import java.time.Duration
import java.time.Instant
@@ -43,7 +44,7 @@ class GenericWsHeadTest {
val wsSub = mock<WsSubscriptions> {
on { connectionInfoFlux() } doReturn Flux.empty()
on { subscribe(ChainRequest("eth_subscribe", ListParams("newHeads"))) } doReturn
WsSubscriptions.SubscribeData(Flux.just(Global.objectMapper.writeValueAsBytes(block)), "id", AtomicReference("subId"))
WsSubscriptions.SubscribeData(Mono.just(Tuples.of("subId", Flux.just(Global.objectMapper.writeValueAsBytes(block)))), "id", AtomicReference("subId"))
}
val connection = mock<WsConnection> {
on { isConnected } doReturn true
@@ -141,7 +142,7 @@ class GenericWsHeadTest {
val wsSub = mock<WsSubscriptions> {
on { connectionInfoFlux() } doReturn Flux.empty()
on { subscribe(ChainRequest("eth_subscribe", ListParams("newHeads"))) } doReturn
WsSubscriptions.SubscribeData(Flux.just(Global.objectMapper.writeValueAsBytes(block1), Global.objectMapper.writeValueAsBytes(block2)), "id", AtomicReference("subId"))
WsSubscriptions.SubscribeData(Mono.just(Tuples.of("subId", Flux.just(Global.objectMapper.writeValueAsBytes(block1), Global.objectMapper.writeValueAsBytes(block2)))), "id", AtomicReference("subId"))
}
val connection = mock<WsConnection>()
val wsPool = mock<WsConnectionPool> {
@@ -189,7 +190,7 @@ class GenericWsHeadTest {
val wsSub = mock<WsSubscriptions> {
on { connectionInfoFlux() } doReturn connectionInfoSink.asFlux()
on { subscribe(ChainRequest("eth_subscribe", ListParams("newHeads"))) } doReturn
WsSubscriptions.SubscribeData(Flux.just(Global.objectMapper.writeValueAsBytes(block)), "id", AtomicReference("subId"))
WsSubscriptions.SubscribeData(Mono.just(Tuples.of("subId", Flux.just(Global.objectMapper.writeValueAsBytes(block)))), "id", AtomicReference("subId"))
}
val connection = mock<WsConnection> {
on { isConnected } doReturn true
@@ -247,7 +248,7 @@ class GenericWsHeadTest {
val wsSub = mock<WsSubscriptions> {
on { connectionInfoFlux() } doReturn Flux.empty()
on { subscribe(ChainRequest("eth_subscribe", ListParams("newHeads"))) } doReturn
WsSubscriptions.SubscribeData(Flux.just(Global.objectMapper.writeValueAsBytes(block1), Global.objectMapper.writeValueAsBytes(block2)), "id", AtomicReference("subId"))
WsSubscriptions.SubscribeData(Mono.just(Tuples.of("sudId", Flux.just(Global.objectMapper.writeValueAsBytes(block1), Global.objectMapper.writeValueAsBytes(block2)))), "id", AtomicReference("subId"))
}
val connection = mock<WsConnection> {
on { isConnected } doReturn true
@@ -299,7 +300,7 @@ class GenericWsHeadTest {
val wsSub = mock<WsSubscriptions> {
on { connectionInfoFlux() } doReturn Flux.empty()
on { subscribe(ChainRequest("eth_subscribe", ListParams("newHeads"))) } doReturn
WsSubscriptions.SubscribeData(Flux.just(Global.objectMapper.writeValueAsBytes(block1), Global.objectMapper.writeValueAsBytes(block2)), "id", AtomicReference("subId"))
WsSubscriptions.SubscribeData(Mono.just(Tuples.of("subId", Flux.just(Global.objectMapper.writeValueAsBytes(block1), Global.objectMapper.writeValueAsBytes(block2)))), "id", AtomicReference("subId"))
}
val connection = mock<WsConnection> {
on { isConnected } doReturn true
@@ -351,7 +352,7 @@ class GenericWsHeadTest {
val wsSub = mock<WsSubscriptions> {
on { connectionInfoFlux() } doReturn Flux.empty()
on { subscribe(ChainRequest("eth_subscribe", ListParams("newHeads"))) } doReturn
WsSubscriptions.SubscribeData(Flux.just(Global.objectMapper.writeValueAsBytes(block1), Global.objectMapper.writeValueAsBytes(block2)), "id", AtomicReference("subId"))
WsSubscriptions.SubscribeData(Mono.just(Tuples.of("subId", Flux.just(Global.objectMapper.writeValueAsBytes(block1), Global.objectMapper.writeValueAsBytes(block2)))), "id", AtomicReference("subId"))
}
val connection = mock<WsConnection> {
on { isConnected } doReturn true
@@ -405,7 +406,7 @@ class GenericWsHeadTest {
val wsSub = mock<WsSubscriptions> {
on { connectionInfoFlux() } doReturn Flux.empty()
on { subscribe(ChainRequest("eth_subscribe", ListParams("newHeads"))) } doReturn
WsSubscriptions.SubscribeData(Flux.just(Global.objectMapper.writeValueAsBytes(block1), Global.objectMapper.writeValueAsBytes(block2)), "id", AtomicReference("subId"))
WsSubscriptions.SubscribeData(Mono.just(Tuples.of("sudId", Flux.just(Global.objectMapper.writeValueAsBytes(block1), Global.objectMapper.writeValueAsBytes(block2)))), "id", AtomicReference("subId"))
}
val connection = mock<WsConnection> {
on { isConnected } doReturn true

View File

@@ -1,5 +1,6 @@
package io.emeraldpay.dshackle.upstream.generic
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.upstream.ChainRequest
import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
@@ -8,7 +9,9 @@ import org.mockito.Mockito.verify
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import reactor.test.StepVerifier
import reactor.util.function.Tuples
import java.time.Duration
import java.util.concurrent.atomic.AtomicReference
@@ -21,10 +24,10 @@ class GenericSubscriptionConnectTest {
val response = "hello".toByteArray()
val ws = mock<WsSubscriptions> {
on { subscribe(ChainRequest(topic, ListParams(param))) } doReturn
WsSubscriptions.SubscribeData(Flux.just(response), "", AtomicReference(""))
WsSubscriptions.SubscribeData(Mono.just(Tuples.of("", Flux.just(response))), "", AtomicReference(""))
}
val genericSubscriptionConnect = GenericSubscriptionConnect(ws, topic, param, "")
val genericSubscriptionConnect = GenericSubscriptionConnect(Chain.ETHEREUM__MAINNET, ws, topic, param, "")
StepVerifier.create(genericSubscriptionConnect.createConnection())
.expectNext(response)