subx_cli/config/
test_service.rs

1//! Test configuration service for isolated testing.
2//!
3//! This module provides a configuration service implementation specifically
4//! designed for testing environments, offering complete isolation and
5//! predictable configuration states.
6
7use crate::config::service::ConfigService;
8use crate::{Result, config::Config};
9
10/// Test configuration service implementation.
11///
12/// This service provides a fixed configuration for testing purposes,
13/// ensuring complete isolation between tests and predictable behavior.
14/// It does not load from external sources or cache.
15pub struct TestConfigService {
16    fixed_config: Config,
17}
18
19impl TestConfigService {
20    /// Create a new test configuration service with the provided configuration.
21    ///
22    /// # Arguments
23    ///
24    /// * `config` - The fixed configuration to use
25    pub fn new(config: Config) -> Self {
26        Self {
27            fixed_config: config,
28        }
29    }
30
31    /// Create a test configuration service with default settings.
32    ///
33    /// This is useful for tests that don't need specific configuration values.
34    pub fn with_defaults() -> Self {
35        Self::new(Config::default())
36    }
37
38    /// Create a test configuration service with specific AI settings.
39    ///
40    /// # Arguments
41    ///
42    /// * `provider` - AI provider name
43    /// * `model` - AI model name
44    pub fn with_ai_settings(provider: &str, model: &str) -> Self {
45        let mut config = Config::default();
46        config.ai.provider = provider.to_string();
47        config.ai.model = model.to_string();
48        Self::new(config)
49    }
50
51    /// Create a test configuration service with specific AI settings including API key.
52    ///
53    /// # Arguments
54    ///
55    /// * `provider` - AI provider name
56    /// * `model` - AI model name
57    /// * `api_key` - API key for the provider
58    pub fn with_ai_settings_and_key(provider: &str, model: &str, api_key: &str) -> Self {
59        let mut config = Config::default();
60        config.ai.provider = provider.to_string();
61        config.ai.model = model.to_string();
62        config.ai.api_key = Some(api_key.to_string());
63        Self::new(config)
64    }
65
66    /// Create a test configuration service with specific sync settings.
67    ///
68    /// # Arguments
69    ///
70    /// * `correlation_threshold` - Correlation threshold for synchronization
71    /// * `max_offset` - Maximum time offset in seconds
72    pub fn with_sync_settings(correlation_threshold: f32, max_offset: f32) -> Self {
73        let mut config = Config::default();
74        config.sync.correlation_threshold = correlation_threshold;
75        config.sync.max_offset_seconds = max_offset;
76        Self::new(config)
77    }
78
79    /// Create a test configuration service with specific parallel processing settings.
80    ///
81    /// # Arguments
82    ///
83    /// * `max_workers` - Maximum number of parallel workers
84    /// * `queue_size` - Task queue size
85    pub fn with_parallel_settings(max_workers: usize, queue_size: usize) -> Self {
86        let mut config = Config::default();
87        config.general.max_concurrent_jobs = max_workers;
88        config.parallel.task_queue_size = queue_size;
89        Self::new(config)
90    }
91
92    /// Get the underlying configuration.
93    ///
94    /// This is useful for tests that need direct access to the configuration object.
95    pub fn config(&self) -> &Config {
96        &self.fixed_config
97    }
98
99    /// Get a mutable reference to the underlying configuration.
100    ///
101    /// This allows tests to modify the configuration after creation.
102    pub fn config_mut(&mut self) -> &mut Config {
103        &mut self.fixed_config
104    }
105}
106
107impl ConfigService for TestConfigService {
108    fn get_config(&self) -> Result<Config> {
109        Ok(self.fixed_config.clone())
110    }
111
112    fn reload(&self) -> Result<()> {
113        // Test configuration doesn't need reloading since it's fixed
114        Ok(())
115    }
116}
117
118impl Default for TestConfigService {
119    fn default() -> Self {
120        Self::with_defaults()
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_config_service_with_defaults() {
130        let service = TestConfigService::with_defaults();
131        let config = service.get_config().unwrap();
132
133        assert_eq!(config.ai.provider, "openai");
134        assert_eq!(config.ai.model, "gpt-4o-mini");
135    }
136
137    #[test]
138    fn test_config_service_with_ai_settings() {
139        let service = TestConfigService::with_ai_settings("anthropic", "claude-3");
140        let config = service.get_config().unwrap();
141
142        assert_eq!(config.ai.provider, "anthropic");
143        assert_eq!(config.ai.model, "claude-3");
144    }
145
146    #[test]
147    fn test_config_service_with_ai_settings_and_key() {
148        let service =
149            TestConfigService::with_ai_settings_and_key("openai", "gpt-4", "test-api-key");
150        let config = service.get_config().unwrap();
151
152        assert_eq!(config.ai.provider, "openai");
153        assert_eq!(config.ai.model, "gpt-4");
154        assert_eq!(config.ai.api_key, Some("test-api-key".to_string()));
155    }
156
157    #[test]
158    fn test_config_service_with_sync_settings() {
159        let service = TestConfigService::with_sync_settings(0.8, 45.0);
160        let config = service.get_config().unwrap();
161
162        assert_eq!(config.sync.correlation_threshold, 0.8);
163        assert_eq!(config.sync.max_offset_seconds, 45.0);
164    }
165
166    #[test]
167    fn test_config_service_with_parallel_settings() {
168        let service = TestConfigService::with_parallel_settings(8, 200);
169        let config = service.get_config().unwrap();
170
171        assert_eq!(config.general.max_concurrent_jobs, 8);
172        assert_eq!(config.parallel.task_queue_size, 200);
173    }
174
175    #[test]
176    fn test_config_service_reload() {
177        let service = TestConfigService::with_defaults();
178
179        // Reload should always succeed for test service
180        assert!(service.reload().is_ok());
181    }
182
183    #[test]
184    fn test_config_service_direct_access() {
185        let mut service = TestConfigService::with_defaults();
186
187        // Test direct read access
188        assert_eq!(service.config().ai.provider, "openai");
189
190        // Test mutable access
191        service.config_mut().ai.provider = "modified".to_string();
192        assert_eq!(service.config().ai.provider, "modified");
193
194        let config = service.get_config().unwrap();
195        assert_eq!(config.ai.provider, "modified");
196    }
197}