Skip to main content

subx_cli/config/
builder.rs

1//! Configuration builder for fluent test configuration creation.
2//!
3//! This module provides a fluent API for building test configurations,
4//! making it easy to create specific configuration scenarios for testing.
5
6use crate::config::test_service::TestConfigService;
7use crate::config::{Config, OverflowStrategy};
8
9/// Fluent builder for creating test configurations.
10///
11/// This builder provides a convenient way to create configurations
12/// for testing with specific settings, using method chaining for clarity.
13///
14/// # Examples
15///
16/// ```rust
17/// use subx_cli::config::TestConfigBuilder;
18///
19/// let config = TestConfigBuilder::new()
20///     .with_ai_provider("openai")
21///     .with_ai_model("gpt-4.1")
22///     .with_vad_enabled(true)
23///     .build_config();
24/// ```
25pub struct TestConfigBuilder {
26    config: Config,
27}
28
29impl TestConfigBuilder {
30    /// Create a new configuration builder with default values.
31    pub fn new() -> Self {
32        Self {
33            config: Config::default(),
34        }
35    }
36
37    // AI Configuration Methods
38
39    /// Set the AI provider.
40    ///
41    /// # Arguments
42    ///
43    /// * `provider` - The AI provider name (e.g., "openai", "anthropic")
44    pub fn with_ai_provider(mut self, provider: &str) -> Self {
45        self.config.ai.provider = provider.to_string();
46        self
47    }
48
49    /// Set the AI model.
50    ///
51    /// # Arguments
52    ///
53    /// * `model` - The AI model name (e.g., "gpt-4.1", "claude-3")
54    pub fn with_ai_model(mut self, model: &str) -> Self {
55        self.config.ai.model = model.to_string();
56        self
57    }
58
59    /// Set the AI API key.
60    ///
61    /// # Arguments
62    ///
63    /// * `api_key` - The API key for authentication
64    pub fn with_ai_api_key(mut self, api_key: &str) -> Self {
65        self.config.ai.api_key = Some(api_key.to_string());
66        self
67    }
68
69    /// Set the AI base URL.
70    ///
71    /// # Arguments
72    ///
73    /// * `base_url` - The base URL for the AI service
74    pub fn with_ai_base_url(mut self, base_url: &str) -> Self {
75        self.config.ai.base_url = base_url.to_string();
76        self
77    }
78
79    /// Set the maximum sample length for AI requests.
80    ///
81    /// # Arguments
82    ///
83    /// * `length` - Maximum sample length in characters
84    pub fn with_max_sample_length(mut self, length: usize) -> Self {
85        self.config.ai.max_sample_length = length;
86        self
87    }
88
89    /// Set the AI temperature parameter.
90    ///
91    /// # Arguments
92    ///
93    /// * `temperature` - Temperature value (0.0-1.0)
94    pub fn with_ai_temperature(mut self, temperature: f32) -> Self {
95        self.config.ai.temperature = temperature;
96        self
97    }
98
99    /// Set the AI max tokens parameter.
100    ///
101    /// # Arguments
102    ///
103    /// * `max_tokens` - Maximum tokens in response (1-100000)
104    pub fn with_ai_max_tokens(mut self, max_tokens: u32) -> Self {
105        self.config.ai.max_tokens = max_tokens;
106        self
107    }
108
109    /// Set the AI retry parameters.
110    ///
111    /// # Arguments
112    ///
113    /// * `attempts` - Number of retry attempts
114    /// * `delay_ms` - Delay between retries in milliseconds
115    pub fn with_ai_retry(mut self, attempts: u32, delay_ms: u64) -> Self {
116        self.config.ai.retry_attempts = attempts;
117        self.config.ai.retry_delay_ms = delay_ms;
118        self
119    }
120
121    /// Set the AI request timeout.
122    ///
123    /// # Arguments
124    ///
125    /// * `timeout_seconds` - Request timeout in seconds
126    pub fn with_ai_request_timeout(mut self, timeout_seconds: u64) -> Self {
127        self.config.ai.request_timeout_seconds = timeout_seconds;
128        self
129    }
130
131    // Sync Configuration Methods
132
133    /// Set the synchronization method.
134    ///
135    /// # Arguments
136    ///
137    /// * `method` - The sync method to use ("vad", "auto", "manual")
138    pub fn with_sync_method(mut self, method: &str) -> Self {
139        self.config.sync.default_method = method.to_string();
140        self
141    }
142
143    /// Enable or disable local VAD.
144    ///
145    /// # Arguments
146    ///
147    /// * `enabled` - Whether to enable local VAD processing
148    pub fn with_vad_enabled(mut self, enabled: bool) -> Self {
149        self.config.sync.vad.enabled = enabled;
150        self
151    }
152
153    /// Set VAD sensitivity.
154    ///
155    /// # Arguments
156    ///
157    /// * `sensitivity` - VAD sensitivity value (0.0-1.0)
158    pub fn with_vad_sensitivity(mut self, sensitivity: f32) -> Self {
159        self.config.sync.vad.sensitivity = sensitivity;
160        self
161    }
162
163    // Formats Configuration Methods
164
165    /// Set the default output format.
166    ///
167    /// # Arguments
168    ///
169    /// * `format` - The output format (e.g., "srt", "ass", "vtt")
170    pub fn with_default_output_format(mut self, format: &str) -> Self {
171        self.config.formats.default_output = format.to_string();
172        self
173    }
174
175    /// Enable or disable style preservation.
176    ///
177    /// # Arguments
178    ///
179    /// * `preserve` - Whether to preserve styling
180    pub fn with_preserve_styling(mut self, preserve: bool) -> Self {
181        self.config.formats.preserve_styling = preserve;
182        self
183    }
184
185    /// Set the default encoding.
186    ///
187    /// # Arguments
188    ///
189    /// * `encoding` - The default encoding (e.g., "utf-8", "gbk")
190    pub fn with_default_encoding(mut self, encoding: &str) -> Self {
191        self.config.formats.default_encoding = encoding.to_string();
192        self
193    }
194
195    /// Set the encoding detection confidence threshold.
196    ///
197    /// # Arguments
198    ///
199    /// * `confidence` - Confidence threshold (0.0-1.0)
200    pub fn with_encoding_detection_confidence(mut self, confidence: f32) -> Self {
201        self.config.formats.encoding_detection_confidence = confidence;
202        self
203    }
204
205    // General Configuration Methods
206
207    /// Enable or disable backup.
208    ///
209    /// # Arguments
210    ///
211    /// * `enabled` - Whether to enable backup
212    pub fn with_backup_enabled(mut self, enabled: bool) -> Self {
213        self.config.general.backup_enabled = enabled;
214        self
215    }
216
217    /// Set the maximum number of concurrent jobs.
218    ///
219    /// # Arguments
220    ///
221    /// * `jobs` - Maximum concurrent jobs
222    pub fn with_max_concurrent_jobs(mut self, jobs: usize) -> Self {
223        self.config.general.max_concurrent_jobs = jobs;
224        self
225    }
226
227    /// Set the task timeout.
228    ///
229    /// # Arguments
230    ///
231    /// * `timeout_seconds` - Timeout in seconds
232    pub fn with_task_timeout(mut self, timeout_seconds: u64) -> Self {
233        self.config.general.task_timeout_seconds = timeout_seconds;
234        self
235    }
236
237    /// Enable or disable progress bar.
238    ///
239    /// # Arguments
240    ///
241    /// * `enabled` - Whether to enable progress bar
242    pub fn with_progress_bar(mut self, enabled: bool) -> Self {
243        self.config.general.enable_progress_bar = enabled;
244        self
245    }
246
247    /// Set the worker idle timeout.
248    ///
249    /// # Arguments
250    ///
251    /// * `timeout_seconds` - Idle timeout in seconds
252    pub fn with_worker_idle_timeout(mut self, timeout_seconds: u64) -> Self {
253        self.config.general.worker_idle_timeout_seconds = timeout_seconds;
254        self
255    }
256
257    // Parallel Configuration Methods
258
259    /// Set the task queue size.
260    ///
261    /// # Arguments
262    ///
263    /// * `size` - Queue size
264    pub fn with_task_queue_size(mut self, size: usize) -> Self {
265        self.config.parallel.task_queue_size = size;
266        self
267    }
268
269    /// Enable or disable task priorities.
270    ///
271    /// # Arguments
272    ///
273    /// * `enabled` - Whether to enable task priorities
274    pub fn with_task_priorities(mut self, enabled: bool) -> Self {
275        self.config.parallel.enable_task_priorities = enabled;
276        self
277    }
278
279    /// Enable or disable auto-balancing of workers.
280    ///
281    /// # Arguments
282    ///
283    /// * `enabled` - Whether to enable auto-balancing
284    pub fn with_auto_balance_workers(mut self, enabled: bool) -> Self {
285        self.config.parallel.auto_balance_workers = enabled;
286        self
287    }
288
289    /// Set the queue overflow strategy.
290    ///
291    /// # Arguments
292    ///
293    /// * `strategy` - Overflow strategy
294    pub fn with_queue_overflow_strategy(mut self, strategy: OverflowStrategy) -> Self {
295        self.config.parallel.overflow_strategy = strategy;
296        self
297    }
298
299    /// Set the number of parallel workers and queue size, used for integration testing.
300    pub fn with_parallel_settings(mut self, max_workers: usize, queue_size: usize) -> Self {
301        self.config.general.max_concurrent_jobs = max_workers;
302        self.config.parallel.task_queue_size = queue_size;
303        self
304    }
305
306    // Translation Configuration Methods
307
308    /// Set the translation batch size used for AI translation requests.
309    pub fn with_translation_batch_size(mut self, batch_size: usize) -> Self {
310        self.config.translation.batch_size = batch_size;
311        self
312    }
313
314    /// Set the default target language for the `translate` command.
315    pub fn with_translation_default_target_language(mut self, language: &str) -> Self {
316        self.config.translation.default_target_language = Some(language.to_string());
317        self
318    }
319
320    /// Clear the default target language for the `translate` command.
321    pub fn without_translation_default_target_language(mut self) -> Self {
322        self.config.translation.default_target_language = None;
323        self
324    }
325
326    // Builder Methods
327
328    /// Build a test configuration service with the configured settings.
329    pub fn build_service(self) -> TestConfigService {
330        TestConfigService::new(self.config)
331    }
332
333    /// Build a configuration object with the configured settings.
334    pub fn build_config(self) -> Config {
335        self.config
336    }
337
338    /// Get a reference to the current configuration being built.
339    pub fn config(&self) -> &Config {
340        &self.config
341    }
342
343    /// Get a mutable reference to the current configuration being built.
344    pub fn config_mut(&mut self) -> &mut Config {
345        &mut self.config
346    }
347    /// Configure AI base URL for mock server integration testing.
348    ///
349    /// Sets up the configuration to use a mock AI server for testing purposes.
350    /// This is primarily used in integration tests to avoid making real API calls.
351    ///
352    /// # Arguments
353    ///
354    /// - `mock_url`: The URL of the mock server to use for AI API calls
355    ///
356    /// # Examples
357    ///
358    /// ```rust
359    /// use subx_cli::config::TestConfigBuilder;
360    ///
361    /// let config = TestConfigBuilder::new()
362    ///     .with_mock_ai_server("http://localhost:3000")
363    ///     .build_config();
364    /// ```
365    pub fn with_mock_ai_server(mut self, mock_url: &str) -> Self {
366        self.config.ai.base_url = mock_url.to_string();
367        self.config.ai.api_key = Some("mock-api-key".to_string());
368        self
369    }
370}
371
372impl Default for TestConfigBuilder {
373    fn default() -> Self {
374        Self::new()
375    }
376}
377
378#[cfg(test)]
379mod tests {
380    use super::*;
381    use crate::config::service::ConfigService;
382
383    #[test]
384    fn test_builder_default() {
385        let config = TestConfigBuilder::new().build_config();
386        let default_config = Config::default();
387
388        assert_eq!(config.ai.provider, default_config.ai.provider);
389        assert_eq!(config.ai.model, default_config.ai.model);
390    }
391
392    #[test]
393    fn test_builder_ai_configuration() {
394        let config = TestConfigBuilder::new()
395            .with_ai_provider("anthropic")
396            .with_ai_model("claude-3")
397            .with_ai_api_key("test-key")
398            .with_max_sample_length(5000)
399            .with_ai_temperature(0.7)
400            .build_config();
401
402        assert_eq!(config.ai.provider, "anthropic");
403        assert_eq!(config.ai.model, "claude-3");
404        assert_eq!(config.ai.api_key, Some("test-key".to_string()));
405        assert_eq!(config.ai.max_sample_length, 5000);
406        assert_eq!(config.ai.temperature, 0.7);
407    }
408
409    #[test]
410    fn test_builder_sync_configuration() {
411        let config = TestConfigBuilder::new()
412            .with_sync_method("vad")
413            .with_vad_enabled(true)
414            .with_vad_sensitivity(0.8)
415            .build_config();
416
417        assert_eq!(config.sync.default_method, "vad");
418        assert!(config.sync.vad.enabled);
419        assert_eq!(config.sync.vad.sensitivity, 0.8);
420    }
421
422    #[test]
423    fn test_builder_service_creation() {
424        let service = TestConfigBuilder::new()
425            .with_ai_provider("test-provider")
426            .build_service();
427
428        let config = service.get_config().unwrap();
429        assert_eq!(config.ai.provider, "test-provider");
430    }
431
432    #[test]
433    fn test_builder_chaining() {
434        let config = TestConfigBuilder::new()
435            .with_ai_provider("openai")
436            .with_ai_model("gpt-4.1")
437            .with_sync_method("vad")
438            .with_vad_sensitivity(0.5)
439            .with_max_concurrent_jobs(8)
440            .with_task_queue_size(200)
441            .build_config();
442
443        assert_eq!(config.ai.provider, "openai");
444        assert_eq!(config.ai.model, "gpt-4.1");
445        assert_eq!(config.sync.default_method, "vad");
446        assert_eq!(config.sync.vad.sensitivity, 0.5);
447        assert_eq!(config.general.max_concurrent_jobs, 8);
448        assert_eq!(config.parallel.task_queue_size, 200);
449    }
450
451    #[test]
452    fn test_builder_ai_configuration_openrouter() {
453        let config = TestConfigBuilder::new()
454            .with_ai_provider("openrouter")
455            .with_ai_model("deepseek/deepseek-r1-0528:free")
456            .with_ai_api_key("test-openrouter-key")
457            .build_config();
458        assert_eq!(config.ai.provider, "openrouter");
459        assert_eq!(config.ai.model, "deepseek/deepseek-r1-0528:free");
460        assert_eq!(config.ai.api_key, Some("test-openrouter-key".to_string()));
461    }
462
463    #[test]
464    fn test_builder_default_trait() {
465        let config = TestConfigBuilder::default().build_config();
466        let default_config = Config::default();
467        assert_eq!(config.ai.provider, default_config.ai.provider);
468        assert_eq!(config.ai.model, default_config.ai.model);
469        assert_eq!(
470            config.formats.default_output,
471            default_config.formats.default_output
472        );
473    }
474
475    #[test]
476    fn test_builder_with_ai_base_url() {
477        let config = TestConfigBuilder::new()
478            .with_ai_base_url("https://custom.api.example.com/v2")
479            .build_config();
480        assert_eq!(config.ai.base_url, "https://custom.api.example.com/v2");
481    }
482
483    #[test]
484    fn test_builder_with_ai_max_tokens() {
485        let config = TestConfigBuilder::new()
486            .with_ai_max_tokens(4096)
487            .build_config();
488        assert_eq!(config.ai.max_tokens, 4096);
489    }
490
491    #[test]
492    fn test_builder_with_ai_retry() {
493        let config = TestConfigBuilder::new()
494            .with_ai_retry(5, 2000)
495            .build_config();
496        assert_eq!(config.ai.retry_attempts, 5);
497        assert_eq!(config.ai.retry_delay_ms, 2000);
498    }
499
500    #[test]
501    fn test_builder_with_ai_request_timeout() {
502        let config = TestConfigBuilder::new()
503            .with_ai_request_timeout(60)
504            .build_config();
505        assert_eq!(config.ai.request_timeout_seconds, 60);
506    }
507
508    #[test]
509    fn test_builder_with_default_output_format() {
510        let config = TestConfigBuilder::new()
511            .with_default_output_format("ass")
512            .build_config();
513        assert_eq!(config.formats.default_output, "ass");
514    }
515
516    #[test]
517    fn test_builder_with_preserve_styling_true() {
518        let config = TestConfigBuilder::new()
519            .with_preserve_styling(true)
520            .build_config();
521        assert!(config.formats.preserve_styling);
522    }
523
524    #[test]
525    fn test_builder_with_preserve_styling_false() {
526        let config = TestConfigBuilder::new()
527            .with_preserve_styling(false)
528            .build_config();
529        assert!(!config.formats.preserve_styling);
530    }
531
532    #[test]
533    fn test_builder_with_default_encoding() {
534        let config = TestConfigBuilder::new()
535            .with_default_encoding("gbk")
536            .build_config();
537        assert_eq!(config.formats.default_encoding, "gbk");
538    }
539
540    #[test]
541    fn test_builder_with_encoding_detection_confidence() {
542        let config = TestConfigBuilder::new()
543            .with_encoding_detection_confidence(0.95)
544            .build_config();
545        assert!((config.formats.encoding_detection_confidence - 0.95).abs() < f32::EPSILON);
546    }
547
548    #[test]
549    fn test_builder_with_backup_enabled_true() {
550        let config = TestConfigBuilder::new()
551            .with_backup_enabled(true)
552            .build_config();
553        assert!(config.general.backup_enabled);
554    }
555
556    #[test]
557    fn test_builder_with_backup_enabled_false() {
558        let config = TestConfigBuilder::new()
559            .with_backup_enabled(false)
560            .build_config();
561        assert!(!config.general.backup_enabled);
562    }
563
564    #[test]
565    fn test_builder_with_task_timeout() {
566        let config = TestConfigBuilder::new()
567            .with_task_timeout(600)
568            .build_config();
569        assert_eq!(config.general.task_timeout_seconds, 600);
570    }
571
572    #[test]
573    fn test_builder_with_progress_bar_enabled() {
574        let config = TestConfigBuilder::new()
575            .with_progress_bar(true)
576            .build_config();
577        assert!(config.general.enable_progress_bar);
578    }
579
580    #[test]
581    fn test_builder_with_progress_bar_disabled() {
582        let config = TestConfigBuilder::new()
583            .with_progress_bar(false)
584            .build_config();
585        assert!(!config.general.enable_progress_bar);
586    }
587
588    #[test]
589    fn test_builder_with_worker_idle_timeout() {
590        let config = TestConfigBuilder::new()
591            .with_worker_idle_timeout(120)
592            .build_config();
593        assert_eq!(config.general.worker_idle_timeout_seconds, 120);
594    }
595
596    #[test]
597    fn test_builder_with_task_priorities_enabled() {
598        let config = TestConfigBuilder::new()
599            .with_task_priorities(true)
600            .build_config();
601        assert!(config.parallel.enable_task_priorities);
602    }
603
604    #[test]
605    fn test_builder_with_task_priorities_disabled() {
606        let config = TestConfigBuilder::new()
607            .with_task_priorities(false)
608            .build_config();
609        assert!(!config.parallel.enable_task_priorities);
610    }
611
612    #[test]
613    fn test_builder_with_auto_balance_workers_enabled() {
614        let config = TestConfigBuilder::new()
615            .with_auto_balance_workers(true)
616            .build_config();
617        assert!(config.parallel.auto_balance_workers);
618    }
619
620    #[test]
621    fn test_builder_with_auto_balance_workers_disabled() {
622        let config = TestConfigBuilder::new()
623            .with_auto_balance_workers(false)
624            .build_config();
625        assert!(!config.parallel.auto_balance_workers);
626    }
627
628    #[test]
629    fn test_builder_with_queue_overflow_strategy_block() {
630        let config = TestConfigBuilder::new()
631            .with_queue_overflow_strategy(OverflowStrategy::Block)
632            .build_config();
633        assert_eq!(config.parallel.overflow_strategy, OverflowStrategy::Block);
634    }
635
636    #[test]
637    fn test_builder_with_queue_overflow_strategy_drop() {
638        let config = TestConfigBuilder::new()
639            .with_queue_overflow_strategy(OverflowStrategy::Drop)
640            .build_config();
641        assert_eq!(config.parallel.overflow_strategy, OverflowStrategy::Drop);
642    }
643
644    #[test]
645    fn test_builder_with_queue_overflow_strategy_expand() {
646        let config = TestConfigBuilder::new()
647            .with_queue_overflow_strategy(OverflowStrategy::Expand)
648            .build_config();
649        assert_eq!(config.parallel.overflow_strategy, OverflowStrategy::Expand);
650    }
651
652    #[test]
653    fn test_builder_with_queue_overflow_strategy_drop_oldest() {
654        let config = TestConfigBuilder::new()
655            .with_queue_overflow_strategy(OverflowStrategy::DropOldest)
656            .build_config();
657        assert_eq!(
658            config.parallel.overflow_strategy,
659            OverflowStrategy::DropOldest
660        );
661    }
662
663    #[test]
664    fn test_builder_with_queue_overflow_strategy_reject() {
665        let config = TestConfigBuilder::new()
666            .with_queue_overflow_strategy(OverflowStrategy::Reject)
667            .build_config();
668        assert_eq!(config.parallel.overflow_strategy, OverflowStrategy::Reject);
669    }
670
671    #[test]
672    fn test_builder_with_parallel_settings() {
673        let config = TestConfigBuilder::new()
674            .with_parallel_settings(16, 500)
675            .build_config();
676        assert_eq!(config.general.max_concurrent_jobs, 16);
677        assert_eq!(config.parallel.task_queue_size, 500);
678    }
679
680    #[test]
681    fn test_builder_config_ref() {
682        let builder = TestConfigBuilder::new().with_ai_provider("ref-provider");
683        let config_ref = builder.config();
684        assert_eq!(config_ref.ai.provider, "ref-provider");
685    }
686
687    #[test]
688    fn test_builder_config_mut() {
689        let mut builder = TestConfigBuilder::new();
690        builder.config_mut().ai.provider = "mutated-provider".to_string();
691        let config = builder.build_config();
692        assert_eq!(config.ai.provider, "mutated-provider");
693    }
694
695    #[test]
696    fn test_builder_with_mock_ai_server() {
697        let config = TestConfigBuilder::new()
698            .with_mock_ai_server("http://localhost:8080")
699            .build_config();
700        assert_eq!(config.ai.base_url, "http://localhost:8080");
701        assert_eq!(config.ai.api_key, Some("mock-api-key".to_string()));
702    }
703
704    #[test]
705    fn test_builder_override_preserves_other_fields() {
706        let config = TestConfigBuilder::new()
707            .with_ai_provider("openai")
708            .with_ai_model("gpt-4.1")
709            .with_ai_temperature(0.9)
710            .with_ai_provider("anthropic")
711            .build_config();
712        assert_eq!(config.ai.provider, "anthropic");
713        assert_eq!(config.ai.model, "gpt-4.1");
714        assert!((config.ai.temperature - 0.9).abs() < f32::EPSILON);
715    }
716
717    #[test]
718    fn test_builder_full_ai_configuration() {
719        let config = TestConfigBuilder::new()
720            .with_ai_provider("openai")
721            .with_ai_model("gpt-4.1")
722            .with_ai_api_key("sk-test-key")
723            .with_ai_base_url("https://api.openai.com/v1")
724            .with_max_sample_length(8000)
725            .with_ai_temperature(0.5)
726            .with_ai_max_tokens(2000)
727            .with_ai_retry(2, 500)
728            .with_ai_request_timeout(30)
729            .build_config();
730
731        assert_eq!(config.ai.provider, "openai");
732        assert_eq!(config.ai.model, "gpt-4.1");
733        assert_eq!(config.ai.api_key, Some("sk-test-key".to_string()));
734        assert_eq!(config.ai.base_url, "https://api.openai.com/v1");
735        assert_eq!(config.ai.max_sample_length, 8000);
736        assert!((config.ai.temperature - 0.5).abs() < f32::EPSILON);
737        assert_eq!(config.ai.max_tokens, 2000);
738        assert_eq!(config.ai.retry_attempts, 2);
739        assert_eq!(config.ai.retry_delay_ms, 500);
740        assert_eq!(config.ai.request_timeout_seconds, 30);
741    }
742
743    #[test]
744    fn test_builder_full_formats_configuration() {
745        let config = TestConfigBuilder::new()
746            .with_default_output_format("vtt")
747            .with_preserve_styling(true)
748            .with_default_encoding("utf-16")
749            .with_encoding_detection_confidence(0.75)
750            .build_config();
751
752        assert_eq!(config.formats.default_output, "vtt");
753        assert!(config.formats.preserve_styling);
754        assert_eq!(config.formats.default_encoding, "utf-16");
755        assert!((config.formats.encoding_detection_confidence - 0.75).abs() < f32::EPSILON);
756    }
757
758    #[test]
759    fn test_builder_full_general_configuration() {
760        let config = TestConfigBuilder::new()
761            .with_backup_enabled(true)
762            .with_max_concurrent_jobs(2)
763            .with_task_timeout(120)
764            .with_progress_bar(false)
765            .with_worker_idle_timeout(30)
766            .build_config();
767
768        assert!(config.general.backup_enabled);
769        assert_eq!(config.general.max_concurrent_jobs, 2);
770        assert_eq!(config.general.task_timeout_seconds, 120);
771        assert!(!config.general.enable_progress_bar);
772        assert_eq!(config.general.worker_idle_timeout_seconds, 30);
773    }
774
775    #[test]
776    fn test_builder_full_parallel_configuration() {
777        let config = TestConfigBuilder::new()
778            .with_task_queue_size(256)
779            .with_task_priorities(true)
780            .with_auto_balance_workers(false)
781            .with_queue_overflow_strategy(OverflowStrategy::Expand)
782            .build_config();
783
784        assert_eq!(config.parallel.task_queue_size, 256);
785        assert!(config.parallel.enable_task_priorities);
786        assert!(!config.parallel.auto_balance_workers);
787        assert_eq!(config.parallel.overflow_strategy, OverflowStrategy::Expand);
788    }
789
790    #[test]
791    fn test_builder_vad_disabled() {
792        let config = TestConfigBuilder::new()
793            .with_vad_enabled(false)
794            .build_config();
795        assert!(!config.sync.vad.enabled);
796    }
797
798    #[test]
799    fn test_builder_vad_sensitivity_boundary_zero() {
800        let config = TestConfigBuilder::new()
801            .with_vad_sensitivity(0.0)
802            .build_config();
803        assert!((config.sync.vad.sensitivity - 0.0).abs() < f32::EPSILON);
804    }
805
806    #[test]
807    fn test_builder_vad_sensitivity_boundary_one() {
808        let config = TestConfigBuilder::new()
809            .with_vad_sensitivity(1.0)
810            .build_config();
811        assert!((config.sync.vad.sensitivity - 1.0).abs() < f32::EPSILON);
812    }
813
814    #[test]
815    fn test_builder_service_returns_correct_config() {
816        let service = TestConfigBuilder::new()
817            .with_ai_provider("test-ai")
818            .with_ai_model("test-model")
819            .with_backup_enabled(true)
820            .build_service();
821
822        let config = service.get_config().unwrap();
823        assert_eq!(config.ai.provider, "test-ai");
824        assert_eq!(config.ai.model, "test-model");
825        assert!(config.general.backup_enabled);
826    }
827
828    #[test]
829    fn test_builder_parallel_settings_overrides_individual_methods() {
830        let config = TestConfigBuilder::new()
831            .with_max_concurrent_jobs(4)
832            .with_task_queue_size(100)
833            .with_parallel_settings(8, 200)
834            .build_config();
835        assert_eq!(config.general.max_concurrent_jobs, 8);
836        assert_eq!(config.parallel.task_queue_size, 200);
837    }
838}