Files
khadhroony-solana-project/crates/ksp-config-lib/unit_tests/logging.rs

332 lines
15 KiB
Rust

// file: crates/ksp-config-lib/unit_tests/logging.rs
// version: 2
#[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(), 3);
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);
assert_eq!(settings.target_filters()[2].target_prefix(), "ksp-app-config-desk");
assert_eq!(settings.target_filters()[2].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::<String, String>::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::<std::vec::Vec<&str>>(), targets.to_vec());
assert_eq!(file.filter().domains().iter().map(String::as_str).collect::<std::vec::Vec<&str>>(), domains.to_vec());
}
fn committed_engine() -> ksp_core_lib::Result<crate::ConfigDocumentEngine> {
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<crate::ResolvedConfigProfile> {
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::<String, String>::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());
}
}