Platform restructured into crates/, added AI service and detector,
migrated control-center-ui to Leptos 0.8
421 lines
12 KiB
Rust
421 lines
12 KiB
Rust
//! Integration tests for ConfigLoader system
|
|
//! Tests config loading across all platform services
|
|
|
|
use std::env;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
use platform_config::ConfigLoader;
|
|
use tempfile::TempDir;
|
|
|
|
/// Test config struct for integration testing
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
|
struct TestConfig {
|
|
#[serde(default)]
|
|
pub name: String,
|
|
#[serde(default)]
|
|
pub port: u16,
|
|
#[serde(default)]
|
|
pub enabled: bool,
|
|
}
|
|
|
|
impl ConfigLoader for TestConfig {
|
|
fn service_name() -> &'static str {
|
|
"test-service"
|
|
}
|
|
|
|
fn load_from_hierarchy() -> std::result::Result<Self, Box<dyn std::error::Error + Send + Sync>>
|
|
{
|
|
let service = Self::service_name();
|
|
|
|
if let Some(path) = platform_config::resolve_config_path(service) {
|
|
return Self::from_path(&path);
|
|
}
|
|
|
|
Ok(Self::default())
|
|
}
|
|
|
|
fn apply_env_overrides(
|
|
&mut self,
|
|
) -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
if let Ok(val) = env::var("TEST_SERVICE_NAME") {
|
|
self.name = val;
|
|
}
|
|
if let Ok(val) = env::var("TEST_SERVICE_PORT") {
|
|
if let Ok(port) = val.parse() {
|
|
self.port = port;
|
|
}
|
|
}
|
|
if let Ok(val) = env::var("TEST_SERVICE_ENABLED") {
|
|
self.enabled = val.parse().unwrap_or(self.enabled);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn from_path<P: AsRef<std::path::Path>>(
|
|
path: P,
|
|
) -> std::result::Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
|
let path = path.as_ref();
|
|
let json_value = platform_config::format::load_config(path).map_err(|e| {
|
|
let err: Box<dyn std::error::Error + Send + Sync> = Box::new(e);
|
|
err
|
|
})?;
|
|
|
|
serde_json::from_value(json_value).map_err(|e| {
|
|
let err_msg = format!("Failed to deserialize test config from {:?}: {}", path, e);
|
|
Box::new(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidData,
|
|
err_msg,
|
|
)) as Box<dyn std::error::Error + Send + Sync>
|
|
})
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_default_config_loading() {
|
|
let config = TestConfig::default();
|
|
assert_eq!(config.name, "");
|
|
assert_eq!(config.port, 0);
|
|
assert!(!config.enabled);
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_serialization_toml() {
|
|
let config = TestConfig {
|
|
name: "test".to_string(),
|
|
port: 8080,
|
|
enabled: true,
|
|
};
|
|
|
|
let toml_str = toml::to_string(&config).expect("Failed to serialize to TOML");
|
|
assert!(toml_str.contains("test"));
|
|
assert!(toml_str.contains("8080"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_serialization_json() {
|
|
let config = TestConfig {
|
|
name: "test".to_string(),
|
|
port: 8080,
|
|
enabled: true,
|
|
};
|
|
|
|
// Test that JSON serialization works (even though load_config only supports
|
|
// TOML/NCL)
|
|
let json_str = serde_json::to_string(&config).expect("Failed to serialize to JSON");
|
|
assert!(json_str.contains("test"));
|
|
assert!(json_str.contains("8080"));
|
|
|
|
// Deserialize back from JSON
|
|
let deserialized: TestConfig =
|
|
serde_json::from_str(&json_str).expect("Failed to deserialize from JSON");
|
|
assert_eq!(deserialized.name, "test");
|
|
assert_eq!(deserialized.port, 8080);
|
|
assert!(deserialized.enabled);
|
|
}
|
|
|
|
#[test]
|
|
fn test_toml_file_loading() {
|
|
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
|
let config_path = temp_dir.path().join("test.toml");
|
|
|
|
let toml_content = r#"
|
|
name = "test-service"
|
|
port = 9090
|
|
enabled = true
|
|
"#;
|
|
|
|
fs::write(&config_path, toml_content).expect("Failed to write TOML file");
|
|
|
|
let config = TestConfig::from_path(&config_path).expect("Failed to load config from TOML file");
|
|
|
|
assert_eq!(config.name, "test-service");
|
|
assert_eq!(config.port, 9090);
|
|
assert!(config.enabled);
|
|
}
|
|
|
|
#[test]
|
|
fn test_environment_variable_overrides() {
|
|
env::set_var("TEST_SERVICE_NAME", "env-service");
|
|
env::set_var("TEST_SERVICE_PORT", "7777");
|
|
env::set_var("TEST_SERVICE_ENABLED", "false");
|
|
|
|
let mut config = TestConfig {
|
|
name: "original".to_string(),
|
|
port: 8080,
|
|
enabled: true,
|
|
};
|
|
|
|
config
|
|
.apply_env_overrides()
|
|
.expect("Failed to apply env overrides");
|
|
|
|
assert_eq!(config.name, "env-service");
|
|
assert_eq!(config.port, 7777);
|
|
assert!(!config.enabled);
|
|
|
|
// Cleanup
|
|
env::remove_var("TEST_SERVICE_NAME");
|
|
env::remove_var("TEST_SERVICE_PORT");
|
|
env::remove_var("TEST_SERVICE_ENABLED");
|
|
}
|
|
|
|
#[test]
|
|
fn test_invalid_toml_file() {
|
|
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
|
let config_path = temp_dir.path().join("invalid.toml");
|
|
|
|
let invalid_toml = r#"
|
|
this is not valid [[ toml
|
|
"#;
|
|
|
|
fs::write(&config_path, invalid_toml).expect("Failed to write invalid TOML file");
|
|
|
|
let result = TestConfig::from_path(&config_path);
|
|
assert!(
|
|
result.is_err(),
|
|
"Expected error loading invalid TOML, but got success"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_invalid_json_file() {
|
|
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
|
let config_path = temp_dir.path().join("invalid.json");
|
|
|
|
let invalid_json = r#"{ this is not valid json"#;
|
|
|
|
fs::write(&config_path, invalid_json).expect("Failed to write invalid JSON file");
|
|
|
|
let result = TestConfig::from_path(&config_path);
|
|
assert!(
|
|
result.is_err(),
|
|
"Expected error loading invalid JSON, but got success"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_missing_file_error() {
|
|
let config_path = PathBuf::from("/nonexistent/path/to/config.toml");
|
|
let result = TestConfig::from_path(&config_path);
|
|
assert!(
|
|
result.is_err(),
|
|
"Expected error loading nonexistent file, but got success"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_nested_config_loading() {
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
struct NestedConfig {
|
|
pub service: ServiceSection,
|
|
pub database: DatabaseSection,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
struct ServiceSection {
|
|
pub name: String,
|
|
pub port: u16,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
struct DatabaseSection {
|
|
pub url: String,
|
|
pub pool_size: usize,
|
|
}
|
|
|
|
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
|
let config_path = temp_dir.path().join("nested.toml");
|
|
|
|
let toml_content = r#"
|
|
[service]
|
|
name = "test"
|
|
port = 8080
|
|
|
|
[database]
|
|
url = "postgres://localhost"
|
|
pool_size = 10
|
|
"#;
|
|
|
|
fs::write(&config_path, toml_content).expect("Failed to write TOML file");
|
|
|
|
let content = fs::read_to_string(&config_path).expect("Failed to read file");
|
|
let parsed: NestedConfig = toml::from_str(&content).expect("Failed to parse nested TOML");
|
|
|
|
assert_eq!(parsed.service.name, "test");
|
|
assert_eq!(parsed.service.port, 8080);
|
|
assert_eq!(parsed.database.url, "postgres://localhost");
|
|
assert_eq!(parsed.database.pool_size, 10);
|
|
}
|
|
|
|
#[test]
|
|
fn test_toml_to_json_conversion() {
|
|
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
|
|
|
// Create TOML version
|
|
let toml_path = temp_dir.path().join("config1.toml");
|
|
let toml_content = r#"
|
|
name = "toml-service"
|
|
port = 8080
|
|
enabled = true
|
|
"#;
|
|
fs::write(&toml_path, toml_content).expect("Failed to write TOML");
|
|
|
|
// Create another TOML version with different values
|
|
let toml_path2 = temp_dir.path().join("config2.toml");
|
|
let toml_content2 = r#"
|
|
name = "toml-service-2"
|
|
port = 9090
|
|
enabled = false
|
|
"#;
|
|
fs::write(&toml_path2, toml_content2).expect("Failed to write TOML 2");
|
|
|
|
// Load both via ConfigLoader (which converts TOML to JSON internally)
|
|
let config1 = TestConfig::from_path(&toml_path).expect("Failed to load TOML config 1");
|
|
let config2 = TestConfig::from_path(&toml_path2).expect("Failed to load TOML config 2");
|
|
|
|
// Verify they deserialized correctly with different values
|
|
assert_eq!(config1.name, "toml-service");
|
|
assert_eq!(config2.name, "toml-service-2");
|
|
assert_eq!(config1.port, 8080);
|
|
assert_eq!(config2.port, 9090);
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_service_name() {
|
|
assert_eq!(TestConfig::service_name(), "test-service");
|
|
}
|
|
|
|
#[test]
|
|
fn test_partial_config_with_defaults() {
|
|
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
|
let config_path = temp_dir.path().join("partial.toml");
|
|
|
|
// Only specify some fields
|
|
let toml_content = r#"
|
|
name = "partial-service"
|
|
"#;
|
|
|
|
fs::write(&config_path, toml_content).expect("Failed to write TOML file");
|
|
|
|
let config = TestConfig::from_path(&config_path).expect("Failed to load partial config");
|
|
|
|
assert_eq!(config.name, "partial-service");
|
|
assert_eq!(config.port, 0); // default value
|
|
assert!(!config.enabled); // default value
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_roundtrip_toml() {
|
|
let original = TestConfig {
|
|
name: "roundtrip-test".to_string(),
|
|
port: 6789,
|
|
enabled: true,
|
|
};
|
|
|
|
// Serialize to TOML
|
|
let toml_str = toml::to_string(&original).expect("Failed to serialize to TOML");
|
|
|
|
// Deserialize back
|
|
let deserialized: TestConfig =
|
|
toml::from_str(&toml_str).expect("Failed to deserialize from TOML");
|
|
|
|
assert_eq!(deserialized.name, original.name);
|
|
assert_eq!(deserialized.port, original.port);
|
|
assert_eq!(deserialized.enabled, original.enabled);
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_roundtrip_json() {
|
|
let original = TestConfig {
|
|
name: "roundtrip-test".to_string(),
|
|
port: 6789,
|
|
enabled: true,
|
|
};
|
|
|
|
// Serialize to JSON
|
|
let json_str = serde_json::to_string(&original).expect("Failed to serialize to JSON");
|
|
|
|
// Deserialize back
|
|
let deserialized: TestConfig =
|
|
serde_json::from_str(&json_str).expect("Failed to deserialize from JSON");
|
|
|
|
assert_eq!(deserialized.name, original.name);
|
|
assert_eq!(deserialized.port, original.port);
|
|
assert_eq!(deserialized.enabled, original.enabled);
|
|
}
|
|
|
|
#[test]
|
|
fn test_empty_config_file_toml() {
|
|
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
|
let config_path = temp_dir.path().join("empty.toml");
|
|
|
|
// Write empty TOML
|
|
fs::write(&config_path, "").expect("Failed to write empty TOML");
|
|
|
|
let config = TestConfig::from_path(&config_path).expect("Failed to load empty TOML file");
|
|
|
|
// Should get defaults
|
|
assert_eq!(config.name, "");
|
|
assert_eq!(config.port, 0);
|
|
assert!(!config.enabled);
|
|
}
|
|
|
|
#[test]
|
|
fn test_environment_partial_override() {
|
|
// Only override one field
|
|
env::set_var("TEST_SERVICE_PORT", "5555");
|
|
|
|
let mut config = TestConfig {
|
|
name: "original".to_string(),
|
|
port: 8080,
|
|
enabled: true,
|
|
};
|
|
|
|
config
|
|
.apply_env_overrides()
|
|
.expect("Failed to apply env overrides");
|
|
|
|
assert_eq!(config.name, "original"); // unchanged
|
|
assert_eq!(config.port, 5555); // overridden
|
|
assert!(config.enabled); // unchanged
|
|
|
|
env::remove_var("TEST_SERVICE_PORT");
|
|
}
|
|
|
|
#[test]
|
|
fn test_invalid_port_environment_variable() {
|
|
env::set_var("TEST_SERVICE_PORT", "not_a_number");
|
|
|
|
let mut config = TestConfig {
|
|
name: "original".to_string(),
|
|
port: 8080,
|
|
enabled: true,
|
|
};
|
|
|
|
// Should not panic, port should remain unchanged
|
|
let _ = config.apply_env_overrides();
|
|
assert_eq!(config.port, 8080);
|
|
|
|
env::remove_var("TEST_SERVICE_PORT");
|
|
}
|
|
|
|
#[test]
|
|
fn test_enabled_parsing_variants() {
|
|
// Rust's str::parse() for bool only accepts lowercase "true" and "false"
|
|
let test_cases = vec![("true", true), ("false", false)];
|
|
|
|
for (input, expected) in test_cases {
|
|
env::set_var("TEST_SERVICE_ENABLED", input);
|
|
|
|
let mut config = TestConfig::default();
|
|
config
|
|
.apply_env_overrides()
|
|
.expect("Failed to apply env overrides");
|
|
|
|
assert_eq!(config.enabled, expected, "Failed for input: {}", input);
|
|
|
|
env::remove_var("TEST_SERVICE_ENABLED");
|
|
}
|
|
}
|