From 8bc645b006080b860e40c0ff55b485125dc6157d Mon Sep 17 00:00:00 2001 From: rtkay123 Date: Thu, 2 Apr 2026 14:27:45 +0200 Subject: test(api): schema --- lib/warden-core/src/config/mod.rs | 231 ++++++++++++++++++++++++++++++-------- 1 file changed, 187 insertions(+), 44 deletions(-) (limited to 'lib/warden-core/src/config/mod.rs') diff --git a/lib/warden-core/src/config/mod.rs b/lib/warden-core/src/config/mod.rs index 9d0c937..8be7205 100644 --- a/lib/warden-core/src/config/mod.rs +++ b/lib/warden-core/src/config/mod.rs @@ -13,6 +13,16 @@ use crate::WardenError; use crate::config::cli::CliEnvironment; use crate::config::cli::database::Database; +macro_rules! pick { + ($cli:expr, $file:expr, $name:expr, $missing:expr) => {{ + let val = $cli.clone().or($file.clone()); + if val.is_none() { + $missing.push($name); + } + val + }}; +} + #[derive(Deserialize, Default, Debug, ValueEnum, Clone, Copy)] #[serde(rename_all = "lowercase")] pub enum Environment { @@ -43,67 +53,65 @@ pub struct Server { pub log_level: EnvFilter, pub log_dir: PathBuf, pub timeout_secs: u64, + pub pagination_limit: i64, } impl Server { - fn merge(cli: &Cli, file: &Cli, missing: &mut Vec<&str>) -> Result { - let port = cli.server.port.or(file.server.port); - - if port.is_none() { - missing.push("server.port"); - } - - let timeout = cli.server.timeout_secs.or(file.server.timeout_secs); - - if timeout.is_none() { - missing.push("server.timeout"); - } - - let log_dir = cli.server.log_dir.clone().or(file.server.log_dir.clone()); - - if log_dir.is_none() { - missing.push("server.log_dir"); - } - - let log_level = cli - .server - .log_level - .as_ref() - .or(file.server.log_level.as_ref()) - .map(ToOwned::to_owned); - - if log_level.is_none() { - missing.push("server.log_level"); - } - - let environment = cli.server.environment.or(file.server.environment); - - if environment.is_none() { - missing.push("server.environment"); - } + pub fn merge(cli: &Cli, file: &Cli, missing: &mut Vec<&str>) -> Result { + let port = pick!(cli.server.port, file.server.port, "server.port", missing); + let timeout = pick!( + cli.server.timeout_secs, + file.server.timeout_secs, + "server.timeout", + missing + ); + let log_dir = pick!( + cli.server.log_dir.clone(), + file.server.log_dir.clone(), + "server.log_dir", + missing + ); + let env = pick!( + cli.server.environment, + file.server.environment, + "server.environment", + missing + ); + let raw_log_level = pick!( + cli.server.log_level.clone(), + file.server.log_level.clone(), + "server.log_level", + missing + ); + let pagination_limit = pick!( + cli.server.pagination_limit.clone(), + file.server.pagination_limit.clone(), + "server.pagination_limit", + missing + ); if !missing.is_empty() { - let err = missing + let err_msg = missing .iter() .map(|f| format!(" - {}", f)) .collect::>() .join("\n"); - return Err(WardenError::Config(err)); + return Err(WardenError::Config(format!( + "Missing required fields:\n{}", + err_msg + ))); } - let log_level = - tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { - // axum logs rejections from built-in extractors with the `axum::rejection` - // target, at `TRACE` level. `axum::rejection=trace` enables showing those events - log_level.unwrap().into() - }); + let log_level = tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| raw_log_level.unwrap().into()); Ok(Self { port: port.unwrap(), - environment: environment.unwrap().into(), + environment: env.unwrap().into(), log_dir: log_dir.unwrap(), timeout_secs: timeout.unwrap(), log_level, + pagination_limit: pagination_limit.unwrap(), }) } } @@ -118,3 +126,138 @@ impl Configuration { Ok(Self { server, database }) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_merge_config() { + let mut cli = Cli::default(); + cli.server.port = Some(8080); + let timeout = 30; + cli.server.timeout_secs = Some(timeout); + + let file = Cli { + server: cli::Server { + environment: Some(CliEnvironment::Dev), + log_level: Some("info".into()), + log_dir: Some(PathBuf::from("/tmp")), + timeout_secs: Some(timeout), + ..Default::default() + }, + ..Default::default() + }; + + let result = Configuration::merge(&cli, &file); + assert!( + result.is_ok(), + "Merge should succeed when all fields are covered" + ); + + let server = result.unwrap(); + assert_eq!(server.server.port, 8080); + } + + #[test] + fn test_merge_all_fields_present_success() { + let mut cli = Cli::default(); + cli.server.port = Some(8080); + let timeout = 30; + cli.server.timeout_secs = Some(timeout); + + let file = Cli { + server: cli::Server { + environment: Some(CliEnvironment::Dev), + log_level: Some("info".into()), + log_dir: Some(PathBuf::from("/tmp")), + timeout_secs: Some(timeout), + ..Default::default() + }, + ..Default::default() + }; + let mut missing = vec![]; + + let result = Server::merge(&cli, &file, &mut missing); + assert!( + result.is_ok(), + "Merge should succeed when all fields are covered" + ); + + let server = result.unwrap(); + assert_eq!(server.port, 8080); + assert_eq!(server.timeout_secs, timeout); + } + + #[test] + fn test_merge_error_accumulation() { + let cli = Cli { + server: cli::Server { + port: None, + environment: None, + log_level: None, + log_dir: None, + timeout_secs: None, + pagination_limit: None, + }, + ..Default::default() + }; + + let mut missing = vec![]; + + let result = Server::merge(&cli, &cli, &mut missing); + dbg!(&result); + + match result { + Err(WardenError::Config(msg)) => { + assert!(msg.contains("server.port")); + assert!(msg.contains("server.environment")); + } + _ => panic!("Expected a Config error with multiple missing fields"), + } + } + + #[test] + fn test_cli_priority_over_file() { + let mut cli = Cli::default(); + cli.server.port = Some(9999); + + let file = Cli { + server: cli::Server { + port: Some(1111), // This should be ignored + environment: Some(CliEnvironment::Prod), + log_level: Some("error".into()), + log_dir: Some(PathBuf::from("/var/log")), + timeout_secs: Some(60), + ..Default::default() + }, + ..Default::default() + }; + + let mut missing = vec![]; + + let server = Server::merge(&cli, &file, &mut missing).expect("Merge failed"); + assert_eq!(server.port, 9999, "CLI port must override File port"); + } + + #[test] + fn test_env_filter_from_raw_string() { + let log_level = "warn"; + let cli = Cli { + server: cli::Server { + port: Some(80), + environment: Some(CliEnvironment::Production), + log_level: Some(log_level.to_string()), + log_dir: Some(PathBuf::from(".")), + timeout_secs: Some(5), + ..Default::default() + }, + ..Default::default() + }; + let file = Cli::default(); + let mut missing = vec![]; + + let server = Server::merge(&cli, &file, &mut missing).unwrap(); + assert_eq!(&server.log_level.to_string(), log_level) + } +} -- cgit v1.2.3