Skip to content

Commit aa6d197

Browse files
Replace alchemy-dependant test with mock server
1 parent 7491446 commit aa6d197

1 file changed

Lines changed: 114 additions & 48 deletions

File tree

  • crates/edr_rpc_eth/tests/integration

crates/edr_rpc_eth/tests/integration/client.rs

Lines changed: 114 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,120 @@ async fn send_request_body_400_status() {
6767
mock.assert_async().await;
6868
}
6969

70+
// This replaces live tests that probed a provider whose
71+
// block-range limits and future-`to_block` clamping vary by plan tier and
72+
// backend node.
73+
// see <https://www.alchemy.com/docs/chains/ethereum/ethereum-api-endpoints/eth-get-logs#supported-block-ranges>
74+
#[tokio::test]
75+
async fn get_logs_by_range_serializes_request_and_parses_response() {
76+
use edr_eth::{filter::OneOrMore, BlockSpec};
77+
use edr_primitives::Address;
78+
79+
const MAX_BLOCK_NUMBER: u64 = u64::MAX >> 1;
80+
81+
// Expected request parameters. EDR forwards `from_block`/`to_block` verbatim
82+
// and does no clamping of its own; a "future" `to_block` such as
83+
// `MAX_BLOCK_NUMBER` is left for the server to resolve.
84+
let from_block = 10496585_u64;
85+
let to_block = MAX_BLOCK_NUMBER;
86+
let address = "0xffffffffffffffffffffffffffffffffffffffff";
87+
88+
// Expected fields of the single log in the response.
89+
let block_number = 0xa74fde_u64;
90+
let log_index = 0x653b_u64;
91+
let transaction_index = 0x1f_u64;
92+
93+
let mut server = mockito::Server::new_async().await;
94+
95+
// `get_logs_by_range` runs through the caching layer, which fires
96+
// `eth_chainId` (to build the cache path) and may fire `eth_blockNumber` (to
97+
// decide whether the response is cacheable) around the actual `eth_getLogs`
98+
// request. Register these as optional responders (we never assert them, so
99+
// the test stays decoupled from the exact caching behavior) to keep those
100+
// requests from falling through to an unmatched-request error.
101+
let _chain_id_mock = server
102+
.mock("POST", "/")
103+
.match_body(mockito::Matcher::PartialJson(serde_json::json!({
104+
"method": "eth_chainId"
105+
})))
106+
.with_status(200)
107+
.with_header("content-type", "application/json")
108+
.with_body(r#"{"jsonrpc": "2.0", "id": 0, "result": "0x1"}"#)
109+
.create_async()
110+
.await;
111+
112+
let _block_number_mock = server
113+
.mock("POST", "/")
114+
.match_body(mockito::Matcher::PartialJson(serde_json::json!({
115+
"method": "eth_blockNumber"
116+
})))
117+
.with_status(200)
118+
.with_header("content-type", "application/json")
119+
.with_body(r#"{"jsonrpc": "2.0", "id": 0, "result": "0xffffff"}"#)
120+
.create_async()
121+
.await;
122+
123+
// The assertion that matters: EDR serializes the request with the expected
124+
// `eth_getLogs` parameters, including the future `to_block` as `0x7fff…`.
125+
let get_logs_mock = server
126+
.mock("POST", "/")
127+
.match_body(mockito::Matcher::PartialJson(serde_json::json!({
128+
"method": "eth_getLogs",
129+
"params": [{
130+
"fromBlock": format!("{from_block:#x}"),
131+
"toBlock": format!("{to_block:#x}"),
132+
"address": address,
133+
}],
134+
})))
135+
.with_status(200)
136+
.with_header("content-type", "application/json")
137+
.with_body(
138+
serde_json::json!({
139+
"jsonrpc": "2.0",
140+
"id": 0,
141+
"result": [{
142+
"address": "0x0000000000000000000000000000000000000011",
143+
"topics": [
144+
"0x000000000000000000000000000000000000000000000000000000000000dead",
145+
"0x000000000000000000000000000000000000000000000000000000000000beef"
146+
],
147+
"data": "0x0100ff",
148+
"transactionHash": "0xc008e9f9bb92057dd0035496fbf4fb54f66b4b18b370928e46d6603933054d5a",
149+
"blockHash": "0x88fadbb673928c61b9ede3694ae0589ac77ae38ec90a24a6e12e83f42f18c7e8",
150+
"blockNumber": format!("{block_number:#x}"),
151+
"logIndex": format!("{log_index:#x}"),
152+
"transactionIndex": format!("{transaction_index:#x}"),
153+
"removed": false,
154+
}],
155+
})
156+
.to_string(),
157+
)
158+
.create_async()
159+
.await;
160+
161+
let logs = TestRpcClient::new(&server.url())
162+
.get_logs_by_range(
163+
BlockSpec::Number(from_block),
164+
BlockSpec::Number(to_block),
165+
Some(OneOrMore::One(
166+
Address::from_str(address).expect("failed to parse address"),
167+
)),
168+
None,
169+
)
170+
.await
171+
.expect("should have parsed the response");
172+
173+
// The request was serialized with the expected `eth_getLogs` parameters.
174+
get_logs_mock.assert_async().await;
175+
176+
// The response was parsed into the expected log.
177+
assert_eq!(logs.len(), 1);
178+
assert_eq!(logs[0].block_number, block_number);
179+
assert_eq!(logs[0].log_index, log_index);
180+
assert_eq!(logs[0].transaction_index, transaction_index);
181+
assert!(!logs[0].removed);
182+
}
183+
70184
#[cfg(feature = "test-remote")]
71185
mod alchemy {
72186
use std::{fs::File, path::PathBuf};
@@ -309,54 +423,6 @@ mod alchemy {
309423
// TODO: consider asserting something about the logs bloom
310424
}
311425

312-
#[tokio::test]
313-
async fn get_logs_future_from_block() {
314-
let provider_url = json_rpc_url_provider::ethereum_mainnet();
315-
let result = TestRpcClient::new(&provider_url)
316-
.get_logs_by_range(
317-
BlockSpec::Number(MAX_BLOCK_NUMBER),
318-
BlockSpec::Number(MAX_BLOCK_NUMBER),
319-
Some(OneOrMore::One(
320-
Address::from_str("0xffffffffffffffffffffffffffffffffffffffff")
321-
.expect("failed to parse data"),
322-
)),
323-
None,
324-
)
325-
.await;
326-
327-
// TODO: https://github.com/NomicFoundation/edr/issues/903
328-
// Alchemy enabled [EIP-4444](https://eips.ethereum.org/EIPS/eip-4444) for part of their clients. As a
329-
// result, it's possible that we get the updated result `[]` instead of the old
330-
// JSON-RPC error.
331-
match result {
332-
Ok(response) => {
333-
assert!(response.is_empty());
334-
}
335-
Err(error) => {
336-
assert!(matches!(error, RpcClientError::JsonRpcError { .. }));
337-
}
338-
}
339-
}
340-
341-
#[tokio::test]
342-
async fn get_logs_future_to_block() {
343-
let provider_url = json_rpc_url_provider::ethereum_mainnet();
344-
let logs = TestRpcClient::new(&provider_url)
345-
.get_logs_by_range(
346-
BlockSpec::Number(10496585),
347-
BlockSpec::Number(MAX_BLOCK_NUMBER),
348-
Some(OneOrMore::One(
349-
Address::from_str("0xffffffffffffffffffffffffffffffffffffffff")
350-
.expect("failed to parse data"),
351-
)),
352-
None,
353-
)
354-
.await
355-
.expect("should have succeeded");
356-
357-
assert_eq!(logs, []);
358-
}
359-
360426
#[tokio::test]
361427
async fn get_transaction_by_hash_some() {
362428
let provider_url = json_rpc_url_provider::ethereum_mainnet();

0 commit comments

Comments
 (0)