use mecha10_cli::services::config::*;
use serial_test::serial;
use tempfile::TempDir;
fn create_test_config(dir: &Path) -> PathBuf {
let config_path = dir.join("mecha10.json");
std::fs::write(
&config_path,
r#"{
"robot": {
"id": "test-robot-001"
},
"nodes": {
"drivers": [],
"custom": []
},
"services": {}
}"#,
)
.unwrap();
config_path
}
#[test]
fn test_is_initialized() {
let temp_dir = TempDir::new().unwrap();
assert!(!ConfigService::is_initialized(temp_dir.path()));
create_test_config(temp_dir.path());
assert!(ConfigService::is_initialized(temp_dir.path()));
}
#[tokio::test]
async fn test_load_from() {
let temp_dir = TempDir::new().unwrap();
let config_path = create_test_config(temp_dir.path());
let config = ConfigService::load_from(&config_path).await.unwrap();
assert_eq!(config.robot.id, "test-robot-001");
}
#[tokio::test]
async fn test_load_from_nonexistent() {
let result = ConfigService::load_from(&PathBuf::from("nonexistent.json")).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("not found"));
}
#[tokio::test]
async fn test_load_robot_id() {
let temp_dir = TempDir::new().unwrap();
let config_path = create_test_config(temp_dir.path());
let robot_id = ConfigService::load_robot_id(&config_path).await.unwrap();
assert_eq!(robot_id, "test-robot-001");
}
#[test]
fn test_find_config_from() {
let temp_dir = TempDir::new().unwrap();
create_test_config(temp_dir.path());
let found = ConfigService::find_config_from(temp_dir.path()).unwrap();
assert_eq!(found, temp_dir.path().join("mecha10.json"));
let sub_dir = temp_dir.path().join("subdir");
std::fs::create_dir(&sub_dir).unwrap();
let found = ConfigService::find_config_from(&sub_dir).unwrap();
assert_eq!(found, temp_dir.path().join("mecha10.json"));
}
#[test]
fn test_find_config_not_found() {
let temp_dir = TempDir::new().unwrap();
let result = ConfigService::find_config_from(temp_dir.path());
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("No mecha10.json found"));
}
#[test]
fn test_default_config_paths() {
let paths = ConfigService::default_config_paths();
assert!(!paths.is_empty());
assert!(paths.contains(&PathBuf::from("mecha10.json")));
}
#[tokio::test]
#[serial]
async fn test_try_load_from_defaults() {
let temp_dir = TempDir::new().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
create_test_config(temp_dir.path());
let (path, config) = ConfigService::try_load_from_defaults().await.unwrap();
assert_eq!(path, PathBuf::from("mecha10.json"));
assert_eq!(config.robot.id, "test-robot-001");
}
#[tokio::test]
#[serial]
async fn test_load_default() {
let temp_dir = TempDir::new().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
create_test_config(temp_dir.path());
let config = ConfigService::load_default().await.unwrap();
assert_eq!(config.robot.id, "test-robot-001");
}
#[tokio::test]
#[serial]
async fn test_load_default_not_found() {
let temp_dir = TempDir::new().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let result = ConfigService::load_default().await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("not found"));
}
#[tokio::test]
async fn test_load_from_invalid_json() {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("mecha10.json");
std::fs::write(&config_path, "{ invalid json }").unwrap();
let result = ConfigService::load_from(&config_path).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Failed to parse"));
}
#[tokio::test]
async fn test_load_from_missing_robot_field() {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("mecha10.json");
std::fs::write(
&config_path,
r#"{
"nodes": {
"drivers": [],
"custom": []
},
"services": {}
}"#,
)
.unwrap();
let result = ConfigService::load_from(&config_path).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_load_from_empty_file() {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("mecha10.json");
std::fs::write(&config_path, "").unwrap();
let result = ConfigService::load_from(&config_path).await;
assert!(result.is_err());
}
#[test]
#[serial]
fn test_is_initialized_here() {
let temp_dir = TempDir::new().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
assert!(!ConfigService::is_initialized_here());
create_test_config(temp_dir.path());
assert!(ConfigService::is_initialized_here());
}
#[test]
fn test_find_config_from_nested_subdirectory() {
let temp_dir = TempDir::new().unwrap();
create_test_config(temp_dir.path());
let level1 = temp_dir.path().join("level1");
let level2 = level1.join("level2");
let level3 = level2.join("level3");
std::fs::create_dir_all(&level3).unwrap();
let found = ConfigService::find_config_from(&level3).unwrap();
assert_eq!(found, temp_dir.path().join("mecha10.json"));
}
#[test]
fn test_find_config_from_prefers_nearest() {
let temp_dir = TempDir::new().unwrap();
create_test_config(temp_dir.path());
let subdir = temp_dir.path().join("subdir");
std::fs::create_dir(&subdir).unwrap();
let subdir_config = create_test_config(&subdir);
let found = ConfigService::find_config_from(&subdir).unwrap();
assert_eq!(found, subdir_config);
}
#[tokio::test]
#[serial]
async fn test_try_load_from_defaults_config_subdirectory() {
let temp_dir = TempDir::new().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let config_dir = temp_dir.path().join("config");
std::fs::create_dir(&config_dir).unwrap();
create_test_config(&config_dir);
let (path, config) = ConfigService::try_load_from_defaults().await.unwrap();
assert_eq!(path, PathBuf::from("config/mecha10.json"));
assert_eq!(config.robot.id, "test-robot-001");
}
#[tokio::test]
#[serial]
async fn test_try_load_from_defaults_mecha10_subdirectory() {
let temp_dir = TempDir::new().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let mecha10_dir = temp_dir.path().join(".mecha10");
std::fs::create_dir(&mecha10_dir).unwrap();
create_test_config(&mecha10_dir);
let (path, config) = ConfigService::try_load_from_defaults().await.unwrap();
assert_eq!(path, PathBuf::from(".mecha10/mecha10.json"));
assert_eq!(config.robot.id, "test-robot-001");
}
#[tokio::test]
#[serial]
async fn test_try_load_from_defaults_no_valid_config() {
let temp_dir = TempDir::new().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let result = ConfigService::try_load_from_defaults().await;
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("No valid mecha10.json"));
assert!(err_msg.contains("mecha10 init"));
}
#[tokio::test]
#[serial]
async fn test_try_load_from_defaults_skips_invalid() {
let temp_dir = TempDir::new().unwrap();
std::env::set_current_dir(temp_dir.path()).unwrap();
let invalid_path = temp_dir.path().join("mecha10.json");
std::fs::write(&invalid_path, "{ invalid }").unwrap();
let config_dir = temp_dir.path().join("config");
std::fs::create_dir(&config_dir).unwrap();
create_test_config(&config_dir);
let (path, config) = ConfigService::try_load_from_defaults().await.unwrap();
assert_eq!(path, PathBuf::from("config/mecha10.json"));
assert_eq!(config.robot.id, "test-robot-001");
}
#[tokio::test]
async fn test_load_robot_id_from_nonexistent() {
let result = ConfigService::load_robot_id(&PathBuf::from("nonexistent.json")).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_load_from_permission_denied() {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let temp_dir = TempDir::new().unwrap();
let config_path = create_test_config(temp_dir.path());
let mut perms = std::fs::metadata(&config_path).unwrap().permissions();
perms.set_mode(0o000);
std::fs::set_permissions(&config_path, perms).unwrap();
let result = ConfigService::load_from(&config_path).await;
assert!(result.is_err());
let mut perms = std::fs::metadata(&config_path).unwrap().permissions();
perms.set_mode(0o644);
std::fs::set_permissions(&config_path, perms).unwrap();
}
}
#[test]
fn test_default_config_paths_order() {
let paths = ConfigService::default_config_paths();
assert_eq!(paths.len(), 3);
assert_eq!(paths[0], PathBuf::from("mecha10.json"));
assert_eq!(paths[1], PathBuf::from("config/mecha10.json"));
assert_eq!(paths[2], PathBuf::from(".mecha10/mecha10.json"));
}
#[test]
fn test_validate_nonexistent_file() {
let result = ConfigService::validate(&PathBuf::from("nonexistent.json"));
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("not found"));
assert!(err_msg.contains("mecha10 init"));
}