From 48296403cf7d911c73dcff9dc49082931c61db63 Mon Sep 17 00:00:00 2001 From: Dawid Rycerz Date: Wed, 30 Jul 2025 19:32:16 +0300 Subject: feat: make tests parallel friendly --- src/config.rs | 148 +++++++++++++++++++++++++--------------------------------- 1 file changed, 63 insertions(+), 85 deletions(-) (limited to 'src/config.rs') diff --git a/src/config.rs b/src/config.rs index 49bd995..04bd94a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,15 +14,26 @@ pub struct Config { impl Config { pub fn from_env() -> Self { - let database_url = Self::get_database_url(); - let openweathermap_api_key = env::var("OPENWEATHERMAP_API_KEY").ok(); - let admin_token = env::var("ADMIN_TOKEN").ok(); - let server_port = env::var("PORT") - .unwrap_or_else(|_| "4000".to_string()) + Self::from_env_vars(&env::vars().collect::>()) + } + + pub fn from_env_vars(env_vars: &[(String, String)]) -> Self { + let get_env = |key: &str| { + env_vars + .iter() + .find(|(k, _)| k == key) + .map(|(_, v)| v.clone()) + }; + + let database_url = Self::get_database_url_from_env_vars(env_vars); + let openweathermap_api_key = get_env("OPENWEATHERMAP_API_KEY"); + let admin_token = get_env("ADMIN_TOKEN"); + let server_port = get_env("PORT") + .unwrap_or_else(|| "4000".to_string()) .parse() .unwrap_or(4000); - let server_host = env::var("HOST").unwrap_or_else(|_| "0.0.0.0".to_string()); - let log_level = env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string()); + let server_host = get_env("HOST").unwrap_or_else(|| "0.0.0.0".to_string()); + let log_level = get_env("LOG_LEVEL").unwrap_or_else(|| "info".to_string()); Self { database_url, @@ -34,19 +45,25 @@ impl Config { } } - fn get_database_url() -> String { + fn get_database_url_from_env_vars(env_vars: &[(String, String)]) -> String { + let get_env = |key: &str| { + env_vars + .iter() + .find(|(k, _)| k == key) + .map(|(_, v)| v.clone()) + }; + // First, check if DATABASE_URL is explicitly set - if let Ok(url) = env::var("DATABASE_URL") { + if let Some(url) = get_env("DATABASE_URL") { return url; } // If not set, construct the path using XDG_DATA_HOME or fallback - let data_dir = if let Ok(xdg_data_home) = env::var("XDG_DATA_HOME") { + let data_dir = if let Some(xdg_data_home) = get_env("XDG_DATA_HOME") { PathBuf::from(xdg_data_home) } else { // Fallback to ~/.local/share - let home = env::var("HOME") - .unwrap_or_else(|_| env::var("USERPROFILE").unwrap_or_else(|_| "/tmp".to_string())); + let home = get_env("HOME").unwrap_or_else(|| "/tmp".to_string()); PathBuf::from(home).join(".local").join("share") }; @@ -79,105 +96,66 @@ impl Config { #[cfg(test)] mod tests { use super::*; - use std::env; - use std::sync::OnceLock; - use std::sync::{Mutex, Once}; - - static INIT: Once = Once::new(); - static TEST_MUTEX: OnceLock> = OnceLock::new(); - - fn setup() { - INIT.call_once(|| { - // Clear all environment variables that might interfere with tests - unsafe { - env::remove_var("DATABASE_URL"); - env::remove_var("XDG_DATA_HOME"); - env::remove_var("PORT"); - env::remove_var("HOST"); - env::remove_var("LOG_LEVEL"); - } - }); - - // Get the test mutex to ensure sequential execution - let _guard = TEST_MUTEX.get_or_init(|| Mutex::new(())).lock().unwrap(); - - // Clear environment variables before each test - unsafe { - env::remove_var("DATABASE_URL"); - env::remove_var("XDG_DATA_HOME"); - env::remove_var("PORT"); - env::remove_var("HOST"); - env::remove_var("LOG_LEVEL"); - } - } #[test] fn test_database_url_fallback() { - setup(); + let env_vars = vec![("HOME".to_string(), "/home/testuser".to_string())]; - let home = env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()); - let expected_path = format!("sqlite:{home}/.local/share/silmataivas/silmataivas.db"); - - let config = Config::from_env(); + let config = Config::from_env_vars(&env_vars); + let expected_path = "sqlite:/home/testuser/.local/share/silmataivas/silmataivas.db"; assert_eq!(config.database_url, expected_path); } #[test] fn test_database_url_xdg_data_home() { - setup(); - - unsafe { - env::set_var("XDG_DATA_HOME", "/custom/data/path"); - } + let env_vars = vec![("XDG_DATA_HOME".to_string(), "/custom/data/path".to_string())]; + let config = Config::from_env_vars(&env_vars); let expected_path = "sqlite:/custom/data/path/silmataivas/silmataivas.db"; - - let config = Config::from_env(); assert_eq!(config.database_url, expected_path); - - // Clean up after this test - unsafe { - env::remove_var("XDG_DATA_HOME"); - } } #[test] fn test_database_url_explicit() { - setup(); + let env_vars = vec![( + "DATABASE_URL".to_string(), + "sqlite:/explicit/path.db".to_string(), + )]; - unsafe { - env::set_var("DATABASE_URL", "sqlite:/explicit/path.db"); - } - - let config = Config::from_env(); + let config = Config::from_env_vars(&env_vars); assert_eq!(config.database_url, "sqlite:/explicit/path.db"); - - // Clean up after this test - unsafe { - env::remove_var("DATABASE_URL"); - } } #[test] fn test_server_config() { - setup(); + let env_vars = vec![ + ("PORT".to_string(), "8080".to_string()), + ("HOST".to_string(), "127.0.0.1".to_string()), + ("LOG_LEVEL".to_string(), "debug".to_string()), + ]; - unsafe { - env::set_var("PORT", "8080"); - env::set_var("HOST", "127.0.0.1"); - env::set_var("LOG_LEVEL", "debug"); - } - - let config = Config::from_env(); + let config = Config::from_env_vars(&env_vars); assert_eq!(config.server_port, 8080); assert_eq!(config.server_host, "127.0.0.1"); assert_eq!(config.log_level, "debug"); + } - // Clean up after this test - unsafe { - env::remove_var("PORT"); - env::remove_var("HOST"); - env::remove_var("LOG_LEVEL"); - } + #[test] + fn test_default_values() { + let env_vars = vec![]; + + let config = Config::from_env_vars(&env_vars); + assert_eq!(config.server_port, 4000); + assert_eq!(config.server_host, "0.0.0.0"); + assert_eq!(config.log_level, "info"); + } + + #[test] + fn test_home_fallback_to_tmp() { + let env_vars = vec![]; + + let config = Config::from_env_vars(&env_vars); + let expected_path = "sqlite:/tmp/.local/share/silmataivas/silmataivas.db"; + assert_eq!(config.database_url, expected_path); } } -- cgit v1.2.3