1use crate::{
15 transport::TransportRequest, DapiClientError, DapiRequestExecutor, ExecutionError,
16 ExecutionResponse, ExecutionResult, RequestSettings,
17};
18use dapi_grpc::mock::Mockable;
19use dapi_grpc::tonic::async_trait;
20use hex::ToHex;
21use sha2::Digest;
22use std::{
23 any::type_name,
24 collections::HashMap,
25 fmt::{Debug, Display},
26 sync::Mutex,
27};
28
29#[derive(Default, Debug)]
35pub struct MockDapiClient {
36 expectations: Expectations,
37 request_counts: Mutex<HashMap<Key, usize>>,
38}
39pub type MockResult<T> = ExecutionResult<<T as TransportRequest>::Response, DapiClientError>;
41
42impl MockDapiClient {
43 pub fn new() -> Self {
45 Self::default()
46 }
47
48 pub fn request_count<R: Mockable>(&self, request: &R) -> usize {
50 self.request_counts
51 .lock()
52 .expect("request counter lock")
53 .get(&Key::new(request))
54 .copied()
55 .unwrap_or_default()
56 }
57
58 pub fn expect<R>(&mut self, request: &R, result: &MockResult<R>) -> Result<&mut Self, MockError>
60 where
61 R: TransportRequest + Mockable,
62 R::Response: Mockable,
63 {
64 let key = self.expectations.add(request, result)?;
65
66 tracing::trace!(
67 %key,
68 request_type = std::any::type_name::<R>(),
69 response_type = std::any::type_name::<R::Response>(),
70 "mock added expectation"
71 );
72
73 Ok(self)
74 }
75
76 pub fn remove<R>(&mut self, request: &R) -> bool
80 where
81 R: TransportRequest + Mockable,
82 R::Response: Mockable,
83 {
84 let key = Key::new(request);
85 let removed = self.expectations.remove(&key);
86
87 if removed {
88 tracing::trace!(
89 %key,
90 request_type = std::any::type_name::<R>(),
91 "mock removed expectation"
92 );
93 }
94
95 removed
96 }
97
98 #[cfg(feature = "dump")]
107 pub fn load<T, P: AsRef<std::path::Path>>(
108 &mut self,
109 file: P,
110 ) -> Result<(T, MockResult<T>), std::io::Error>
111 where
112 T: TransportRequest + Mockable,
113 T::Response: Mockable,
114 {
115 use crate::DumpData;
116
117 let buf = std::fs::read(file)?;
118 let data = DumpData::<T>::mock_deserialize(&buf).ok_or({
119 std::io::Error::new(std::io::ErrorKind::InvalidData, "unable to parse json")
120 })?;
121
122 let (request, response) = data.deserialize();
123 self.expect(&request, &response).map_err(|e| {
124 std::io::Error::new(
125 std::io::ErrorKind::InvalidInput,
126 format!("unable to add expectation: {}", e),
127 )
128 })?;
129 Ok((request, response))
130 }
131}
132
133#[async_trait]
134impl DapiRequestExecutor for MockDapiClient {
135 async fn execute<R: TransportRequest>(
136 &self,
137 request: R,
138 _settings: RequestSettings,
139 ) -> MockResult<R>
140 where
141 R: Mockable,
142 R::Response: Mockable,
143 {
144 let (key, response) = self.expectations.get(&request);
145 *self
146 .request_counts
147 .lock()
148 .expect("request counter lock")
149 .entry(key.clone())
150 .or_default() += 1;
151
152 tracing::trace!(
153 %key,
154 request_type = std::any::type_name::<R>(),
155 response_type = std::any::type_name::<R::Response>(),
156 response = ?response,
157 "mock execute"
158 );
159
160 if let Some(response) = response {
161 response
162 } else {
163 let error = MockError::MockExpectationNotFound(format!(
164 "unexpected mock request with key {}, use MockDapiClient::expect(): {:?}",
165 key, request
166 ));
167
168 Err(ExecutionError {
169 inner: DapiClientError::Mock(error),
170 retries: 0,
171 address: None,
172 })
173 }
174 }
175}
176
177#[derive(Eq, Hash, PartialEq, PartialOrd, Ord, Clone, Debug)]
178pub struct Key([u8; 32]);
180
181impl Key {
182 pub fn new<S: Mockable>(request: &S) -> Self {
188 Self::try_new(request).expect("unable to create a key")
189 }
190
191 pub fn try_new<S: Mockable>(request: &S) -> Result<Self, std::io::Error> {
193 let typ = type_name::<S>().replace('&', ""); let mut encoded = S::mock_serialize(request).ok_or(std::io::Error::new(
198 std::io::ErrorKind::InvalidData,
199 format!("mocking not supported for object of type {}", typ),
200 ))?;
201 encoded.append(&mut typ.as_bytes().to_vec());
202
203 let mut e = sha2::Sha256::new();
204 e.update(encoded);
205 let key = e.finalize().into();
206
207 Ok(Self(key))
208 }
209}
210
211impl ToHex for Key {
212 fn encode_hex<T: std::iter::FromIterator<char>>(&self) -> T {
213 self.0.encode_hex()
214 }
215
216 fn encode_hex_upper<T: std::iter::FromIterator<char>>(&self) -> T {
217 self.0.encode_hex_upper()
218 }
219}
220impl Display for Key {
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 Display::fmt(&self.encode_hex::<String>(), f)
223 }
224}
225
226#[derive(Debug, thiserror::Error, Clone)]
227#[cfg_attr(feature = "mocks", derive(serde::Serialize, serde::Deserialize))]
228pub enum MockError {
230 #[error("mock expectation not found for request: {0}")]
231 MockExpectationNotFound(String),
233
234 #[error("expectation already defined for request: {0}")]
235 MockExpectationConflict(String),
237}
238
239#[derive(Debug)]
240struct ExpectedResult(Vec<u8>);
242
243impl ExpectedResult {
244 fn serialize<I: Mockable>(request: &I) -> Self {
245 Self(request.mock_serialize().expect("encode value"))
247 }
248
249 fn deserialize<O: Mockable>(&self) -> O {
250 O::mock_deserialize(&self.0).expect("deserialize value")
252 }
253}
254
255#[derive(Default, Debug)]
256struct Expectations {
258 expectations: HashMap<Key, ExpectedResult>,
259}
260
261impl Expectations {
262 pub fn add<I: Mockable + Debug, O: Mockable>(
266 &mut self,
267 request: &I,
268 result: &O,
269 ) -> Result<Key, MockError> {
270 let key = Key::new(request);
271 let value = ExpectedResult::serialize(result);
272
273 if self.expectations.contains_key(&key) {
274 return Err(MockError::MockExpectationConflict(format!(
275 "expectation with key {} already defined for {} request",
276 key,
277 std::any::type_name::<I>(),
278 )));
279 }
280
281 self.expectations.insert(key.clone(), value);
282
283 Ok(key)
284 }
285
286 pub fn get<I: Mockable, O: Mockable>(&self, request: &I) -> (Key, Option<O>) {
290 let key = Key::new(request);
291
292 let response = self.expectations.get(&key).and_then(|v| v.deserialize());
293
294 (key, response)
295 }
296
297 pub fn remove(&mut self, key: &Key) -> bool {
299 self.expectations.remove(key).is_some()
300 }
301}
302
303impl<R: Mockable> Mockable for ExecutionResponse<R> {
304 fn mock_serialize(&self) -> Option<Vec<u8>> {
305 R::mock_serialize(&self.inner)
306 }
307
308 fn mock_deserialize(data: &[u8]) -> Option<Self> {
309 R::mock_deserialize(data).map(|inner| ExecutionResponse {
311 inner,
312 retries: 0,
313 address: "http://127.0.0.1:9000"
314 .parse()
315 .expect("failed to parse address"),
316 })
317 }
318}
319
320impl<E: Mockable> Mockable for ExecutionError<E> {
321 fn mock_serialize(&self) -> Option<Vec<u8>> {
322 E::mock_serialize(&self.inner)
323 }
324
325 fn mock_deserialize(data: &[u8]) -> Option<Self> {
326 E::mock_deserialize(data).map(|inner| ExecutionError {
328 inner,
329 retries: 0,
330 address: None,
331 })
332 }
333}
334
335pub trait FromInner<R>
338where
339 Self: Default,
340{
341 fn from_inner(inner: R) -> Self;
346}
347
348impl<R> FromInner<R> for ExecutionResponse<R>
349where
350 Self: Default,
351{
352 fn from_inner(inner: R) -> Self {
353 Self {
354 inner,
355 ..Default::default()
356 }
357 }
358}