// file: crates/ksp-config-lib/unit_tests/logging.rs // version: 1 #[test] fn committed_logging_profile_maps_complete_runtime_contract() { let engine = committed_engine(); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new()); let resolved = engine.load_resolved_logging_config(std::option::Option::None, &environment); assert!(resolved.is_ok(), "committed Logging Config should map"); let resolved = match resolved { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert_eq!(resolved.file_id().as_str(), crate::FILE_ID_STD_LOGGING); assert_eq!(resolved.profile_id(), "local_dev"); assert_eq!(resolved.selection_source(), crate::ConfigProfileSelectionSource::DefaultProfile); assert_eq!(resolved.logs_directory(), current_directory().join("logs").as_path()); assert_eq!(resolved.effective().value()["logs_directory"], serde_json::Value::String("logs".to_owned())); let settings = resolved.settings(); assert_eq!(settings.default_filter(), ksp_logging_lib::LogFilterLevel::Warn); assert_eq!(settings.span_events(), ksp_logging_lib::SpanEvents::NewAndClose); assert_eq!(settings.target_filters().len(), 2); assert_eq!(settings.target_filters()[0].target_prefix(), "ksp-config-lib"); assert_eq!(settings.target_filters()[0].level(), ksp_logging_lib::LogFilterLevel::Trace); assert_eq!(settings.target_filters()[1].target_prefix(), "ksp-logging-lib"); assert_eq!(settings.target_filters()[1].level(), ksp_logging_lib::LogFilterLevel::Debug); let console = settings.console(); assert!(console.is_some(), "committed Logging Config declares console settings"); let console = match console { std::option::Option::Some(value) => value, std::option::Option::None => return, }; assert!(console.enabled()); assert_eq!(console.output(), ksp_logging_lib::ConsoleOutput::Stderr); assert!(console.ansi()); assert_eq!(console.format(), ksp_logging_lib::LogFormat::Compact); assert_eq!(console.filter().level(), ksp_logging_lib::LogFilterLevel::Debug); assert_eq!(console.filter().targets(), &["*".to_owned()]); assert_eq!(console.filter().domains(), &["*".to_owned()]); assert_eq!(settings.files().len(), 2); assert_file( &settings.files()[0], "file.all.debug", current_directory().join("logs/debug").as_path(), "ksp-debug.log", ksp_logging_lib::FileRotation::Daily, ksp_logging_lib::LogFormat::Human, ksp_logging_lib::LogFilterLevel::Debug, &["*"], &["*"], ); assert_file( &settings.files()[1], "file.config.error", current_directory().join("logs/config").as_path(), "ksp-config-errors.jsonl", ksp_logging_lib::FileRotation::Daily, ksp_logging_lib::LogFormat::Json, ksp_logging_lib::LogFilterLevel::Error, &["ksp-config-lib"], &["config"], ); } #[test] fn relative_logs_directory_is_anchored_to_process_current_directory() { let engine = committed_engine(); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let environment = environment_with_logs_directory("relative-ksp-logs"); let resolved = engine.load_resolved_logging_config(std::option::Option::None, &environment); assert!(resolved.is_ok(), "relative Logging root should map"); if let std::result::Result::Ok(resolved) = resolved { assert_eq!(resolved.logs_directory(), current_directory().join("relative-ksp-logs").as_path()); assert_eq!(resolved.settings().files()[0].directory(), current_directory().join("relative-ksp-logs/debug").as_path()); } } #[test] fn absolute_logs_directory_is_preserved() { let engine = committed_engine(); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let absolute = std::env::temp_dir().join(format!("ksp-pre012-absolute-{}", std::process::id())); let environment = environment_with_logs_directory(absolute.to_string_lossy().as_ref()); let resolved = engine.load_resolved_logging_config(std::option::Option::None, &environment); assert!(resolved.is_ok(), "absolute Logging root should map even when it does not exist yet"); if let std::result::Result::Ok(resolved) = resolved { assert_eq!(resolved.logs_directory(), absolute.as_path()); } } #[test] fn effective_file_paths_cannot_escape_logging_root() { assert!(super::relative_file_path_is_valid("debug/ksp.log")); assert!(super::relative_file_path_is_valid("ksp.log")); assert!(!super::relative_file_path_is_valid("../ksp.log")); assert!(!super::relative_file_path_is_valid("./ksp.log")); assert!(!super::relative_file_path_is_valid("/var/log/ksp.log")); } #[test] fn explicit_empty_logs_directory_is_invalid_instead_of_using_fallback() { let engine = committed_engine(); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let environment = environment_with_logs_directory(""); let resolved = engine.load_resolved_logging_config(std::option::Option::None, &environment); let error = match resolved { std::result::Result::Ok(_) => return, std::result::Result::Err(error) => error, }; assert_eq!(error.code(), crate::ERROR_CODE_EFFECTIVE_CONFIG_INVALID); assert!(error.context().iter().any(|item| -> bool { return item.key() == "field" && item.value() == "logs_directory"; })); } #[test] fn existing_non_directory_logging_root_is_rejected_without_secret_leak() { let engine = committed_engine(); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let secret = format!("ksp-secret-path-canary-{}", std::process::id()); let path = std::env::temp_dir().join(secret.as_str()); let write = std::fs::write(path.as_path(), b"not a directory"); assert!(write.is_ok(), "secret canary file should be created"); let profile = load_committed_profile(&engine); let profile = match profile { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => { cleanup_file(path.as_path()); return; }, }; let resolved = super::resolve_logs_directory(path.to_string_lossy().as_ref(), crate::REDACTED_CONFIG_VALUE, &profile); cleanup_file(path.as_path()); let error = match resolved { std::result::Result::Ok(_) => return, std::result::Result::Err(error) => error, }; assert_eq!(error.code(), crate::ERROR_CODE_EFFECTIVE_CONFIG_INVALID); let debug = format!("{error:?}"); assert!(!debug.contains(secret.as_str()), "effective Config diagnostics must not reveal real secret-derived paths"); assert!(debug.contains(crate::REDACTED_CONFIG_VALUE)); } #[test] fn logging_adapter_rejects_secret_effective_values_without_exposing_canary() { let engine = committed_engine(); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let profile = load_committed_profile(&engine); let profile = match profile { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let canary = "ksp-pre012-secret-canary-c3e4"; let mut process = std::collections::BTreeMap::::new(); process.insert("KSP_SECRET_LOGGING_CANARY".to_owned(), canary.to_owned()); let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new()); let effective = environment.resolve_json_detailed(&serde_json::json!({"canary": "${KSP_SECRET_LOGGING_CANARY}"})); assert!(effective.is_ok(), "secret canary fixture should resolve"); let effective = match effective { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let validation = super::validate_logging_sensitivity(&profile, &effective); let error = match validation { std::result::Result::Ok(()) => return, std::result::Result::Err(error) => error, }; assert_eq!(error.code(), crate::ERROR_CODE_EFFECTIVE_CONFIG_INVALID); let debug = format!("{error:?}"); assert!(!debug.contains(canary), "Logging adapter diagnostics must not reveal secret canaries"); } #[test] fn mapped_logging_settings_can_initialize_and_reinitialize_runtime() { let engine = committed_engine(); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let root = std::env::temp_dir().join(format!("ksp-pre012-runtime-{}", std::process::id())); cleanup_directory(root.as_path()); let environment = environment_with_logs_directory(root.to_string_lossy().as_ref()); let resolved = engine.load_resolved_logging_config(std::option::Option::None, &environment); assert!(resolved.is_ok(), "runtime Logging Config should map"); let resolved = match resolved { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let guard = ksp_logging_lib::initialize(resolved.settings()); assert!(guard.is_ok(), "mapped Logging settings should initialize the Logging runtime"); let mut guard = match guard { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; assert!(root.join("debug").is_dir(), "Logging initialization should create the first configured file directory"); assert!(root.join("config").is_dir(), "Logging initialization should create the second configured file directory"); let reload = ksp_logging_lib::reinitialize(&mut guard, resolved.settings()); assert!(reload.is_ok(), "mapped Logging settings should support hot reload"); let disabled = ksp_logging_lib::LoggingSettings::new( ksp_logging_lib::LogFilterLevel::Off, ksp_logging_lib::SpanEvents::Off, std::option::Option::None, std::vec::Vec::new(), ); let disable = ksp_logging_lib::reinitialize(&mut guard, &disabled); assert!(disable.is_ok(), "test Logging runtime should disable outputs before cleanup"); drop(guard); cleanup_directory(root.as_path()); } #[test] fn resolved_logging_debug_uses_safe_effective_view() { let engine = committed_engine(); let engine = match engine { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let root = std::env::temp_dir().join(format!("ksp-pre012-debug-{}", std::process::id())); let environment = environment_with_logs_directory(root.to_string_lossy().as_ref()); let resolved = engine.load_resolved_logging_config(std::option::Option::None, &environment); assert!(resolved.is_ok(), "Logging Config should map for Debug contract"); if let std::result::Result::Ok(resolved) = resolved { let debug = format!("{resolved:?}"); assert!(debug.contains("ResolvedLoggingConfig")); assert!(debug.contains("effective")); } } fn assert_file( file: &ksp_logging_lib::FileSettings, output_id: &str, directory: &std::path::Path, file_name: &str, rotation: ksp_logging_lib::FileRotation, format: ksp_logging_lib::LogFormat, level: ksp_logging_lib::LogFilterLevel, targets: &[&str], domains: &[&str], ) { assert_eq!(file.output_id(), output_id); assert!(file.enabled()); assert_eq!(file.directory(), directory); assert_eq!(file.file_name_prefix(), file_name); assert_eq!(file.rotation(), rotation); assert_eq!(file.format(), format); assert!(!file.ansi()); assert_eq!(file.filter().level(), level); assert_eq!(file.filter().targets().iter().map(String::as_str).collect::>(), targets.to_vec()); assert_eq!(file.filter().domains().iter().map(String::as_str).collect::>(), domains.to_vec()); } fn committed_engine() -> ksp_core_lib::Result { let workspace = workspace_root(); let bootstrap = crate::ConfigBootstrapOptions::from_paths(workspace.join("config"), workspace.join("config/schemas")); let bootstrap = match bootstrap { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let registry = crate::ConfigFileRegistry::defaults(); let registry = match registry { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; return std::result::Result::Ok(crate::ConfigDocumentEngine::new(bootstrap, registry)); } fn load_committed_profile(engine: &crate::ConfigDocumentEngine) -> ksp_core_lib::Result { let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING); let file_id = match file_id { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; return engine.load_resolved_profile(&file_id, std::option::Option::None); } fn environment_with_logs_directory(value: &str) -> crate::ConfigEnvironment { let mut process = std::collections::BTreeMap::::new(); process.insert("KSP_LOGS_DIRECTORY".to_owned(), value.to_owned()); return crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new()); } fn workspace_root() -> std::path::PathBuf { return std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../.."); } fn current_directory() -> std::path::PathBuf { let current = std::env::current_dir(); return match current { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => std::path::PathBuf::new(), }; } fn cleanup_file(path: &std::path::Path) { let removal = std::fs::remove_file(path); if let std::result::Result::Err(error) = removal && error.kind() != std::io::ErrorKind::NotFound { eprintln!("unable to cleanup Config Logging adapter file {}: {error}", path.display()); } } fn cleanup_directory(path: &std::path::Path) { let removal = std::fs::remove_dir_all(path); if let std::result::Result::Err(error) = removal && error.kind() != std::io::ErrorKind::NotFound { eprintln!("unable to cleanup Config Logging adapter directory {}: {error}", path.display()); } }