sdkt-cli 2.7.0

Command Line Interface (CLI) application combining core Soroban DevKit components.
use assert_cmd::Command;
use predicates::prelude::*;
use std::fs;
use tempfile::TempDir;

fn sdkt() -> Command {
    Command::cargo_bin("sdkt").unwrap()
}

const TEST_WASM: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/us_old.wasm");

#[test]
fn generate_client_writes_file() {
    let tmp = TempDir::new().unwrap();
    let out = tmp.path().join("client.rs");
    sdkt()
        .args(["generate", "client", TEST_WASM, "-o"])
        .arg(&out)
        .assert()
        .success()
        .stdout(predicate::str::contains("Generated client"));
    let code = fs::read_to_string(&out).unwrap();
    assert!(code.contains("pub struct TransferCall"));
    assert!(code.contains("pub struct MintCall"));
}

#[test]
fn generate_client_prints_to_stdout() {
    let assert = sdkt().args(["generate", "client", TEST_WASM]).assert();
    assert
        .success()
        .stdout(predicate::str::contains("pub struct TransferCall"))
        .stdout(predicate::str::contains("pub fn contract_functions()"));
}

#[test]
fn generate_client_rejects_missing_file() {
    sdkt()
        .args(["generate", "client", "/nonexistent.wasm"])
        .assert()
        .failure()
        .stderr(predicate::str::contains("cannot read WASM"));
}

#[test]
fn generate_client_rejects_non_contract_wasm() {
    let tmp = TempDir::new().unwrap();
    let plain = tmp.path().join("plain.wasm");
    fs::write(&plain, b"\x00asm\x01\x00\x00\x00").unwrap();
    sdkt()
        .args(["generate", "client"])
        .arg(&plain)
        .assert()
        .failure()
        .stderr(predicate::str::contains("No contractspecv0 section found"));
}

#[test]
fn generate_client_output_is_deterministic() {
    let tmp = TempDir::new().unwrap();
    let out1 = tmp.path().join("a.rs");
    let out2 = tmp.path().join("b.rs");
    sdkt()
        .args(["generate", "client", TEST_WASM, "-o"])
        .arg(&out1)
        .assert()
        .success();
    sdkt()
        .args(["generate", "client", TEST_WASM, "-o"])
        .arg(&out2)
        .assert()
        .success();
    let a = fs::read_to_string(&out1).unwrap();
    let b = fs::read_to_string(&out2).unwrap();
    assert_eq!(a, b, "generated output must be deterministic");
}

#[test]
fn generate_client_help() {
    sdkt()
        .args(["generate", "client", "--help"])
        .assert()
        .success()
        .stdout(predicate::str::contains("typed Rust client"))
        .stdout(predicate::str::contains("--output"))
        .stdout(predicate::str::contains("--skip-unsupported"));
}

#[test]
fn generate_help_lists_client() {
    sdkt()
        .args(["generate", "--help"])
        .assert()
        .success()
        .stdout(predicate::str::contains("client"));
}

#[test]
fn generate_client_skip_unsupported_on_supported_contract_matches_default() {
    let tmp = TempDir::new().unwrap();
    let out_default = tmp.path().join("default.rs");
    let out_skip = tmp.path().join("skip.rs");

    sdkt()
        .args(["generate", "client", TEST_WASM, "-o"])
        .arg(&out_default)
        .assert()
        .success();

    sdkt()
        .args(["generate", "client", TEST_WASM, "--skip-unsupported", "-o"])
        .arg(&out_skip)
        .assert()
        .success();

    let default_code = fs::read_to_string(&out_default).unwrap();
    let skip_code = fs::read_to_string(&out_skip).unwrap();
    assert_eq!(
        default_code, skip_code,
        "output with --skip-unsupported must be identical when contract has no unsupported types"
    );
    assert!(!skip_code.contains("Skipped"));
}

#[test]
fn generate_client_compiles_with_rustc() {
    let tmp = TempDir::new().unwrap();
    let client_rs = tmp.path().join("client.rs");

    sdkt()
        .args(["generate", "client", TEST_WASM, "--skip-unsupported", "-o"])
        .arg(&client_rs)
        .assert()
        .success();

    let status = std::process::Command::new("rustc")
        .args(["--crate-type", "lib", "--emit", "metadata"])
        .arg(&client_rs)
        .arg("--out-dir")
        .arg(tmp.path())
        .status()
        .expect("failed to run rustc");
    assert!(
        status.success(),
        "generated client code must compile with rustc"
    );
}