barter_data/exchange/bitmex/
subscription.rs

1use barter_integration::{Validator, error::SocketError};
2use serde::{Deserialize, Serialize};
3
4/// ### Raw Payload Examples
5/// See docs: <https://www.bitmex.com/app/wsAPI#Response-Format>
6/// #### Subscription response payload
7/// ```json
8/// {
9///     "success": true,
10///     "subscribe": "trade:XBTUSD",
11///     "request": {
12///         "op":"subscribe",
13///         "args":[
14///             "trade:XBTUSD"
15///         ]
16///     }
17/// }
18///```
19#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
20pub struct BitmexSubResponse {
21    success: bool,
22    subscribe: String,
23}
24
25impl Validator for BitmexSubResponse {
26    fn validate(self) -> Result<Self, SocketError>
27    where
28        Self: Sized,
29    {
30        if self.success {
31            Ok(self)
32        } else {
33            Err(SocketError::Subscribe(format!(
34                "received failure subscription response for {} subscription",
35                self.subscribe
36            )))
37        }
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    mod de {
46        use super::*;
47
48        #[test]
49        fn test_bitmex_sub_response() {
50            struct TestCase {
51                input: &'static str,
52                expected: Result<BitmexSubResponse, SocketError>,
53            }
54
55            let cases = vec![
56                TestCase {
57                    // TC0: input response is Subscribed
58                    input: r#"
59                        {
60                            "success": true,
61                            "subscribe": "orderBookL2_25:XBTUSD",
62                            "request": {
63                                "op":"subscribe",
64                                "args":[
65                                    "orderBookL2_25:XBTUSD"
66                                ]
67                            }
68                        }
69                    "#,
70                    expected: Ok(BitmexSubResponse {
71                        success: true,
72                        subscribe: "orderBookL2_25:XBTUSD".to_string(),
73                    }),
74                },
75                TestCase {
76                    // TC1: input response is failed subscription
77                    input: r#"
78                    {
79                        "success": false,
80                        "subscribe": "orderBookL2_25:XBTUSD"
81                    }
82                    "#,
83                    expected: Ok(BitmexSubResponse {
84                        success: false,
85                        subscribe: "orderBookL2_25:XBTUSD".to_string(),
86                    }),
87                },
88            ];
89
90            for (index, test) in cases.into_iter().enumerate() {
91                let actual = serde_json::from_str::<BitmexSubResponse>(test.input);
92                match (actual, test.expected) {
93                    (Ok(actual), Ok(expected)) => {
94                        assert_eq!(actual, expected, "TC{} failed", index)
95                    }
96                    (Err(_), Err(_)) => {
97                        // Test passed
98                    }
99                    (actual, expected) => {
100                        // Test failed
101                        panic!(
102                            "TC{index} failed because actual != expected. \nActual: {actual:?}\nExpected: {expected:?}\n"
103                        );
104                    }
105                }
106            }
107        }
108    }
109
110    #[test]
111    fn test_validate_bitmex_sub_response() {
112        struct TestCase {
113            input_response: BitmexSubResponse,
114            is_valid: bool,
115        }
116
117        let cases = vec![
118            TestCase {
119                // TC0: input response is successful subscription
120                input_response: BitmexSubResponse {
121                    success: true,
122                    subscribe: "orderBookL2_25:XBTUSD".to_string(),
123                },
124                is_valid: true,
125            },
126            TestCase {
127                // TC1: input response is failed subscription
128                input_response: BitmexSubResponse {
129                    success: false,
130                    subscribe: "orderBookL2_25:XBTUSD".to_string(),
131                },
132                is_valid: false,
133            },
134        ];
135
136        for (index, test) in cases.into_iter().enumerate() {
137            let actual = test.input_response.validate().is_ok();
138            assert_eq!(actual, test.is_valid, "TestCase {} failed", index);
139        }
140    }
141}