mod common;
use std::fs;
use std::time::Duration;
use tempfile::tempdir;
use tokio::time::sleep;
use vmonitor::app::App;
use vmonitor::config::{AppConfig, Endpoint, ConnectionConfig};
use common::TestConfig;
fn create_default_config() -> AppConfig {
AppConfig {
endpoints: vec![],
connection: ConnectionConfig {
base_delay: 1,
max_delay: 60,
max_retries: -1,
},
}
}
#[test]
fn test_endpoint_with_defaults() {
let default_config = create_default_config();
let endpoint = Endpoint {
name: "test".to_string(),
server: "ws://test.com".to_string(),
secret: "test-secret".to_string(),
enabled: true,
connection: None,
};
assert_eq!(
endpoint.connection.clone().unwrap_or_else(|| default_config.connection.clone()),
default_config.connection
);
}
#[test]
fn test_endpoint_with_overrides() {
let default_config = create_default_config();
let custom_connection = ConnectionConfig {
base_delay: 2,
max_delay: 30,
max_retries: 3,
};
let endpoint = Endpoint {
name: "test".to_string(),
server: "ws://test.com".to_string(),
secret: "test-secret".to_string(),
enabled: true,
connection: Some(custom_connection.clone()),
};
assert_eq!(endpoint.connection.clone().unwrap_or_else(|| default_config.connection.clone()), custom_connection);
}
#[test]
fn test_config_serialization() {
let config = AppConfig {
endpoints: vec![
Endpoint {
name: "test1".to_string(),
server: "ws://test1.com".to_string(),
secret: "secret1".to_string(),
enabled: true,
connection: None,
},
Endpoint {
name: "test2".to_string(),
server: "ws://test2.com".to_string(),
secret: "secret2".to_string(),
enabled: true,
connection: Some(ConnectionConfig {
base_delay: 2,
max_delay: 30,
max_retries: 3,
}),
},
],
connection: ConnectionConfig {
base_delay: 1,
max_delay: 60,
max_retries: -1,
},
};
let serialized = toml::to_string_pretty(&config).unwrap();
let deserialized: AppConfig = toml::from_str(&serialized).unwrap();
assert_eq!(config, deserialized);
}
#[test]
fn test_config_parsing() {
let config_str = r#"
[connection]
base_delay = 2
max_delay = 120
max_retries = 3
[[endpoints]]
name = "test-endpoint"
server = "wss://test.example.com/ws"
secret = "test-secret"
enabled = true
[[endpoints]]
name = "disabled-endpoint"
server = "wss://disabled.example.com/ws"
secret = "disabled-secret"
enabled = false
"#;
let test_config = TestConfig::new();
std::fs::write(&test_config.config_path, config_str).unwrap();
let config = AppConfig::from_file(test_config.config_path.to_str().unwrap()).unwrap();
assert_eq!(config.connection.base_delay, 2);
assert_eq!(config.connection.max_delay, 120);
assert_eq!(config.connection.max_retries, 3);
assert_eq!(config.endpoints.len(), 2);
let endpoint = &config.endpoints[0];
assert_eq!(endpoint.name, "test-endpoint");
assert_eq!(endpoint.server, "wss://test.example.com/ws");
assert_eq!(endpoint.secret, "test-secret");
assert!(endpoint.enabled);
let endpoint = &config.endpoints[1];
assert_eq!(endpoint.name, "disabled-endpoint");
assert!(!endpoint.enabled);
}
#[tokio::test]
async fn test_dynamic_endpoint_management() {
let temp_dir = tempdir().unwrap();
let config_path = temp_dir.path().join("test_config.toml");
let initial_config = AppConfig {
endpoints: vec![
Endpoint {
name: "test1".to_string(),
server: "wss://test1.example.com/ws".to_string(),
secret: "secret1".to_string(),
enabled: true,
connection: None,
}
],
connection: ConnectionConfig {
base_delay: 1,
max_delay: 5,
max_retries: 1,
},
};
initial_config.save_to_file(config_path.to_str().unwrap()).unwrap();
let app = App::new(initial_config);
let app_handle = tokio::spawn(async move {
app.run().await;
});
sleep(Duration::from_secs(1)).await;
let mut config = AppConfig::from_file(config_path.to_str().unwrap()).unwrap();
config.endpoints.push(Endpoint {
name: "test2".to_string(),
server: "wss://test2.example.com/ws".to_string(),
secret: "secret2".to_string(),
enabled: true,
connection: None,
});
config.save_to_file(config_path.to_str().unwrap()).unwrap();
sleep(Duration::from_secs(2)).await;
let mut config = AppConfig::from_file(config_path.to_str().unwrap()).unwrap();
if let Some(endpoint) = config.endpoints.iter_mut().find(|e| e.name == "test1") {
endpoint.enabled = false;
}
config.save_to_file(config_path.to_str().unwrap()).unwrap();
sleep(Duration::from_secs(2)).await;
let mut config = AppConfig::from_file(config_path.to_str().unwrap()).unwrap();
if let Some(endpoint) = config.endpoints.iter_mut().find(|e| e.name == "test1") {
endpoint.enabled = true;
}
config.save_to_file(config_path.to_str().unwrap()).unwrap();
sleep(Duration::from_secs(2)).await;
let mut config = AppConfig::from_file(config_path.to_str().unwrap()).unwrap();
config.endpoints.retain(|e| e.name != "test2");
config.save_to_file(config_path.to_str().unwrap()).unwrap();
sleep(Duration::from_secs(2)).await;
app_handle.abort();
let _ = app_handle.await;
}
#[tokio::test]
async fn test_config_file_monitoring() {
let temp_dir = tempdir().unwrap();
let config_path = temp_dir.path().join("test_config.toml");
let initial_config = AppConfig {
endpoints: vec![
Endpoint {
name: "test".to_string(),
server: "wss://test.example.com/ws".to_string(),
secret: "secret".to_string(),
enabled: true,
connection: None,
}
],
connection: ConnectionConfig {
base_delay: 1,
max_delay: 5,
max_retries: 1,
},
};
initial_config.save_to_file(config_path.to_str().unwrap()).unwrap();
let app = App::new(initial_config);
let app_handle = tokio::spawn(async move {
app.run().await;
});
sleep(Duration::from_secs(1)).await;
fs::write(&config_path, "invalid toml content").unwrap();
sleep(Duration::from_secs(2)).await;
let valid_config = AppConfig {
endpoints: vec![
Endpoint {
name: "test".to_string(),
server: "wss://test.example.com/ws".to_string(),
secret: "secret".to_string(),
enabled: true,
connection: None,
}
],
connection: ConnectionConfig {
base_delay: 1,
max_delay: 5,
max_retries: 1,
},
};
valid_config.save_to_file(config_path.to_str().unwrap()).unwrap();
sleep(Duration::from_secs(2)).await;
app_handle.abort();
let _ = app_handle.await;
}
#[tokio::test]
async fn test_concurrent_config_changes() {
let temp_dir = tempdir().unwrap();
let config_path = temp_dir.path().join("test_config.toml");
let initial_config = AppConfig {
endpoints: vec![
Endpoint {
name: "test".to_string(),
server: "wss://test.example.com/ws".to_string(),
secret: "secret".to_string(),
enabled: true,
connection: None,
}
],
connection: ConnectionConfig {
base_delay: 1,
max_delay: 5,
max_retries: 1,
},
};
initial_config.save_to_file(config_path.to_str().unwrap()).unwrap();
let app = App::new(initial_config);
let app_handle = tokio::spawn(async move {
app.run().await;
});
sleep(Duration::from_secs(1)).await;
for i in 0..5 {
let mut config = AppConfig::from_file(config_path.to_str().unwrap()).unwrap();
config.endpoints[0].enabled = i % 2 == 0;
config.save_to_file(config_path.to_str().unwrap()).unwrap();
sleep(Duration::from_millis(100)).await;
}
sleep(Duration::from_secs(2)).await;
app_handle.abort();
let _ = app_handle.await;
}