1use crate::error::ComposioError;
4use crate::retry::RetryPolicy;
5use std::time::Duration;
6
7#[derive(Debug, Clone)]
9pub struct ComposioConfig {
10 pub api_key: String,
11 pub base_url: String,
12 pub timeout: Duration,
13 pub retry_policy: RetryPolicy,
14}
15
16impl ComposioConfig {
17 pub fn new(api_key: impl Into<String>) -> Self {
39 Self {
40 api_key: api_key.into(),
41 base_url: "https://backend.composio.dev/api/v3".to_string(),
42 timeout: Duration::from_secs(30),
43 retry_policy: RetryPolicy::default(),
44 }
45 }
46
47 pub fn validate(&self) -> Result<(), ComposioError> {
67 if self.api_key.is_empty() {
68 return Err(ComposioError::InvalidInput(
69 "API key cannot be empty".to_string(),
70 ));
71 }
72
73 if !self.base_url.starts_with("http") {
74 return Err(ComposioError::ConfigError(
75 "Base URL must start with http:// or https://".to_string(),
76 ));
77 }
78
79 Ok(())
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_new_config_with_defaults() {
89 let config = ComposioConfig::new("test_api_key");
90
91 assert_eq!(config.api_key, "test_api_key");
92 assert_eq!(config.base_url, "https://backend.composio.dev/api/v3");
93 assert_eq!(config.timeout, Duration::from_secs(30));
94 assert_eq!(config.retry_policy.max_retries, 3);
95 assert_eq!(config.retry_policy.initial_delay, Duration::from_secs(1));
96 assert_eq!(config.retry_policy.max_delay, Duration::from_secs(10));
97 }
98
99 #[test]
100 fn test_new_config_accepts_string() {
101 let config = ComposioConfig::new("test_key".to_string());
102 assert_eq!(config.api_key, "test_key");
103 }
104
105 #[test]
106 fn test_new_config_accepts_str() {
107 let config = ComposioConfig::new("test_key");
108 assert_eq!(config.api_key, "test_key");
109 }
110
111 #[test]
112 fn test_validate_valid_config() {
113 let config = ComposioConfig::new("valid_api_key");
114 assert!(config.validate().is_ok());
115 }
116
117 #[test]
118 fn test_validate_empty_api_key() {
119 let config = ComposioConfig::new("");
120 let result = config.validate();
121
122 assert!(result.is_err());
123 match result {
124 Err(ComposioError::InvalidInput(msg)) => {
125 assert_eq!(msg, "API key cannot be empty");
126 }
127 _ => panic!("Expected InvalidInput error"),
128 }
129 }
130
131 #[test]
132 fn test_validate_invalid_base_url() {
133 let mut config = ComposioConfig::new("test_key");
134 config.base_url = "invalid-url".to_string();
135
136 let result = config.validate();
137 assert!(result.is_err());
138 match result {
139 Err(ComposioError::ConfigError(msg)) => {
140 assert_eq!(msg, "Base URL must start with http:// or https://");
141 }
142 _ => panic!("Expected ConfigError"),
143 }
144 }
145
146 #[test]
147 fn test_validate_http_base_url() {
148 let mut config = ComposioConfig::new("test_key");
149 config.base_url = "http://localhost:8080".to_string();
150
151 assert!(config.validate().is_ok());
152 }
153
154 #[test]
155 fn test_validate_https_base_url() {
156 let mut config = ComposioConfig::new("test_key");
157 config.base_url = "https://api.example.com".to_string();
158
159 assert!(config.validate().is_ok());
160 }
161
162 #[test]
163 fn test_config_is_cloneable() {
164 let config = ComposioConfig::new("test_key");
165 let cloned = config.clone();
166
167 assert_eq!(config.api_key, cloned.api_key);
168 assert_eq!(config.base_url, cloned.base_url);
169 assert_eq!(config.timeout, cloned.timeout);
170 }
171
172 #[test]
173 fn test_config_is_debuggable() {
174 let config = ComposioConfig::new("test_key");
175 let debug_str = format!("{:?}", config);
176
177 assert!(debug_str.contains("ComposioConfig"));
178 assert!(debug_str.contains("test_key"));
179 }
180
181 #[test]
182 fn test_default_base_url() {
183 let config = ComposioConfig::new("key");
184 assert_eq!(config.base_url, "https://backend.composio.dev/api/v3");
185 }
186
187 #[test]
188 fn test_default_timeout() {
189 let config = ComposioConfig::new("key");
190 assert_eq!(config.timeout, Duration::from_secs(30));
191 }
192
193 #[test]
194 fn test_default_retry_policy() {
195 let config = ComposioConfig::new("key");
196 assert_eq!(config.retry_policy.max_retries, 3);
197 assert_eq!(config.retry_policy.initial_delay, Duration::from_secs(1));
198 assert_eq!(config.retry_policy.max_delay, Duration::from_secs(10));
199 }
200
201 #[test]
202 fn test_custom_base_url() {
203 let mut config = ComposioConfig::new("key");
204 config.base_url = "https://custom.api.com".to_string();
205
206 assert!(config.validate().is_ok());
207 assert_eq!(config.base_url, "https://custom.api.com");
208 }
209
210 #[test]
211 fn test_custom_timeout() {
212 let mut config = ComposioConfig::new("key");
213 config.timeout = Duration::from_secs(60);
214
215 assert_eq!(config.timeout, Duration::from_secs(60));
216 }
217
218 #[test]
219 fn test_custom_retry_policy() {
220 let mut config = ComposioConfig::new("key");
221 config.retry_policy = RetryPolicy {
222 max_retries: 5,
223 initial_delay: Duration::from_secs(2),
224 max_delay: Duration::from_secs(20),
225 };
226
227 assert_eq!(config.retry_policy.max_retries, 5);
228 assert_eq!(config.retry_policy.initial_delay, Duration::from_secs(2));
229 assert_eq!(config.retry_policy.max_delay, Duration::from_secs(20));
230 }
231}