mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 16:28:15 +00:00
feat: add comprehensive test coverage for config module
This commit is contained in:
@@ -21,3 +21,174 @@ impl Default for RustFsConfig {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_rustfs_config_new() {
|
||||
let config = RustFsConfig::new();
|
||||
|
||||
// Verify that observability config is properly initialized
|
||||
assert!(!config.observability.sinks.is_empty(), "Observability sinks should not be empty");
|
||||
assert!(config.observability.logger.is_some(), "Logger config should be present");
|
||||
|
||||
// Verify that event config is properly initialized
|
||||
assert!(!config.event.store_path.is_empty(), "Event store path should not be empty");
|
||||
assert!(config.event.channel_capacity > 0, "Channel capacity should be positive");
|
||||
assert!(!config.event.adapters.is_empty(), "Event adapters should not be empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustfs_config_default() {
|
||||
let config = RustFsConfig::default();
|
||||
|
||||
// Default should be equivalent to new()
|
||||
let new_config = RustFsConfig::new();
|
||||
|
||||
// Compare observability config
|
||||
assert_eq!(config.observability.sinks.len(), new_config.observability.sinks.len());
|
||||
assert_eq!(config.observability.logger.is_some(), new_config.observability.logger.is_some());
|
||||
|
||||
// Compare event config
|
||||
assert_eq!(config.event.store_path, new_config.event.store_path);
|
||||
assert_eq!(config.event.channel_capacity, new_config.event.channel_capacity);
|
||||
assert_eq!(config.event.adapters.len(), new_config.event.adapters.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustfs_config_components_independence() {
|
||||
let mut config = RustFsConfig::new();
|
||||
|
||||
// Modify observability config
|
||||
config.observability.sinks.clear();
|
||||
|
||||
// Event config should remain unchanged
|
||||
assert!(!config.event.adapters.is_empty(), "Event adapters should remain unchanged");
|
||||
assert!(config.event.channel_capacity > 0, "Channel capacity should remain unchanged");
|
||||
|
||||
// Create new config to verify independence
|
||||
let new_config = RustFsConfig::new();
|
||||
assert!(!new_config.observability.sinks.is_empty(), "New config should have default sinks");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustfs_config_observability_integration() {
|
||||
let config = RustFsConfig::new();
|
||||
|
||||
// Test observability config properties
|
||||
assert!(config.observability.otel.endpoint.is_empty() || !config.observability.otel.endpoint.is_empty());
|
||||
assert!(config.observability.otel.use_stdout.is_some());
|
||||
assert!(config.observability.otel.sample_ratio.is_some());
|
||||
assert!(config.observability.otel.meter_interval.is_some());
|
||||
assert!(config.observability.otel.service_name.is_some());
|
||||
assert!(config.observability.otel.service_version.is_some());
|
||||
assert!(config.observability.otel.environment.is_some());
|
||||
assert!(config.observability.otel.logger_level.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustfs_config_event_integration() {
|
||||
let config = RustFsConfig::new();
|
||||
|
||||
// Test event config properties
|
||||
assert!(!config.event.store_path.is_empty(), "Store path should not be empty");
|
||||
assert!(config.event.channel_capacity >= 1000, "Channel capacity should be reasonable for production");
|
||||
|
||||
// Test that store path is a valid path format
|
||||
let store_path = &config.event.store_path;
|
||||
assert!(!store_path.contains('\0'), "Store path should not contain null characters");
|
||||
|
||||
// Test adapters configuration
|
||||
for adapter in &config.event.adapters {
|
||||
// Each adapter should have a valid configuration
|
||||
match adapter {
|
||||
crate::event::adapters::AdapterConfig::Webhook(_) => {
|
||||
// Webhook adapter should be properly configured
|
||||
},
|
||||
crate::event::adapters::AdapterConfig::Kafka(_) => {
|
||||
// Kafka adapter should be properly configured
|
||||
},
|
||||
crate::event::adapters::AdapterConfig::Mqtt(_) => {
|
||||
// MQTT adapter should be properly configured
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustfs_config_memory_usage() {
|
||||
// Test that config doesn't use excessive memory
|
||||
let config = RustFsConfig::new();
|
||||
|
||||
// Basic memory usage checks
|
||||
assert!(std::mem::size_of_val(&config) < 10000, "Config should not use excessive memory");
|
||||
|
||||
// Test that strings are not excessively long
|
||||
assert!(config.event.store_path.len() < 1000, "Store path should not be excessively long");
|
||||
|
||||
// Test that collections are reasonably sized
|
||||
assert!(config.observability.sinks.len() < 100, "Sinks collection should be reasonably sized");
|
||||
assert!(config.event.adapters.len() < 100, "Adapters collection should be reasonably sized");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustfs_config_serialization_compatibility() {
|
||||
let config = RustFsConfig::new();
|
||||
|
||||
// Test that observability config can be serialized (it has Serialize trait)
|
||||
let observability_json = serde_json::to_string(&config.observability);
|
||||
assert!(observability_json.is_ok(), "Observability config should be serializable");
|
||||
|
||||
// Test that event config can be serialized (it has Serialize trait)
|
||||
let event_json = serde_json::to_string(&config.event);
|
||||
assert!(event_json.is_ok(), "Event config should be serializable");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustfs_config_debug_format() {
|
||||
let config = RustFsConfig::new();
|
||||
|
||||
// Test that observability config has Debug trait
|
||||
let observability_debug = format!("{:?}", config.observability);
|
||||
assert!(!observability_debug.is_empty(), "Observability config should have debug output");
|
||||
assert!(observability_debug.contains("ObservabilityConfig"), "Debug output should contain type name");
|
||||
|
||||
// Test that event config has Debug trait
|
||||
let event_debug = format!("{:?}", config.event);
|
||||
assert!(!event_debug.is_empty(), "Event config should have debug output");
|
||||
assert!(event_debug.contains("NotifierConfig"), "Debug output should contain type name");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustfs_config_clone_behavior() {
|
||||
let config = RustFsConfig::new();
|
||||
|
||||
// Test that observability config can be cloned
|
||||
let observability_clone = config.observability.clone();
|
||||
assert_eq!(observability_clone.sinks.len(), config.observability.sinks.len());
|
||||
|
||||
// Test that event config can be cloned
|
||||
let event_clone = config.event.clone();
|
||||
assert_eq!(event_clone.store_path, config.event.store_path);
|
||||
assert_eq!(event_clone.channel_capacity, config.event.channel_capacity);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustfs_config_environment_independence() {
|
||||
// Test that config creation doesn't depend on specific environment variables
|
||||
// This test ensures the config can be created in any environment
|
||||
|
||||
let config1 = RustFsConfig::new();
|
||||
let config2 = RustFsConfig::new();
|
||||
|
||||
// Both configs should have the same structure
|
||||
assert_eq!(config1.observability.sinks.len(), config2.observability.sinks.len());
|
||||
assert_eq!(config1.event.adapters.len(), config2.event.adapters.len());
|
||||
|
||||
// Store paths should be consistent
|
||||
assert_eq!(config1.event.store_path, config2.event.store_path);
|
||||
assert_eq!(config1.event.channel_capacity, config2.event.channel_capacity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ mod tests {
|
||||
"Log level should be a valid tracing level"
|
||||
);
|
||||
|
||||
assert_eq!(USE_STDOUT, true);
|
||||
assert_eq!(USE_STDOUT, false);
|
||||
|
||||
assert_eq!(SAMPLE_RATIO, 1.0);
|
||||
assert!(SAMPLE_RATIO >= 0.0 && SAMPLE_RATIO <= 1.0, "Sample ratio should be between 0.0 and 1.0");
|
||||
|
||||
@@ -41,3 +41,284 @@ fn default_store_path() -> String {
|
||||
fn default_channel_capacity() -> usize {
|
||||
10000 // Reasonable default values for high concurrency systems
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_new() {
|
||||
let config = NotifierConfig::new();
|
||||
|
||||
// Verify store path is set
|
||||
assert!(!config.store_path.is_empty(), "Store path should not be empty");
|
||||
assert!(config.store_path.contains("event-notification"), "Store path should contain event-notification");
|
||||
|
||||
// Verify channel capacity is reasonable
|
||||
assert_eq!(config.channel_capacity, 10000, "Channel capacity should be 10000");
|
||||
assert!(config.channel_capacity > 0, "Channel capacity should be positive");
|
||||
|
||||
// Verify adapters are initialized
|
||||
assert!(!config.adapters.is_empty(), "Adapters should not be empty");
|
||||
assert_eq!(config.adapters.len(), 1, "Should have exactly one default adapter");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_default() {
|
||||
let config = NotifierConfig::default();
|
||||
let new_config = NotifierConfig::new();
|
||||
|
||||
// Default should be equivalent to new()
|
||||
assert_eq!(config.store_path, new_config.store_path);
|
||||
assert_eq!(config.channel_capacity, new_config.channel_capacity);
|
||||
assert_eq!(config.adapters.len(), new_config.adapters.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_store_path() {
|
||||
let store_path = default_store_path();
|
||||
|
||||
// Verify store path properties
|
||||
assert!(!store_path.is_empty(), "Store path should not be empty");
|
||||
assert!(store_path.contains("event-notification"), "Store path should contain event-notification");
|
||||
|
||||
// Verify it's a valid path format
|
||||
let path = Path::new(&store_path);
|
||||
assert!(path.is_absolute() || path.is_relative(), "Store path should be a valid path");
|
||||
|
||||
// Verify it doesn't contain invalid characters
|
||||
assert!(!store_path.contains('\0'), "Store path should not contain null characters");
|
||||
|
||||
// Verify it's based on temp directory
|
||||
let temp_dir = env::temp_dir();
|
||||
let expected_path = temp_dir.join("event-notification");
|
||||
assert_eq!(store_path, expected_path.to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_channel_capacity() {
|
||||
let capacity = default_channel_capacity();
|
||||
|
||||
// Verify capacity is reasonable
|
||||
assert_eq!(capacity, 10000, "Default capacity should be 10000");
|
||||
assert!(capacity > 0, "Capacity should be positive");
|
||||
assert!(capacity >= 1000, "Capacity should be at least 1000 for production use");
|
||||
assert!(capacity <= 1_000_000, "Capacity should not be excessively large");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_serialization() {
|
||||
let config = NotifierConfig::new();
|
||||
|
||||
// Test serialization to JSON
|
||||
let json_result = serde_json::to_string(&config);
|
||||
assert!(json_result.is_ok(), "Config should be serializable to JSON");
|
||||
|
||||
let json_str = json_result.unwrap();
|
||||
assert!(!json_str.is_empty(), "Serialized JSON should not be empty");
|
||||
assert!(json_str.contains("store_path"), "JSON should contain store_path");
|
||||
assert!(json_str.contains("channel_capacity"), "JSON should contain channel_capacity");
|
||||
assert!(json_str.contains("adapters"), "JSON should contain adapters");
|
||||
|
||||
// Test deserialization from JSON
|
||||
let deserialized_result: Result<NotifierConfig, _> = serde_json::from_str(&json_str);
|
||||
assert!(deserialized_result.is_ok(), "Config should be deserializable from JSON");
|
||||
|
||||
let deserialized_config = deserialized_result.unwrap();
|
||||
assert_eq!(deserialized_config.store_path, config.store_path);
|
||||
assert_eq!(deserialized_config.channel_capacity, config.channel_capacity);
|
||||
assert_eq!(deserialized_config.adapters.len(), config.adapters.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_serialization_with_defaults() {
|
||||
// Test serialization with minimal JSON (using serde defaults)
|
||||
let minimal_json = r#"{"adapters": []}"#;
|
||||
|
||||
let deserialized_result: Result<NotifierConfig, _> = serde_json::from_str(minimal_json);
|
||||
assert!(deserialized_result.is_ok(), "Config should deserialize with defaults");
|
||||
|
||||
let config = deserialized_result.unwrap();
|
||||
assert_eq!(config.store_path, default_store_path(), "Should use default store path");
|
||||
assert_eq!(config.channel_capacity, default_channel_capacity(), "Should use default channel capacity");
|
||||
assert!(config.adapters.is_empty(), "Should have empty adapters as specified");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_debug_format() {
|
||||
let config = NotifierConfig::new();
|
||||
|
||||
let debug_str = format!("{:?}", config);
|
||||
assert!(!debug_str.is_empty(), "Debug output should not be empty");
|
||||
assert!(debug_str.contains("NotifierConfig"), "Debug output should contain struct name");
|
||||
assert!(debug_str.contains("store_path"), "Debug output should contain store_path field");
|
||||
assert!(debug_str.contains("channel_capacity"), "Debug output should contain channel_capacity field");
|
||||
assert!(debug_str.contains("adapters"), "Debug output should contain adapters field");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_clone() {
|
||||
let config = NotifierConfig::new();
|
||||
let cloned_config = config.clone();
|
||||
|
||||
// Test that clone creates an independent copy
|
||||
assert_eq!(cloned_config.store_path, config.store_path);
|
||||
assert_eq!(cloned_config.channel_capacity, config.channel_capacity);
|
||||
assert_eq!(cloned_config.adapters.len(), config.adapters.len());
|
||||
|
||||
// Verify they are independent (modifying one doesn't affect the other)
|
||||
let mut modified_config = config.clone();
|
||||
modified_config.channel_capacity = 5000;
|
||||
assert_ne!(modified_config.channel_capacity, config.channel_capacity);
|
||||
assert_eq!(cloned_config.channel_capacity, config.channel_capacity);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_modification() {
|
||||
let mut config = NotifierConfig::new();
|
||||
|
||||
// Test modifying store path
|
||||
let original_store_path = config.store_path.clone();
|
||||
config.store_path = "/custom/path".to_string();
|
||||
assert_ne!(config.store_path, original_store_path);
|
||||
assert_eq!(config.store_path, "/custom/path");
|
||||
|
||||
// Test modifying channel capacity
|
||||
let original_capacity = config.channel_capacity;
|
||||
config.channel_capacity = 5000;
|
||||
assert_ne!(config.channel_capacity, original_capacity);
|
||||
assert_eq!(config.channel_capacity, 5000);
|
||||
|
||||
// Test modifying adapters
|
||||
let original_adapters_len = config.adapters.len();
|
||||
config.adapters.push(AdapterConfig::new());
|
||||
assert_eq!(config.adapters.len(), original_adapters_len + 1);
|
||||
|
||||
// Test clearing adapters
|
||||
config.adapters.clear();
|
||||
assert!(config.adapters.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_adapters() {
|
||||
let config = NotifierConfig::new();
|
||||
|
||||
// Test default adapter configuration
|
||||
assert_eq!(config.adapters.len(), 1, "Should have exactly one default adapter");
|
||||
|
||||
// Test that we can add more adapters
|
||||
let mut config_mut = config.clone();
|
||||
config_mut.adapters.push(AdapterConfig::new());
|
||||
assert_eq!(config_mut.adapters.len(), 2, "Should be able to add more adapters");
|
||||
|
||||
// Test adapter types
|
||||
for adapter in &config.adapters {
|
||||
match adapter {
|
||||
AdapterConfig::Webhook(_) => {
|
||||
// Webhook adapter should be properly configured
|
||||
},
|
||||
AdapterConfig::Kafka(_) => {
|
||||
// Kafka adapter should be properly configured
|
||||
},
|
||||
AdapterConfig::Mqtt(_) => {
|
||||
// MQTT adapter should be properly configured
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_edge_cases() {
|
||||
// Test with empty adapters
|
||||
let mut config = NotifierConfig::new();
|
||||
config.adapters.clear();
|
||||
assert!(config.adapters.is_empty(), "Adapters should be empty after clearing");
|
||||
|
||||
// Test serialization with empty adapters
|
||||
let json_result = serde_json::to_string(&config);
|
||||
assert!(json_result.is_ok(), "Config with empty adapters should be serializable");
|
||||
|
||||
// Test with very large channel capacity
|
||||
config.channel_capacity = 1_000_000;
|
||||
assert_eq!(config.channel_capacity, 1_000_000);
|
||||
|
||||
// Test with minimum channel capacity
|
||||
config.channel_capacity = 1;
|
||||
assert_eq!(config.channel_capacity, 1);
|
||||
|
||||
// Test with empty store path
|
||||
config.store_path = String::new();
|
||||
assert!(config.store_path.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_memory_efficiency() {
|
||||
let config = NotifierConfig::new();
|
||||
|
||||
// Test that config doesn't use excessive memory
|
||||
let config_size = std::mem::size_of_val(&config);
|
||||
assert!(config_size < 5000, "Config should not use excessive memory");
|
||||
|
||||
// Test that store path is not excessively long
|
||||
assert!(config.store_path.len() < 1000, "Store path should not be excessively long");
|
||||
|
||||
// Test that adapters collection is reasonably sized
|
||||
assert!(config.adapters.len() < 100, "Adapters collection should be reasonably sized");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_consistency() {
|
||||
// Create multiple configs and ensure they're consistent
|
||||
let config1 = NotifierConfig::new();
|
||||
let config2 = NotifierConfig::new();
|
||||
|
||||
// Both configs should have the same default values
|
||||
assert_eq!(config1.store_path, config2.store_path);
|
||||
assert_eq!(config1.channel_capacity, config2.channel_capacity);
|
||||
assert_eq!(config1.adapters.len(), config2.adapters.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_path_validation() {
|
||||
let config = NotifierConfig::new();
|
||||
|
||||
// Test that store path is a valid path
|
||||
let path = Path::new(&config.store_path);
|
||||
|
||||
// Path should be valid
|
||||
assert!(path.components().count() > 0, "Path should have components");
|
||||
|
||||
// Path should not contain invalid characters for most filesystems
|
||||
assert!(!config.store_path.contains('\0'), "Path should not contain null characters");
|
||||
assert!(!config.store_path.contains('\x01'), "Path should not contain control characters");
|
||||
|
||||
// Path should be reasonable length
|
||||
assert!(config.store_path.len() < 260, "Path should be shorter than Windows MAX_PATH");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notifier_config_production_readiness() {
|
||||
let config = NotifierConfig::new();
|
||||
|
||||
// Test production readiness criteria
|
||||
assert!(config.channel_capacity >= 1000, "Channel capacity should be sufficient for production");
|
||||
assert!(!config.store_path.is_empty(), "Store path should be configured");
|
||||
assert!(!config.adapters.is_empty(), "At least one adapter should be configured");
|
||||
|
||||
// Test that configuration is reasonable for high-load scenarios
|
||||
assert!(config.channel_capacity <= 10_000_000, "Channel capacity should not be excessive");
|
||||
|
||||
// Test that store path is in a reasonable location (temp directory)
|
||||
assert!(config.store_path.contains("event-notification"), "Store path should be identifiable");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_config_file_constant() {
|
||||
// Test that the constant is properly defined
|
||||
assert_eq!(DEFAULT_CONFIG_FILE, "event");
|
||||
assert!(!DEFAULT_CONFIG_FILE.is_empty(), "Config file name should not be empty");
|
||||
assert!(!DEFAULT_CONFIG_FILE.contains('/'), "Config file name should not contain path separators");
|
||||
assert!(!DEFAULT_CONFIG_FILE.contains('\\'), "Config file name should not contain Windows path separators");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,3 +26,251 @@ impl Default for ObservabilityConfig {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_new() {
|
||||
let config = ObservabilityConfig::new();
|
||||
|
||||
// Verify OTEL config is initialized
|
||||
assert!(config.otel.use_stdout.is_some(), "OTEL use_stdout should be configured");
|
||||
assert!(config.otel.sample_ratio.is_some(), "OTEL sample_ratio should be configured");
|
||||
assert!(config.otel.meter_interval.is_some(), "OTEL meter_interval should be configured");
|
||||
assert!(config.otel.service_name.is_some(), "OTEL service_name should be configured");
|
||||
assert!(config.otel.service_version.is_some(), "OTEL service_version should be configured");
|
||||
assert!(config.otel.environment.is_some(), "OTEL environment should be configured");
|
||||
assert!(config.otel.logger_level.is_some(), "OTEL logger_level should be configured");
|
||||
|
||||
// Verify sinks are initialized
|
||||
assert!(!config.sinks.is_empty(), "Sinks should not be empty");
|
||||
assert_eq!(config.sinks.len(), 1, "Should have exactly one default sink");
|
||||
|
||||
// Verify logger is initialized
|
||||
assert!(config.logger.is_some(), "Logger should be configured");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_default() {
|
||||
let config = ObservabilityConfig::default();
|
||||
let new_config = ObservabilityConfig::new();
|
||||
|
||||
// Default should be equivalent to new()
|
||||
assert_eq!(config.sinks.len(), new_config.sinks.len());
|
||||
assert_eq!(config.logger.is_some(), new_config.logger.is_some());
|
||||
|
||||
// OTEL configs should be equivalent
|
||||
assert_eq!(config.otel.use_stdout, new_config.otel.use_stdout);
|
||||
assert_eq!(config.otel.sample_ratio, new_config.otel.sample_ratio);
|
||||
assert_eq!(config.otel.meter_interval, new_config.otel.meter_interval);
|
||||
assert_eq!(config.otel.service_name, new_config.otel.service_name);
|
||||
assert_eq!(config.otel.service_version, new_config.otel.service_version);
|
||||
assert_eq!(config.otel.environment, new_config.otel.environment);
|
||||
assert_eq!(config.otel.logger_level, new_config.otel.logger_level);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_otel_defaults() {
|
||||
let config = ObservabilityConfig::new();
|
||||
|
||||
// Test OTEL default values
|
||||
if let Some(use_stdout) = config.otel.use_stdout {
|
||||
assert!(use_stdout == true || use_stdout == false, "use_stdout should be a valid boolean");
|
||||
}
|
||||
|
||||
if let Some(sample_ratio) = config.otel.sample_ratio {
|
||||
assert!(sample_ratio >= 0.0 && sample_ratio <= 1.0, "Sample ratio should be between 0.0 and 1.0");
|
||||
}
|
||||
|
||||
if let Some(meter_interval) = config.otel.meter_interval {
|
||||
assert!(meter_interval > 0, "Meter interval should be positive");
|
||||
assert!(meter_interval <= 3600, "Meter interval should be reasonable (≤ 1 hour)");
|
||||
}
|
||||
|
||||
if let Some(service_name) = &config.otel.service_name {
|
||||
assert!(!service_name.is_empty(), "Service name should not be empty");
|
||||
assert!(!service_name.contains(' '), "Service name should not contain spaces");
|
||||
}
|
||||
|
||||
if let Some(service_version) = &config.otel.service_version {
|
||||
assert!(!service_version.is_empty(), "Service version should not be empty");
|
||||
}
|
||||
|
||||
if let Some(environment) = &config.otel.environment {
|
||||
assert!(!environment.is_empty(), "Environment should not be empty");
|
||||
assert!(
|
||||
["development", "staging", "production", "test"].contains(&environment.as_str()),
|
||||
"Environment should be a standard environment name"
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(logger_level) = &config.otel.logger_level {
|
||||
assert!(
|
||||
["trace", "debug", "info", "warn", "error"].contains(&logger_level.as_str()),
|
||||
"Logger level should be a valid tracing level"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_sinks() {
|
||||
let config = ObservabilityConfig::new();
|
||||
|
||||
// Test default sink configuration
|
||||
assert_eq!(config.sinks.len(), 1, "Should have exactly one default sink");
|
||||
|
||||
let _default_sink = &config.sinks[0];
|
||||
// Test that the sink has valid configuration
|
||||
// Note: We can't test specific values without knowing SinkConfig implementation
|
||||
// but we can test that it's properly initialized
|
||||
|
||||
// Test that we can add more sinks
|
||||
let mut config_mut = config.clone();
|
||||
config_mut.sinks.push(SinkConfig::new());
|
||||
assert_eq!(config_mut.sinks.len(), 2, "Should be able to add more sinks");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_logger() {
|
||||
let config = ObservabilityConfig::new();
|
||||
|
||||
// Test logger configuration
|
||||
assert!(config.logger.is_some(), "Logger should be configured by default");
|
||||
|
||||
if let Some(_logger) = &config.logger {
|
||||
// Test that logger has valid configuration
|
||||
// Note: We can't test specific values without knowing LoggerConfig implementation
|
||||
// but we can test that it's properly initialized
|
||||
}
|
||||
|
||||
// Test that logger can be disabled
|
||||
let mut config_mut = config.clone();
|
||||
config_mut.logger = None;
|
||||
assert!(config_mut.logger.is_none(), "Logger should be able to be disabled");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_serialization() {
|
||||
let config = ObservabilityConfig::new();
|
||||
|
||||
// Test serialization to JSON
|
||||
let json_result = serde_json::to_string(&config);
|
||||
assert!(json_result.is_ok(), "Config should be serializable to JSON");
|
||||
|
||||
let json_str = json_result.unwrap();
|
||||
assert!(!json_str.is_empty(), "Serialized JSON should not be empty");
|
||||
assert!(json_str.contains("otel"), "JSON should contain otel configuration");
|
||||
assert!(json_str.contains("sinks"), "JSON should contain sinks configuration");
|
||||
assert!(json_str.contains("logger"), "JSON should contain logger configuration");
|
||||
|
||||
// Test deserialization from JSON
|
||||
let deserialized_result: Result<ObservabilityConfig, _> = serde_json::from_str(&json_str);
|
||||
assert!(deserialized_result.is_ok(), "Config should be deserializable from JSON");
|
||||
|
||||
let deserialized_config = deserialized_result.unwrap();
|
||||
assert_eq!(deserialized_config.sinks.len(), config.sinks.len());
|
||||
assert_eq!(deserialized_config.logger.is_some(), config.logger.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_debug_format() {
|
||||
let config = ObservabilityConfig::new();
|
||||
|
||||
let debug_str = format!("{:?}", config);
|
||||
assert!(!debug_str.is_empty(), "Debug output should not be empty");
|
||||
assert!(debug_str.contains("ObservabilityConfig"), "Debug output should contain struct name");
|
||||
assert!(debug_str.contains("otel"), "Debug output should contain otel field");
|
||||
assert!(debug_str.contains("sinks"), "Debug output should contain sinks field");
|
||||
assert!(debug_str.contains("logger"), "Debug output should contain logger field");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_clone() {
|
||||
let config = ObservabilityConfig::new();
|
||||
let cloned_config = config.clone();
|
||||
|
||||
// Test that clone creates an independent copy
|
||||
assert_eq!(cloned_config.sinks.len(), config.sinks.len());
|
||||
assert_eq!(cloned_config.logger.is_some(), config.logger.is_some());
|
||||
assert_eq!(cloned_config.otel.endpoint, config.otel.endpoint);
|
||||
assert_eq!(cloned_config.otel.use_stdout, config.otel.use_stdout);
|
||||
assert_eq!(cloned_config.otel.sample_ratio, config.otel.sample_ratio);
|
||||
assert_eq!(cloned_config.otel.meter_interval, config.otel.meter_interval);
|
||||
assert_eq!(cloned_config.otel.service_name, config.otel.service_name);
|
||||
assert_eq!(cloned_config.otel.service_version, config.otel.service_version);
|
||||
assert_eq!(cloned_config.otel.environment, config.otel.environment);
|
||||
assert_eq!(cloned_config.otel.logger_level, config.otel.logger_level);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_modification() {
|
||||
let mut config = ObservabilityConfig::new();
|
||||
|
||||
// Test modifying OTEL endpoint
|
||||
let original_endpoint = config.otel.endpoint.clone();
|
||||
config.otel.endpoint = "http://localhost:4317".to_string();
|
||||
assert_ne!(config.otel.endpoint, original_endpoint);
|
||||
assert_eq!(config.otel.endpoint, "http://localhost:4317");
|
||||
|
||||
// Test modifying sinks
|
||||
let original_sinks_len = config.sinks.len();
|
||||
config.sinks.push(SinkConfig::new());
|
||||
assert_eq!(config.sinks.len(), original_sinks_len + 1);
|
||||
|
||||
// Test disabling logger
|
||||
config.logger = None;
|
||||
assert!(config.logger.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_edge_cases() {
|
||||
// Test with empty sinks
|
||||
let mut config = ObservabilityConfig::new();
|
||||
config.sinks.clear();
|
||||
assert!(config.sinks.is_empty(), "Sinks should be empty after clearing");
|
||||
|
||||
// Test serialization with empty sinks
|
||||
let json_result = serde_json::to_string(&config);
|
||||
assert!(json_result.is_ok(), "Config with empty sinks should be serializable");
|
||||
|
||||
// Test with no logger
|
||||
config.logger = None;
|
||||
let json_result = serde_json::to_string(&config);
|
||||
assert!(json_result.is_ok(), "Config with no logger should be serializable");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_memory_efficiency() {
|
||||
let config = ObservabilityConfig::new();
|
||||
|
||||
// Test that config doesn't use excessive memory
|
||||
let config_size = std::mem::size_of_val(&config);
|
||||
assert!(config_size < 5000, "Config should not use excessive memory");
|
||||
|
||||
// Test that endpoint string is not excessively long
|
||||
assert!(config.otel.endpoint.len() < 1000, "Endpoint should not be excessively long");
|
||||
|
||||
// Test that collections are reasonably sized
|
||||
assert!(config.sinks.len() < 100, "Sinks collection should be reasonably sized");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_observability_config_consistency() {
|
||||
// Create multiple configs and ensure they're consistent
|
||||
let config1 = ObservabilityConfig::new();
|
||||
let config2 = ObservabilityConfig::new();
|
||||
|
||||
// Both configs should have the same default structure
|
||||
assert_eq!(config1.sinks.len(), config2.sinks.len());
|
||||
assert_eq!(config1.logger.is_some(), config2.logger.is_some());
|
||||
assert_eq!(config1.otel.use_stdout, config2.otel.use_stdout);
|
||||
assert_eq!(config1.otel.sample_ratio, config2.otel.sample_ratio);
|
||||
assert_eq!(config1.otel.meter_interval, config2.otel.meter_interval);
|
||||
assert_eq!(config1.otel.service_name, config2.otel.service_name);
|
||||
assert_eq!(config1.otel.service_version, config2.otel.service_version);
|
||||
assert_eq!(config1.otel.environment, config2.otel.environment);
|
||||
assert_eq!(config1.otel.logger_level, config2.otel.logger_level);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user