reth_e2e_test_utils/testsuite/
mod.rs

1//! Utilities for running e2e tests against a node or a network of nodes.
2
3use crate::{
4    testsuite::actions::{Action, ActionBox},
5    NodeBuilderHelper, PayloadAttributesBuilder,
6};
7use eyre::Result;
8use jsonrpsee::http_client::{transport::HttpBackend, HttpClient};
9use reth_engine_local::LocalPayloadAttributesBuilder;
10use reth_node_api::{NodeTypesWithEngine, PayloadTypes};
11use reth_rpc_layer::AuthClientService;
12use setup::Setup;
13use std::marker::PhantomData;
14
15pub mod actions;
16pub mod setup;
17
18#[cfg(test)]
19mod examples;
20
21/// Client handles for both regular RPC and Engine API endpoints
22#[derive(Debug)]
23pub struct NodeClient {
24    /// Regular JSON-RPC client
25    pub rpc: HttpClient,
26    /// Engine API client
27    pub engine: HttpClient<AuthClientService<HttpBackend>>,
28}
29
30/// Represents a test environment.
31#[derive(Debug)]
32pub struct Environment<I> {
33    /// Combined clients with both RPC and Engine API endpoints
34    pub node_clients: Vec<NodeClient>,
35    /// Tracks instance generic.
36    _phantom: PhantomData<I>,
37}
38
39impl<I> Default for Environment<I> {
40    fn default() -> Self {
41        Self { node_clients: vec![], _phantom: Default::default() }
42    }
43}
44
45/// Builder for creating test scenarios
46#[allow(missing_debug_implementations)]
47#[derive(Default)]
48pub struct TestBuilder<I> {
49    setup: Option<Setup<I>>,
50    actions: Vec<ActionBox<I>>,
51    env: Environment<I>,
52}
53
54impl<I: 'static> TestBuilder<I> {
55    /// Create a new test builder
56    pub fn new() -> Self {
57        Self { setup: None, actions: Vec::new(), env: Default::default() }
58    }
59
60    /// Set the test setup
61    pub fn with_setup(mut self, setup: Setup<I>) -> Self {
62        self.setup = Some(setup);
63        self
64    }
65
66    /// Add an action to the test
67    pub fn with_action<A>(mut self, action: A) -> Self
68    where
69        A: Action<I>,
70    {
71        self.actions.push(ActionBox::<I>::new(action));
72        self
73    }
74
75    /// Add multiple actions to the test
76    pub fn with_actions<II, A>(mut self, actions: II) -> Self
77    where
78        II: IntoIterator<Item = A>,
79        A: Action<I>,
80    {
81        self.actions.extend(actions.into_iter().map(ActionBox::new));
82        self
83    }
84
85    /// Run the test scenario
86    pub async fn run<N>(mut self) -> Result<()>
87    where
88        N: NodeBuilderHelper,
89        LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
90            <<N as NodeTypesWithEngine>::Engine as PayloadTypes>::PayloadAttributes,
91        >,
92    {
93        let mut setup = self.setup.take();
94
95        if let Some(ref mut s) = setup {
96            s.apply::<N>(&mut self.env).await?;
97        }
98
99        let actions = std::mem::take(&mut self.actions);
100
101        for action in actions {
102            action.execute(&mut self.env).await?;
103        }
104
105        // explicitly drop the setup to shutdown the nodes
106        // after all actions have completed
107        drop(setup);
108
109        Ok(())
110    }
111}