robotrt-cli 0.1.0-beta.2

RobotRT modular robotics runtime and middleware components.
use super::*;

#[test]
fn sdk_init_creates_scaffold_files() {
    let root = temp_dir("sdk-init-root").expect("create temp sdk root");
    let project_name = "demo_sdk_app";
    let root_arg = root.to_str().expect("utf-8 path");

    let output = run_cli(&[
        "sdk",
        "init",
        project_name,
        "--template",
        "local",
        "--output",
        root_arg,
        "--json",
    ]);
    assert!(output.status.success(), "stderr: {}", output.stderr);

    let payload: Value = serde_json::from_str(&output.stdout).expect("parse sdk init json");
    assert_eq!(
        payload.get("api_version").and_then(Value::as_str),
        Some("robotrt.sdk.init.v1")
    );

    let project_dir = root.join(project_name);
    assert!(project_dir.join("Cargo.toml").exists());
    assert!(project_dir.join("README.md").exists());
    assert!(project_dir.join("robotrt.toml").exists());
    assert!(project_dir.join("src/main.rs").exists());

    let cargo = fs::read_to_string(project_dir.join("Cargo.toml")).expect("read cargo file");
    assert!(cargo.contains("core-api"));
    assert!(cargo.contains("data-model"));
}

#[test]
fn sdk_init_supports_action_template() {
    let root = temp_dir("sdk-init-action-root").expect("create temp sdk action root");
    let project_name = "demo_sdk_action_app";

    let output = run_cli(&[
        "sdk",
        "init",
        project_name,
        "--template",
        "action",
        "--output",
        root.to_str().expect("utf-8 path"),
    ]);
    assert!(output.status.success(), "stderr: {}", output.stderr);

    let main_rs = fs::read_to_string(root.join(project_name).join("src/main.rs"))
        .expect("read action template main.rs");
    assert!(main_rs.contains("action template scaffold created"));
}

#[test]
fn sdk_migrate_updates_config_and_creates_backup() {
    let root = temp_dir("sdk-migrate-root").expect("create temp sdk migrate root");
    let project_name = "demo_sdk_migrate_app";
    let root_arg = root.to_str().expect("utf-8 path");

    let init = run_cli(&[
        "sdk",
        "init",
        project_name,
        "--template",
        "local",
        "--output",
        root_arg,
    ]);
    assert!(init.status.success(), "stderr: {}", init.stderr);

    let project_dir = root.join(project_name);
    let project_arg = project_dir.to_str().expect("utf-8 path");

    let migrate = run_cli(&["sdk", "migrate", project_arg, "--json"]);
    assert!(migrate.status.success(), "stderr: {}", migrate.stderr);
    let migrate_payload: Value =
        serde_json::from_str(&migrate.stdout).expect("parse sdk migrate json");
    assert_eq!(
        migrate_payload.get("api_version").and_then(Value::as_str),
        Some("robotrt.sdk.migrate.v1")
    );
    assert_eq!(
        migrate_payload["result"]["to_schema_version"].as_u64(),
        Some(2)
    );
    assert_eq!(migrate_payload["result"]["updated"].as_bool(), Some(true));

    let config =
        fs::read_to_string(project_dir.join("robotrt.toml")).expect("read migrated robotrt.toml");
    assert!(config.contains("schema_version = 2"));
    assert!(project_dir.join("robotrt.toml.bak").exists());

    let migrate_again = run_cli(&["sdk", "migrate", "--project", project_arg, "--json"]);
    assert!(
        migrate_again.status.success(),
        "stderr: {}",
        migrate_again.stderr
    );
    let migrate_again_payload: Value =
        serde_json::from_str(&migrate_again.stdout).expect("parse sdk migrate second json");
    assert_eq!(
        migrate_again_payload["result"]["updated"].as_bool(),
        Some(false)
    );
}

#[test]
fn sdk_migrate_supports_chain_dry_run_and_rollback() {
    let root = temp_dir("sdk-migrate-chain-root").expect("create temp sdk migrate chain root");
    let project_name = "demo_sdk_chain_app";

    let init = run_cli(&[
        "sdk",
        "init",
        project_name,
        "--template",
        "action",
        "--output",
        root.to_str().expect("utf-8 path"),
    ]);
    assert!(init.status.success(), "stderr: {}", init.stderr);

    let project_dir = root.join(project_name);
    let project_arg = project_dir.to_str().expect("utf-8 path");

    let dry_run = run_cli(&[
        "sdk",
        "migrate",
        project_arg,
        "--target-schema",
        "4",
        "--dry-run",
        "--json",
    ]);
    assert!(dry_run.status.success(), "stderr: {}", dry_run.stderr);
    let dry_payload: Value = serde_json::from_str(&dry_run.stdout).expect("parse dry-run json");
    assert_eq!(dry_payload["query"]["dry_run"].as_bool(), Some(true));
    assert_eq!(dry_payload["result"]["to_schema_version"].as_u64(), Some(4));

    let migrate = run_cli(&[
        "sdk",
        "migrate",
        project_arg,
        "--target-schema",
        "4",
        "--json",
    ]);
    assert!(migrate.status.success(), "stderr: {}", migrate.stderr);
    let migrate_payload: Value = serde_json::from_str(&migrate.stdout).expect("parse migrate json");
    assert_eq!(
        migrate_payload["result"]["to_schema_version"].as_u64(),
        Some(4)
    );
    assert_eq!(migrate_payload["result"]["updated"].as_bool(), Some(true));

    let migrated_config =
        fs::read_to_string(project_dir.join("robotrt.toml")).expect("read migrated config");
    assert!(migrated_config.contains("schema_version = 4"));
    assert!(project_dir.join("robotrt.migrate.manifest.json").exists());

    let rollback = run_cli(&["sdk", "rollback", project_arg, "--json"]);
    assert!(rollback.status.success(), "stderr: {}", rollback.stderr);
    let rollback_payload: Value =
        serde_json::from_str(&rollback.stdout).expect("parse rollback json");
    assert_eq!(
        rollback_payload.get("api_version").and_then(Value::as_str),
        Some("robotrt.sdk.rollback.v1")
    );

    let rolled_back_config =
        fs::read_to_string(project_dir.join("robotrt.toml")).expect("read rolled back config");
    assert!(
        !rolled_back_config.contains("schema_version = 4"),
        "rollback should restore previous config"
    );
}