robotrt-cli 0.1.0-beta.2

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

#[test]
fn sdk_migrate_report_aggregates_failure_groups_and_rollback_suggestions() {
    let root = temp_dir("sdk-migrate-report-root").expect("create temp sdk report root");
    let report = temp_path("sdk-migrate-report", "json");

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

    let ok_dir = root.join("demo_sdk_report_ok");
    let bad_dir = root.join("demo_sdk_report_missing");
    let projects_arg = format!(
        "{},{}",
        ok_dir.to_str().expect("utf-8 path"),
        bad_dir.to_str().expect("utf-8 path")
    );

    let batch = run_cli(&[
        "sdk",
        "migrate-batch",
        "--projects",
        projects_arg.as_str(),
        "--target-schema",
        "4",
        "--output",
        report.to_str().expect("utf-8 path"),
        "--json",
    ]);
    assert!(!batch.status.success(), "batch should contain one failure");

    let summary = run_cli(&[
        "sdk",
        "migrate-report",
        "--batch-report",
        report.to_str().expect("utf-8 path"),
        "--json",
    ]);
    assert!(summary.status.success(), "stderr: {}", summary.stderr);

    let payload: Value = serde_json::from_str(&summary.stdout).expect("parse migrate-report json");
    assert_eq!(
        payload.get("api_version").and_then(Value::as_str),
        Some("robotrt.sdk.migrate-report.v1")
    );
    assert!(
        !payload["result"]["failure_groups"]
            .as_object()
            .expect("failure_groups object")
            .is_empty()
    );
    assert!(
        payload["result"]["failure_code_distribution"]
            .as_object()
            .expect("failure code distribution object")
            .contains_key("E_PROJECT_NOT_FOUND")
    );
    assert_eq!(
        payload["result"]["failure_severity_distribution"]["critical"].as_u64(),
        Some(1)
    );
    assert!(
        !payload["result"]["rollback_suggestions"]
            .as_array()
            .expect("rollback suggestions array")
            .is_empty()
    );
}

#[test]
fn sdk_migrate_report_maps_warn_and_info_severity_buckets() {
    let report = temp_path("sdk-migrate-report-severity", "json");
    let report_payload = serde_json::json!({
        "api_version": "robotrt.sdk.migrate-batch.v1",
        "kind": "sdk_migrate_batch",
        "result": {
            "projects": [
                {
                    "project_dir": "/tmp/demo_warn",
                    "status": "error",
                    "error": "unsupported target schema 99 (latest supported: 4)",
                    "to_schema_version": 99
                },
                {
                    "project_dir": "/tmp/demo_info",
                    "status": "error",
                    "error": "custom pipeline timeout",
                    "to_schema_version": 4
                },
                {
                    "project_dir": "/tmp/demo_ok",
                    "status": "ok",
                    "backup_path": "/tmp/demo_ok/robotrt.toml.bak",
                    "to_schema_version": 4
                }
            ]
        }
    });
    fs::write(
        &report,
        serde_json::to_string_pretty(&report_payload).expect("serialize migrate report fixture"),
    )
    .expect("write migrate report fixture");

    let summary = run_cli(&[
        "sdk",
        "migrate-report",
        "--batch-report",
        report.to_str().expect("utf-8 path"),
        "--json",
    ]);
    assert!(summary.status.success(), "stderr: {}", summary.stderr);

    let payload: Value = serde_json::from_str(&summary.stdout).expect("parse migrate-report json");
    assert_eq!(
        payload["result"]["failure_code_distribution"]["E_UNSUPPORTED_TARGET_SCHEMA"].as_u64(),
        Some(1)
    );
    assert_eq!(
        payload["result"]["failure_code_distribution"]["E_UNKNOWN"].as_u64(),
        Some(1)
    );
    assert_eq!(
        payload["result"]["failure_severity_distribution"]["warn"].as_u64(),
        Some(1)
    );
    assert_eq!(
        payload["result"]["failure_severity_distribution"]["info"].as_u64(),
        Some(1)
    );
}

#[test]
fn sdk_migrate_report_supports_severity_map_override() {
    let report = temp_path("sdk-migrate-report-severity-override", "json");
    let severity_map = temp_path("sdk-migrate-report-severity-map", "json");

    let report_payload = serde_json::json!({
        "api_version": "robotrt.sdk.migrate-batch.v1",
        "kind": "sdk_migrate_batch",
        "result": {
            "projects": [
                {
                    "project_dir": "/tmp/demo_override",
                    "status": "error",
                    "error": "project dir not found: /tmp/demo_override",
                    "to_schema_version": 4
                }
            ]
        }
    });
    fs::write(
        &report,
        serde_json::to_string_pretty(&report_payload).expect("serialize migrate report fixture"),
    )
    .expect("write migrate report fixture");

    let severity_payload = serde_json::json!({
        "code_to_severity": {
            "E_PROJECT_NOT_FOUND": "info"
        }
    });
    fs::write(
        &severity_map,
        serde_json::to_string_pretty(&severity_payload).expect("serialize severity map fixture"),
    )
    .expect("write severity map fixture");

    let summary = run_cli(&[
        "sdk",
        "migrate-report",
        "--batch-report",
        report.to_str().expect("utf-8 path"),
        "--severity-map",
        severity_map.to_str().expect("utf-8 path"),
        "--json",
    ]);
    assert!(summary.status.success(), "stderr: {}", summary.stderr);

    let payload: Value =
        serde_json::from_str(&summary.stdout).expect("parse migrate-report severity override json");
    assert_eq!(
        payload["query"]["severity_map"].as_str(),
        severity_map.to_str()
    );
    assert_eq!(
        payload["result"]["failure_severity_distribution"]["info"].as_u64(),
        Some(1)
    );
}

#[test]
fn sdk_retry_failed_requires_batch_report_argument() {
    let output = run_cli(&["sdk", "retry-failed", "--json"]);
    assert!(!output.status.success(), "stdout: {}", output.stdout);
    assert!(
        output
            .stderr
            .contains("missing --batch-report for sdk retry-failed"),
        "stderr: {}",
        output.stderr
    );
}

#[test]
fn sdk_retry_failed_rejects_report_without_failed_projects() {
    let root = temp_dir("sdk-retry-no-failure-root").expect("create retry no-failure root");
    let report = temp_path("sdk-retry-no-failure-report", "json");

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

    let batch = run_cli(&[
        "sdk",
        "migrate-batch",
        "--projects",
        root.join("demo_sdk_retry_clean")
            .to_str()
            .expect("utf-8 path"),
        "--target-schema",
        "4",
        "--output",
        report.to_str().expect("utf-8 path"),
        "--json",
    ]);
    assert!(batch.status.success(), "stderr: {}", batch.stderr);

    let retry = run_cli(&[
        "sdk",
        "retry-failed",
        "--batch-report",
        report.to_str().expect("utf-8 path"),
        "--json",
    ]);
    assert!(!retry.status.success(), "stdout: {}", retry.stdout);
    assert!(
        retry
            .stderr
            .contains("no failed projects found in batch report"),
        "stderr: {}",
        retry.stderr
    );
}

#[test]
fn sdk_migrate_report_rejects_invalid_report_shape() {
    let bad_report = temp_path("sdk-migrate-report-invalid-shape", "json");
    fs::write(
        &bad_report,
        serde_json::json!({"api_version": "x", "result": {"oops": []}}).to_string(),
    )
    .expect("write invalid shape report");

    let output = run_cli(&[
        "sdk",
        "migrate-report",
        "--batch-report",
        bad_report.to_str().expect("utf-8 path"),
        "--json",
    ]);
    assert!(!output.status.success(), "stdout: {}", output.stdout);
    assert!(
        output.stderr.contains("missing result.projects array"),
        "stderr: {}",
        output.stderr
    );
}

#[test]
fn sdk_rollback_batch_rejects_invalid_report_json() {
    let bad_report = temp_path("sdk-rollback-batch-invalid-json", "json");
    fs::write(&bad_report, "{ this is not valid json").expect("write invalid json report");

    let output = run_cli(&[
        "sdk",
        "rollback-batch",
        "--batch-report",
        bad_report.to_str().expect("utf-8 path"),
        "--json",
    ]);
    assert!(!output.status.success(), "stdout: {}", output.stdout);
    assert!(
        output.stderr.contains("parse") && output.stderr.contains("failed"),
        "stderr: {}",
        output.stderr
    );
}