//! Automated validation suite for environment variable namespace and precedence. use nx9_wg_core::config::{AppConfig, BootstrapConfig}; use std::path::PathBuf; use std::sync::Mutex; static ENV_MUTEX: Mutex<()> = Mutex::new(()); #[test] fn test_nx9_wg_env_variable_precedence_and_overrides() { let _lock = ENV_MUTEX.lock().unwrap(); let mut config = AppConfig::default(); // Set canonical NX9_WG_ environment variables unsafe { std::env::set_var("NX9_WG_CONFIG", "/custom/etc/config.toml"); std::env::set_var("NX9_WG_DATA_DIR", "/custom/var/data"); std::env::set_var("NX9_WG_LISTEN_ADDR", "127.0.0.1:9090"); std::env::set_var("NX9_WG_LOG_LEVEL", "warn"); std::env::set_var("NX9_WG_SESSION_TIMEOUT", "72"); std::env::set_var("NX9_WG_RECONCILIATION_INTERVAL", "15"); std::env::set_var("NX9_WG_BACKUP_DIR", "/custom/backups"); std::env::set_var("NX9_WG_BACKUP_MAX_COUNT", "20"); std::env::set_var("NX9_WG_BACKUP_SCHEDULE", "0 3 * * *"); std::env::set_var("NX9_WG_ADMIN_USERNAME", "superadmin"); std::env::set_var("NX9_WG_ADMIN_PASSWORD", "SuperSecretPW987!"); } config.apply_env_overrides().expect("apply env overrides"); assert_eq!(config.config_file, PathBuf::from("/custom/etc/config.toml")); assert_eq!(config.data_dir, PathBuf::from("/custom/var/data")); assert_eq!(config.bind_address, "127.0.0.1:9090".parse().unwrap()); assert_eq!(config.log_level, "warn"); assert_eq!(config.session_expiry_hours, 72); assert_eq!(config.reconciliation_interval_secs, 15); assert_eq!(config.backup.dir, PathBuf::from("/custom/backups")); assert_eq!(config.backup.max_count, 20); assert_eq!(config.backup.schedule, Some("0 3 * * *".to_string())); let boot = config.bootstrap.expect("bootstrap should be present"); assert_eq!(boot.admin_username, Some("superadmin".to_string())); assert_eq!(boot.admin_password, Some("SuperSecretPW987!".to_string())); // Clean up unsafe { std::env::remove_var("NX9_WG_CONFIG"); std::env::remove_var("NX9_WG_DATA_DIR"); std::env::remove_var("NX9_WG_LISTEN_ADDR"); std::env::remove_var("NX9_WG_LOG_LEVEL"); std::env::remove_var("NX9_WG_SESSION_TIMEOUT"); std::env::remove_var("NX9_WG_RECONCILIATION_INTERVAL"); std::env::remove_var("NX9_WG_BACKUP_DIR"); std::env::remove_var("NX9_WG_BACKUP_MAX_COUNT"); std::env::remove_var("NX9_WG_BACKUP_SCHEDULE"); std::env::remove_var("NX9_WG_ADMIN_USERNAME"); std::env::remove_var("NX9_WG_ADMIN_PASSWORD"); } } #[test] fn test_invalid_env_variable_values() { let _lock = ENV_MUTEX.lock().unwrap(); let mut config = AppConfig::default(); unsafe { std::env::set_var("NX9_WG_LISTEN_ADDR", "invalid-ip-and-port"); } assert!(config.apply_env_overrides().is_err()); unsafe { std::env::remove_var("NX9_WG_LISTEN_ADDR"); } unsafe { std::env::set_var("NX9_WG_SESSION_TIMEOUT", "not-a-number"); } assert!(config.apply_env_overrides().is_err()); unsafe { std::env::remove_var("NX9_WG_SESSION_TIMEOUT"); } } #[test] fn test_secret_redaction() { let boot = BootstrapConfig { admin_username: Some("admin".to_string()), admin_password: Some("secret12345".to_string()), }; let formatted = format!("{boot:?}"); assert!(!formatted.contains("secret12345")); assert!(formatted.contains("[REDACTED]")); } #[test] fn test_database_path_resolution() { // Default database path should be data_dir/nx9-wg.db let config = AppConfig::default(); let expected_db = config.data_dir.join("nx9-wg.db"); assert_eq!(expected_db, PathBuf::from("/var/lib/nx9-wg/nx9-wg.db")); } #[test] fn test_explicit_database_dir_override() { let _lock = ENV_MUTEX.lock().unwrap(); let config = AppConfig::default(); // When --database is explicitly provided, it should be used directly // The test verifies the default path is what we expect assert_eq!(config.data_dir, PathBuf::from("/var/lib/nx9-wg")); } #[test] fn test_backup_directory_consistency() { let config = AppConfig::default(); // Backup directory should always be consistent: /var/lib/nx9-wg/backups assert_eq!(config.backup.dir, PathBuf::from("/var/lib/nx9-wg/backups")); } #[test] fn test_nx9_wg_database_env_var() { let _lock = ENV_MUTEX.lock().unwrap(); // NX9_WG_DATABASE should be honored via CLI args // This test documents that the env var is in the canonical namespace let cli_args = &["--database", "/custom/path/test.db"]; // We're verifying this is the correct pattern to use assert_eq!(cli_args[0], "--database"); assert_eq!(cli_args[1], "/custom/path/test.db"); } #[test] fn test_canonical_env_namespace_only() { let _lock = ENV_MUTEX.lock().unwrap(); // Verify only NX9_WG_* variables are used let mut config = AppConfig::default(); // Try setting a non-canonical variable - should be ignored unsafe { std::env::set_var("RUST_LOG", "debug"); std::env::set_var("NX9_LOG_LEVEL", "error"); } config.apply_env_overrides().expect("apply env overrides"); // These non-canonical variables should be ignored assert_eq!(config.log_level, "info"); // Should remain default // Clean up unsafe { std::env::remove_var("RUST_LOG"); std::env::remove_var("NX9_LOG_LEVEL"); } } #[test] fn test_default_bind_address_is_localhost() { let config = AppConfig::default(); // Verify bind address default assert_eq!(config.bind_address, "127.0.0.1:8080".parse().unwrap()); } #[test] fn test_default_reconciliation_interval() { let config = AppConfig::default(); // Default reconciliation interval should be 60 seconds assert_eq!(config.reconciliation_interval_secs, 60); }