// file: crates/ksp-logging-lib/unit_tests/settings.rs // version: 3 fn unrestricted_file(output_id: &str) -> crate::FileSettings { return crate::FileSettings::new( output_id, true, "logs", format!("{output_id}.log"), crate::FileRotation::Daily, crate::LogFormat::Human, crate::OutputFilter::unrestricted(), ); } #[test] fn level_variants_are_distinct() { assert_ne!(crate::LogFilterLevel::Off, crate::LogFilterLevel::Error); assert_ne!(crate::LogFilterLevel::Error, crate::LogFilterLevel::Warn); assert_ne!(crate::LogFilterLevel::Warn, crate::LogFilterLevel::Info); assert_ne!(crate::LogFilterLevel::Info, crate::LogFilterLevel::Debug); assert_ne!(crate::LogFilterLevel::Debug, crate::LogFilterLevel::Trace); } #[test] fn format_variants_are_distinct() { assert_ne!(crate::LogFormat::Human, crate::LogFormat::Compact); assert_ne!(crate::LogFormat::Compact, crate::LogFormat::Pretty); assert_ne!(crate::LogFormat::Pretty, crate::LogFormat::Json); } #[test] fn target_filter_preserves_prefix_and_level() { let filter = crate::TargetFilter::new("ksp-store-lib", crate::LogFilterLevel::Trace); assert_eq!(filter.target_prefix(), "ksp-store-lib"); assert_eq!(filter.level(), crate::LogFilterLevel::Trace); } #[test] fn output_filter_preserves_level_targets_and_domains() { let filter = crate::OutputFilter::new( crate::LogFilterLevel::Debug, std::vec!["ksp-config-lib".to_string(), "ksp-logging-lib".to_string()], std::vec!["config".to_string(), "logging.runtime".to_string()], ); assert_eq!(filter.level(), crate::LogFilterLevel::Debug); assert_eq!(filter.targets().len(), 2); assert_eq!(filter.targets()[0], "ksp-config-lib"); assert_eq!(filter.targets()[1], "ksp-logging-lib"); assert_eq!(filter.domains().len(), 2); assert_eq!(filter.domains()[0], "config"); assert_eq!(filter.domains()[1], "logging.runtime"); let unrestricted = crate::OutputFilter::unrestricted(); assert_eq!(unrestricted.level(), crate::LogFilterLevel::Trace); assert_eq!(unrestricted.targets(), &["*".to_string()]); assert_eq!(unrestricted.domains(), &["*".to_string()]); } #[test] fn console_settings_preserve_enabled_stream_ansi_format_and_filter() { let filter = crate::OutputFilter::new(crate::LogFilterLevel::Debug, std::vec!["*".to_string()], std::vec!["config".to_string()]); let settings = crate::ConsoleSettings::new(true, crate::ConsoleOutput::Stderr, true, crate::LogFormat::Compact, filter.clone()); assert!(settings.enabled()); assert_eq!(settings.output(), crate::ConsoleOutput::Stderr); assert!(settings.ansi()); assert_eq!(settings.format(), crate::LogFormat::Compact); assert_eq!(settings.filter(), &filter); } #[test] fn compatibility_console_constructors_are_unrestricted_human_and_non_ansi() { let stdout = crate::ConsoleSettings::stdout(); let stderr = crate::ConsoleSettings::stderr(); assert_eq!(stdout.output(), crate::ConsoleOutput::Stdout); assert_eq!(stderr.output(), crate::ConsoleOutput::Stderr); assert!(stdout.enabled()); assert!(!stdout.ansi()); assert_eq!(stdout.format(), crate::LogFormat::Human); assert_eq!(stdout.filter(), &crate::OutputFilter::unrestricted()); } #[test] fn file_settings_preserve_multi_output_contract() { let filter = crate::OutputFilter::new(crate::LogFilterLevel::Error, std::vec!["ksp-config-lib".to_string()], std::vec!["config".to_string()]); let settings = crate::FileSettings::new("file.config.error", true, "logs/config", "error.jsonl", crate::FileRotation::Daily, crate::LogFormat::Json, filter.clone()); assert_eq!(settings.output_id(), "file.config.error"); assert!(settings.enabled()); assert_eq!(settings.directory(), std::path::Path::new("logs/config")); assert_eq!(settings.file_name_prefix(), "error.jsonl"); assert_eq!(settings.rotation(), crate::FileRotation::Daily); assert_eq!(settings.format(), crate::LogFormat::Json); assert!(!settings.ansi()); assert_eq!(settings.filter(), &filter); } #[test] fn logging_settings_represent_multiple_file_outputs() { let first = unrestricted_file("file.debug"); let second = crate::FileSettings::new( "file.config.error", false, "logs/config", "error.jsonl", crate::FileRotation::Hourly, crate::LogFormat::Json, crate::OutputFilter::new(crate::LogFilterLevel::Error, std::vec!["ksp-config-lib".to_string()], std::vec!["config".to_string()]), ); let settings = crate::LoggingSettings::new( crate::LogFilterLevel::Info, crate::SpanEvents::NewAndClose, std::option::Option::Some(crate::ConsoleSettings::stdout()), std::vec![first, second], ) .with_target_filter(crate::TargetFilter::new("ksp-store-lib", crate::LogFilterLevel::Trace)); assert_eq!(settings.default_filter(), crate::LogFilterLevel::Info); assert_eq!(settings.span_events(), crate::SpanEvents::NewAndClose); assert_eq!(settings.target_filters().len(), 1); assert_eq!(settings.files().len(), 2); assert_eq!(settings.files()[0].output_id(), "file.debug"); assert_eq!(settings.files()[1].format(), crate::LogFormat::Json); assert_eq!(settings.console().map(crate::ConsoleSettings::output), std::option::Option::Some(crate::ConsoleOutput::Stdout)); assert!(settings.validate().is_ok()); } #[test] fn validation_rejects_empty_or_external_global_target_prefix() { let empty = crate::LoggingSettings::new(crate::LogFilterLevel::Info, crate::SpanEvents::Off, std::option::Option::None, std::vec::Vec::new()) .with_target_filter(crate::TargetFilter::new("", crate::LogFilterLevel::Debug)); let external = crate::LoggingSettings::new(crate::LogFilterLevel::Info, crate::SpanEvents::Off, std::option::Option::None, std::vec::Vec::new()) .with_target_filter(crate::TargetFilter::new("sqlx", crate::LogFilterLevel::Debug)); assert!(empty.validate().is_err()); assert!(external.validate().is_err()); } #[test] fn validation_rejects_invalid_file_identity_and_duplicate_output_ids() { let invalid_id = crate::LoggingSettings::new(crate::LogFilterLevel::Info, crate::SpanEvents::Off, std::option::Option::None, std::vec![unrestricted_file("File.Debug")]); let duplicate_id = crate::LoggingSettings::new( crate::LogFilterLevel::Info, crate::SpanEvents::Off, std::option::Option::None, std::vec![unrestricted_file("file.debug"), unrestricted_file("file.debug")], ); assert!(invalid_id.validate().is_err()); assert!(duplicate_id.validate().is_err()); } #[test] fn validation_rejects_empty_file_paths_and_persistent_ansi() { let empty_directory = crate::FileSettings::new( "file.empty.dir", true, "", "debug.log", crate::FileRotation::Never, crate::LogFormat::Human, crate::OutputFilter::unrestricted(), ); let empty_prefix = crate::FileSettings::new( "file.empty.prefix", true, "logs", "", crate::FileRotation::Never, crate::LogFormat::Human, crate::OutputFilter::unrestricted(), ); let ansi_file = crate::FileSettings::new( "file.ansi", true, "logs", "ansi.log", crate::FileRotation::Never, crate::LogFormat::Human, crate::OutputFilter::unrestricted(), ) .with_ansi(true); let empty_directory_settings = crate::LoggingSettings::new(crate::LogFilterLevel::Info, crate::SpanEvents::Off, std::option::Option::None, std::vec![empty_directory]); let empty_prefix_settings = crate::LoggingSettings::new(crate::LogFilterLevel::Info, crate::SpanEvents::Off, std::option::Option::None, std::vec![empty_prefix]); let ansi_file_settings = crate::LoggingSettings::new(crate::LogFilterLevel::Info, crate::SpanEvents::Off, std::option::Option::None, std::vec![ansi_file]); assert!(empty_directory_settings.validate().is_err()); assert!(empty_prefix_settings.validate().is_err()); assert!(ansi_file_settings.validate().is_err()); } #[test] fn validation_rejects_ansi_json_console() { let console = crate::ConsoleSettings::new(true, crate::ConsoleOutput::Stdout, true, crate::LogFormat::Json, crate::OutputFilter::unrestricted()); let settings = crate::LoggingSettings::new(crate::LogFilterLevel::Info, crate::SpanEvents::Off, std::option::Option::Some(console), std::vec::Vec::new()); assert!(settings.validate().is_err()); } #[test] fn validation_rejects_invalid_output_selectors() { let external_target = crate::OutputFilter::new(crate::LogFilterLevel::Debug, std::vec!["sqlx".to_string()], std::vec!["*".to_string()]); let wildcard_mix = crate::OutputFilter::new(crate::LogFilterLevel::Debug, std::vec!["*".to_string(), "ksp-config-lib".to_string()], std::vec!["*".to_string()]); let duplicate_domain = crate::OutputFilter::new(crate::LogFilterLevel::Debug, std::vec!["*".to_string()], std::vec!["config".to_string(), "config".to_string()]); for filter in [external_target, wildcard_mix, duplicate_domain] { let console = crate::ConsoleSettings::new(true, crate::ConsoleOutput::Stdout, false, crate::LogFormat::Human, filter); let settings = crate::LoggingSettings::new(crate::LogFilterLevel::Info, crate::SpanEvents::Off, std::option::Option::Some(console), std::vec::Vec::new()); assert!(settings.validate().is_err()); } } #[test] fn settings_allow_all_outputs_to_be_disabled() { let console = crate::ConsoleSettings::new(false, crate::ConsoleOutput::Stderr, true, crate::LogFormat::Pretty, crate::OutputFilter::unrestricted()); let file = crate::FileSettings::new( "file.disabled", false, "logs", "disabled.jsonl", crate::FileRotation::Daily, crate::LogFormat::Json, crate::OutputFilter::new(crate::LogFilterLevel::Error, std::vec!["ksp-config-lib".to_string()], std::vec!["config".to_string()]), ); let settings = crate::LoggingSettings::new(crate::LogFilterLevel::Off, crate::SpanEvents::Off, std::option::Option::Some(console), std::vec![file]); assert!(settings.validate().is_ok()); }