use assert_cmd::Command;
use predicates::prelude::*;
use std::fs;
use std::io::Write;
use tempfile::TempDir;
fn jsonette_cmd(temp_dir: &TempDir) -> Command {
let mut cmd = Command::cargo_bin("jsonette").unwrap();
cmd.env("HOME", temp_dir.path())
.env("XDG_CONFIG_HOME", temp_dir.path())
.env("LOCALAPPDATA", temp_dir.path());
cmd
}
#[test]
fn test_cli_format_stdin() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("format")
.write_stdin(r#"{"b":2,"a":1}"#)
.assert()
.success()
.stdout(predicate::str::contains("\"b\": 2"))
.stdout(predicate::str::contains("\"a\": 1"));
}
#[test]
fn test_cli_format_sort_keys() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("format")
.arg("--sort-keys")
.arg("true")
.write_stdin(r#"{"b":2,"a":1}"#)
.assert()
.success()
.stdout(predicate::str::starts_with("{\n \"a\": 1,\n \"b\": 2\n}"));
}
#[test]
fn test_cli_format_minify() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("format")
.arg("--minify")
.write_stdin(
r#"{
"b": 2,
"a": 1
}"#,
)
.assert()
.success()
.stdout(predicate::str::starts_with("{\"b\":2,\"a\":1}"));
}
#[test]
fn test_cli_format_file() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.json");
let mut file = fs::File::create(&file_path).unwrap();
file.write_all(r#"{"x":1}"#.as_bytes()).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("format")
.arg(&file_path)
.assert()
.success()
.stdout(predicate::str::contains("{\n \"x\": 1\n}"));
}
#[test]
fn test_cli_format_inplace() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.json");
let mut file = fs::File::create(&file_path).unwrap();
file.write_all(r#"{"x":1}"#.as_bytes()).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("format")
.arg(&file_path)
.arg("--in-place")
.assert()
.success()
.stdout(predicate::str::is_empty());
let content = fs::read_to_string(file_path).unwrap();
assert_eq!(content, "{\n \"x\": 1\n}\n");
}
#[test]
fn test_cli_format_invalid_json() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("format")
.write_stdin(r#"{"a": 1"#)
.assert()
.failure()
.code(1)
.stderr(predicate::str::contains("Error in <stdin>:1:7"))
.stderr(predicate::str::contains(" |"))
.stderr(predicate::str::contains("1 | {\"a\": 1"))
.stderr(predicate::str::contains(" | ^"));
}
#[test]
fn test_cli_config_management() {
let temp_dir = TempDir::new().unwrap();
let mut list_cmd = jsonette_cmd(&temp_dir);
list_cmd
.arg("config")
.arg("list")
.assert()
.success()
.stdout(
predicate::str::contains("\"use_tabs\": false")
.and(predicate::str::contains("\"indent\": 2")),
);
let mut set_cmd = jsonette_cmd(&temp_dir);
set_cmd
.arg("config")
.arg("set")
.arg("format.indent")
.arg("4")
.assert()
.success()
.stdout(predicate::str::contains(
"Configuration updated: format.indent = 4",
));
let mut get_cmd = jsonette_cmd(&temp_dir);
get_cmd
.arg("config")
.arg("get")
.arg("format.indent")
.assert()
.success()
.stdout(predicate::str::starts_with("4"));
let mut format_cmd = jsonette_cmd(&temp_dir);
format_cmd
.arg("format")
.write_stdin(r#"{"a":1}"#)
.assert()
.success()
.stdout(predicate::str::starts_with("{\n \"a\": 1\n}"));
}
#[test]
fn test_cli_format_output() {
let temp_dir = TempDir::new().unwrap();
let out_path = temp_dir.path().join("out.json");
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("format")
.arg("--output")
.arg(&out_path)
.write_stdin(r#"{"a":1}"#)
.assert()
.success()
.stdout(predicate::str::is_empty());
let content = fs::read_to_string(out_path).unwrap();
assert_eq!(content, "{\n \"a\": 1\n}\n");
}
#[test]
fn test_cli_format_output_and_inplace_conflict() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("format")
.arg("dummy.json")
.arg("--output")
.arg("out.json")
.arg("--in-place")
.assert()
.failure()
.code(1)
.stderr(predicate::str::contains(
"Error: Cannot specify both --in-place and --output file.",
));
}
#[test]
fn test_cli_completions() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("completions")
.arg("zsh")
.assert()
.success()
.stdout(predicate::str::contains("#compdef jsonette"))
.stdout(predicate::str::contains("format"))
.stdout(predicate::str::contains("query"))
.stdout(predicate::str::contains("config"));
}
#[test]
fn test_cli_query_dot_key() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"{"name": "jsonette", "version": "0.1.0"}"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("query")
.arg("$.name")
.arg(json_file.to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("jsonette"));
}
#[test]
fn test_cli_query_wildcard_array() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("users.json");
fs::write(
&json_file,
r#"{"users": [{"name": "Alice"}, {"name": "Bob"}]}"#,
)
.unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("query")
.arg("$.users[*].name")
.arg(json_file.to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("Alice"))
.stdout(predicate::str::contains("Bob"));
}
#[test]
fn test_cli_query_no_match_prints_empty_array() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"{"a": 1}"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("query")
.arg("$.nonexistent")
.arg(json_file.to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("[]"));
}
#[test]
fn test_cli_query_stdin() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("query")
.arg("$.x")
.write_stdin(r#"{"x": 42}"#)
.assert()
.success()
.stdout(predicate::str::contains("42"));
}
#[test]
fn test_cli_query_invalid_jsonpath_reports_error() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("query")
.arg("NOT A VALID PATH")
.write_stdin(r#"{"a": 1}"#)
.assert()
.failure()
.stderr(predicate::str::contains("Error"));
}
#[test]
fn test_cli_query_invalid_json_reports_error() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("query")
.arg("$.foo")
.write_stdin("NOT VALID JSON")
.assert()
.failure()
.stderr(predicate::str::is_empty().not());
}
#[test]
fn test_cli_query_missing_file_reports_error() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("query")
.arg("$.foo")
.arg("/nonexistent/path/missing.json")
.assert()
.failure()
.stderr(predicate::str::contains("Error").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_explore_object_keys_sorted() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"{"b": 2, "a": 1, "c": 3}"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg("$")
.arg(json_file.to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("a\nb\nc"));
}
#[test]
fn test_cli_explore_array_length() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"[1, 2, 3]"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg("$")
.arg(json_file.to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("Length: 3 elements"));
}
#[test]
fn test_cli_explore_single_file_argument() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"{"a": 1}"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg(json_file.to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("a\n"));
}
#[test]
fn test_cli_explore_object_regex_filter() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"{"foo": 1, "bar": 2, "baz": 3}"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg("--regex")
.arg("^ba")
.arg("$")
.arg(json_file.to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("bar\nbaz"))
.stdout(predicate::str::contains("foo").not());
}
#[test]
fn test_cli_explore_invalid_regex_error() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"{"a": 1}"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg("--regex")
.arg("[invalid")
.arg("$")
.arg(json_file.to_str().unwrap())
.assert()
.failure()
.stderr(predicate::str::contains("Invalid regex"));
}
#[test]
fn test_cli_explore_invalid_jsonpath_error() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"{"a": 1}"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg("NOT_A_PATH")
.arg(json_file.to_str().unwrap())
.assert()
.failure()
.stderr(predicate::str::contains("Error"));
}
#[test]
fn test_cli_explore_no_match() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"{"a": 1}"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg("$.b")
.arg(json_file.to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("No nodes matched"));
}
#[test]
fn test_cli_explore_primitive_node() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"{"a": 42}"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg("$.a")
.arg(json_file.to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("Matched a number node"));
}
#[test]
fn test_cli_explore_invalid_json_reports_error() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg("$")
.write_stdin("NOT VALID JSON")
.assert()
.failure()
.stderr(predicate::str::is_empty().not());
}
#[test]
fn test_cli_explore_missing_file_reports_error() {
let temp_dir = TempDir::new().unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg("$")
.arg("/nonexistent/path/missing.json")
.assert()
.failure()
.stderr(predicate::str::contains("Error").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_explore_object_limit() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
fs::write(&json_file, r#"{"a": 1, "b": 2, "c": 3}"#).unwrap();
let mut cmd = jsonette_cmd(&temp_dir);
cmd.arg("explore")
.arg("-n")
.arg("1")
.arg("$")
.arg(json_file.to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("a\n... and 2 more keys"));
}