mecha10-cli 0.9.0

Mecha10 CLI tool
Documentation

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"));
}


#[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]
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_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"));
}