dapi_grpc/
mock.rs

1//! Mocking support for messages.
2//!
3//! Contains [Mockable] trait that should be implemented by any object that can be used in the DAPI.
4//!
5//! Note that this trait is defined even if mocks are not supported, but it should always return `None` on serialization.
6
7#[cfg(feature = "mocks")]
8pub mod serde_mockable;
9
10use tonic::Streaming;
11
12/// Mocking support for messages.
13///
14/// This trait should be implemented by any object that can be used in the DAPI.
15///
16/// We use serde_json to serialize/deserialize messages.
17//  TODO: Move to a different crate where it can be easily shared by dapi-grpc, dash-platform-sdk, and rs-dapi-client.
18pub trait Mockable
19where
20    Self: std::marker::Sized,
21{
22    /// Serialize the message to bytes for mocking purposes.
23    ///
24    /// Returns None if the message is not serializable or mocking is disabled.
25    ///
26    /// # Panics
27    ///
28    /// Panics on any error.
29    fn mock_serialize(&self) -> Option<Vec<u8>> {
30        None
31    }
32    /// Deserialize the message serialized with [mock_serialize()].
33    ///
34    /// Returns None if the message is not serializable or mocking is disabled.
35    ///
36    /// # Panics
37    ///
38    /// Panics on any error.
39    fn mock_deserialize(_data: &[u8]) -> Option<Self> {
40        None
41    }
42}
43#[cfg(feature = "mocks")]
44#[derive(serde::Serialize, serde::Deserialize)]
45enum SerializableResult {
46    Ok(Vec<u8>),
47    Err(Vec<u8>),
48}
49impl<T, E> Mockable for Result<T, E>
50where
51    T: Mockable,
52    E: Mockable,
53{
54    #[cfg(feature = "mocks")]
55    fn mock_serialize(&self) -> Option<Vec<u8>> {
56        let serializable = match self {
57            Ok(value) => SerializableResult::Ok(value.mock_serialize()?),
58            Err(error) => SerializableResult::Err(error.mock_serialize()?),
59        };
60        serde_json::to_vec(&serializable).ok()
61    }
62
63    #[cfg(feature = "mocks")]
64    fn mock_deserialize(data: &[u8]) -> Option<Self> {
65        if data.is_empty() {
66            return None;
67        }
68        let deser: SerializableResult =
69            serde_json::from_slice(data).expect("unable to deserialize mock data");
70        Some(match deser {
71            SerializableResult::Ok(data) => Ok(T::mock_deserialize(&data)?),
72            SerializableResult::Err(data) => Err(E::mock_deserialize(&data)?),
73        })
74    }
75}
76
77impl<T: Mockable> Mockable for Option<T> {
78    #[cfg(feature = "mocks")]
79    fn mock_serialize(&self) -> Option<Vec<u8>> {
80        self.as_ref().and_then(|value| value.mock_serialize())
81    }
82
83    #[cfg(feature = "mocks")]
84    fn mock_deserialize(data: &[u8]) -> Option<Self> {
85        T::mock_deserialize(data).map(Some)
86    }
87}
88
89impl Mockable for Vec<u8> {
90    #[cfg(feature = "mocks")]
91    fn mock_serialize(&self) -> Option<Vec<u8>> {
92        serde_json::to_vec(self).ok()
93    }
94
95    #[cfg(feature = "mocks")]
96    fn mock_deserialize(data: &[u8]) -> Option<Self> {
97        serde_json::from_slice(data).ok()
98    }
99}
100#[cfg(feature = "mocks")]
101#[derive(serde::Serialize, serde::Deserialize)]
102struct MockableStatus {
103    code: i32,
104    message: Vec<u8>,
105}
106impl Mockable for crate::tonic::Status {
107    #[cfg(feature = "mocks")]
108    fn mock_serialize(&self) -> Option<Vec<u8>> {
109        let mockable = MockableStatus {
110            code: self.code().into(),
111            message: self.message().as_bytes().to_vec(),
112        };
113
114        Some(serde_json::to_vec(&mockable).expect("unable to serialize tonic::Status"))
115    }
116
117    #[cfg(feature = "mocks")]
118    fn mock_deserialize(data: &[u8]) -> Option<Self> {
119        let MockableStatus { code, message } =
120            serde_json::from_slice(data).expect("unable to deserialize tonic::Status");
121        let message = std::str::from_utf8(&message).expect("invalid utf8 message in tonic::Status");
122        Some(Self::new(code.into(), message))
123    }
124}
125
126/// Mocking of gRPC streaming responses is not supported.
127///
128/// This will return `None` on serialization,
129/// effectively disabling mocking of streaming responses.
130impl<T: Mockable> Mockable for Streaming<T> {}