mecha10-cli 0.1.47

Mecha10 CLI tool
Documentation
//! Integration tests for ConfigService
//!
//! These tests verify that the ConfigService correctly handles configuration
//! file loading, validation, and project detection.

mod common;

use common::TestProject;
use mecha10_cli::services::ConfigService;
use std::path::PathBuf;

#[test]
fn test_is_initialized_here() {
    let project = TestProject::new().unwrap();
    std::env::set_current_dir(&project.root).unwrap();

    assert!(!ConfigService::is_initialized_here());

    project
        .create_file(
            "mecha10.json",
            r#"{
        "robot": {"id": "test-001"},
        "nodes": {"drivers": [], "custom": []},
        "services": {}
    }"#,
        )
        .unwrap();

    assert!(ConfigService::is_initialized_here());
}

#[test]
fn test_is_initialized() {
    let project = TestProject::new().unwrap();

    assert!(!ConfigService::is_initialized(&project.root));

    project
        .create_file(
            "mecha10.json",
            r#"{
        "robot": {"id": "test-001"},
        "nodes": {"drivers": [], "custom": []},
        "services": {}
    }"#,
        )
        .unwrap();

    assert!(ConfigService::is_initialized(&project.root));
}

#[tokio::test]
async fn test_load_default() {
    let project = TestProject::new().unwrap();
    std::env::set_current_dir(&project.root).unwrap();

    project
        .create_file(
            "mecha10.json",
            r#"{
        "name": "test-robot",
        "version": "0.1.0",
        "robot": {"id": "test-robot-001"},
        "nodes": {"drivers": [], "custom": []},
        "services": {}
    }"#,
        )
        .unwrap();

    let config = ConfigService::load_default().await.unwrap();
    assert_eq!(config.robot.id, "test-robot-001");
}

#[tokio::test]
async fn test_load_from_specific_path() {
    let project = TestProject::new().unwrap();

    let config_path = project
        .create_file(
            "custom.json",
            r#"{
        "name": "custom-robot",
        "version": "0.1.0",
        "robot": {"id": "custom-robot"},
        "nodes": {"drivers": [], "custom": []},
        "services": {}
    }"#,
        )
        .unwrap();

    let config = ConfigService::load_from(&config_path).await.unwrap();
    assert_eq!(config.robot.id, "custom-robot");
}

#[tokio::test]
async fn test_load_from_nonexistent_file() {
    let result = ConfigService::load_from(&PathBuf::from("/nonexistent/path/mecha10.json")).await;
    assert!(result.is_err());
    let err_msg = result.unwrap_err().to_string();
    assert!(err_msg.contains("not found"));
}

#[tokio::test]
async fn test_load_invalid_json() {
    let project = TestProject::new().unwrap();

    let config_path = project.create_file("mecha10.json", "{ invalid json ").unwrap();

    let result = ConfigService::load_from(&config_path).await;
    assert!(result.is_err());
    let err_msg = result.unwrap_err().to_string();
    assert!(err_msg.contains("Failed to parse"));
}

#[tokio::test]
async fn test_load_robot_id() {
    let project = TestProject::new().unwrap();

    let config_path = project
        .create_file(
            "mecha10.json",
            r#"{
        "name": "robot-123",
        "version": "0.1.0",
        "robot": {"id": "robot-123"},
        "nodes": {"drivers": [], "custom": []},
        "services": {}
    }"#,
        )
        .unwrap();

    let robot_id = ConfigService::load_robot_id(&config_path).await.unwrap();
    assert_eq!(robot_id, "robot-123");
}

#[test]
fn test_find_config_in_current_dir() {
    let project = TestProject::new().unwrap();

    project
        .create_file(
            "mecha10.json",
            r#"{
        "robot": {"id": "test"},
        "nodes": {"drivers": [], "custom": []},
        "services": {}
    }"#,
        )
        .unwrap();

    let found = ConfigService::find_config_from(&project.root).unwrap();
    assert_eq!(found, project.root.join("mecha10.json"));
}

#[test]
fn test_find_config_in_parent_dir() {
    let project = TestProject::new().unwrap();

    project
        .create_file(
            "mecha10.json",
            r#"{
        "robot": {"id": "test"},
        "nodes": {"drivers": [], "custom": []},
        "services": {}
    }"#,
        )
        .unwrap();

    let subdir = project.root.join("subdir/nested");
    std::fs::create_dir_all(&subdir).unwrap();

    let found = ConfigService::find_config_from(&subdir).unwrap();
    assert_eq!(found, project.root.join("mecha10.json"));
}

#[test]
fn test_find_config_not_found() {
    let project = TestProject::new().unwrap();

    let result = ConfigService::find_config_from(&project.root);
    assert!(result.is_err());
    let err_msg = result.unwrap_err().to_string();
    assert!(err_msg.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]
async fn test_try_load_from_defaults_success() {
    let project = TestProject::new().unwrap();
    std::env::set_current_dir(&project.root).unwrap();

    project
        .create_file(
            "mecha10.json",
            r#"{
        "name": "default-robot",
        "version": "0.1.0",
        "robot": {"id": "default-robot"},
        "nodes": {"drivers": [], "custom": []},
        "services": {}
    }"#,
        )
        .unwrap();

    let (path, config) = ConfigService::try_load_from_defaults().await.unwrap();
    assert_eq!(path, PathBuf::from("mecha10.json"));
    assert_eq!(config.robot.id, "default-robot");
}

#[tokio::test]
async fn test_try_load_from_defaults_not_found() {
    let project = TestProject::new().unwrap();
    std::env::set_current_dir(&project.root).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 found"));
}

#[test]
fn test_validate_nonexistent_file() {
    let result = ConfigService::validate(&PathBuf::from("/nonexistent/mecha10.json"));
    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("not found"));
}

#[tokio::test]
async fn test_config_with_services() {
    let project = TestProject::new().unwrap();

    let config_path = project
        .create_file(
            "mecha10.json",
            r#"{
        "name": "service-robot",
        "version": "0.1.0",
        "robot": {"id": "service-robot"},
        "nodes": {"drivers": [], "custom": []},
        "services": {
            "http_api": {
                "host": "0.0.0.0",
                "port": 8080
            },
            "database": {
                "url": "postgresql://localhost/test",
                "max_connections": 20,
                "timeout_seconds": 60
            }
        }
    }"#,
        )
        .unwrap();

    let config = ConfigService::load_from(&config_path).await.unwrap();
    assert_eq!(config.robot.id, "service-robot");
    assert!(config.services.http_api.is_some());
    assert!(config.services.database.is_some());

    let http_api = config.services.http_api.unwrap();
    assert_eq!(http_api.host, "0.0.0.0");
    assert_eq!(http_api.port, 8080);

    let database = config.services.database.unwrap();
    assert_eq!(database.url, "postgresql://localhost/test");
    assert_eq!(database.max_connections, 20);
    assert_eq!(database.timeout_seconds, 60);
}

#[tokio::test]
async fn test_config_with_nodes() {
    let project = TestProject::new().unwrap();

    let config_path = project
        .create_file(
            "mecha10.json",
            r#"{
        "name": "node-robot",
        "version": "0.1.0",
        "robot": {"id": "node-robot"},
        "nodes": [
            "@mecha10/camera",
            "@local/custom-node"
        ],
        "services": {}
    }"#,
        )
        .unwrap();

    let config = ConfigService::load_from(&config_path).await.unwrap();
    assert_eq!(config.nodes.len(), 2);
    assert!(config.nodes.contains("camera"));
    assert!(config.nodes.contains("custom-node"));
}