Skip to content

Commit b6cfa91

Browse files
greged93yiweichifrisitano
authored
tests: add test fixture for easier abstraction (#407)
* tests: add test fixture for easier abstraction * fix: lints * fix: smol rework * fix: fmt * tests: extend helpers and migrate * tests: answer comments + finish migration * tests: migrate remaining tests * tests: migration after merge * tests: cleaning + fix * tests: disable eth wire bridge * tests: use expect_tx * fix: clippy * chore: answer comments --------- Co-authored-by: Morty <[email protected]> Co-authored-by: Morty <[email protected]> Co-authored-by: frisitano <[email protected]>
1 parent 05870a0 commit b6cfa91

File tree

12 files changed

+2706
-1737
lines changed

12 files changed

+2706
-1737
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ reth-provider = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll
169169
reth-rpc-api = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91", default-features = false }
170170
reth-rpc-eth-api = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91", default-features = false }
171171
reth-rpc-eth-types = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91", default-features = false }
172+
reth-rpc-layer = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91", default-features = false }
172173
reth-rpc-server-types = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91", default-features = false }
173174
reth-storage-api = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91", default-features = false }
174175
reth-tasks = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91", default-features = false }

crates/node/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,16 @@ aws-config = "1.8.0"
7676
aws-sdk-kms = "1.76.0"
7777

7878
# test-utils
79+
alloy-eips = { workspace = true, optional = true }
80+
alloy-rpc-types-eth = { workspace = true, optional = true }
7981
alloy-rpc-types-engine = { workspace = true, optional = true }
8082
reth-e2e-test-utils = { workspace = true, optional = true }
8183
reth-engine-local = { workspace = true, optional = true }
8284
reth-provider = { workspace = true, optional = true }
85+
reth-rpc-layer = { workspace = true, optional = true }
8386
reth-rpc-server-types = { workspace = true, optional = true }
87+
reth-storage-api = { workspace = true, optional = true }
88+
reth-tokio-util = { workspace = true, optional = true }
8489
scroll-alloy-rpc-types-engine = { workspace = true, optional = true }
8590
scroll-alloy-rpc-types.workspace = true
8691

@@ -108,6 +113,7 @@ reth-e2e-test-utils.workspace = true
108113
reth-node-core.workspace = true
109114
reth-provider.workspace = true
110115
reth-primitives-traits.workspace = true
116+
reth-rpc-layer.workspace = true
111117
reth-rpc-server-types.workspace = true
112118
reth-scroll-node = { workspace = true, features = ["test-utils"] }
113119
reth-storage-api.workspace = true
@@ -140,10 +146,15 @@ test-utils = [
140146
"rollup-node/test-utils",
141147
"reth-e2e-test-utils",
142148
"reth-rpc-server-types",
149+
"reth-rpc-layer",
150+
"reth-tokio-util",
143151
"scroll-alloy-rpc-types-engine",
144152
"alloy-rpc-types-engine",
145153
"reth-primitives-traits/test-utils",
146154
"reth-network-p2p/test-utils",
147155
"rollup-node-chain-orchestrator/test-utils",
148156
"scroll-network/test-utils",
157+
"alloy-eips",
158+
"reth-storage-api",
159+
"alloy-rpc-types-eth",
149160
]
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
//! Block building helpers for test fixtures.
2+
3+
use super::fixture::TestFixture;
4+
use crate::test_utils::EventAssertions;
5+
6+
use alloy_primitives::B256;
7+
use reth_primitives_traits::transaction::TxHashRef;
8+
use reth_scroll_primitives::ScrollBlock;
9+
use scroll_alloy_consensus::ScrollTransaction;
10+
11+
/// Builder for constructing and validating blocks in tests.
12+
#[derive(Debug)]
13+
pub struct BlockBuilder<'a> {
14+
fixture: &'a mut TestFixture,
15+
expected_tx_hashes: Vec<B256>,
16+
expected_tx_count: Option<usize>,
17+
expected_base_fee: Option<u64>,
18+
expected_block_number: Option<u64>,
19+
expected_l1_message: Option<L1MessagesAssertion>,
20+
}
21+
22+
/// The assertion on the L1 messages.
23+
#[derive(Debug)]
24+
pub enum L1MessagesAssertion {
25+
/// Expect at least a single L1 message.
26+
ExpectL1Message,
27+
/// Expect an exact number of L1 messages.
28+
ExpectL1MessageCount(usize),
29+
}
30+
31+
impl L1MessagesAssertion {
32+
/// Assert the L1 messages count is correct.
33+
pub fn assert(&self, got: usize) -> eyre::Result<()> {
34+
match self {
35+
Self::ExpectL1Message => {
36+
if got == 0 {
37+
return Err(eyre::eyre!("Expected at least one L1 message, but block has none"));
38+
}
39+
}
40+
Self::ExpectL1MessageCount(count) => {
41+
if got != *count {
42+
return Err(eyre::eyre!("Expected at {count} L1 messages, but block has {got}"));
43+
}
44+
}
45+
}
46+
Ok(())
47+
}
48+
}
49+
50+
impl<'a> BlockBuilder<'a> {
51+
/// Create a new block builder.
52+
pub(crate) fn new(fixture: &'a mut TestFixture) -> Self {
53+
Self {
54+
fixture,
55+
expected_tx_hashes: Vec::new(),
56+
expected_tx_count: None,
57+
expected_block_number: None,
58+
expected_base_fee: None,
59+
expected_l1_message: None,
60+
}
61+
}
62+
63+
/// Expect a specific transaction to be included in the block.
64+
pub fn expect_tx(mut self, tx_hash: B256) -> Self {
65+
self.expected_tx_hashes.push(tx_hash);
66+
self
67+
}
68+
69+
/// Expect a specific number of transactions in the block.
70+
pub const fn expect_tx_count(mut self, count: usize) -> Self {
71+
self.expected_tx_count = Some(count);
72+
self
73+
}
74+
75+
/// Expect a specific block number.
76+
pub const fn expect_block_number(mut self, number: u64) -> Self {
77+
self.expected_block_number = Some(number);
78+
self
79+
}
80+
81+
/// Expect at least one L1 message in the block.
82+
pub const fn expect_l1_message(mut self) -> Self {
83+
self.expected_l1_message = Some(L1MessagesAssertion::ExpectL1Message);
84+
self
85+
}
86+
87+
/// Expect a specific number of L1 messages in the block.
88+
pub const fn expect_l1_message_count(mut self, count: usize) -> Self {
89+
self.expected_l1_message = Some(L1MessagesAssertion::ExpectL1MessageCount(count));
90+
self
91+
}
92+
93+
/// Build the block and validate against expectations.
94+
pub async fn build_and_await_block(self) -> eyre::Result<ScrollBlock> {
95+
let sequencer_node = &self.fixture.nodes[0];
96+
97+
// Get the sequencer from the rollup manager handle
98+
let handle = &sequencer_node.rollup_manager_handle;
99+
100+
// Trigger block building
101+
handle.build_block();
102+
103+
// If extract the block number.
104+
let expect = self.fixture.expect_event();
105+
let block =
106+
if let Some(b) = self.expected_block_number {
107+
expect.block_sequenced(b).await?
108+
} else {
109+
expect.extract(|e| {
110+
if let rollup_node_chain_orchestrator::ChainOrchestratorEvent::BlockSequenced(
111+
block,
112+
) = e
113+
{
114+
Some(block.clone())
115+
} else {
116+
None
117+
}
118+
}).await?.first().expect("should have block sequenced").clone()
119+
};
120+
121+
// Finally validate the block.
122+
self.validate_block(&block)
123+
}
124+
125+
/// Validate the block against expectations.
126+
fn validate_block(self, block: &ScrollBlock) -> eyre::Result<ScrollBlock> {
127+
// Check transaction count
128+
if let Some(expected_count) = self.expected_tx_count {
129+
if block.body.transactions.len() != expected_count {
130+
return Err(eyre::eyre!(
131+
"Expected {} transactions, but block has {}",
132+
expected_count,
133+
block.body.transactions.len()
134+
));
135+
}
136+
}
137+
138+
// Check block number
139+
if let Some(expected_number) = self.expected_block_number {
140+
if block.header.number != expected_number {
141+
return Err(eyre::eyre!(
142+
"Expected {} number, but block has {}",
143+
expected_number,
144+
block.header.number
145+
));
146+
}
147+
}
148+
149+
// Check specific transaction hashes
150+
for expected_hash in &self.expected_tx_hashes {
151+
if !block.body.transactions.iter().any(|tx| tx.tx_hash() == expected_hash) {
152+
return Err(eyre::eyre!(
153+
"Expected transaction {:?} not found in block",
154+
expected_hash
155+
));
156+
}
157+
}
158+
159+
// Check base fee
160+
if let Some(expected_base_fee) = self.expected_base_fee {
161+
let actual_base_fee = block
162+
.header
163+
.base_fee_per_gas
164+
.ok_or_else(|| eyre::eyre!("Block has no base fee"))?;
165+
if actual_base_fee != expected_base_fee {
166+
return Err(eyre::eyre!(
167+
"Expected base fee {}, but block has {}",
168+
expected_base_fee,
169+
actual_base_fee
170+
));
171+
}
172+
}
173+
174+
// Check L1 messages
175+
if let Some(assertion) = self.expected_l1_message {
176+
let l1_message_count =
177+
block.body.transactions.iter().filter(|tx| tx.queue_index().is_some()).count();
178+
assertion.assert(l1_message_count)?;
179+
}
180+
181+
Ok(block.clone())
182+
}
183+
}

0 commit comments

Comments
 (0)