greentic-component 0.5.1

High-level component loader and store for Greentic components
Documentation
#![cfg(feature = "cli")]

use assert_cmd::cargo::cargo_bin_cmd;
use serde_json::Value as JsonValue;
use std::fs;
use tempfile::TempDir;

#[test]
fn readme_quickstart_example_stays_fresh() {
    let temp = TempDir::new().expect("temp dir");
    let component_dir = temp.path().join("hello-world");

    let mut new_cmd = cargo_bin_cmd!("greentic-component");
    new_cmd
        .arg("new")
        .arg("--name")
        .arg("hello-world")
        .arg("--org")
        .arg("ai.greentic")
        .arg("--path")
        .arg(&component_dir)
        .arg("--non-interactive")
        .arg("--no-check")
        .env("HOME", temp.path())
        .env("CARGO_NET_OFFLINE", "true")
        .env("GIT_AUTHOR_NAME", "Greentic Labs")
        .env("GIT_AUTHOR_EMAIL", "greentic-labs@example.com")
        .env("GIT_COMMITTER_NAME", "Greentic Labs")
        .env("GIT_COMMITTER_EMAIL", "greentic-labs@example.com")
        .env_remove("USER")
        .env_remove("USERNAME");
    new_cmd.assert().success();

    let mut flow_cmd = cargo_bin_cmd!("greentic-component");
    flow_cmd
        .current_dir(&component_dir)
        .arg("flow")
        .arg("update")
        .env("CARGO_NET_OFFLINE", "true");
    flow_cmd.assert().success();

    let manifest_raw =
        fs::read_to_string(component_dir.join("component.manifest.json")).expect("manifest");
    let manifest_json: JsonValue =
        serde_json::from_str(&manifest_raw).expect("manifest json parses");
    assert_eq!(
        manifest_json["id"].as_str(),
        Some("ai.greentic.hello-world"),
        "scaffold id should match name/org"
    );
    let operations = manifest_json["operations"]
        .as_array()
        .expect("operations array");
    assert!(
        operations.iter().any(|op| op["name"] == "handle_message"),
        "scaffold should include handle_message operation"
    );

    let default_template =
        manifest_json["dev_flows"]["default"]["graph"]["nodes"]["emit_config"]["template"]
            .as_str()
            .expect("default template string");
    let default_flow_json: JsonValue =
        serde_json::from_str(default_template).expect("template json");
    assert_eq!(default_flow_json["node_id"], "hello-world");
    assert!(
        default_flow_json["node"].get("component.exec").is_none(),
        "YGTc v2 should not emit component.exec wrapper"
    );
    let op_node = default_flow_json["node"]["handle_message"]
        .as_object()
        .expect("operation node");
    assert!(
        op_node.contains_key("input"),
        "operation node should include input"
    );
    assert_eq!(
        default_flow_json["node"]["routing"][0]["to"],
        "NEXT_NODE_PLACEHOLDER"
    );

    let lib_rs = fs::read_to_string(component_dir.join("src/lib.rs")).expect("lib.rs");
    assert!(
        lib_rs.contains("impl node::Guest") && lib_rs.contains("export_component_v060!("),
        "scaffold should expose the canonical 0.6 node export"
    );
}