ccxt_exchanges/bitget/
endpoint_router.rs

1//! Bitget-specific endpoint router trait.
2//!
3//! This module provides the `BitgetEndpointRouter` trait for routing API requests
4//! to the correct Bitget endpoints based on endpoint type (public/private).
5//!
6//! # Bitget API Structure
7//!
8//! Bitget uses a simple dual-endpoint structure:
9//! - **REST**: `api.bitget.com` - Single REST endpoint for all operations
10//! - **WebSocket Public**: `ws.bitget.com/v2/ws/public` - Public market data
11//! - **WebSocket Private**: `ws.bitget.com/v2/ws/private` - Account data
12//!
13//! # Testnet/Sandbox Mode
14//!
15//! Bitget provides a separate testnet environment:
16//! - REST: `api-testnet.bitget.com`
17//! - WS Public: `ws-testnet.bitget.com/v2/ws/public`
18//! - WS Private: `ws-testnet.bitget.com/v2/ws/private`
19//!
20//! # Example
21//!
22//! ```rust,no_run
23//! use ccxt_exchanges::bitget::{Bitget, BitgetEndpointRouter};
24//! use ccxt_core::types::EndpointType;
25//! use ccxt_core::ExchangeConfig;
26//!
27//! let bitget = Bitget::new(ExchangeConfig::default()).unwrap();
28//!
29//! // Get REST endpoint
30//! let rest_url = bitget.rest_endpoint();
31//! assert!(rest_url.contains("api.bitget.com"));
32//!
33//! // Get WebSocket endpoint for public data
34//! let ws_public = bitget.ws_endpoint(EndpointType::Public);
35//! assert!(ws_public.contains("/v2/ws/public"));
36//!
37//! // Get WebSocket endpoint for private data
38//! let ws_private = bitget.ws_endpoint(EndpointType::Private);
39//! assert!(ws_private.contains("/v2/ws/private"));
40//! ```
41
42use ccxt_core::types::EndpointType;
43
44/// Bitget-specific endpoint router trait.
45///
46/// This trait defines methods for obtaining the correct API endpoints for Bitget.
47/// Bitget uses a simple structure with separate public and private WebSocket endpoints.
48///
49/// # Implementation Notes
50///
51/// - REST API uses a single domain for all operations
52/// - WebSocket has separate endpoints for public and private data
53/// - Testnet mode switches to completely different domains
54///
55/// # Sandbox/Testnet Mode
56///
57/// When sandbox mode is enabled (via `config.sandbox` or `options.testnet`):
58/// - REST URL changes to `api-testnet.bitget.com`
59/// - WebSocket URLs change to `ws-testnet.bitget.com`
60pub trait BitgetEndpointRouter {
61    /// Returns the REST API endpoint.
62    ///
63    /// Bitget uses a single REST domain for all market types and operations.
64    /// The product type (spot, umcbl, dmcbl) is specified in the API path,
65    /// not in the domain.
66    ///
67    /// # Returns
68    ///
69    /// The REST API base URL string.
70    ///
71    /// # Production vs Testnet
72    ///
73    /// - Production: `https://api.bitget.com`
74    /// - Testnet: `https://api-testnet.bitget.com`
75    ///
76    /// # Example
77    ///
78    /// ```rust,no_run
79    /// use ccxt_exchanges::bitget::{Bitget, BitgetEndpointRouter};
80    /// use ccxt_core::ExchangeConfig;
81    ///
82    /// let bitget = Bitget::new(ExchangeConfig::default()).unwrap();
83    /// let url = bitget.rest_endpoint();
84    /// assert_eq!(url, "https://api.bitget.com");
85    /// ```
86    fn rest_endpoint(&self) -> &'static str;
87
88    /// Returns the WebSocket endpoint for a specific endpoint type.
89    ///
90    /// Bitget uses separate WebSocket URLs for public and private data:
91    /// - `Public`: `/v2/ws/public` - Market data (no authentication)
92    /// - `Private`: `/v2/ws/private` - Account data (authentication required)
93    ///
94    /// # Arguments
95    ///
96    /// * `endpoint_type` - The endpoint type (Public or Private)
97    ///
98    /// # Returns
99    ///
100    /// The complete WebSocket URL for the specified endpoint type.
101    ///
102    /// # Production vs Testnet
103    ///
104    /// Production:
105    /// - Public: `wss://ws.bitget.com/v2/ws/public`
106    /// - Private: `wss://ws.bitget.com/v2/ws/private`
107    ///
108    /// Testnet:
109    /// - Public: `wss://ws-testnet.bitget.com/v2/ws/public`
110    /// - Private: `wss://ws-testnet.bitget.com/v2/ws/private`
111    ///
112    /// # Example
113    ///
114    /// ```rust,no_run
115    /// use ccxt_exchanges::bitget::{Bitget, BitgetEndpointRouter};
116    /// use ccxt_core::types::EndpointType;
117    /// use ccxt_core::ExchangeConfig;
118    ///
119    /// let bitget = Bitget::new(ExchangeConfig::default()).unwrap();
120    ///
121    /// // Get WebSocket URL for public market data
122    /// let ws_public = bitget.ws_endpoint(EndpointType::Public);
123    /// assert!(ws_public.contains("/v2/ws/public"));
124    ///
125    /// // Get WebSocket URL for private account data
126    /// let ws_private = bitget.ws_endpoint(EndpointType::Private);
127    /// assert!(ws_private.contains("/v2/ws/private"));
128    /// ```
129    fn ws_endpoint(&self, endpoint_type: EndpointType) -> &str;
130}
131
132use super::Bitget;
133
134impl BitgetEndpointRouter for Bitget {
135    fn rest_endpoint(&self) -> &'static str {
136        // Return static reference based on sandbox mode
137        // This matches the pattern used by other exchanges
138        if self.is_sandbox() {
139            "https://api-testnet.bitget.com"
140        } else {
141            "https://api.bitget.com"
142        }
143    }
144
145    fn ws_endpoint(&self, endpoint_type: EndpointType) -> &str {
146        // Return static reference based on sandbox mode and endpoint type
147        if self.is_sandbox() {
148            match endpoint_type {
149                EndpointType::Public => "wss://ws-testnet.bitget.com/v2/ws/public",
150                EndpointType::Private => "wss://ws-testnet.bitget.com/v2/ws/private",
151            }
152        } else {
153            match endpoint_type {
154                EndpointType::Public => "wss://ws.bitget.com/v2/ws/public",
155                EndpointType::Private => "wss://ws.bitget.com/v2/ws/private",
156            }
157        }
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164    use crate::bitget::BitgetOptions;
165    use ccxt_core::ExchangeConfig;
166
167    fn create_test_bitget() -> Bitget {
168        Bitget::new(ExchangeConfig::default()).unwrap()
169    }
170
171    fn create_sandbox_bitget() -> Bitget {
172        let config = ExchangeConfig {
173            sandbox: true,
174            ..Default::default()
175        };
176        Bitget::new(config).unwrap()
177    }
178
179    // ==================== REST Endpoint Tests ====================
180
181    #[test]
182    fn test_rest_endpoint_production() {
183        let bitget = create_test_bitget();
184        let url = bitget.rest_endpoint();
185        assert_eq!(url, "https://api.bitget.com");
186        assert!(!url.contains("testnet"));
187    }
188
189    #[test]
190    fn test_rest_endpoint_sandbox() {
191        let bitget = create_sandbox_bitget();
192        let url = bitget.rest_endpoint();
193        assert_eq!(url, "https://api-testnet.bitget.com");
194    }
195
196    // ==================== WebSocket Public Endpoint Tests ====================
197
198    #[test]
199    fn test_ws_endpoint_public_production() {
200        let bitget = create_test_bitget();
201        let url = bitget.ws_endpoint(EndpointType::Public);
202        assert_eq!(url, "wss://ws.bitget.com/v2/ws/public");
203        assert!(!url.contains("testnet"));
204    }
205
206    #[test]
207    fn test_ws_endpoint_public_sandbox() {
208        let bitget = create_sandbox_bitget();
209        let url = bitget.ws_endpoint(EndpointType::Public);
210        assert_eq!(url, "wss://ws-testnet.bitget.com/v2/ws/public");
211    }
212
213    // ==================== WebSocket Private Endpoint Tests ====================
214
215    #[test]
216    fn test_ws_endpoint_private_production() {
217        let bitget = create_test_bitget();
218        let url = bitget.ws_endpoint(EndpointType::Private);
219        assert_eq!(url, "wss://ws.bitget.com/v2/ws/private");
220        assert!(!url.contains("testnet"));
221    }
222
223    #[test]
224    fn test_ws_endpoint_private_sandbox() {
225        let bitget = create_sandbox_bitget();
226        let url = bitget.ws_endpoint(EndpointType::Private);
227        assert_eq!(url, "wss://ws-testnet.bitget.com/v2/ws/private");
228    }
229
230    // ==================== Testnet Option Tests ====================
231
232    #[test]
233    fn test_rest_endpoint_with_testnet_option() {
234        let config = ExchangeConfig::default();
235        let options = BitgetOptions {
236            testnet: true,
237            ..Default::default()
238        };
239        let bitget = Bitget::new_with_options(config, options).unwrap();
240
241        let url = bitget.rest_endpoint();
242        assert_eq!(url, "https://api-testnet.bitget.com");
243    }
244
245    #[test]
246    fn test_ws_endpoint_public_with_testnet_option() {
247        let config = ExchangeConfig::default();
248        let options = BitgetOptions {
249            testnet: true,
250            ..Default::default()
251        };
252        let bitget = Bitget::new_with_options(config, options).unwrap();
253
254        let url = bitget.ws_endpoint(EndpointType::Public);
255        assert_eq!(url, "wss://ws-testnet.bitget.com/v2/ws/public");
256    }
257
258    #[test]
259    fn test_ws_endpoint_private_with_testnet_option() {
260        let config = ExchangeConfig::default();
261        let options = BitgetOptions {
262            testnet: true,
263            ..Default::default()
264        };
265        let bitget = Bitget::new_with_options(config, options).unwrap();
266
267        let url = bitget.ws_endpoint(EndpointType::Private);
268        assert_eq!(url, "wss://ws-testnet.bitget.com/v2/ws/private");
269    }
270
271    // ==================== Consistency Tests ====================
272
273    #[test]
274    fn test_rest_endpoint_consistency_with_urls() {
275        let bitget = create_test_bitget();
276        let router_url = bitget.rest_endpoint();
277        let urls_rest = bitget.urls().rest;
278        assert_eq!(router_url, urls_rest);
279    }
280
281    #[test]
282    fn test_ws_public_endpoint_consistency_with_urls() {
283        let bitget = create_test_bitget();
284        let router_url = bitget.ws_endpoint(EndpointType::Public);
285        let urls_ws_public = bitget.urls().ws_public;
286        assert_eq!(router_url, urls_ws_public);
287    }
288
289    #[test]
290    fn test_ws_private_endpoint_consistency_with_urls() {
291        let bitget = create_test_bitget();
292        let router_url = bitget.ws_endpoint(EndpointType::Private);
293        let urls_ws_private = bitget.urls().ws_private;
294        assert_eq!(router_url, urls_ws_private);
295    }
296
297    #[test]
298    fn test_sandbox_rest_endpoint_consistency_with_urls() {
299        let bitget = create_sandbox_bitget();
300        let router_url = bitget.rest_endpoint();
301        let urls_rest = bitget.urls().rest;
302        assert_eq!(router_url, urls_rest);
303    }
304
305    #[test]
306    fn test_sandbox_ws_public_endpoint_consistency_with_urls() {
307        let bitget = create_sandbox_bitget();
308        let router_url = bitget.ws_endpoint(EndpointType::Public);
309        let urls_ws_public = bitget.urls().ws_public;
310        assert_eq!(router_url, urls_ws_public);
311    }
312
313    #[test]
314    fn test_sandbox_ws_private_endpoint_consistency_with_urls() {
315        let bitget = create_sandbox_bitget();
316        let router_url = bitget.ws_endpoint(EndpointType::Private);
317        let urls_ws_private = bitget.urls().ws_private;
318        assert_eq!(router_url, urls_ws_private);
319    }
320
321    // ==================== Endpoint Type Tests ====================
322
323    #[test]
324    fn test_endpoint_type_public_is_public() {
325        assert!(EndpointType::Public.is_public());
326        assert!(!EndpointType::Public.is_private());
327    }
328
329    #[test]
330    fn test_endpoint_type_private_is_private() {
331        assert!(EndpointType::Private.is_private());
332        assert!(!EndpointType::Private.is_public());
333    }
334
335    // ==================== URL Format Tests ====================
336
337    #[test]
338    fn test_rest_endpoint_uses_https() {
339        let bitget = create_test_bitget();
340        let url = bitget.rest_endpoint();
341        assert!(url.starts_with("https://"));
342    }
343
344    #[test]
345    fn test_ws_endpoint_uses_wss() {
346        let bitget = create_test_bitget();
347
348        let ws_public = bitget.ws_endpoint(EndpointType::Public);
349        assert!(ws_public.starts_with("wss://"));
350
351        let ws_private = bitget.ws_endpoint(EndpointType::Private);
352        assert!(ws_private.starts_with("wss://"));
353    }
354
355    #[test]
356    fn test_ws_endpoint_contains_v2_path() {
357        let bitget = create_test_bitget();
358
359        let ws_public = bitget.ws_endpoint(EndpointType::Public);
360        assert!(ws_public.contains("/v2/ws/"));
361
362        let ws_private = bitget.ws_endpoint(EndpointType::Private);
363        assert!(ws_private.contains("/v2/ws/"));
364    }
365}