Files
MercuryToolbox/crates/config/tests/config_cli.rs
T

248 lines
7.1 KiB
Rust

//! Integration tests for the `config` command.
use std::fs;
use assert_cmd::Command;
use predicates::prelude::*;
use serde_json::json;
use tempfile::tempdir;
fn cargo_command() -> Command {
Command::cargo_bin("config").expect("binary")
}
#[test]
fn no_args_prints_quick_help_card() {
let mut command = cargo_command();
command
.assert()
.code(2)
.stdout(predicate::str::is_empty())
.stderr(predicate::str::contains(
"error: provide a subcommand: get, set, delete, or ls",
))
.stderr(predicate::str::contains("config - Mercury Toolbox"))
.stderr(predicate::str::contains("Usage:"))
.stderr(predicate::str::contains("get [PATH] [POINTER]"))
.stderr(predicate::str::contains(
"config [OPTIONS] inspect [PATH] [POINTER]",
))
.stderr(predicate::str::contains(
"Type 'config --help' for the full command reference.",
));
}
#[test]
fn get_reads_toml_pointer_as_json() {
let temp = tempdir().expect("tempdir");
let path = temp.path().join("sample.toml");
fs::write(
&path,
"[package]\nname = \"toolbox\"\nversion = \"3.0.0\"\n",
)
.expect("fixture");
let mut command = cargo_command();
command
.arg("--json")
.arg("get")
.arg(&path)
.arg("/package/version")
.assert()
.success()
.stdout(predicate::str::contains("\"format\":\"toml\""))
.stdout(predicate::str::contains("\"pointer\":\"/package/version\""))
.stdout(predicate::str::contains("\"value\":\"3.0.0\""));
}
#[test]
fn set_then_get_updates_json_number_value() {
let temp = tempdir().expect("tempdir");
let path = temp.path().join("config.json");
fs::write(&path, "{\"retries\":1}\n").expect("fixture");
let mut set = cargo_command();
set.arg("set")
.arg(&path)
.arg("/retries")
.arg("5")
.arg("--json")
.arg("--value-type")
.arg("number")
.assert()
.success()
.stdout(predicate::str::contains("\"changed\":true"));
let mut get = cargo_command();
get.arg("--json")
.arg("get")
.arg(&path)
.arg("/retries")
.assert()
.success()
.stdout(predicate::str::contains("\"value\":5"));
}
#[test]
fn delete_removes_env_key() {
let temp = tempdir().expect("tempdir");
let path = temp.path().join(".env");
fs::write(&path, "KEEP=yes\nDROP=no\n").expect("fixture");
let mut command = cargo_command();
command
.arg("delete")
.arg(&path)
.arg("/DROP")
.assert()
.success()
.stdout(predicate::str::contains("op=delete"));
let content = fs::read_to_string(&path).expect("updated");
assert!(content.contains("KEEP=yes"));
assert!(!content.contains("DROP=no"));
}
#[test]
fn ls_lists_ini_section_entries() {
let temp = tempdir().expect("tempdir");
let path = temp.path().join("settings.ini");
fs::write(&path, "mode=dev\n[server]\nport=8080\nhost=127.0.0.1\n").expect("fixture");
let mut command = cargo_command();
command
.arg("--json")
.arg("ls")
.arg(&path)
.arg("/server")
.assert()
.success()
.stdout(predicate::str::contains("\"key\":\"host\""))
.stdout(predicate::str::contains("\"key\":\"port\""));
}
#[test]
fn ls_accepts_single_stdin_path_stream() {
let temp = tempdir().expect("tempdir");
let path = temp.path().join("config.json");
fs::write(&path, "{\"mode\":\"dev\",\"nested\":{\"ok\":true}}\n").expect("fixture");
let mut command = cargo_command();
command
.arg("--json")
.arg("ls")
.write_stdin(format!("{}\n", path.display()))
.assert()
.success()
.stdout(predicate::str::contains("\"pointer\":\"/\""))
.stdout(predicate::str::contains("\"key\":\"mode\""))
.stdout(predicate::str::contains("\"key\":\"nested\""));
}
#[test]
fn help_includes_new_subcommands_and_flags() {
let mut command = cargo_command();
command
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("get [PATH] [POINTER]"))
.stdout(predicate::str::contains("set <PATH> <POINTER> <VALUE>"))
.stdout(predicate::str::contains("--value-type"))
.stdout(predicate::str::contains("--format"))
.stdout(predicate::str::contains("config"));
}
#[test]
fn ls_on_scalar_pointer_returns_no_results_exit_code() {
let temp = tempdir().expect("tempdir");
let path = temp.path().join("config.json");
fs::write(&path, "{\"mode\":\"dev\"}\n").expect("fixture");
let mut command = cargo_command();
command
.arg("ls")
.arg(&path)
.arg("/mode")
.assert()
.code(1)
.stdout(predicate::str::contains("entries=0"));
}
#[test]
fn delete_missing_json_key_returns_no_results_and_preserves_file() {
let temp = tempdir().expect("tempdir");
let path = temp.path().join("config.json");
fs::write(&path, "{\"present\":true}\n").expect("fixture");
let mut command = cargo_command();
command
.arg("--json")
.arg("delete")
.arg(&path)
.arg("/missing")
.assert()
.code(1)
.stdout(predicate::str::contains("\"changed\":false"))
.stdout(predicate::str::contains("\"pointer\":\"/missing\""));
let content = fs::read_to_string(&path).expect("updated");
let parsed: serde_json::Value = serde_json::from_str(&content).expect("valid json");
assert_eq!(parsed, json!({"present": true}));
}
#[test]
fn set_creates_nested_json_arrays_and_objects() {
let temp = tempdir().expect("tempdir");
let path = temp.path().join("config.json");
fs::write(&path, "{}\n").expect("fixture");
let mut command = cargo_command();
command
.arg("--json")
.arg("set")
.arg(&path)
.arg("/pkg/deps/0/name")
.arg("serde")
.assert()
.success()
.stdout(predicate::str::contains("\"changed\":true"));
let content = fs::read_to_string(&path).expect("updated");
let parsed: serde_json::Value = serde_json::from_str(&content).expect("valid json");
assert_eq!(parsed, json!({"pkg": {"deps": [{"name": "serde"}]}}));
}
#[test]
fn env_set_rejects_nested_pointer_and_non_scalar_json_values() {
let temp = tempdir().expect("tempdir");
let path = temp.path().join(".env");
fs::write(&path, "KEEP=yes\n").expect("fixture");
let mut nested_pointer = cargo_command();
nested_pointer
.arg("set")
.arg(&path)
.arg("/A/B")
.arg("x")
.assert()
.code(3)
.stderr(predicate::str::contains(
"env write operations only support root keys like /NAME",
));
let mut object_value = cargo_command();
object_value
.arg("set")
.arg(&path)
.arg("/OBJ")
.arg("{\"nested\":true}")
.arg("--value-type")
.arg("json")
.assert()
.code(3)
.stderr(predicate::str::contains(
"env/ini writes require scalar string|number|bool values",
));
}