sylphx-cli 0.1.2

Sylphx Platform CLI — dogfoods the Rust Management SDK
//! Launch the real `sylphx` binary against the wire-mock server.
//! Full agent operator surface — field asserts, twice.

use assert_cmd::cargo::cargo_bin;
use predicates::prelude::*;
use sylphx_wire_mock::WireMockServer;
use tokio::process::Command;

/// Every shipped product command, twice, against wire-mock.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn cli_all_shipped_commands_twice() {
    let mock = WireMockServer::start().await;
    let base = mock.management_base_url();
    let bin = cargo_bin!("sylphx");

    let runs: &[&[&str]] = &[
        &["health", "--json"],
        &["whoami", "--json"],
        &["orgs", "list", "--json"],
        &["projects", "list", "--json"],
        &["projects", "get", "prj_demo", "--json"],
        &["projects", "create", "NewApp", "--slug", "new-app", "--json"],
        &["environments", "list", "prj_demo", "--json"],
        &["environments", "get", "prj_demo", "env_prod", "--json"],
        &["deployments", "list", "prj_demo", "--json"],
        &["deploy", "prj_demo", "--env", "production", "--json"],
        &["rollback", "prj_demo", "--json"],
        &["status", "prj_demo", "--json"],
        &["env", "list", "prj_demo", "--env", "production", "--json"],
        &[
            "env",
            "set",
            "prj_demo",
            "FOO=bar",
            "--env",
            "production",
            "--json",
        ],
        &["env", "rm", "prj_demo", "FOO", "--env", "production", "--json"],
        &["logs", "--project", "prj_demo", "--type", "build", "--tail", "10", "--json"],
        &[
            "logs",
            "--environment",
            "env_prod",
            "--type",
            "runtime",
            "--tail",
            "10",
            "--json",
        ],
        &["resources", "list", "--kind", "database", "--json"],
        &["resources", "get", "res_db1", "--json"],
        &[
            "resources",
            "create",
            "database",
            "analytics",
            "--project",
            "prj_demo",
            "--tier",
            "standard",
            "--storage",
            "10",
            "--json",
        ],
        &["resources", "bind", "res_db1", "env_prod", "--json"],
        &["resources", "delete", "res_analytics", "--json"],
        &[
            "db",
            "create",
            "app-db",
            "--project",
            "prj_demo",
            "--tier",
            "standard",
            "--storage",
            "10",
            "--json",
        ],
        &["db", "list", "--project", "prj_demo", "--json"],
        &["db", "get", "res_db1", "--json"],
        &["db", "branch", "res_db1", "--name", "staging-copy", "--json"],
        &["db", "delete", "res_app-db", "--json"],
        &["storage", "create", "prj_demo", "primary", "--json"],
        &["storage", "list", "prj_demo", "--json"],
        &["tasks", "list", "--project", "prj_demo", "--json"],
        &[
            "tasks",
            "create",
            "send-email",
            "--mode",
            "job",
            "--handler",
            "src/tasks/send-email.ts",
            "--json",
        ],
        &["tasks", "get", "task_1", "--json"],
        &["backup", "list", "--resource", "res_db1", "--json"],
        &["backup", "create", "--resource", "res_db1", "--tag", "pre", "--json"],
        &["backup", "restore", "bak_1", "--resource", "res_db1", "--json"],
        &["flags", "list", "--project", "prj_demo", "--json"],
        &["flags", "create", "checkout-v2", "--json"],
        &["flags", "toggle", "checkout-v2", "--enabled", "true", "--json"],
        &["previews", "cost", "prj_demo", "1234", "--json"],
        &["api", "get", "/projects", "--json"],
        &["version-info", "--json"],
    ];

    for run in 1..=2 {
        for args in runs {
            let mut cmd = Command::new(&bin);
            if *args != &["version-info", "--json"] {
                cmd.args(["--token", "svc_test", "--api-url", &base]);
            }
            let output = cmd.args(*args).output().await.expect("spawn");
            assert!(
                output.status.success(),
                "run {run} {:?} failed: {}",
                args,
                String::from_utf8_lossy(&output.stderr)
            );
            let stdout = String::from_utf8_lossy(&output.stdout);
            assert!(!stdout.trim().is_empty(), "run {run} {:?} empty stdout", args);
            match args[0] {
                "health" => assert!(stdout.contains("ok"), "run {run}: {stdout}"),
                "whoami" => assert!(stdout.contains("user_1"), "run {run}: {stdout}"),
                "orgs" => assert!(stdout.contains("acme"), "run {run}: {stdout}"),
                "projects" if args.get(1) == Some(&"list") => {
                    assert!(stdout.contains("prj_demo"), "run {run}: {stdout}")
                }
                "projects" if args.get(1) == Some(&"get") => {
                    assert!(stdout.contains("Demo"), "run {run}: {stdout}")
                }
                "projects" if args.get(1) == Some(&"create") => {
                    assert!(stdout.contains("prj_new") || stdout.contains("NewApp"), "run {run}: {stdout}")
                }
                "environments" if args.get(1) == Some(&"list") => {
                    assert!(stdout.contains("production"), "run {run}: {stdout}")
                }
                "environments" if args.get(1) == Some(&"get") => {
                    assert!(
                        stdout.contains("env_prod") || stdout.contains("production"),
                        "run {run}: {stdout}"
                    )
                }
                "deployments" => {
                    assert!(stdout.contains("dep_1"), "run {run}: {stdout}");
                    assert!(stdout.contains("ready"), "run {run}: {stdout}");
                }
                "deploy" => {
                    assert!(stdout.contains("dep_new") || stdout.contains("building"), "run {run}: {stdout}")
                }
                "rollback" => {
                    assert!(
                        stdout.contains("rolling_back") || stdout.contains("dep_rollback"),
                        "run {run}: {stdout}"
                    )
                }
                "status" => {
                    assert!(stdout.contains("running"), "run {run}: {stdout}");
                    assert!(stdout.contains("dep_1"), "run {run}: {stdout}");
                }
                "env" if args.get(1) == Some(&"list") => {
                    assert!(stdout.contains("NODE_ENV") || stdout.contains("FOO") || stdout.contains("key"), "run {run}: {stdout}")
                }
                "env" if args.get(1) == Some(&"set") => {
                    assert!(stdout.contains("FOO") || stdout.contains("created"), "run {run}: {stdout}")
                }
                "env" if args.get(1) == Some(&"rm") => {
                    assert!(stdout.contains("deleted") || stdout.contains("FOO"), "run {run}: {stdout}")
                }
                "logs" => assert!(stdout.contains("log line"), "run {run}: {stdout}"),
                "resources" | "db" => {
                    assert!(
                        stdout.contains("res_")
                            || stdout.contains("database")
                            || stdout.contains("ready")
                            || stdout.contains("provisioning")
                            || stdout.contains("deleted")
                            || stdout.contains("bound"),
                        "run {run}: {stdout}"
                    )
                }
                "storage" => {
                    assert!(
                        stdout.contains("storage.sylphx.com") || stdout.contains("sto_"),
                        "run {run}: {stdout}"
                    )
                }
                "tasks" => {
                    assert!(
                        stdout.contains("task_") || stdout.contains("send-email") || stdout.contains("queued"),
                        "run {run}: {stdout}"
                    )
                }
                "backup" => {
                    assert!(
                        stdout.contains("bak_") || stdout.contains("restoring") || stdout.contains("ready"),
                        "run {run}: {stdout}"
                    )
                }
                "flags" => {
                    assert!(
                        stdout.contains("checkout-v2") || stdout.contains("enabled"),
                        "run {run}: {stdout}"
                    )
                }
                "previews" => {
                    assert!(stdout.contains("1.23") || stdout.contains("USD"), "run {run}: {stdout}")
                }
                "api" => {
                    assert!(stdout.contains("\"status\""), "run {run}: {stdout}");
                    assert!(stdout.contains("demo"), "run {run}: {stdout}");
                }
                "version-info" => {
                    assert!(stdout.contains("sylphx-sdk-management"), "run {run}: {stdout}");
                    assert!(stdout.contains("shippedCommands"), "run {run}: {stdout}");
                    assert!(stdout.contains("deploy"), "run {run}: {stdout}");
                    assert!(stdout.contains("env list"), "run {run}: {stdout}");
                    assert!(stdout.contains("logs"), "run {run}: {stdout}");
                    assert!(
                        stdout.contains("\"product\": \"rust\"")
                            || stdout.contains("\"product\":\"rust\""),
                        "run {run}: {stdout}"
                    );
                }
                _ => {}
            }
            eprintln!("OK run={run} args={args:?} fields_ok");
        }
    }

    eprintln!("AGENT_PRODUCT_SURFACE_OK");
    mock.shutdown().await;
}

#[test]
fn cli_rejects_missing_token() {
    let bin = cargo_bin!("sylphx");
    let assert = assert_cmd::Command::new(bin)
        .env_remove("SYLPHX_TOKEN")
        .args(["health"])
        .assert();
    assert
        .failure()
        .stderr(predicate::str::contains("SYLPHX_TOKEN").or(predicate::str::contains("token")));
}

#[test]
fn cli_help_lists_agent_operator_commands() {
    let bin = cargo_bin!("sylphx");
    let assert = assert_cmd::Command::new(bin).arg("--help").assert();
    assert
        .success()
        .stdout(predicate::str::contains("deploy"))
        .stdout(predicate::str::contains("rollback"))
        .stdout(predicate::str::contains("env"))
        .stdout(predicate::str::contains("logs"))
        .stdout(predicate::str::contains("resources"))
        .stdout(predicate::str::contains("db"))
        .stdout(predicate::str::contains("storage"))
        .stdout(predicate::str::contains("tasks"))
        .stdout(predicate::str::contains("backup"))
        .stdout(predicate::str::contains("flags"))
        .stdout(predicate::str::contains("previews"));
}