use assert_cmd::Command;
use std::fs;
use tempfile::tempdir;
fn run(dir: &std::path::Path, args: &[&str]) -> Result<String, String> {
let output = Command::cargo_bin("sheeter")
.unwrap()
.current_dir(dir)
.args(args)
.output()
.unwrap();
if !output.status.success() {
return Err(String::from_utf8_lossy(&output.stderr).to_string());
}
Ok(String::from_utf8(output.stdout).unwrap())
}
fn read_json(path: &std::path::Path) -> serde_json::Value {
serde_json::from_str(&fs::read_to_string(path).unwrap()).unwrap()
}
#[test]
fn concat_two_objects() {
let dir = tempdir().unwrap();
fs::write(dir.path().join("a.json"), r#"{"x":1}"#).unwrap();
fs::write(dir.path().join("b.json"), r#"{"y":2}"#).unwrap();
run(
dir.path(),
&["json:concat", "a.json", "b.json", "-o", "out.json"],
)
.unwrap();
let parsed = read_json(&dir.path().join("out.json"));
assert_eq!(parsed["a"]["x"], 1);
assert_eq!(parsed["b"]["y"], 2);
}
#[test]
fn concat_preserves_arrays_and_scalars() {
let dir = tempdir().unwrap();
fs::write(dir.path().join("arr.json"), r#"[1,2,3]"#).unwrap();
fs::write(dir.path().join("scalar.json"), r#"42"#).unwrap();
run(
dir.path(),
&["json:concat", "arr.json", "scalar.json", "-o", "out.json"],
)
.unwrap();
let parsed = read_json(&dir.path().join("out.json"));
assert_eq!(parsed["arr"], serde_json::json!([1, 2, 3]));
assert_eq!(parsed["scalar"], 42);
}
#[test]
fn concat_collision_appends_suffix() {
let dir = tempdir().unwrap();
fs::create_dir_all(dir.path().join("sub")).unwrap();
fs::write(dir.path().join("a.json"), r#"{"from":"root"}"#).unwrap();
fs::write(dir.path().join("sub/a.json"), r#"{"from":"sub"}"#).unwrap();
run(
dir.path(),
&["json:concat", "a.json", "sub/a.json", "-o", "out.json"],
)
.unwrap();
let parsed = read_json(&dir.path().join("out.json"));
assert_eq!(parsed["a"]["from"], "root");
assert_eq!(parsed["a_2"]["from"], "sub");
}
#[test]
fn append_to_existing_object() {
let dir = tempdir().unwrap();
fs::write(dir.path().join("base.json"), r#"{"keep":true}"#).unwrap();
fs::write(dir.path().join("a.json"), r#"{"x":1}"#).unwrap();
fs::write(dir.path().join("b.json"), r#"{"y":2}"#).unwrap();
run(
dir.path(),
&["json:append", "base.json", "a.json", "b.json"],
)
.unwrap();
let parsed = read_json(&dir.path().join("base.json"));
assert_eq!(parsed["keep"], true);
assert_eq!(parsed["a"]["x"], 1);
assert_eq!(parsed["b"]["y"], 2);
}
#[test]
fn append_creates_missing_target() {
let dir = tempdir().unwrap();
fs::write(dir.path().join("a.json"), r#"{"x":1}"#).unwrap();
run(dir.path(), &["json:append", "new.json", "a.json"]).unwrap();
let parsed = read_json(&dir.path().join("new.json"));
assert_eq!(parsed["a"]["x"], 1);
}
#[test]
fn append_overwrites_existing_key() {
let dir = tempdir().unwrap();
fs::write(dir.path().join("base.json"), r#"{"a":{"old":true}}"#).unwrap();
fs::write(dir.path().join("a.json"), r#"{"new":1}"#).unwrap();
run(dir.path(), &["json:append", "base.json", "a.json"]).unwrap();
let parsed = read_json(&dir.path().join("base.json"));
assert_eq!(parsed["a"]["new"], 1);
assert!(parsed["a"].get("old").is_none());
}
#[test]
fn append_with_output_flag_does_not_touch_source() {
let dir = tempdir().unwrap();
fs::write(dir.path().join("base.json"), r#"{"keep":true}"#).unwrap();
fs::write(dir.path().join("a.json"), r#"{"x":1}"#).unwrap();
run(
dir.path(),
&["json:append", "base.json", "a.json", "-o", "out.json"],
)
.unwrap();
let base = read_json(&dir.path().join("base.json"));
assert!(base.get("a").is_none());
let out = read_json(&dir.path().join("out.json"));
assert_eq!(out["a"]["x"], 1);
}
#[test]
fn append_rejects_non_object_target() {
let dir = tempdir().unwrap();
fs::write(dir.path().join("base.json"), r#"[1,2,3]"#).unwrap();
fs::write(dir.path().join("a.json"), r#"{"x":1}"#).unwrap();
let err = run(dir.path(), &["json:append", "base.json", "a.json"]).unwrap_err();
assert!(err.contains("not a JSON object") || err.contains("object"));
}
#[test]
fn frag_level_zero_writes_root_children() {
let dir = tempdir().unwrap();
fs::write(
dir.path().join("in.json"),
r#"{"ObjetoA":{"id":1},"ObjetoB":{"id":2}}"#,
)
.unwrap();
run(dir.path(), &["json:frag", "in.json", "--level", "0"]).unwrap();
assert!(dir.path().join("objetoa.json").exists());
assert!(dir.path().join("objetob.json").exists());
let a = read_json(&dir.path().join("objetoa.json"));
assert_eq!(a["id"], 1);
}
#[test]
fn frag_level_one_descends_one_into_root() {
let dir = tempdir().unwrap();
fs::write(
dir.path().join("in.json"),
r#"{"nivel_1":{"ObjetoA":{"id":1},"ObjetoB":{"id":2}}}"#,
)
.unwrap();
run(dir.path(), &["json:frag", "in.json", "--level", "1"]).unwrap();
assert!(dir.path().join("objetoa.json").exists());
assert!(dir.path().join("objetob.json").exists());
let a = read_json(&dir.path().join("objetoa.json"));
assert_eq!(a["id"], 1);
}
#[test]
fn frag_level_two_descends_two_into_root() {
let dir = tempdir().unwrap();
fs::write(
dir.path().join("in.json"),
r#"{"l1":{"l2":{"a":{"v":1},"b":{"v":2}}}}"#,
)
.unwrap();
run(dir.path(), &["json:frag", "in.json", "--level", "2"]).unwrap();
assert!(dir.path().join("a.json").exists());
assert!(dir.path().join("b.json").exists());
}
#[test]
fn frag_sanitizes_names() {
let dir = tempdir().unwrap();
fs::write(
dir.path().join("in.json"),
r#"{"Relatório (2024)":{"x":1}}"#,
)
.unwrap();
run(dir.path(), &["json:frag", "in.json"]).unwrap();
assert!(dir.path().join("relatorio_2024.json").exists());
}
#[test]
fn frag_out_dir() {
let dir = tempdir().unwrap();
fs::write(dir.path().join("in.json"), r#"{"a":{"x":1}}"#).unwrap();
let out = dir.path().join("sub");
run(
dir.path(),
&["json:frag", "in.json", "--out", out.to_str().unwrap()],
)
.unwrap();
assert!(out.join("a.json").exists());
}
#[test]
fn frag_handles_array_root() {
let dir = tempdir().unwrap();
fs::write(
dir.path().join("in.json"),
r#"[{"id":1},{"id":2},{"id":3}]"#,
)
.unwrap();
run(dir.path(), &["json:frag", "in.json", "--level", "0"]).unwrap();
assert!(dir.path().join("0.json").exists());
assert!(dir.path().join("1.json").exists());
assert!(dir.path().join("2.json").exists());
}