//! Integration tests for the `jsonshape` command. use std::path::PathBuf; use assert_cmd::Command; use predicates::prelude::*; use serde_json::Value; use tempfile::tempdir; fn cargo_command() -> Command { Command::cargo_bin("jsonshape").expect("binary") } fn fixture(path: &str) -> PathBuf { let fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("..") .join("..") .join("fixtures") .join(path); assert!( fixture.exists(), "missing fixture `{path}` at {}", fixture.display() ); fixture } fn json_stdout(output: &[u8]) -> Value { serde_json::from_slice(output).unwrap_or_else(|error| { panic!( "stdout should be valid JSON: {error}\n{}", String::from_utf8_lossy(output) ) }) } #[test] fn accepts_stdin_path_streams_in_auto_mode() { let output = cargo_command() .arg("--json") .write_stdin(format!( "{}\n{}\n", fixture("reading/config.json").display(), fixture("toon/config.json").display() )) .assert() .success() .get_output() .stdout .clone(); let payload = json_stdout(&output); assert_eq!( payload["documents"], 2, "stdout={}", String::from_utf8_lossy(&output) ); let paths = payload["paths"].as_array().unwrap_or_else(|| { panic!( "paths should be an array: {}", String::from_utf8_lossy(&output) ) }); assert!( paths.iter().any(|entry| entry["path"] == "$.app.name"), "stdout={}", String::from_utf8_lossy(&output) ); assert!( paths.iter().any(|entry| entry["path"] == "$.context.task"), "stdout={}", String::from_utf8_lossy(&output) ); } #[test] fn help_mentions_repo_safe_examples() { cargo_command() .arg("--help") .assert() .success() .stdout(predicate::str::contains( "bat --style=plain --paging=never .\\fixtures\\jsonshape\\events.jsonl", )) .stdout(predicate::str::contains( "fd -g config.json . .\\fixtures | jsonshape --json", )); } #[test] fn diff_compares_paths_hidden_by_display_limit() { let temp = tempdir().expect("tempdir"); let before = temp.path().join("before.json"); let after = temp.path().join("after.json"); std::fs::write(&before, r#"{"a":1,"z":1}"#).expect("before fixture"); std::fs::write(&after, r#"{"a":1,"z":"changed"}"#).expect("after fixture"); cargo_command() .arg("diff") .arg(&before) .arg(&after) .arg("--limit") .arg("1") .assert() .success() .stdout(predicate::str::contains("change=changed")) .stdout(predicate::str::contains("$.z")); } #[test] fn auto_mode_rejects_pretty_json_in_explicit_jsonl_and_ndjson_paths() { let temp = tempdir().expect("tempdir"); let jsonl = temp.path().join("pretty.jsonl"); let ndjson = temp.path().join("pretty.ndjson"); std::fs::write(&jsonl, "{\n \"ok\": true\n}\n").expect("jsonl fixture"); std::fs::write(&ndjson, "{\n \"ok\": true\n}\n").expect("ndjson fixture"); for path in [&jsonl, &ndjson] { cargo_command() .arg(path) .assert() .failure() .stderr(predicate::str::contains("invalid JSONL at line 1")); } }