Skip to main content

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    #![allow(clippy::disallowed_methods)]
164    use super::*;
165    use crate::bitget::BitgetOptions;
166    use ccxt_core::ExchangeConfig;
167
168    fn create_test_bitget() -> Bitget {
169        Bitget::new(ExchangeConfig::default()).unwrap()
170    }
171
172    fn create_sandbox_bitget() -> Bitget {
173        let config = ExchangeConfig {
174            sandbox: true,
175            ..Default::default()
176        };
177        Bitget::new(config).unwrap()
178    }
179
180    // ==================== REST Endpoint Tests ====================
181
182    #[test]
183    fn test_rest_endpoint_production() {
184        let bitget = create_test_bitget();
185        let url = bitget.rest_endpoint();
186        assert_eq!(url, "https://api.bitget.com");
187        assert!(!url.contains("testnet"));
188    }
189
190    #[test]
191    fn test_rest_endpoint_sandbox() {
192        let bitget = create_sandbox_bitget();
193        let url = bitget.rest_endpoint();
194        assert_eq!(url, "https://api-testnet.bitget.com");
195    }
196
197    // ==================== WebSocket Public Endpoint Tests ====================
198
199    #[test]
200    fn test_ws_endpoint_public_production() {
201        let bitget = create_test_bitget();
202        let url = bitget.ws_endpoint(EndpointType::Public);
203        assert_eq!(url, "wss://ws.bitget.com/v2/ws/public");
204        assert!(!url.contains("testnet"));
205    }
206
207    #[test]
208    fn test_ws_endpoint_public_sandbox() {
209        let bitget = create_sandbox_bitget();
210        let url = bitget.ws_endpoint(EndpointType::Public);
211        assert_eq!(url, "wss://ws-testnet.bitget.com/v2/ws/public");
212    }
213
214    // ==================== WebSocket Private Endpoint Tests ====================
215
216    #[test]
217    fn test_ws_endpoint_private_production() {
218        let bitget = create_test_bitget();
219        let url = bitget.ws_endpoint(EndpointType::Private);
220        assert_eq!(url, "wss://ws.bitget.com/v2/ws/private");
221        assert!(!url.contains("testnet"));
222    }
223
224    #[test]
225    fn test_ws_endpoint_private_sandbox() {
226        let bitget = create_sandbox_bitget();
227        let url = bitget.ws_endpoint(EndpointType::Private);
228        assert_eq!(url, "wss://ws-testnet.bitget.com/v2/ws/private");
229    }
230
231    // ==================== Testnet Option Tests ====================
232
233    #[test]
234    fn test_rest_endpoint_with_testnet_option() {
235        let config = ExchangeConfig::default();
236        let options = BitgetOptions {
237            testnet: true,
238            ..Default::default()
239        };
240        let bitget = Bitget::new_with_options(config, options).unwrap();
241
242        let url = bitget.rest_endpoint();
243        assert_eq!(url, "https://api-testnet.bitget.com");
244    }
245
246    #[test]
247    fn test_ws_endpoint_public_with_testnet_option() {
248        let config = ExchangeConfig::default();
249        let options = BitgetOptions {
250            testnet: true,
251            ..Default::default()
252        };
253        let bitget = Bitget::new_with_options(config, options).unwrap();
254
255        let url = bitget.ws_endpoint(EndpointType::Public);
256        assert_eq!(url, "wss://ws-testnet.bitget.com/v2/ws/public");
257    }
258
259    #[test]
260    fn test_ws_endpoint_private_with_testnet_option() {
261        let config = ExchangeConfig::default();
262        let options = BitgetOptions {
263            testnet: true,
264            ..Default::default()
265        };
266        let bitget = Bitget::new_with_options(config, options).unwrap();
267
268        let url = bitget.ws_endpoint(EndpointType::Private);
269        assert_eq!(url, "wss://ws-testnet.bitget.com/v2/ws/private");
270    }
271
272    // ==================== Consistency Tests ====================
273
274    #[test]
275    fn test_rest_endpoint_consistency_with_urls() {
276        let bitget = create_test_bitget();
277        let router_url = bitget.rest_endpoint();
278        let urls_rest = bitget.urls().rest;
279        assert_eq!(router_url, urls_rest);
280    }
281
282    #[test]
283    fn test_ws_public_endpoint_consistency_with_urls() {
284        let bitget = create_test_bitget();
285        let router_url = bitget.ws_endpoint(EndpointType::Public);
286        let urls_ws_public = bitget.urls().ws_public;
287        assert_eq!(router_url, urls_ws_public);
288    }
289
290    #[test]
291    fn test_ws_private_endpoint_consistency_with_urls() {
292        let bitget = create_test_bitget();
293        let router_url = bitget.ws_endpoint(EndpointType::Private);
294        let urls_ws_private = bitget.urls().ws_private;
295        assert_eq!(router_url, urls_ws_private);
296    }
297
298    #[test]
299    fn test_sandbox_rest_endpoint_consistency_with_urls() {
300        let bitget = create_sandbox_bitget();
301        let router_url = bitget.rest_endpoint();
302        let urls_rest = bitget.urls().rest;
303        assert_eq!(router_url, urls_rest);
304    }
305
306    #[test]
307    fn test_sandbox_ws_public_endpoint_consistency_with_urls() {
308        let bitget = create_sandbox_bitget();
309        let router_url = bitget.ws_endpoint(EndpointType::Public);
310        let urls_ws_public = bitget.urls().ws_public;
311        assert_eq!(router_url, urls_ws_public);
312    }
313
314    #[test]
315    fn test_sandbox_ws_private_endpoint_consistency_with_urls() {
316        let bitget = create_sandbox_bitget();
317        let router_url = bitget.ws_endpoint(EndpointType::Private);
318        let urls_ws_private = bitget.urls().ws_private;
319        assert_eq!(router_url, urls_ws_private);
320    }
321
322    // ==================== Endpoint Type Tests ====================
323
324    #[test]
325    fn test_endpoint_type_public_is_public() {
326        assert!(EndpointType::Public.is_public());
327        assert!(!EndpointType::Public.is_private());
328    }
329
330    #[test]
331    fn test_endpoint_type_private_is_private() {
332        assert!(EndpointType::Private.is_private());
333        assert!(!EndpointType::Private.is_public());
334    }
335
336    // ==================== URL Format Tests ====================
337
338    #[test]
339    fn test_rest_endpoint_uses_https() {
340        let bitget = create_test_bitget();
341        let url = bitget.rest_endpoint();
342        assert!(url.starts_with("https://"));
343    }
344
345    #[test]
346    fn test_ws_endpoint_uses_wss() {
347        let bitget = create_test_bitget();
348
349        let ws_public = bitget.ws_endpoint(EndpointType::Public);
350        assert!(ws_public.starts_with("wss://"));
351
352        let ws_private = bitget.ws_endpoint(EndpointType::Private);
353        assert!(ws_private.starts_with("wss://"));
354    }
355
356    #[test]
357    fn test_ws_endpoint_contains_v2_path() {
358        let bitget = create_test_bitget();
359
360        let ws_public = bitget.ws_endpoint(EndpointType::Public);
361        assert!(ws_public.contains("/v2/ws/"));
362
363        let ws_private = bitget.ws_endpoint(EndpointType::Private);
364        assert!(ws_private.contains("/v2/ws/"));
365    }
366}