use std::collections::HashMap;
use std::io::Write;
use confers::interface::ConfigProvider;
use confers::types::{AnnotatedValue, ConfigValue, SourceId};
use tempfile::NamedTempFile;
#[cfg(feature = "remote")]
use std::time::Duration;
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct TestConfig {
pub timeout_ms: u32,
pub max_connections: usize,
pub database_host: String,
pub database_port: u16,
pub values: HashMap<String, AnnotatedValue>,
}
impl Default for TestConfig {
fn default() -> Self {
Self::new(0, 0)
}
}
impl TestConfig {
pub fn new(timeout_ms: u32, max_connections: usize) -> Self {
Self::with_all(timeout_ms, max_connections, "localhost".to_string(), 5432)
}
#[allow(dead_code)]
pub fn with_timeout(timeout_ms: u32) -> Self {
Self::new(timeout_ms, 0)
}
#[allow(dead_code)]
pub fn with_connections(max_connections: usize) -> Self {
Self::new(0, max_connections)
}
#[allow(dead_code)]
pub fn random() -> Self {
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hasher};
let state = RandomState::new();
let mut hasher = state.build_hasher();
hasher.write_u32(42);
let random_u32 = hasher.finish() as u32;
Self::new(random_u32 % 10000, (random_u32 as usize) % 1000)
}
pub fn with_all(
timeout_ms: u32,
max_connections: usize,
database_host: String,
database_port: u16,
) -> Self {
let mut values = HashMap::new();
values.insert(
"timeout_ms".to_string(),
AnnotatedValue::new(
ConfigValue::from(timeout_ms),
SourceId::new("test-config"),
"timeout_ms",
),
);
values.insert(
"max_connections".to_string(),
AnnotatedValue::new(
ConfigValue::from(max_connections as i64),
SourceId::new("test-config"),
"max_connections",
),
);
values.insert(
"database_host".to_string(),
AnnotatedValue::new(
ConfigValue::from(database_host.clone()),
SourceId::new("test-config"),
"database_host",
),
);
values.insert(
"database_port".to_string(),
AnnotatedValue::new(
ConfigValue::from(database_port as i64),
SourceId::new("test-config"),
"database_port",
),
);
Self {
timeout_ms,
max_connections,
database_host,
database_port,
values,
}
}
}
impl ConfigProvider for TestConfig {
fn get_raw(&self, key: &str) -> Option<&AnnotatedValue> {
self.values.get(key)
}
fn keys(&self) -> Vec<String> {
self.values.keys().cloned().collect()
}
}
#[cfg(feature = "remote")]
#[allow(dead_code)]
pub async fn is_service_available(url: &str, timeout: Duration) -> bool {
let client = reqwest::Client::builder().timeout(timeout).build().unwrap();
match client.get(url).send().await {
Ok(resp) => resp.status().is_success(),
Err(_) => false,
}
}
pub fn with_env_var<F, R>(key: &str, value: &str, f: F) -> R
where
F: FnOnce() -> R,
{
std::env::set_var(key, value);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
std::env::remove_var(key);
match result {
Ok(value) => value,
Err(panic) => std::panic::resume_unwind(panic),
}
}
pub fn create_temp_config(content: &str, extension: &str) -> NamedTempFile {
let mut file = NamedTempFile::with_suffix(extension).unwrap();
file.write_all(content.as_bytes()).unwrap();
file.flush().unwrap();
file
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_test_config_new() {
let config = TestConfig::new(100, 10);
assert_eq!(config.timeout_ms, 100);
assert_eq!(config.max_connections, 10);
}
#[test]
fn test_test_config_default() {
let config = TestConfig::default();
assert_eq!(config.timeout_ms, 0);
assert_eq!(config.max_connections, 0);
}
#[test]
fn test_test_config_provider() {
let config = TestConfig::new(500, 100);
assert!(config.get_raw("timeout_ms").is_some());
assert!(config.get_raw("max_connections").is_some());
assert!(config.get_raw("nonexistent").is_none());
let keys = config.keys();
assert_eq!(keys.len(), 4);
}
#[test]
fn test_with_env_var() {
with_env_var("TEST_VAR_COMMON", "test_value", || {
assert_eq!(std::env::var("TEST_VAR_COMMON").unwrap(), "test_value");
});
assert!(std::env::var("TEST_VAR_COMMON").is_err());
}
#[test]
fn test_create_temp_config() {
let file = create_temp_config("key = \"value\"", ".toml");
assert!(file.path().exists());
assert!(file.path().to_string_lossy().ends_with(".toml"));
}
}