osp-cli 1.5.1

CLI and REPL for querying and managing OSP infrastructure data
Documentation
#[test]
fn plugins_list_and_doctor_contract() {
    let mut list = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    list.args(["plugins", "list"]);
    list.assert().success();

    let mut commands = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    commands.args(["plugins", "commands"]);
    commands.assert().success();

    let mut doctor = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    doctor.args(["plugins", "doctor"]);
    doctor.assert().success();
}

#[test]
fn debug_flag_enables_developer_logs_contract() {
    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    cmd.env_remove("RUST_LOG").args(["-dd", "plugins", "list"]);
    cmd.assert()
        .success()
        .stderr(predicate::str::contains("resolving runtime config"))
        .stderr(predicate::str::contains("osp session initialized"));
}

#[test]
fn plugins_enable_and_disable_contract() {
    let home = make_temp_dir("osp-cli-plugin-test");
    let dir = make_temp_dir("osp-cli-plugin-toggle");
    let _plugin = write_named_plugin(&dir, "hello", "hello");

    let mut enable = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    enable
        .envs(crate::test_env::isolated_env(&home))
        .env("OSP_PLUGIN_PATH", &dir)
        .args(["plugins", "enable", "hello"]);
    enable
        .assert()
        .success()
        .stderr(predicate::str::contains("enabled command: hello"));

    let config_path = home.join(".config").join("osp").join("config.toml");
    let config = parse_toml_file(&config_path);
    assert_eq!(
        config["profile"]["default"]["plugins"]["hello"]["state"]
            .as_str()
            .expect("plugin state should be a string"),
        "enabled"
    );

    let mut disable = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    disable
        .envs(crate::test_env::isolated_env(&home))
        .env("OSP_PLUGIN_PATH", &dir)
        .args(["plugins", "disable", "hello"]);
    disable
        .assert()
        .success()
        .stderr(predicate::str::contains("disabled command: hello"));

    let updated = parse_toml_file(&config_path);
    assert_eq!(
        updated["profile"]["default"]["plugins"]["hello"]["state"]
            .as_str()
            .expect("updated plugin state should be a string"),
        "disabled"
    );
}

#[test]
fn quiet_hides_success_messages_contract() {
    let home = make_temp_dir("osp-cli-plugin-quiet-test");
    let dir = make_temp_dir("osp-cli-plugin-quiet-toggle");
    let _plugin = write_named_plugin(&dir, "hello", "hello");

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    cmd.envs(crate::test_env::isolated_env(&home))
        .env("OSP_PLUGIN_PATH", &dir)
        .args(["-q", "plugins", "enable", "hello"]);
    cmd.assert().success().stderr(predicate::str::is_empty());
}

#[test]
fn plugins_refresh_reports_success_contract() {
    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    cmd.args(["plugins", "refresh"]);
    cmd.assert()
        .success()
        .stderr(predicate::str::contains("refreshed plugin discovery cache"));
}

#[test]
#[cfg(unix)]
fn profile_override_can_select_unscoped_profile_contract() {
    let home = make_temp_dir("osp-cli-profile-override-home");
    write_config(
        &home,
        r#"
[default]
profile.default = "uio"

[profile.uio]
ui.format = "table"

[profile.tsd]
ui.format = "json"
"#,
    );

    let mut cmd = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    cmd.envs(crate::test_env::isolated_env(&home))
        .env("PATH", "/usr/bin:/bin")
        .args(["--profile", "prod", "plugins", "list"]);
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("No plugins discovered."));

}

#[cfg(unix)]
#[test]
fn enabling_one_plugin_does_not_disable_other_default_enabled_plugins_contract() {
    let dir = make_temp_dir("osp-cli-plugin-enable-overrides");
    let _alpha = write_named_plugin(&dir, "alpha", "alpha");
    let _beta = write_named_plugin(&dir, "beta", "beta");
    let home = make_temp_dir("osp-cli-plugin-enable-overrides-home");

    let mut enable = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    enable
        .envs(crate::test_env::isolated_env(&home))
        .env("OSP_PLUGIN_PATH", &dir)
        .args(["plugins", "enable", "alpha"]);
    enable
        .assert()
        .success()
        .stderr(predicate::str::contains("enabled command: alpha"));

    let mut clear = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    clear
        .envs(crate::test_env::isolated_env(&home))
        .env("OSP_PLUGIN_PATH", &dir)
        .args(["plugins", "clear-state", "alpha"]);
    clear
        .assert()
        .success()
        .stderr(predicate::str::contains("cleared command state for alpha"));

    let mut beta = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    beta.envs(crate::test_env::isolated_env(&home))
        .env("OSP_PLUGIN_PATH", &dir)
        .args(["beta"]);
    beta.assert()
        .success()
        .stdout(predicate::str::contains("beta-from-plugin"));

}

#[test]
fn plugins_enable_with_terminal_scope_and_clear_state_contract() {
    let home = make_temp_dir("osp-cli-plugin-terminal-scope");
    let dir = make_temp_dir("osp-cli-plugin-terminal-toggle");
    let _plugin = write_named_plugin(&dir, "hello", "hello");

    let mut enable = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    enable
        .envs(crate::test_env::isolated_env(&home))
        .env("OSP_PLUGIN_PATH", &dir)
        .args(["plugins", "enable", "hello", "--terminal", "repl"]);
    enable.assert().success();

    let config_path = home.join(".config").join("osp").join("config.toml");
    let config = parse_toml_file(&config_path);
    assert_eq!(
        config["terminal"]["repl"]["profile"]["default"]["plugins"]["hello"]["state"]
            .as_str()
            .expect("terminal-scoped plugin state should be a string"),
        "enabled"
    );

    let mut clear = Command::new(assert_cmd::cargo::cargo_bin!("osp"));
    clear
        .envs(crate::test_env::isolated_env(&home))
        .env("OSP_PLUGIN_PATH", &dir)
        .args(["plugins", "clear-state", "hello", "--terminal", "repl"]);
    clear
        .assert()
        .success()
        .stderr(predicate::str::contains("cleared command state for hello"));

    let updated = parse_toml_file(&config_path);
    assert!(
        updated
            .get("terminal")
            .and_then(|table| table.get("repl"))
            .and_then(|table| table.get("profile"))
            .and_then(|table| table.get("default"))
            .and_then(|table| table.get("plugins"))
            .and_then(|table| table.get("hello"))
            .is_none(),
        "expected terminal-scoped plugin state to be cleared"
    );
}