Platform restructured into crates/, added AI service and detector,
migrated control-center-ui to Leptos 0.8
422 lines
12 KiB
Rust
422 lines
12 KiB
Rust
//! Integration tests for Nickel (NCL) config file support
|
|
//! Tests that actual Nickel config files can be exported and loaded via
|
|
//! ConfigLoader
|
|
|
|
use std::process::Command;
|
|
|
|
use platform_config::ConfigLoader;
|
|
|
|
/// Test config struct for Nickel integration testing
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
|
struct TestNickelConfig {
|
|
#[serde(default)]
|
|
pub name: String,
|
|
#[serde(default)]
|
|
pub port: u16,
|
|
#[serde(default)]
|
|
pub enabled: bool,
|
|
}
|
|
|
|
impl ConfigLoader for TestNickelConfig {
|
|
fn service_name() -> &'static str {
|
|
"test-nickel-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>> {
|
|
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_nickel_cli_available() {
|
|
let output = Command::new("nickel")
|
|
.arg("--version")
|
|
.output()
|
|
.expect("Failed to run nickel --version");
|
|
|
|
assert!(
|
|
output.status.success(),
|
|
"nickel CLI not available or failed to run"
|
|
);
|
|
|
|
let version_str = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
version_str.contains("nickel"),
|
|
"nickel version output doesn't contain 'nickel'"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_simple_nickel_export_to_json() {
|
|
let ncl_code = r#"
|
|
{
|
|
name = "test-service",
|
|
port = 8080,
|
|
enabled = true,
|
|
}
|
|
"#;
|
|
|
|
// Write NCL to temp file
|
|
let temp_file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
|
|
let temp_path = temp_file.path().to_str().unwrap().to_string();
|
|
std::fs::write(&temp_path, ncl_code).expect("Failed to write NCL file");
|
|
|
|
// Run nickel export
|
|
let output = Command::new("nickel")
|
|
.arg("export")
|
|
.arg("--format")
|
|
.arg("json")
|
|
.arg(&temp_path)
|
|
.output()
|
|
.expect("Failed to run nickel export");
|
|
|
|
assert!(
|
|
output.status.success(),
|
|
"nickel export failed: {}",
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
|
|
// Parse JSON output
|
|
let json_str = String::from_utf8(output.stdout).expect("Failed to read JSON output");
|
|
let json_value: serde_json::Value =
|
|
serde_json::from_str(&json_str).expect("Failed to parse JSON");
|
|
|
|
// Verify structure
|
|
assert_eq!(json_value["name"].as_str(), Some("test-service"));
|
|
assert_eq!(json_value["port"].as_u64(), Some(8080));
|
|
assert_eq!(json_value["enabled"].as_bool(), Some(true));
|
|
}
|
|
|
|
#[test]
|
|
fn test_nested_nickel_structure_export() {
|
|
let ncl_code = r#"
|
|
{
|
|
server = {
|
|
host = "127.0.0.1",
|
|
port = 8080,
|
|
workers = 4,
|
|
},
|
|
database = {
|
|
url = "postgres://localhost",
|
|
pool_size = 10,
|
|
},
|
|
logging = {
|
|
level = "info",
|
|
format = "json",
|
|
},
|
|
}
|
|
"#;
|
|
|
|
let temp_file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
|
|
let temp_path = temp_file.path().to_str().unwrap().to_string();
|
|
std::fs::write(&temp_path, ncl_code).expect("Failed to write NCL file");
|
|
|
|
let output = Command::new("nickel")
|
|
.arg("export")
|
|
.arg("--format")
|
|
.arg("json")
|
|
.arg(&temp_path)
|
|
.output()
|
|
.expect("Failed to run nickel export");
|
|
|
|
assert!(output.status.success());
|
|
|
|
let json_str = String::from_utf8(output.stdout).expect("Failed to read JSON output");
|
|
let json_value: serde_json::Value =
|
|
serde_json::from_str(&json_str).expect("Failed to parse JSON");
|
|
|
|
// Verify nested structure
|
|
assert_eq!(json_value["server"]["host"].as_str(), Some("127.0.0.1"));
|
|
assert_eq!(json_value["server"]["port"].as_u64(), Some(8080));
|
|
assert_eq!(json_value["database"]["pool_size"].as_u64(), Some(10));
|
|
assert_eq!(json_value["logging"]["format"].as_str(), Some("json"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_nickel_array_support() {
|
|
let ncl_code = r#"
|
|
{
|
|
endpoints = {
|
|
resources = "/resources",
|
|
tools = "/tools",
|
|
prompts = "/prompts",
|
|
},
|
|
features = {
|
|
enabled_modules = ["resources", "tools", "prompts"],
|
|
cache_ttl_seconds = 300,
|
|
},
|
|
}
|
|
"#;
|
|
|
|
let temp_file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
|
|
let temp_path = temp_file.path().to_str().unwrap().to_string();
|
|
std::fs::write(&temp_path, ncl_code).expect("Failed to write NCL file");
|
|
|
|
let output = Command::new("nickel")
|
|
.arg("export")
|
|
.arg("--format")
|
|
.arg("json")
|
|
.arg(&temp_path)
|
|
.output()
|
|
.expect("Failed to run nickel export");
|
|
|
|
assert!(output.status.success());
|
|
|
|
let json_str = String::from_utf8(output.stdout).expect("Failed to read JSON output");
|
|
let json_value: serde_json::Value =
|
|
serde_json::from_str(&json_str).expect("Failed to parse JSON");
|
|
|
|
// Verify array
|
|
let modules = json_value["features"]["enabled_modules"]
|
|
.as_array()
|
|
.expect("enabled_modules should be an array");
|
|
assert_eq!(modules.len(), 3);
|
|
assert_eq!(modules[0].as_str(), Some("resources"));
|
|
assert_eq!(modules[1].as_str(), Some("tools"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_nickel_with_comments() {
|
|
let ncl_code = r#"
|
|
# Configuration file with comments
|
|
# This tests that comments are properly ignored
|
|
|
|
{
|
|
# Server section
|
|
server = {
|
|
# Bind address
|
|
host = "127.0.0.1",
|
|
# Port number
|
|
port = 8080,
|
|
},
|
|
# Enable/disable
|
|
enabled = true,
|
|
}
|
|
"#;
|
|
|
|
let temp_file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
|
|
let temp_path = temp_file.path().to_str().unwrap().to_string();
|
|
std::fs::write(&temp_path, ncl_code).expect("Failed to write NCL file");
|
|
|
|
let output = Command::new("nickel")
|
|
.arg("export")
|
|
.arg("--format")
|
|
.arg("json")
|
|
.arg(&temp_path)
|
|
.output()
|
|
.expect("Failed to run nickel export");
|
|
|
|
assert!(
|
|
output.status.success(),
|
|
"nickel export failed: {}",
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
|
|
let json_str = String::from_utf8(output.stdout).expect("Failed to read JSON output");
|
|
let json_value: serde_json::Value =
|
|
serde_json::from_str(&json_str).expect("Failed to parse JSON");
|
|
|
|
assert_eq!(json_value["server"]["host"].as_str(), Some("127.0.0.1"));
|
|
assert_eq!(json_value["enabled"].as_bool(), Some(true));
|
|
}
|
|
|
|
#[test]
|
|
fn test_nickel_with_null_values() {
|
|
let ncl_code = r#"
|
|
{
|
|
name = "service",
|
|
description = null,
|
|
optional_port = null,
|
|
enabled = true,
|
|
}
|
|
"#;
|
|
|
|
let temp_file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
|
|
let temp_path = temp_file.path().to_str().unwrap().to_string();
|
|
std::fs::write(&temp_path, ncl_code).expect("Failed to write NCL file");
|
|
|
|
let output = Command::new("nickel")
|
|
.arg("export")
|
|
.arg("--format")
|
|
.arg("json")
|
|
.arg(&temp_path)
|
|
.output()
|
|
.expect("Failed to run nickel export");
|
|
|
|
assert!(output.status.success());
|
|
|
|
let json_str = String::from_utf8(output.stdout).expect("Failed to read JSON output");
|
|
let json_value: serde_json::Value =
|
|
serde_json::from_str(&json_str).expect("Failed to parse JSON");
|
|
|
|
assert!(json_value["description"].is_null());
|
|
assert!(json_value["optional_port"].is_null());
|
|
assert_eq!(json_value["name"].as_str(), Some("service"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_nickel_with_numeric_types() {
|
|
let ncl_code = r#"
|
|
{
|
|
port = 8080,
|
|
workers = 4,
|
|
timeout_seconds = 30,
|
|
cache_ttl_ms = 300000,
|
|
threshold = 0.95,
|
|
retries = 3,
|
|
}
|
|
"#;
|
|
|
|
let temp_file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
|
|
let temp_path = temp_file.path().to_str().unwrap().to_string();
|
|
std::fs::write(&temp_path, ncl_code).expect("Failed to write NCL file");
|
|
|
|
let output = Command::new("nickel")
|
|
.arg("export")
|
|
.arg("--format")
|
|
.arg("json")
|
|
.arg(&temp_path)
|
|
.output()
|
|
.expect("Failed to run nickel export");
|
|
|
|
assert!(output.status.success());
|
|
|
|
let json_str = String::from_utf8(output.stdout).expect("Failed to read JSON output");
|
|
let json_value: serde_json::Value =
|
|
serde_json::from_str(&json_str).expect("Failed to parse JSON");
|
|
|
|
assert_eq!(json_value["port"].as_u64(), Some(8080));
|
|
assert_eq!(json_value["workers"].as_u64(), Some(4));
|
|
assert_eq!(json_value["timeout_seconds"].as_u64(), Some(30));
|
|
assert_eq!(json_value["cache_ttl_ms"].as_u64(), Some(300000));
|
|
assert!(
|
|
(json_value["threshold"].as_f64().unwrap() - 0.95).abs() < 0.001,
|
|
"threshold should be 0.95"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_nickel_with_multiline_strings() {
|
|
let ncl_code = r#"
|
|
{
|
|
name = "service",
|
|
description = "This is a multiline description
|
|
that spans multiple lines
|
|
in the configuration",
|
|
version = "1.0.0",
|
|
}
|
|
"#;
|
|
|
|
let temp_file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
|
|
let temp_path = temp_file.path().to_str().unwrap().to_string();
|
|
std::fs::write(&temp_path, ncl_code).expect("Failed to write NCL file");
|
|
|
|
let output = Command::new("nickel")
|
|
.arg("export")
|
|
.arg("--format")
|
|
.arg("json")
|
|
.arg(&temp_path)
|
|
.output()
|
|
.expect("Failed to run nickel export");
|
|
|
|
assert!(
|
|
output.status.success(),
|
|
"nickel export failed: {}",
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
|
|
let json_str = String::from_utf8(output.stdout).expect("Failed to read JSON output");
|
|
let _json_value: serde_json::Value =
|
|
serde_json::from_str(&json_str).expect("Failed to parse JSON");
|
|
|
|
// Verify multiline strings are handled correctly
|
|
assert!(json_str.contains("multiline"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_nickel_invalid_syntax_error() {
|
|
let ncl_code = r#"
|
|
{
|
|
name = "service",
|
|
port = invalid_value, # This is invalid syntax
|
|
}
|
|
"#;
|
|
|
|
let temp_file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
|
|
let temp_path = temp_file.path().to_str().unwrap().to_string();
|
|
std::fs::write(&temp_path, ncl_code).expect("Failed to write NCL file");
|
|
|
|
let output = Command::new("nickel")
|
|
.arg("export")
|
|
.arg("--format")
|
|
.arg("json")
|
|
.arg(&temp_path)
|
|
.output()
|
|
.expect("Failed to run nickel export");
|
|
|
|
// Should fail due to invalid syntax
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
!stderr.is_empty(),
|
|
"Expected error message for invalid syntax"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_nickel_via_configloader_from_path() {
|
|
let ncl_code = r#"
|
|
{
|
|
name = "configloader-test",
|
|
port = 9999,
|
|
enabled = true,
|
|
}
|
|
"#;
|
|
|
|
let temp_file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
|
|
let temp_path = temp_file.path().to_path_buf();
|
|
|
|
// Change extension to .ncl for ConfigLoader to recognize it
|
|
let ncl_path = temp_path.with_extension("ncl");
|
|
std::fs::write(&ncl_path, ncl_code).expect("Failed to write NCL file");
|
|
|
|
// Use ConfigLoader to load the NCL file
|
|
let config =
|
|
TestNickelConfig::from_path(&ncl_path).expect("Failed to load NCL config via ConfigLoader");
|
|
|
|
assert_eq!(config.name, "configloader-test");
|
|
assert_eq!(config.port, 9999);
|
|
assert!(config.enabled);
|
|
}
|