robotrt-cli 0.1.0-beta.1

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

#[test]
fn topic_list_json_refresh_demo_contains_expected_fields() {
    let report = temp_path("resource-report", "json");
    let output = run_cli(&[
        "topic",
        "list",
        "--report",
        report.to_str().expect("utf-8 path"),
        "--refresh-demo",
        "--json",
    ]);

    assert!(output.status.success(), "stderr: {}", output.stderr);
    assert!(output.stdout.contains("\"kind\": \"topic\""));
    assert!(output.stdout.contains("/demo/camera/raw"));
}

#[test]
fn middleware_load_refresh_demo_json_contains_totals() {
    let report = temp_path("middleware-load", "json");
    let output = run_cli(&[
        "middleware",
        "load",
        "--report",
        report.to_str().expect("utf-8 path"),
        "--refresh-demo",
        "--json",
    ]);

    assert!(output.status.success(), "stderr: {}", output.stderr);
    assert!(output.stdout.contains("\"topic_pending_total\""));
    assert!(output.stdout.contains("\"service_pending_requests_total\""));
}

#[test]
fn help_command_and_flag_show_usage() {
    let help = run_cli(&["help"]);
    assert!(help.status.success(), "stderr: {}", help.stderr);
    assert!(help.stdout.contains("robotrt-cli commands:"));
    assert!(help.stdout.contains("topic hz <name>"));

    let flag = run_cli(&["--help"]);
    assert!(flag.status.success(), "stderr: {}", flag.stderr);
    assert!(flag.stdout.contains("plugin list"));
}

#[test]
fn bag_record_and_play_json_roundtrip() {
    let bag = temp_path("demo-bag", "rrbag");

    let record = run_cli(&[
        "bag",
        "record",
        "--output",
        bag.to_str().expect("utf-8 path"),
        "--count",
        "5",
        "--topic",
        "/demo/test",
    ]);
    assert!(record.status.success(), "stderr: {}", record.stderr);

    let play = run_cli(&[
        "bag",
        "play",
        "--input",
        bag.to_str().expect("utf-8 path"),
        "--json",
    ]);
    assert!(play.status.success(), "stderr: {}", play.stderr);
    assert!(play.stdout.contains("\"total_entries\": 5"));
    assert!(play.stdout.contains("\"emitted_entries\": 5"));

    let hz = run_cli(&[
        "topic",
        "hz",
        "/demo/test",
        "--input",
        bag.to_str().expect("utf-8 path"),
        "--json",
    ]);
    assert!(hz.status.success(), "stderr: {}", hz.stderr);
    assert!(hz.stdout.contains("\"observed_entries\": 5"));

    let echo = run_cli(&[
        "topic",
        "echo",
        "/demo/test",
        "--input",
        bag.to_str().expect("utf-8 path"),
        "--limit",
        "2",
        "--json",
    ]);
    assert!(echo.status.success(), "stderr: {}", echo.stderr);
    assert!(echo.stdout.contains("\"entries\""));
    assert!(echo.stdout.contains("\"sequence\""));
}

#[test]
fn node_topic_health_graph_plugin_and_mission_commands_work_with_demo_refresh() {
    let status = temp_path("status-report", "json");
    let status_arg = status.to_str().expect("utf-8 path");

    let node_info = run_cli(&[
        "node",
        "info",
        "demo_node",
        "--report",
        status_arg,
        "--refresh-demo",
        "--json",
    ]);
    assert!(node_info.status.success(), "stderr: {}", node_info.stderr);
    assert!(
        node_info
            .stdout
            .contains("\"api_version\": \"robotrt.node.info.v1\"")
    );
    assert!(node_info.stdout.contains("\"kind\": \"node_info\""));
    assert!(node_info.stdout.contains("\"node_name\": \"demo_node\""));
    assert!(node_info.stdout.contains("\"name\": \"demo_node\""));

    let topic_info = run_cli(&[
        "topic",
        "info",
        "/demo/camera/raw",
        "--report",
        status_arg,
        "--json",
    ]);
    assert!(topic_info.status.success(), "stderr: {}", topic_info.stderr);
    assert!(
        topic_info
            .stdout
            .contains("\"api_version\": \"robotrt.topic.info.v1\"")
    );
    assert!(topic_info.stdout.contains("\"kind\": \"topic_info\""));
    assert!(
        topic_info
            .stdout
            .contains("\"topic_name\": \"/demo/camera/raw\"")
    );
    assert!(topic_info.stdout.contains("\"schema\": \"sample.image\""));

    let health = run_cli(&["health", "--report", status_arg, "--json"]);
    assert!(health.status.success(), "stderr: {}", health.stderr);
    assert!(health.stdout.contains("\"component\": \"runtime\""));

    let graph = run_cli(&["graph", "--report", status_arg, "--json"]);
    assert!(graph.status.success(), "stderr: {}", graph.stderr);
    assert!(graph.stdout.contains("\"relation\": \"publishes\""));

    let plugins = run_cli(&[
        "plugin", "list", "--report", status_arg, "--loaded", "--json",
    ]);
    assert!(plugins.status.success(), "stderr: {}", plugins.stderr);
    assert!(plugins.stdout.contains("\"loaded_only\": true"));
    assert!(
        plugins
            .stdout
            .contains("\"name\": \"sample-device-plugin\"")
    );

    let watch = run_cli(&[
        "mission",
        "watch",
        "demo_mission",
        "--report",
        status_arg,
        "--iterations",
        "1",
        "--json",
    ]);
    assert!(watch.status.success(), "stderr: {}", watch.stderr);
    assert!(watch.stdout.contains("\"name\": \"demo_mission\""));

    let action_info = run_cli(&[
        "action",
        "info",
        "/demo/calibrate",
        "--report",
        status_arg,
        "--json",
    ]);
    assert!(
        action_info.status.success(),
        "stderr: {}",
        action_info.stderr
    );
    assert!(
        action_info
            .stdout
            .contains("\"api_version\": \"robotrt.action.info.v1\"")
    );
    assert!(action_info.stdout.contains("\"health_state\": \"active\""));

    let action_watch = run_cli(&[
        "action",
        "watch",
        "/demo/calibrate",
        "--report",
        status_arg,
        "--iterations",
        "1",
        "--json",
    ]);
    assert!(
        action_watch.status.success(),
        "stderr: {}",
        action_watch.stderr
    );
    assert!(
        action_watch
            .stdout
            .contains("\"api_version\": \"robotrt.action.watch.v1\"")
    );
}

#[test]
fn node_info_supports_remote_status_query_endpoint() {
    let endpoint = allocate_udp_endpoint();
    let mut server = Command::new(env!("CARGO_BIN_EXE_robotrt-cli"))
        .args([
            "status",
            "serve",
            "--bind",
            endpoint.as_str(),
            "--source",
            "demo",
            "--once",
        ])
        .stdout(Stdio::null())
        .stderr(Stdio::piped())
        .spawn()
        .expect("spawn robotrt-cli status serve");

    std::thread::sleep(Duration::from_millis(120));

    let mut output = run_cli(&[
        "node",
        "info",
        "demo_node",
        "--endpoint",
        endpoint.as_str(),
        "--timeout-ms",
        "2000",
        "--json",
    ]);
    for _ in 0..4 {
        if output.status.success() {
            break;
        }
        std::thread::sleep(Duration::from_millis(150));
        output = run_cli(&[
            "node",
            "info",
            "demo_node",
            "--endpoint",
            endpoint.as_str(),
            "--timeout-ms",
            "2000",
            "--json",
        ]);
    }

    assert!(output.status.success(), "stderr: {}", output.stderr);
    assert!(output.stdout.contains("\"mode\": \"remote_service\""));
    assert!(
        output
            .stdout
            .contains("\"service\": \"/robotrt/status/query\"")
    );
    assert!(output.stdout.contains("\"endpoint\": \""));
    assert!(
        output
            .stdout
            .contains("\"api_version\": \"robotrt.node.info.v1\"")
    );

    let status = server.wait().expect("wait status server child");
    assert!(
        status.success(),
        "status serve child exited with {status:?}"
    );
}

#[test]
fn daemon_mode_switch_works_for_info_and_list_commands() {
    let endpoint = allocate_udp_endpoint();

    let mut server_info = Command::new(env!("CARGO_BIN_EXE_robotrt-cli"))
        .args([
            "status",
            "serve",
            "--bind",
            endpoint.as_str(),
            "--source",
            "demo",
            "--once",
        ])
        .stdout(Stdio::null())
        .stderr(Stdio::piped())
        .spawn()
        .expect("spawn robotrt-cli status serve for info");

    std::thread::sleep(Duration::from_millis(120));

    let mut info_output = run_cli(&[
        "node",
        "info",
        "demo_node",
        "--mode",
        "daemon",
        "--endpoint",
        endpoint.as_str(),
        "--json",
    ]);
    for _ in 0..4 {
        if info_output.status.success() {
            break;
        }
        std::thread::sleep(Duration::from_millis(150));
        info_output = run_cli(&[
            "node",
            "info",
            "demo_node",
            "--mode",
            "daemon",
            "--endpoint",
            endpoint.as_str(),
            "--json",
        ]);
    }
    assert!(info_output.status.success(), "stderr: {}", info_output.stderr);
    assert!(info_output.stdout.contains("\"mode\": \"remote_service\""));

    let status_info = server_info.wait().expect("wait status serve info child");
    assert!(status_info.success(), "status serve info exited with {status_info:?}");

    let mut server_list = Command::new(env!("CARGO_BIN_EXE_robotrt-cli"))
        .args([
            "status",
            "serve",
            "--bind",
            endpoint.as_str(),
            "--source",
            "demo",
            "--once",
        ])
        .stdout(Stdio::null())
        .stderr(Stdio::piped())
        .spawn()
        .expect("spawn robotrt-cli status serve for list");

    std::thread::sleep(Duration::from_millis(120));

    let mut list_output = run_cli(&[
        "service",
        "list",
        "--mode",
        "daemon",
        "--endpoint",
        endpoint.as_str(),
        "--json",
    ]);
    for _ in 0..4 {
        if list_output.status.success() {
            break;
        }
        std::thread::sleep(Duration::from_millis(150));
        list_output = run_cli(&[
            "service",
            "list",
            "--mode",
            "daemon",
            "--endpoint",
            endpoint.as_str(),
            "--json",
        ]);
    }
    assert!(list_output.status.success(), "stderr: {}", list_output.stderr);
    assert!(list_output.stdout.contains("\"mode\": \"remote_service\""));
    assert!(list_output.stdout.contains("\"/demo/echo\""));

    let status_list = server_list.wait().expect("wait status serve list child");
    assert!(status_list.success(), "status serve list exited with {status_list:?}");
}