mecha10-cli 0.1.47

Mecha10 CLI tool
Documentation
// Tests for mecha10_cli::services::template

use mecha10_cli::services::template::*;

use tempfile::TempDir;

#[test]
fn test_create_vars() {
    let service = TemplateService::new();
    let mut vars = service.create_vars();
    vars.add_name("camera_driver");

    let map = vars.to_hashmap();
    assert_eq!(map.get("name"), Some(&"camera_driver"));
    assert_eq!(map.get("PascalName"), Some(&"CameraDriver"));
    assert_eq!(map.get("UPPER_NAME"), Some(&"CAMERA_DRIVER"));
}

#[test]
fn test_render_string() {
    let service = TemplateService::new();
    let template = "Hello {{name}}, your type is {{type}}!";

    let mut vars = HashMap::new();
    vars.insert("name", "World");
    vars.insert("type", "Robot");

    let result = service.render_string(template, &vars).unwrap();
    assert_eq!(result, "Hello World, your type is Robot!");
}

#[tokio::test]
async fn test_generate_node_creates_directory_structure() -> Result<()> {
    let temp = TempDir::new()?;
    let _service = TemplateService::new();

    // Create a minimal test template
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(template_dir.join("nodes")).await?;
    tokio::fs::write(
        template_dir.join("nodes").join("sensor.rs.template"),
        "// Node: {{name}}\n// Type: {{PascalName}}",
    )
    .await?;

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output");

    service
        .generate_node("temperature_sensor", "sensor", &output_dir)
        .await?;

    let generated_file = output_dir.join("temperature_sensor").join("src").join("lib.rs");
    assert!(generated_file.exists());

    let content = tokio::fs::read_to_string(generated_file).await?;
    assert!(content.contains("// Node: temperature_sensor"));
    assert!(content.contains("// Type: TemperatureSensor"));

    Ok(())
}

#[tokio::test]
async fn test_generate_driver_creates_directory_structure() -> Result<()> {
    let temp = TempDir::new()?;
    let _service = TemplateService::new();

    // Create a minimal test template
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(template_dir.join("drivers")).await?;
    tokio::fs::write(
        template_dir.join("drivers").join("camera.rs.template"),
        "// Driver: {{name}}\n// Type: {{PascalName}}",
    )
    .await?;

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output");

    service.generate_driver("webcam_driver", "camera", &output_dir).await?;

    let generated_file = output_dir.join("webcam_driver").join("src").join("lib.rs");
    assert!(generated_file.exists());

    let content = tokio::fs::read_to_string(generated_file).await?;
    assert!(content.contains("// Driver: webcam_driver"));
    assert!(content.contains("// Type: WebcamDriver"));

    Ok(())
}

// === New Comprehensive Tests ===

#[test]
fn test_new() {
    let service = TemplateService::new();
    // Service should be created without errors
    let vars = service.create_vars();
    assert!(vars.to_hashmap().is_empty());
}

#[test]
fn test_default() {
    let service = TemplateService::default();
    let vars = service.create_vars();
    assert!(vars.to_hashmap().is_empty());
}

#[test]
fn test_with_template_dir() {
    let temp = TempDir::new().unwrap();
    let template_dir = temp.path().join("custom_templates");
    std::fs::create_dir(&template_dir).unwrap();

    let service = TemplateService::with_template_dir(&template_dir);
    // Service should be created with custom directory
    let vars = service.create_vars();
    assert!(vars.to_hashmap().is_empty());
}

#[test]
fn test_render_string_multiple_variables() {
    let service = TemplateService::new();
    let template = "{{greeting}} {{name}}, your {{item}} is ready!";

    let mut vars = HashMap::new();
    vars.insert("greeting", "Hello");
    vars.insert("name", "Alice");
    vars.insert("item", "robot");

    let result = service.render_string(template, &vars).unwrap();
    assert_eq!(result, "Hello Alice, your robot is ready!");
}

#[test]
fn test_render_string_no_variables() {
    let service = TemplateService::new();
    let template = "Static content with no placeholders";

    let vars = HashMap::new();
    let result = service.render_string(template, &vars).unwrap();
    assert_eq!(result, "Static content with no placeholders");
}

#[test]
fn test_render_string_empty_template() {
    let service = TemplateService::new();
    let vars = HashMap::new();

    let result = service.render_string("", &vars).unwrap();
    assert_eq!(result, "");
}

#[test]
fn test_render_string_duplicate_placeholders() {
    let service = TemplateService::new();
    let template = "{{name}} and {{name}} are the same";

    let mut vars = HashMap::new();
    vars.insert("name", "Bob");

    let result = service.render_string(template, &vars).unwrap();
    assert_eq!(result, "Bob and Bob are the same");
}

#[tokio::test]
async fn test_generate_type() -> Result<()> {
    let temp = TempDir::new()?;
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(template_dir.join("types")).await?;
    tokio::fs::write(
        template_dir.join("types").join("custom.rs.template"),
        "pub struct {{PascalName}} {{\n    // {{name}}\n}}",
    )
    .await?;

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output");

    service.generate_type("sensor_data", &output_dir).await?;

    let generated_file = output_dir.join("sensor_data.rs");
    assert!(generated_file.exists());

    let content = tokio::fs::read_to_string(generated_file).await?;
    assert!(content.contains("pub struct SensorData"));
    assert!(content.contains("// sensor_data"));

    Ok(())
}

#[tokio::test]
async fn test_generate_cargo_toml() -> Result<()> {
    let temp = TempDir::new()?;
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(template_dir.join("cargo")).await?;
    tokio::fs::write(
        template_dir.join("cargo").join("node.toml.template"),
        "[package]\nname = \"{{name}}\"\nversion = \"0.1.0\"",
    )
    .await?;

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output");
    tokio::fs::create_dir(&output_dir).await?;

    service.generate_cargo_toml("test_node", "node", &output_dir).await?;

    let generated_file = output_dir.join("Cargo.toml");
    assert!(generated_file.exists());

    let content = tokio::fs::read_to_string(generated_file).await?;
    assert!(content.contains("name = \"test_node\""));
    assert!(content.contains("version = \"0.1.0\""));

    Ok(())
}

#[tokio::test]
async fn test_generate_node_missing_template() {
    let temp = TempDir::new().unwrap();
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(&template_dir).await.unwrap();

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output");

    let result = service.generate_node("test_node", "nonexistent", &output_dir).await;
    assert!(result.is_err());
    let err_msg = result.unwrap_err().to_string();
    assert!(err_msg.contains("Template not found"));
}

#[tokio::test]
async fn test_generate_driver_missing_template() {
    let temp = TempDir::new().unwrap();
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(&template_dir).await.unwrap();

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output");

    let result = service.generate_driver("test_driver", "nonexistent", &output_dir).await;
    assert!(result.is_err());
    let err_msg = result.unwrap_err().to_string();
    assert!(err_msg.contains("Template not found"));
}

#[tokio::test]
async fn test_generate_type_missing_template() {
    let temp = TempDir::new().unwrap();
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(&template_dir).await.unwrap();

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output");

    let result = service.generate_type("test_type", &output_dir).await;
    assert!(result.is_err());
    let err_msg = result.unwrap_err().to_string();
    assert!(err_msg.contains("Template not found"));
}

#[tokio::test]
async fn test_generate_cargo_toml_missing_template() {
    let temp = TempDir::new().unwrap();
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(&template_dir).await.unwrap();

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output");

    let result = service.generate_cargo_toml("test", "nonexistent", &output_dir).await;
    assert!(result.is_err());
    let err_msg = result.unwrap_err().to_string();
    assert!(err_msg.contains("Template not found"));
}

#[tokio::test]
async fn test_generate_node_creates_parent_directories() -> Result<()> {
    let temp = TempDir::new()?;
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(template_dir.join("nodes")).await?;
    tokio::fs::write(template_dir.join("nodes").join("sensor.rs.template"), "// {{name}}").await?;

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output").join("nested").join("path");

    service.generate_node("test_node", "sensor", &output_dir).await?;

    let generated_file = output_dir.join("test_node").join("src").join("lib.rs");
    assert!(generated_file.exists());

    Ok(())
}

#[tokio::test]
async fn test_generate_driver_creates_parent_directories() -> Result<()> {
    let temp = TempDir::new()?;
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(template_dir.join("drivers")).await?;
    tokio::fs::write(template_dir.join("drivers").join("motor.rs.template"), "// {{name}}").await?;

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output").join("nested").join("path");

    service.generate_driver("test_driver", "motor", &output_dir).await?;

    let generated_file = output_dir.join("test_driver").join("src").join("lib.rs");
    assert!(generated_file.exists());

    Ok(())
}

#[tokio::test]
async fn test_generate_type_creates_parent_directories() -> Result<()> {
    let temp = TempDir::new()?;
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(template_dir.join("types")).await?;
    tokio::fs::write(template_dir.join("types").join("custom.rs.template"), "// {{name}}").await?;

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output").join("nested").join("path");
    tokio::fs::create_dir_all(&output_dir).await?;

    service.generate_type("test_type", &output_dir).await?;

    let generated_file = output_dir.join("test_type.rs");
    assert!(generated_file.exists());

    Ok(())
}

#[test]
fn test_render_string_with_special_characters() {
    let service = TemplateService::new();
    let template = "{{name}} has $pecial ch@rs!";

    let mut vars = HashMap::new();
    vars.insert("name", "Test");

    let result = service.render_string(template, &vars).unwrap();
    assert_eq!(result, "Test has $pecial ch@rs!");
}

#[test]
fn test_render_string_with_unicode() {
    let service = TemplateService::new();
    let template = "Hello {{name}}! 你好 {{greeting}}";

    let mut vars = HashMap::new();
    vars.insert("name", "世界");
    vars.insert("greeting", "🤖");

    let result = service.render_string(template, &vars).unwrap();
    assert_eq!(result, "Hello 世界! 你好 🤖");
}

#[tokio::test]
async fn test_generate_cargo_toml_overwrites_existing() -> Result<()> {
    let temp = TempDir::new()?;
    let template_dir = temp.path().join("templates");
    tokio::fs::create_dir_all(template_dir.join("cargo")).await?;
    tokio::fs::write(
        template_dir.join("cargo").join("driver.toml.template"),
        "[package]\nname = \"{{name}}\"",
    )
    .await?;

    let service = TemplateService::with_template_dir(template_dir);
    let output_dir = temp.path().join("output");
    tokio::fs::create_dir(&output_dir).await?;

    // Write initial file
    tokio::fs::write(output_dir.join("Cargo.toml"), "old content").await?;

    // Generate should overwrite
    service.generate_cargo_toml("new_name", "driver", &output_dir).await?;

    let content = tokio::fs::read_to_string(output_dir.join("Cargo.toml")).await?;
    assert!(content.contains("name = \"new_name\""));
    assert!(!content.contains("old content"));

    Ok(())
}

#[test]
fn test_render_string_preserves_unmatched_braces() {
    let service = TemplateService::new();
    let template = "{{name}} has {unmatched} and {{type}}";

    let mut vars = HashMap::new();
    vars.insert("name", "Test");
    vars.insert("type", "Robot");

    let result = service.render_string(template, &vars).unwrap();
    assert_eq!(result, "Test has {unmatched} and Robot");
}