hivehub_internal_sdk/
config.rs

1use crate::error::HiveHubCloudError;
2use std::time::Duration;
3
4/// Client configuration
5#[derive(Debug, Clone)]
6pub struct ClientConfig {
7    /// Service API key for authentication
8    pub api_key: String,
9
10    /// Base URL of the HiveHub.Cloud API
11    pub base_url: String,
12
13    /// Request timeout
14    pub timeout: Duration,
15
16    /// Number of retry attempts
17    pub retries: u32,
18
19    /// Delay between retries
20    pub retry_delay: Duration,
21}
22
23impl ClientConfig {
24    /// Create new configuration
25    pub fn new(api_key: String, base_url: String) -> Self {
26        Self {
27            api_key,
28            base_url,
29            timeout: Duration::from_secs(30),
30            retries: 3,
31            retry_delay: Duration::from_millis(1000),
32        }
33    }
34
35    /// Create configuration from environment variables
36    pub fn from_env() -> Result<Self, HiveHubCloudError> {
37        let api_key = std::env::var("HIVEHUB_CLOUD_SERVICE_API_KEY").map_err(|_| {
38            HiveHubCloudError::Configuration(
39                "HIVEHUB_CLOUD_SERVICE_API_KEY environment variable not set".to_string(),
40            )
41        })?;
42
43        let base_url = std::env::var("HIVEHUB_CLOUD_BASE_URL")
44            .unwrap_or_else(|_| "http://localhost:12000".to_string());
45
46        let timeout = std::env::var("HIVEHUB_CLOUD_TIMEOUT")
47            .ok()
48            .and_then(|v| v.parse().ok())
49            .unwrap_or(30);
50
51        let retries = std::env::var("HIVEHUB_CLOUD_RETRIES")
52            .ok()
53            .and_then(|v| v.parse().ok())
54            .unwrap_or(3);
55
56        Ok(Self {
57            api_key,
58            base_url,
59            timeout: Duration::from_secs(timeout),
60            retries,
61            retry_delay: Duration::from_millis(1000),
62        })
63    }
64}
65
66impl Default for ClientConfig {
67    fn default() -> Self {
68        Self {
69            api_key: String::new(),
70            base_url: "http://localhost:12000".to_string(),
71            timeout: Duration::from_secs(30),
72            retries: 3,
73            retry_delay: Duration::from_millis(1000),
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use std::sync::Mutex;
82
83    // Mutex to ensure env var tests run sequentially
84    static ENV_MUTEX: Mutex<()> = Mutex::new(());
85
86    #[test]
87    fn test_config_new() {
88        let config = ClientConfig::new("test_key".to_string(), "http://example.com".to_string());
89        assert_eq!(config.api_key, "test_key");
90        assert_eq!(config.base_url, "http://example.com");
91        assert_eq!(config.timeout, Duration::from_secs(30));
92        assert_eq!(config.retries, 3);
93        assert_eq!(config.retry_delay, Duration::from_millis(1000));
94    }
95
96    #[test]
97    fn test_config_default() {
98        let config = ClientConfig::default();
99        assert_eq!(config.api_key, "");
100        assert_eq!(config.base_url, "http://localhost:12000");
101        assert_eq!(config.timeout, Duration::from_secs(30));
102        assert_eq!(config.retries, 3);
103        assert_eq!(config.retry_delay, Duration::from_millis(1000));
104    }
105
106    #[test]
107    fn test_config_from_env_missing_key() {
108        let _guard = ENV_MUTEX.lock().unwrap();
109        // SAFETY: Single-threaded test context protected by mutex
110        unsafe {
111            std::env::remove_var("HIVEHUB_CLOUD_SERVICE_API_KEY");
112        }
113        let result = ClientConfig::from_env();
114        assert!(result.is_err());
115        match result.unwrap_err() {
116            HiveHubCloudError::Configuration(msg) => {
117                assert!(msg.contains("HIVEHUB_CLOUD_SERVICE_API_KEY"));
118            }
119            _ => panic!("Expected Configuration error"),
120        }
121    }
122
123    #[test]
124    fn test_config_from_env_with_all_vars() {
125        let _guard = ENV_MUTEX.lock().unwrap();
126        // SAFETY: Single-threaded test context protected by mutex
127        unsafe {
128            std::env::set_var("HIVEHUB_CLOUD_SERVICE_API_KEY", "test_api_key_all");
129            std::env::set_var("HIVEHUB_CLOUD_BASE_URL", "http://custom.url");
130            std::env::set_var("HIVEHUB_CLOUD_TIMEOUT", "60");
131            std::env::set_var("HIVEHUB_CLOUD_RETRIES", "5");
132        }
133
134        let config = ClientConfig::from_env().unwrap();
135        assert_eq!(config.api_key, "test_api_key_all");
136        assert_eq!(config.base_url, "http://custom.url");
137        assert_eq!(config.timeout, Duration::from_secs(60));
138        assert_eq!(config.retries, 5);
139
140        // Clean up
141        unsafe {
142            std::env::remove_var("HIVEHUB_CLOUD_SERVICE_API_KEY");
143            std::env::remove_var("HIVEHUB_CLOUD_BASE_URL");
144            std::env::remove_var("HIVEHUB_CLOUD_TIMEOUT");
145            std::env::remove_var("HIVEHUB_CLOUD_RETRIES");
146        }
147    }
148
149    #[test]
150    fn test_config_from_env_with_defaults() {
151        let _guard = ENV_MUTEX.lock().unwrap();
152        // SAFETY: Single-threaded test context protected by mutex
153        unsafe {
154            std::env::set_var("HIVEHUB_CLOUD_SERVICE_API_KEY", "default_test_key");
155            std::env::remove_var("HIVEHUB_CLOUD_BASE_URL");
156            std::env::remove_var("HIVEHUB_CLOUD_TIMEOUT");
157            std::env::remove_var("HIVEHUB_CLOUD_RETRIES");
158        }
159
160        let config = ClientConfig::from_env().unwrap();
161        assert_eq!(config.api_key, "default_test_key");
162        assert_eq!(config.base_url, "http://localhost:12000");
163        assert_eq!(config.timeout, Duration::from_secs(30));
164        assert_eq!(config.retries, 3);
165
166        // Clean up
167        unsafe {
168            std::env::remove_var("HIVEHUB_CLOUD_SERVICE_API_KEY");
169        }
170    }
171
172    #[test]
173    fn test_config_from_env_with_invalid_timeout() {
174        let _guard = ENV_MUTEX.lock().unwrap();
175        // SAFETY: Single-threaded test context protected by mutex
176        unsafe {
177            std::env::set_var("HIVEHUB_CLOUD_SERVICE_API_KEY", "test_key_timeout");
178            std::env::set_var("HIVEHUB_CLOUD_TIMEOUT", "invalid");
179            std::env::remove_var("HIVEHUB_CLOUD_BASE_URL");
180            std::env::remove_var("HIVEHUB_CLOUD_RETRIES");
181        }
182
183        let config = ClientConfig::from_env().unwrap();
184        // Invalid timeout should fall back to default
185        assert_eq!(config.timeout, Duration::from_secs(30));
186
187        // Clean up
188        unsafe {
189            std::env::remove_var("HIVEHUB_CLOUD_SERVICE_API_KEY");
190            std::env::remove_var("HIVEHUB_CLOUD_TIMEOUT");
191        }
192    }
193
194    #[test]
195    fn test_config_from_env_with_invalid_retries() {
196        let _guard = ENV_MUTEX.lock().unwrap();
197        // SAFETY: Single-threaded test context protected by mutex
198        unsafe {
199            std::env::set_var("HIVEHUB_CLOUD_SERVICE_API_KEY", "test_key_retries");
200            std::env::set_var("HIVEHUB_CLOUD_RETRIES", "not_a_number");
201            std::env::remove_var("HIVEHUB_CLOUD_BASE_URL");
202            std::env::remove_var("HIVEHUB_CLOUD_TIMEOUT");
203        }
204
205        let config = ClientConfig::from_env().unwrap();
206        // Invalid retries should fall back to default
207        assert_eq!(config.retries, 3);
208
209        // Clean up
210        unsafe {
211            std::env::remove_var("HIVEHUB_CLOUD_SERVICE_API_KEY");
212            std::env::remove_var("HIVEHUB_CLOUD_RETRIES");
213        }
214    }
215
216    #[test]
217    fn test_config_clone() {
218        let config = ClientConfig::new("key".to_string(), "url".to_string());
219        let cloned = config.clone();
220        assert_eq!(config.api_key, cloned.api_key);
221        assert_eq!(config.base_url, cloned.base_url);
222    }
223
224    #[test]
225    fn test_config_debug() {
226        let config = ClientConfig::new("key".to_string(), "url".to_string());
227        let debug_str = format!("{config:?}");
228        assert!(debug_str.contains("ClientConfig"));
229        assert!(debug_str.contains("key"));
230    }
231}