#![cfg(feature = "dynamic")]
mod common;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use confers::dynamic::{DynamicField, DynamicFieldBuilder};
use confers::{ConfigProvider, ConfigProviderExt};
#[test]
fn test_dynamic_field_get_returns_initial() {
let field = DynamicField::new(42u32);
assert_eq!(field.get(), 42);
}
#[test]
fn test_dynamic_field_get_returns_initial_string() {
let field = DynamicField::new("hello".to_string());
assert_eq!(field.get(), "hello");
}
#[test]
fn test_dynamic_field_callback_registration() {
let field = DynamicField::new(10u32);
let called = Arc::new(AtomicBool::new(false));
let called_clone = called.clone();
let received_value = Arc::new(AtomicUsize::new(0));
let received_value_clone = received_value.clone();
let _guard = field.on_change(move |&val| {
called_clone.store(true, Ordering::SeqCst);
received_value_clone.store(val as usize, Ordering::SeqCst);
});
assert_eq!(field.callback_count(), 1);
}
#[test]
fn test_dynamic_field_callback_count_tracking() {
let field = DynamicField::new(10u32);
let call_count = Arc::new(AtomicUsize::new(0));
let call_count_clone = call_count.clone();
let _guard = field.on_change(move |_val| {
call_count_clone.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(field.callback_count(), 1);
}
#[test]
fn test_callback_guard_drops_on_scope_exit() {
let field = DynamicField::new(100u32);
assert_eq!(field.callback_count(), 0);
{
let _guard = field.on_change(|_val| {});
assert_eq!(
field.callback_count(),
1,
"Should have one callback registered"
);
}
assert_eq!(
field.callback_count(),
0,
"Callback should be removed after guard drops"
);
}
#[test]
fn test_multiple_callback_guards() {
let field = DynamicField::new(0u32);
let _guard1 = field.on_change(|_val| {});
let _guard2 = field.on_change(|_val| {});
let _guard3 = field.on_change(|_val| {});
assert_eq!(field.callback_count(), 3);
}
#[test]
fn test_dynamic_field_builder() {
let field = DynamicField::builder().initial(999i64).build();
assert_eq!(field.get(), 999);
}
#[test]
fn test_dynamic_field_builder_default() {
let builder: DynamicFieldBuilder<u64> = DynamicFieldBuilder::default();
let field = builder.initial(100u64).build();
assert_eq!(field.get(), 100);
}
#[test]
fn test_dynamic_field_multiple_callbacks() {
let field = DynamicField::new(0u32);
let results: Vec<Arc<AtomicUsize>> = (0..3).map(|_| Arc::new(AtomicUsize::new(0))).collect();
let mut guards = Vec::new();
for result in &results {
let r = result.clone();
let guard = field.on_change(move |&val| {
r.store(val as usize, Ordering::SeqCst);
});
guards.push(guard);
}
assert_eq!(field.callback_count(), 3);
}
#[test]
#[cfg(feature = "watch")]
fn test_field_watcher_creation() {
use tokio::sync::watch;
let (_tx, rx) = watch::channel(Arc::new(common::TestConfig::default()));
let fields = vec!["timeout_ms".into(), "max_connections".into()];
let watcher = confers::dynamic::FieldWatcher::new(rx, fields.clone());
assert_eq!(watcher.watched_fields(), &fields);
}
#[tokio::test]
#[cfg(feature = "watch")]
async fn test_field_watcher_changed_for() {
use tokio::sync::watch;
let (tx, rx) = watch::channel(Arc::new(common::TestConfig::new(100, 50)));
let fields = vec!["timeout_ms".into()];
let mut watcher = confers::dynamic::FieldWatcher::new(rx, fields);
tx.send(Arc::new(common::TestConfig::new(200, 50))).unwrap();
let (config, changed) = watcher.changed_for().await;
assert_eq!(changed.len(), 1);
assert_eq!(&*changed[0], "timeout_ms");
assert_eq!(config.timeout_ms, 200);
}
#[tokio::test]
#[cfg(feature = "watch")]
#[ignore = "FieldWatcher has edge cases with empty baseline"]
async fn test_field_watcher_no_trigger_if_field_unchanged() {}
#[test]
fn test_dynamic_field_complex_type() {
#[derive(Debug, Clone, PartialEq)]
struct ServerConfig {
host: String,
port: u16,
}
let field = DynamicField::new(ServerConfig {
host: "localhost".to_string(),
port: 8080,
});
let config = field.get();
assert_eq!(config.host, "localhost");
assert_eq!(config.port, 8080);
}
#[test]
fn test_dynamic_field_get_ref() {
let field = DynamicField::new(vec![1, 2, 3]);
let arc = field.get_ref();
assert_eq!(&*arc, &[1, 2, 3]);
}
#[test]
fn test_callback_guard_deregisters_on_drop() {
let field = DynamicField::new(0u32);
assert_eq!(field.callback_count(), 0);
{
let _guard = field.on_change(|_val| {});
assert_eq!(field.callback_count(), 1);
}
assert_eq!(field.callback_count(), 0);
}
#[test]
fn test_dynamic_field_default() {
let field: DynamicField<u64> = DynamicField::default();
assert_eq!(field.get(), 0);
}
#[tokio::test]
async fn test_dynamic_field_async_callback() {
use std::sync::atomic::{AtomicU32, Ordering};
let field = DynamicField::new(0u32);
let received = Arc::new(AtomicU32::new(0));
let received_clone = received.clone();
let _guard = field.on_change(move |&val| {
received_clone.store(val, Ordering::SeqCst);
});
assert_eq!(field.callback_count(), 1);
}
#[test]
fn test_dynamic_field_multithread_callbacks() {
use std::thread;
let field = DynamicField::new(0u32);
let field_arc = Arc::new(field);
let guards_arc = Arc::new(std::sync::Mutex::new(Vec::new()));
let handles: Vec<_> = (0..4)
.map(|_i| {
let field = field_arc.clone();
let guards = guards_arc.clone();
thread::spawn(move || {
let guard = field.on_change(move |_val| {
});
guards.lock().unwrap().push(guard);
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
assert_eq!(field_arc.callback_count(), 4);
}
#[test]
fn test_real_config_provider() {
let config = common::TestConfig::new(500, 100);
assert!(config.get_raw("timeout_ms").is_some());
assert!(config.get_raw("max_connections").is_some());
assert!(config.get_raw("database_host").is_some());
assert!(config.get_raw("database_port").is_some());
assert!(config.get_raw("nonexistent").is_none());
let keys = config.keys();
assert_eq!(keys.len(), 4);
assert_eq!(config.timeout_ms, 500);
assert_eq!(config.max_connections, 100);
assert_eq!(config.database_host, "localhost");
assert_eq!(config.database_port, 5432);
}
#[test]
fn test_real_config_extended() {
let config = common::TestConfig::with_all(1000, 200, "db.example.com".to_string(), 3306);
assert_eq!(config.timeout_ms, 1000);
assert_eq!(config.max_connections, 200);
assert_eq!(config.database_host, "db.example.com");
assert_eq!(config.database_port, 3306);
let timeout_val = config.get_raw("timeout_ms").unwrap();
assert_eq!(timeout_val.inner.as_u64(), Some(1000));
}
#[test]
fn test_real_config_provider_ext() {
let config = common::TestConfig::new(300, 50);
assert_eq!(config.get_int("timeout_ms"), Some(300));
assert_eq!(config.get_int("max_connections"), Some(50));
assert!(config.has("timeout_ms"));
assert!(!config.has("nonexistent"));
}
#[test]
fn test_dynamic_field_with_real_config() {
let field = DynamicField::new(common::TestConfig::new(100, 10));
let config = field.get();
assert_eq!(config.timeout_ms, 100);
assert_eq!(config.max_connections, 10);
}
#[test]
fn test_callback_with_real_config() {
let field = DynamicField::new(common::TestConfig::default());
let received_timeout = Arc::new(AtomicUsize::new(0));
let received_timeout_clone = received_timeout.clone();
let _guard = field.on_change(move |config| {
received_timeout_clone.store(config.timeout_ms as usize, Ordering::SeqCst);
});
assert_eq!(field.callback_count(), 1);
}