ethers_providers/rpc/transports/
mock.rs1use crate::{JsonRpcClient, ProviderError};
2use async_trait::async_trait;
3use serde::{de::DeserializeOwned, Serialize};
4use serde_json::Value;
5use std::{
6 borrow::Borrow,
7 collections::VecDeque,
8 sync::{Arc, Mutex},
9};
10use thiserror::Error;
11
12#[derive(Debug)]
16enum MockParams {
17 Value(Value),
18 Zst,
19}
20
21#[derive(Clone, Debug)]
24pub enum MockResponse {
25 Value(Value),
27
28 Error(super::JsonRpcError),
30}
31
32#[derive(Clone, Debug)]
33pub struct MockProvider {
35 requests: Arc<Mutex<VecDeque<(String, MockParams)>>>,
36 responses: Arc<Mutex<VecDeque<MockResponse>>>,
37}
38
39impl Default for MockProvider {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
46#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
47impl JsonRpcClient for MockProvider {
48 type Error = MockError;
49
50 async fn request<T: Serialize + Send + Sync, R: DeserializeOwned>(
53 &self,
54 method: &str,
55 params: T,
56 ) -> Result<R, MockError> {
57 let params = if std::mem::size_of::<T>() == 0 {
58 MockParams::Zst
59 } else {
60 MockParams::Value(serde_json::to_value(params)?)
61 };
62 self.requests.lock().unwrap().push_back((method.to_owned(), params));
63 let mut data = self.responses.lock().unwrap();
64 let element = data.pop_back().ok_or(MockError::EmptyResponses)?;
65 match element {
66 MockResponse::Value(value) => {
67 let res: R = serde_json::from_value(value)?;
68 Ok(res)
69 }
70 MockResponse::Error(error) => Err(MockError::JsonRpcError(error)),
71 }
72 }
73}
74
75impl MockProvider {
76 pub fn assert_request<T: Serialize + Send + Sync>(
78 &self,
79 method: &str,
80 data: T,
81 ) -> Result<(), MockError> {
82 let (m, inp) = self.requests.lock().unwrap().pop_front().ok_or(MockError::EmptyRequests)?;
83 assert_eq!(m, method);
84 assert!(!matches!(inp, MockParams::Value(serde_json::Value::Null)));
85 if std::mem::size_of::<T>() == 0 {
86 assert!(matches!(inp, MockParams::Zst));
87 } else if let MockParams::Value(inp) = inp {
88 assert_eq!(serde_json::to_value(data).expect("could not serialize data"), inp);
89 } else {
90 unreachable!("Zero sized types must be denoted with MockParams::Zst")
91 }
92
93 Ok(())
94 }
95
96 pub fn new() -> Self {
98 Self {
99 requests: Arc::new(Mutex::new(VecDeque::new())),
100 responses: Arc::new(Mutex::new(VecDeque::new())),
101 }
102 }
103
104 pub fn push<T: Serialize + Send + Sync, K: Borrow<T>>(&self, data: K) -> Result<(), MockError> {
106 let value = serde_json::to_value(data.borrow())?;
107 self.responses.lock().unwrap().push_back(MockResponse::Value(value));
108 Ok(())
109 }
110
111 pub fn push_response(&self, response: MockResponse) {
113 self.responses.lock().unwrap().push_back(response);
114 }
115}
116
117#[derive(Error, Debug)]
118pub enum MockError {
120 #[error(transparent)]
122 SerdeJson(#[from] serde_json::Error),
123
124 #[error("empty requests array, please push some requests")]
126 EmptyRequests,
127
128 #[error("empty responses array, please push some responses")]
130 EmptyResponses,
131
132 #[error("JSON-RPC error: {0}")]
134 JsonRpcError(super::JsonRpcError),
135}
136
137impl crate::RpcError for MockError {
138 fn as_error_response(&self) -> Option<&super::JsonRpcError> {
139 match self {
140 MockError::JsonRpcError(e) => Some(e),
141 _ => None,
142 }
143 }
144
145 fn as_serde_error(&self) -> Option<&serde_json::Error> {
146 match self {
147 MockError::SerdeJson(e) => Some(e),
148 _ => None,
149 }
150 }
151}
152
153impl From<MockError> for ProviderError {
154 fn from(src: MockError) -> Self {
155 ProviderError::JsonRpcClientError(Box::new(src))
156 }
157}
158
159#[cfg(test)]
160#[cfg(not(target_arch = "wasm32"))]
161mod tests {
162 use super::*;
163 use crate::{JsonRpcError, Middleware};
164 use ethers_core::types::U64;
165
166 #[tokio::test]
167 async fn pushes_request_and_response() {
168 let mock = MockProvider::new();
169 mock.push(U64::from(12)).unwrap();
170 let block: U64 = mock.request("eth_blockNumber", ()).await.unwrap();
171 mock.assert_request("eth_blockNumber", ()).unwrap();
172 assert_eq!(block.as_u64(), 12);
173 }
174
175 #[tokio::test]
176 async fn empty_responses() {
177 let mock = MockProvider::new();
178 let err = mock.request::<_, ()>("eth_blockNumber", ()).await.unwrap_err();
180 match err {
181 MockError::EmptyResponses => {}
182 _ => panic!("expected empty responses"),
183 };
184 }
185
186 #[tokio::test]
187 async fn pushes_error_response() {
188 let mock = MockProvider::new();
189 let error = JsonRpcError {
190 code: 3,
191 data: Some(serde_json::from_str(r#""0x556f1830...""#).unwrap()),
192 message: "execution reverted".to_string(),
193 };
194 mock.push_response(MockResponse::Error(error.clone()));
195
196 let result: Result<U64, MockError> = mock.request("eth_blockNumber", ()).await;
197 match result {
198 Err(MockError::JsonRpcError(e)) => {
199 assert_eq!(e.code, error.code);
200 assert_eq!(e.message, error.message);
201 assert_eq!(e.data, error.data);
202 }
203 _ => panic!("Expected JsonRpcError"),
204 }
205 }
206
207 #[tokio::test]
208 async fn empty_requests() {
209 let mock = MockProvider::new();
210 let err = mock.assert_request("eth_blockNumber", ()).unwrap_err();
212 match err {
213 MockError::EmptyRequests => {}
214 _ => panic!("expected empty request"),
215 };
216 }
217
218 #[tokio::test]
219 async fn composes_with_provider() {
220 let (provider, mock) = crate::Provider::mocked();
221
222 mock.push(U64::from(12)).unwrap();
223 let block = provider.get_block_number().await.unwrap();
224 assert_eq!(block.as_u64(), 12);
225 }
226}