1use std::time::Duration;
2
3#[derive(Debug, Clone)]
49pub struct NonceConfig {
50 pub db_path: String,
53 pub cache_size_kb: i32,
55 pub wal_mode: bool,
57 pub sync_mode: String,
59 pub temp_store: String,
61
62 pub cleanup_batch_size: usize,
65 pub cleanup_optimize_threshold: usize,
67
68 pub default_ttl: Duration,
71 pub time_window: Duration,
73}
74
75impl Default for NonceConfig {
76 fn default() -> Self {
77 Self {
78 db_path: std::env::var("NONCE_AUTH_DB_PATH")
80 .unwrap_or_else(|_| "nonce_auth.db".to_string()),
81 cache_size_kb: std::env::var("NONCE_AUTH_CACHE_SIZE")
82 .ok()
83 .and_then(|s| s.parse().ok())
84 .unwrap_or(2048),
85 wal_mode: std::env::var("NONCE_AUTH_WAL_MODE")
86 .map(|s| s.to_lowercase() != "false")
87 .unwrap_or(true),
88 sync_mode: std::env::var("NONCE_AUTH_SYNC_MODE")
89 .unwrap_or_else(|_| "NORMAL".to_string()),
90 temp_store: std::env::var("NONCE_AUTH_TEMP_STORE")
91 .unwrap_or_else(|_| "MEMORY".to_string()),
92
93 cleanup_batch_size: std::env::var("NONCE_AUTH_CLEANUP_BATCH_SIZE")
95 .ok()
96 .and_then(|s| s.parse().ok())
97 .unwrap_or(1000),
98 cleanup_optimize_threshold: std::env::var("NONCE_AUTH_CLEANUP_THRESHOLD")
99 .ok()
100 .and_then(|s| s.parse().ok())
101 .unwrap_or(100),
102
103 default_ttl: Duration::from_secs(
105 std::env::var("NONCE_AUTH_DEFAULT_TTL")
106 .ok()
107 .and_then(|s| s.parse().ok())
108 .unwrap_or(300),
109 ),
110 time_window: Duration::from_secs(
111 std::env::var("NONCE_AUTH_DEFAULT_TIME_WINDOW")
112 .ok()
113 .and_then(|s| s.parse().ok())
114 .unwrap_or(60),
115 ),
116 }
117 }
118}
119
120impl NonceConfig {
121 pub fn from_env() -> Self {
144 let preset =
146 std::env::var("NONCE_AUTH_PRESET").unwrap_or_else(|_| "production".to_string());
147
148 let mut config = match preset.to_lowercase().as_str() {
149 "development" => Self::development(),
150 "high_performance" => Self::high_performance(),
151 _ => Self::production(), };
153
154 if let Ok(db_path) = std::env::var("NONCE_AUTH_DB_PATH") {
156 config.db_path = db_path;
157 }
158
159 if let Some(size) = std::env::var("NONCE_AUTH_CACHE_SIZE")
160 .ok()
161 .and_then(|s| s.parse().ok())
162 {
163 config.cache_size_kb = size;
164 }
165
166 if let Ok(wal_mode) = std::env::var("NONCE_AUTH_WAL_MODE") {
167 config.wal_mode = wal_mode.to_lowercase() != "false";
168 }
169
170 if let Ok(sync_mode) = std::env::var("NONCE_AUTH_SYNC_MODE") {
171 config.sync_mode = sync_mode;
172 }
173
174 if let Ok(temp_store) = std::env::var("NONCE_AUTH_TEMP_STORE") {
175 config.temp_store = temp_store;
176 }
177
178 if let Some(size) = std::env::var("NONCE_AUTH_CLEANUP_BATCH_SIZE")
179 .ok()
180 .and_then(|s| s.parse().ok())
181 {
182 config.cleanup_batch_size = size;
183 }
184
185 if let Some(thresh) = std::env::var("NONCE_AUTH_CLEANUP_THRESHOLD")
186 .ok()
187 .and_then(|s| s.parse().ok())
188 {
189 config.cleanup_optimize_threshold = thresh;
190 }
191
192 if let Some(secs) = std::env::var("NONCE_AUTH_DEFAULT_TTL")
193 .ok()
194 .and_then(|s| s.parse().ok())
195 {
196 config.default_ttl = Duration::from_secs(secs);
197 }
198
199 if let Some(secs) = std::env::var("NONCE_AUTH_DEFAULT_TIME_WINDOW")
200 .ok()
201 .and_then(|s| s.parse().ok())
202 {
203 config.time_window = Duration::from_secs(secs);
204 }
205
206 config
207 }
208
209 pub fn update_from_env(mut self) -> Self {
227 if let Ok(db_path) = std::env::var("NONCE_AUTH_DB_PATH") {
229 self.db_path = db_path;
230 }
231
232 if let Some(size) = std::env::var("NONCE_AUTH_CACHE_SIZE")
233 .ok()
234 .and_then(|s| s.parse().ok())
235 {
236 self.cache_size_kb = size;
237 }
238
239 if let Ok(wal_mode) = std::env::var("NONCE_AUTH_WAL_MODE") {
240 self.wal_mode = wal_mode.to_lowercase() != "false";
241 }
242
243 if let Ok(sync_mode) = std::env::var("NONCE_AUTH_SYNC_MODE") {
244 self.sync_mode = sync_mode;
245 }
246
247 if let Ok(temp_store) = std::env::var("NONCE_AUTH_TEMP_STORE") {
248 self.temp_store = temp_store;
249 }
250
251 if let Some(size) = std::env::var("NONCE_AUTH_CLEANUP_BATCH_SIZE")
252 .ok()
253 .and_then(|s| s.parse().ok())
254 {
255 self.cleanup_batch_size = size;
256 }
257
258 if let Some(thresh) = std::env::var("NONCE_AUTH_CLEANUP_THRESHOLD")
259 .ok()
260 .and_then(|s| s.parse().ok())
261 {
262 self.cleanup_optimize_threshold = thresh;
263 }
264
265 if let Some(secs) = std::env::var("NONCE_AUTH_DEFAULT_TTL")
266 .ok()
267 .and_then(|s| s.parse().ok())
268 {
269 self.default_ttl = Duration::from_secs(secs);
270 }
271
272 if let Some(secs) = std::env::var("NONCE_AUTH_DEFAULT_TIME_WINDOW")
273 .ok()
274 .and_then(|s| s.parse().ok())
275 {
276 self.time_window = Duration::from_secs(secs);
277 }
278
279 self
280 }
281
282 pub fn production() -> Self {
303 Self {
304 db_path: "nonce_auth.db".to_string(),
305 cache_size_kb: 8192, wal_mode: true,
307 sync_mode: "NORMAL".to_string(),
308 temp_store: "MEMORY".to_string(),
309 cleanup_batch_size: 2000,
310 cleanup_optimize_threshold: 500,
311 default_ttl: Duration::from_secs(300), time_window: Duration::from_secs(60), }
314 }
315
316 pub fn development() -> Self {
337 Self {
338 db_path: ":memory:".to_string(),
339 cache_size_kb: 512, wal_mode: false, sync_mode: "OFF".to_string(), temp_store: "MEMORY".to_string(),
343 cleanup_batch_size: 100,
344 cleanup_optimize_threshold: 50,
345 default_ttl: Duration::from_secs(60), time_window: Duration::from_secs(300), }
348 }
349
350 pub fn high_performance() -> Self {
371 Self {
372 db_path: "nonce_auth.db".to_string(),
373 cache_size_kb: 16384, wal_mode: true,
375 sync_mode: "NORMAL".to_string(),
376 temp_store: "MEMORY".to_string(),
377 cleanup_batch_size: 5000,
378 cleanup_optimize_threshold: 1000,
379 default_ttl: Duration::from_secs(300),
380 time_window: Duration::from_secs(60),
381 }
382 }
383
384 pub fn validate(&self) -> Vec<String> {
407 let mut issues = Vec::new();
408
409 if self.cache_size_kb < 64 {
411 issues.push(
412 "Cache size is very small, consider increasing for better performance".to_string(),
413 );
414 }
415 if self.cache_size_kb > 32768 {
416 issues.push("Cache size is very large, may consume excessive memory".to_string());
417 }
418
419 if !["OFF", "NORMAL", "FULL"].contains(&self.sync_mode.as_str()) {
421 issues.push(format!(
422 "Invalid sync_mode '{}', must be OFF, NORMAL, or FULL",
423 self.sync_mode
424 ));
425 }
426
427 if !["MEMORY", "FILE"].contains(&self.temp_store.as_str()) {
429 issues.push(format!(
430 "Invalid temp_store '{}', must be MEMORY or FILE",
431 self.temp_store
432 ));
433 }
434
435 if self.default_ttl.as_secs() < 30 {
437 issues
438 .push("Default TTL is very short, may cause frequent cleanup overhead".to_string());
439 }
440 if self.default_ttl.as_secs() > 86400 {
441 issues.push("Default TTL is very long, may cause database bloat".to_string());
442 }
443
444 if self.time_window.as_secs() < 10 {
446 issues.push(
447 "Time window is very short, may cause legitimate requests to be rejected"
448 .to_string(),
449 );
450 }
451 if self.time_window.as_secs() > 3600 {
452 issues.push(
453 "Time window is very long, may reduce security against replay attacks".to_string(),
454 );
455 }
456
457 if self.cleanup_batch_size < 10 {
459 issues
460 .push("Cleanup batch size is very small, may cause performance issues".to_string());
461 }
462 if self.cleanup_batch_size > 10000 {
463 issues.push(
464 "Cleanup batch size is very large, may cause long-running transactions".to_string(),
465 );
466 }
467
468 issues
469 }
470
471 pub fn summary(&self) -> String {
489 format!(
490 "Nonce Authentication Configuration:
491Database:
492 Path: {}
493 Cache Size: {} KB
494 WAL Mode: {}
495 Sync Mode: {}
496 Temp Store: {}
497
498Performance:
499 Cleanup Batch Size: {}
500 Optimize Threshold: {}
501
502Security:
503 Default TTL: {} seconds
504 Time Window: {} seconds",
505 self.db_path,
506 self.cache_size_kb,
507 self.wal_mode,
508 self.sync_mode,
509 self.temp_store,
510 self.cleanup_batch_size,
511 self.cleanup_optimize_threshold,
512 self.default_ttl.as_secs(),
513 self.time_window.as_secs()
514 )
515 }
516}
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521 use serial_test::serial;
522 use std::env;
523
524 fn clear_env_vars() {
526 let vars = [
527 "NONCE_AUTH_DB_PATH",
528 "NONCE_AUTH_CACHE_SIZE",
529 "NONCE_AUTH_WAL_MODE",
530 "NONCE_AUTH_SYNC_MODE",
531 "NONCE_AUTH_TEMP_STORE",
532 "NONCE_AUTH_CLEANUP_BATCH_SIZE",
533 "NONCE_AUTH_CLEANUP_THRESHOLD",
534 "NONCE_AUTH_DEFAULT_TTL",
535 "NONCE_AUTH_DEFAULT_TIME_WINDOW",
536 ];
537
538 for var in &vars {
539 unsafe {
540 env::remove_var(var);
541 }
542 }
543 }
544
545 #[test]
546 #[serial]
547 fn test_default_configuration() {
548 let saved_vars: Vec<_> = [
550 "NONCE_AUTH_DB_PATH",
551 "NONCE_AUTH_CACHE_SIZE",
552 "NONCE_AUTH_WAL_MODE",
553 "NONCE_AUTH_SYNC_MODE",
554 "NONCE_AUTH_TEMP_STORE",
555 "NONCE_AUTH_CLEANUP_BATCH_SIZE",
556 "NONCE_AUTH_CLEANUP_THRESHOLD",
557 "NONCE_AUTH_DEFAULT_TTL",
558 "NONCE_AUTH_DEFAULT_TIME_WINDOW",
559 ]
560 .iter()
561 .map(|var| (*var, env::var(var).ok()))
562 .collect();
563
564 clear_env_vars();
565
566 let config = NonceConfig::default();
567
568 assert_eq!(config.db_path, "nonce_auth.db");
570 assert_eq!(config.cache_size_kb, 2048);
571 assert!(config.wal_mode);
572 assert_eq!(config.sync_mode, "NORMAL");
573 assert_eq!(config.temp_store, "MEMORY");
574 assert_eq!(config.cleanup_batch_size, 1000);
575 assert_eq!(config.cleanup_optimize_threshold, 100);
576 assert_eq!(config.default_ttl, Duration::from_secs(300));
577 assert_eq!(config.time_window, Duration::from_secs(60));
578
579 for (var, value) in saved_vars {
581 match value {
582 Some(val) => unsafe {
583 env::set_var(var, val);
584 },
585 None => unsafe {
586 env::remove_var(var);
587 },
588 }
589 }
590 }
591
592 #[test]
593 #[serial]
594 fn test_environment_variable_override() {
595 let saved_vars: Vec<_> = [
597 "NONCE_AUTH_DB_PATH",
598 "NONCE_AUTH_CACHE_SIZE",
599 "NONCE_AUTH_WAL_MODE",
600 "NONCE_AUTH_SYNC_MODE",
601 "NONCE_AUTH_TEMP_STORE",
602 "NONCE_AUTH_CLEANUP_BATCH_SIZE",
603 "NONCE_AUTH_CLEANUP_THRESHOLD",
604 "NONCE_AUTH_DEFAULT_TTL",
605 "NONCE_AUTH_DEFAULT_TIME_WINDOW",
606 ]
607 .iter()
608 .map(|var| (*var, env::var(var).ok()))
609 .collect();
610
611 clear_env_vars();
612
613 unsafe {
615 env::set_var("NONCE_AUTH_DB_PATH", "test.db");
616 env::set_var("NONCE_AUTH_CACHE_SIZE", "4096");
617 env::set_var("NONCE_AUTH_WAL_MODE", "false");
618 env::set_var("NONCE_AUTH_SYNC_MODE", "FULL");
619 env::set_var("NONCE_AUTH_TEMP_STORE", "FILE");
620 env::set_var("NONCE_AUTH_CLEANUP_BATCH_SIZE", "2000");
621 env::set_var("NONCE_AUTH_CLEANUP_THRESHOLD", "200");
622 env::set_var("NONCE_AUTH_DEFAULT_TTL", "600");
623 env::set_var("NONCE_AUTH_DEFAULT_TIME_WINDOW", "120");
624 }
625
626 let config = NonceConfig::default();
627
628 assert_eq!(config.db_path, "test.db");
630 assert_eq!(config.cache_size_kb, 4096);
631 assert!(!config.wal_mode);
632 assert_eq!(config.sync_mode, "FULL");
633 assert_eq!(config.temp_store, "FILE");
634 assert_eq!(config.cleanup_batch_size, 2000);
635 assert_eq!(config.cleanup_optimize_threshold, 200);
636 assert_eq!(config.default_ttl, Duration::from_secs(600));
637 assert_eq!(config.time_window, Duration::from_secs(120));
638
639 for (var, value) in saved_vars {
641 match value {
642 Some(val) => unsafe {
643 env::set_var(var, val);
644 },
645 None => unsafe {
646 env::remove_var(var);
647 },
648 }
649 }
650 }
651
652 #[test]
653 #[serial]
654 fn test_wal_mode_parsing() {
655 let saved_vars: Vec<_> = [
657 "NONCE_AUTH_DB_PATH",
658 "NONCE_AUTH_CACHE_SIZE",
659 "NONCE_AUTH_WAL_MODE",
660 "NONCE_AUTH_SYNC_MODE",
661 "NONCE_AUTH_TEMP_STORE",
662 "NONCE_AUTH_CLEANUP_BATCH_SIZE",
663 "NONCE_AUTH_CLEANUP_THRESHOLD",
664 "NONCE_AUTH_DEFAULT_TTL",
665 "NONCE_AUTH_DEFAULT_TIME_WINDOW",
666 ]
667 .iter()
668 .map(|var| (*var, env::var(var).ok()))
669 .collect();
670
671 clear_env_vars();
672
673 let test_cases = [
675 ("true", true),
676 ("TRUE", true),
677 ("True", true),
678 ("false", false),
679 ("FALSE", false),
680 ("False", false),
681 ("0", true), ("1", true),
683 ("", true),
684 ];
685
686 for (env_value, expected) in &test_cases {
687 unsafe {
688 env::set_var("NONCE_AUTH_WAL_MODE", env_value);
689 }
690 let config = NonceConfig::default();
691 assert_eq!(
692 config.wal_mode, *expected,
693 "Failed for WAL_MODE='{env_value}'"
694 );
695 }
696
697 for (var, value) in saved_vars {
699 match value {
700 Some(val) => unsafe {
701 env::set_var(var, val);
702 },
703 None => unsafe {
704 env::remove_var(var);
705 },
706 }
707 }
708 }
709
710 #[test]
711 #[serial]
712 fn test_invalid_numeric_env_vars() {
713 let saved_vars: Vec<_> = [
715 "NONCE_AUTH_DB_PATH",
716 "NONCE_AUTH_CACHE_SIZE",
717 "NONCE_AUTH_WAL_MODE",
718 "NONCE_AUTH_SYNC_MODE",
719 "NONCE_AUTH_TEMP_STORE",
720 "NONCE_AUTH_CLEANUP_BATCH_SIZE",
721 "NONCE_AUTH_CLEANUP_THRESHOLD",
722 "NONCE_AUTH_DEFAULT_TTL",
723 "NONCE_AUTH_DEFAULT_TIME_WINDOW",
724 ]
725 .iter()
726 .map(|var| (*var, env::var(var).ok()))
727 .collect();
728
729 clear_env_vars();
730
731 unsafe {
733 env::set_var("NONCE_AUTH_CACHE_SIZE", "invalid");
734 env::set_var("NONCE_AUTH_CLEANUP_BATCH_SIZE", "not_a_number");
735 env::set_var("NONCE_AUTH_CLEANUP_THRESHOLD", "");
736 env::set_var("NONCE_AUTH_DEFAULT_TTL", "abc");
737 env::set_var("NONCE_AUTH_DEFAULT_TIME_WINDOW", "-1");
738 }
739
740 let config = NonceConfig::default();
741
742 assert_eq!(config.cache_size_kb, 2048);
744 assert_eq!(config.cleanup_batch_size, 1000);
745 assert_eq!(config.cleanup_optimize_threshold, 100);
746 assert_eq!(config.default_ttl, Duration::from_secs(300));
747 assert_eq!(config.time_window, Duration::from_secs(60));
748
749 for (var, value) in saved_vars {
751 match value {
752 Some(val) => unsafe {
753 env::set_var(var, val);
754 },
755 None => unsafe {
756 env::remove_var(var);
757 },
758 }
759 }
760 }
761
762 #[test]
763 fn test_production_preset() {
764 let config = NonceConfig::production();
765
766 assert_eq!(config.cache_size_kb, 8192);
767 assert!(config.wal_mode);
768 assert_eq!(config.sync_mode, "NORMAL");
769 assert_eq!(config.temp_store, "MEMORY");
770 assert_eq!(config.cleanup_batch_size, 2000);
771 assert_eq!(config.cleanup_optimize_threshold, 500);
772 assert_eq!(config.default_ttl, Duration::from_secs(300));
773 assert_eq!(config.time_window, Duration::from_secs(60));
774 }
775
776 #[test]
777 fn test_development_preset() {
778 let config = NonceConfig::development();
779
780 assert_eq!(config.db_path, ":memory:");
781 assert_eq!(config.cache_size_kb, 512);
782 assert!(!config.wal_mode);
783 assert_eq!(config.sync_mode, "OFF");
784 assert_eq!(config.temp_store, "MEMORY");
785 assert_eq!(config.cleanup_batch_size, 100);
786 assert_eq!(config.cleanup_optimize_threshold, 50);
787 assert_eq!(config.default_ttl, Duration::from_secs(60));
788 assert_eq!(config.time_window, Duration::from_secs(300));
789 }
790
791 #[test]
792 fn test_high_performance_preset() {
793 let config = NonceConfig::high_performance();
794
795 assert_eq!(config.cache_size_kb, 16384);
796 assert!(config.wal_mode);
797 assert_eq!(config.sync_mode, "NORMAL");
798 assert_eq!(config.temp_store, "MEMORY");
799 assert_eq!(config.cleanup_batch_size, 5000);
800 assert_eq!(config.cleanup_optimize_threshold, 1000);
801 assert_eq!(config.default_ttl, Duration::from_secs(300));
802 assert_eq!(config.time_window, Duration::from_secs(60));
803 }
804
805 #[test]
806 #[serial]
807 fn test_from_env() {
808 let saved_vars: Vec<_> = [
810 "NONCE_AUTH_DB_PATH",
811 "NONCE_AUTH_CACHE_SIZE",
812 "NONCE_AUTH_WAL_MODE",
813 "NONCE_AUTH_SYNC_MODE",
814 "NONCE_AUTH_TEMP_STORE",
815 "NONCE_AUTH_CLEANUP_BATCH_SIZE",
816 "NONCE_AUTH_CLEANUP_THRESHOLD",
817 "NONCE_AUTH_DEFAULT_TTL",
818 "NONCE_AUTH_DEFAULT_TIME_WINDOW",
819 ]
820 .iter()
821 .map(|var| (*var, env::var(var).ok()))
822 .collect();
823
824 clear_env_vars();
825
826 unsafe {
828 env::set_var("NONCE_AUTH_DB_PATH", "custom.db");
829 env::set_var("NONCE_AUTH_CACHE_SIZE", "1024");
830 env::set_var("NONCE_AUTH_WAL_MODE", "false");
831 env::set_var("NONCE_AUTH_SYNC_MODE", "OFF");
832 env::set_var("NONCE_AUTH_TEMP_STORE", "FILE");
833 env::set_var("NONCE_AUTH_CLEANUP_BATCH_SIZE", "500");
834 env::set_var("NONCE_AUTH_CLEANUP_THRESHOLD", "250");
835 env::set_var("NONCE_AUTH_DEFAULT_TTL", "120");
836 env::set_var("NONCE_AUTH_DEFAULT_TIME_WINDOW", "30");
837 }
838
839 let config = NonceConfig::from_env();
840
841 assert_eq!(config.db_path, "custom.db");
843 assert_eq!(config.cache_size_kb, 1024);
844 assert!(!config.wal_mode);
845 assert_eq!(config.sync_mode, "OFF");
846 assert_eq!(config.temp_store, "FILE");
847 assert_eq!(config.cleanup_batch_size, 500);
848 assert_eq!(config.cleanup_optimize_threshold, 250);
849 assert_eq!(config.default_ttl, Duration::from_secs(120));
850 assert_eq!(config.time_window, Duration::from_secs(30));
851
852 for (var, value) in saved_vars {
854 match value {
855 Some(val) => unsafe {
856 env::set_var(var, val);
857 },
858 None => unsafe {
859 env::remove_var(var);
860 },
861 }
862 }
863 }
864
865 #[test]
866 fn test_validation_valid_config() {
867 let config = NonceConfig::production();
868 let issues = config.validate();
869 assert!(issues.is_empty(), "Production config should be valid");
870 }
871
872 #[test]
873 fn test_validation_cache_size_warnings() {
874 let config = NonceConfig {
876 cache_size_kb: 32,
877 ..NonceConfig::default()
878 };
879 let issues = config.validate();
880 assert!(
881 issues
882 .iter()
883 .any(|issue| issue.contains("Cache size is very small"))
884 );
885
886 let config = NonceConfig {
888 cache_size_kb: 50000,
889 ..NonceConfig::default()
890 };
891 let issues = config.validate();
892 assert!(
893 issues
894 .iter()
895 .any(|issue| issue.contains("Cache size is very large"))
896 );
897 }
898
899 #[test]
900 fn test_validation_invalid_sync_mode() {
901 let config = NonceConfig {
902 sync_mode: "INVALID".to_string(),
903 ..NonceConfig::default()
904 };
905 let issues = config.validate();
906 assert!(
907 issues
908 .iter()
909 .any(|issue| issue.contains("Invalid sync_mode"))
910 );
911 }
912
913 #[test]
914 fn test_validation_invalid_temp_store() {
915 let config = NonceConfig {
916 temp_store: "INVALID".to_string(),
917 ..NonceConfig::default()
918 };
919 let issues = config.validate();
920 assert!(
921 issues
922 .iter()
923 .any(|issue| issue.contains("Invalid temp_store"))
924 );
925 }
926
927 #[test]
928 fn test_validation_ttl_warnings() {
929 let config = NonceConfig {
931 default_ttl: Duration::from_secs(10),
932 ..NonceConfig::default()
933 };
934 let issues = config.validate();
935 assert!(
936 issues
937 .iter()
938 .any(|issue| issue.contains("Default TTL is very short"))
939 );
940
941 let config = NonceConfig {
943 default_ttl: Duration::from_secs(100000),
944 ..NonceConfig::default()
945 };
946 let issues = config.validate();
947 assert!(
948 issues
949 .iter()
950 .any(|issue| issue.contains("Default TTL is very long"))
951 );
952 }
953
954 #[test]
955 fn test_validation_time_window_warnings() {
956 let config = NonceConfig {
958 time_window: Duration::from_secs(5),
959 ..NonceConfig::default()
960 };
961 let issues = config.validate();
962 assert!(
963 issues
964 .iter()
965 .any(|issue| issue.contains("Time window is very short"))
966 );
967
968 let config = NonceConfig {
970 time_window: Duration::from_secs(5000),
971 ..NonceConfig::default()
972 };
973 let issues = config.validate();
974 assert!(
975 issues
976 .iter()
977 .any(|issue| issue.contains("Time window is very long"))
978 );
979 }
980
981 #[test]
982 fn test_validation_batch_size_warnings() {
983 let config = NonceConfig {
985 cleanup_batch_size: 5,
986 ..NonceConfig::default()
987 };
988 let issues = config.validate();
989 assert!(
990 issues
991 .iter()
992 .any(|issue| issue.contains("Cleanup batch size is very small"))
993 );
994
995 let config = NonceConfig {
997 cleanup_batch_size: 20000,
998 ..NonceConfig::default()
999 };
1000 let issues = config.validate();
1001 assert!(
1002 issues
1003 .iter()
1004 .any(|issue| issue.contains("Cleanup batch size is very large"))
1005 );
1006 }
1007
1008 #[test]
1009 fn test_summary_format() {
1010 let config = NonceConfig::default();
1011 let summary = config.summary();
1012
1013 assert!(summary.contains("Nonce Authentication Configuration"));
1015 assert!(summary.contains("Database:"));
1016 assert!(summary.contains("Performance:"));
1017 assert!(summary.contains("Security:"));
1018 assert!(summary.contains(&config.db_path));
1019 assert!(summary.contains(&config.cache_size_kb.to_string()));
1020 assert!(summary.contains(&config.sync_mode));
1021 assert!(summary.contains(&config.cleanup_batch_size.to_string()));
1022 assert!(summary.contains(&config.default_ttl.as_secs().to_string()));
1023 }
1024
1025 #[test]
1026 fn test_config_clone_and_debug() {
1027 let config = NonceConfig::default();
1028
1029 let cloned_config = config.clone();
1031 assert_eq!(config.db_path, cloned_config.db_path);
1032 assert_eq!(config.cache_size_kb, cloned_config.cache_size_kb);
1033
1034 let debug_str = format!("{config:?}");
1036 assert!(debug_str.contains("NonceConfig"));
1037 assert!(debug_str.contains("db_path"));
1038 }
1039
1040 #[test]
1041 fn test_custom_configuration() {
1042 let config = NonceConfig {
1043 db_path: "custom_path.db".to_string(),
1044 cache_size_kb: 4096,
1045 wal_mode: false,
1046 sync_mode: "FULL".to_string(),
1047 temp_store: "FILE".to_string(),
1048 cleanup_batch_size: 1500,
1049 cleanup_optimize_threshold: 300,
1050 default_ttl: Duration::from_secs(600),
1051 time_window: Duration::from_secs(120),
1052 };
1053
1054 assert_eq!(config.db_path, "custom_path.db");
1055 assert_eq!(config.cache_size_kb, 4096);
1056 assert!(!config.wal_mode);
1057 assert_eq!(config.sync_mode, "FULL");
1058 assert_eq!(config.temp_store, "FILE");
1059 assert_eq!(config.cleanup_batch_size, 1500);
1060 assert_eq!(config.cleanup_optimize_threshold, 300);
1061 assert_eq!(config.default_ttl, Duration::from_secs(600));
1062 assert_eq!(config.time_window, Duration::from_secs(120));
1063 }
1064
1065 #[test]
1066 #[serial]
1067 fn test_preset_independence() {
1068 let saved_vars: Vec<_> = [
1070 "NONCE_AUTH_DB_PATH",
1071 "NONCE_AUTH_CACHE_SIZE",
1072 "NONCE_AUTH_WAL_MODE",
1073 "NONCE_AUTH_SYNC_MODE",
1074 "NONCE_AUTH_TEMP_STORE",
1075 "NONCE_AUTH_CLEANUP_BATCH_SIZE",
1076 "NONCE_AUTH_CLEANUP_THRESHOLD",
1077 "NONCE_AUTH_DEFAULT_TTL",
1078 "NONCE_AUTH_DEFAULT_TIME_WINDOW",
1079 ]
1080 .iter()
1081 .map(|var| (*var, env::var(var).ok()))
1082 .collect();
1083
1084 clear_env_vars();
1085
1086 unsafe {
1088 env::set_var("NONCE_AUTH_DB_PATH", "env_override.db");
1089 env::set_var("NONCE_AUTH_CACHE_SIZE", "1024");
1090 }
1091
1092 let production_config = NonceConfig::production();
1094 assert_eq!(production_config.db_path, "nonce_auth.db"); assert_eq!(production_config.cache_size_kb, 8192); assert_eq!(production_config.cleanup_batch_size, 2000);
1097
1098 let dev_config = NonceConfig::development();
1099 assert_eq!(dev_config.db_path, ":memory:"); assert_eq!(dev_config.cache_size_kb, 512); let perf_config = NonceConfig::high_performance();
1103 assert_eq!(perf_config.db_path, "nonce_auth.db"); assert_eq!(perf_config.cache_size_kb, 16384); let env_config = NonceConfig::from_env();
1108 assert_eq!(env_config.db_path, "env_override.db");
1109 assert_eq!(env_config.cache_size_kb, 1024);
1110
1111 for (var, value) in saved_vars {
1113 match value {
1114 Some(val) => unsafe {
1115 env::set_var(var, val);
1116 },
1117 None => unsafe {
1118 env::remove_var(var);
1119 },
1120 }
1121 }
1122 }
1123
1124 #[test]
1125 #[serial]
1126 fn test_env_preset_selection() {
1127 let saved_env = env::var("NONCE_AUTH_PRESET").ok();
1129
1130 unsafe {
1132 env::remove_var("NONCE_AUTH_PRESET");
1133 }
1134 let config = NonceConfig::from_env();
1135 let prod_config = NonceConfig::production();
1136 assert_eq!(config.cache_size_kb, prod_config.cache_size_kb);
1137 assert_eq!(config.cleanup_batch_size, prod_config.cleanup_batch_size);
1138
1139 unsafe {
1141 env::set_var("NONCE_AUTH_PRESET", "development");
1142 }
1143 let config = NonceConfig::from_env();
1144 let dev_config = NonceConfig::development();
1145 assert_eq!(config.cache_size_kb, dev_config.cache_size_kb);
1146 assert_eq!(config.db_path, dev_config.db_path);
1147
1148 unsafe {
1150 env::set_var("NONCE_AUTH_PRESET", "high_performance");
1151 }
1152 let config = NonceConfig::from_env();
1153 let hp_config = NonceConfig::high_performance();
1154 assert_eq!(config.cache_size_kb, hp_config.cache_size_kb);
1155 assert_eq!(config.cleanup_batch_size, hp_config.cleanup_batch_size);
1156
1157 unsafe {
1159 env::set_var("NONCE_AUTH_PRESET", "invalid");
1160 }
1161 let config = NonceConfig::from_env();
1162 let prod_config = NonceConfig::production();
1163 assert_eq!(config.cache_size_kb, prod_config.cache_size_kb);
1164
1165 match saved_env {
1167 Some(val) => unsafe {
1168 env::set_var("NONCE_AUTH_PRESET", val);
1169 },
1170 None => unsafe {
1171 env::remove_var("NONCE_AUTH_PRESET");
1172 },
1173 }
1174 }
1175
1176 #[test]
1177 #[serial]
1178 fn test_env_override_preset() {
1179 let saved_vars: Vec<_> = [
1181 "NONCE_AUTH_PRESET",
1182 "NONCE_AUTH_CACHE_SIZE",
1183 "NONCE_AUTH_DB_PATH",
1184 ]
1185 .iter()
1186 .map(|var| (*var, env::var(var).ok()))
1187 .collect();
1188
1189 unsafe {
1191 env::set_var("NONCE_AUTH_PRESET", "production");
1192 env::set_var("NONCE_AUTH_CACHE_SIZE", "12345");
1193 env::set_var("NONCE_AUTH_DB_PATH", "custom_test.db");
1194 }
1195
1196 let config = NonceConfig::from_env();
1197 let prod_config = NonceConfig::production();
1198
1199 assert_eq!(config.cache_size_kb, 12345);
1201 assert_eq!(config.db_path, "custom_test.db");
1202
1203 assert_eq!(config.cleanup_batch_size, prod_config.cleanup_batch_size);
1205 assert_eq!(config.wal_mode, prod_config.wal_mode);
1206
1207 for (var, value) in saved_vars {
1209 match value {
1210 Some(val) => unsafe {
1211 env::set_var(var, val);
1212 },
1213 None => unsafe {
1214 env::remove_var(var);
1215 },
1216 }
1217 }
1218 }
1219}