//! Integration tests for the `zon` command. use std::path::PathBuf; use assert_cmd::Command; use predicates::prelude::*; fn cargo_command() -> Command { Command::cargo_bin("zon").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 } #[test] fn json_file_encodes_to_v1_zon_object() { cargo_command() .arg(fixture("json-family/zon/person.json")) .assert() .success() .stdout(predicate::str::contains("name:\"Ada\"\n")) .stdout(predicate::str::contains("active:true\n")) .stdout(predicate::str::contains("score:42\n")) .stdout(predicate::str::contains("tags:[\"math\",\"logic\"]\n")) .stdout(predicate::str::contains( "profile:{\"city\":\"London\",\"year\":1843}\n", )); } #[test] fn zon_file_decodes_to_pretty_json() { cargo_command() .args(["--from", "zon", "--to", "json"]) .arg(fixture("json-family/zon/person.zon")) .assert() .success() .stdout(predicate::str::contains("\"name\": \"Ada\"")) .stdout(predicate::str::contains("\"active\": true")) .stdout(predicate::str::contains("\"profile\": {")); } #[test] fn jsonl_stdin_encodes_to_root_table() { cargo_command() .args(["--from", "json", "--input-format", "jsonl"]) .write_stdin("{\"name\":\"Ada\",\"score\":10,\"active\":true}\n{\"name\":\"Bob\",\"score\":11,\"active\":false}\n") .assert() .success() .stdout(predicate::str::contains("@(2):name,score,active\n")) .stdout(predicate::str::contains("Ada,10,true\n")) .stdout(predicate::str::contains("Bob,11,false\n")); } #[test] fn root_table_decodes_to_json_array() { cargo_command() .args(["--from", "zon", "--to", "json"]) .arg(fixture("json-family/zon/records.zon")) .assert() .success() .stdout(predicate::str::contains("\"name\": \"Ada\"")) .stdout(predicate::str::contains("\"score\": 11")) .stdout(predicate::str::contains("\"active\": false")); } #[test] fn explicit_path_wins_over_piped_stdin_noise() { cargo_command() .arg(fixture("json-family/zon/person.json")) .write_stdin("not zon or json from upstream pipeline") .assert() .success() .stdout(predicate::str::contains("name:\"Ada\"")); } #[test] fn accepts_stdin_path_stream_in_lines_mode() { cargo_command() .args(["--input-format", "lines"]) .write_stdin(format!( "{}\n", fixture("json-family/zon/person.json").display() )) .assert() .success() .stdout(predicate::str::contains("name:\"Ada\"")); } #[test] fn lines_mode_accepts_path_with_spaces() { let temp = tempfile::tempdir().expect("tempdir"); let path = temp.path().join("person with spaces.json"); std::fs::write(&path, "{\"name\":\"Ada\",\"active\":true}\n").expect("spaced path fixture"); cargo_command() .args(["--input-format", "lines"]) .write_stdin(format!("{}\n", path.display())) .assert() .success() .stdout(predicate::str::contains("name:\"Ada\"")); } #[test] fn json_wrapper_reports_target_format_and_document_count() { cargo_command() .args(["--json", "--from", "json", "--input-format", "jsonl"]) .arg(fixture("json-family/zon/records.jsonl")) .assert() .success() .stdout(predicate::str::contains("\"format\":\"zon\"")) .stdout(predicate::str::contains("\"documents\":2")) .stdout(predicate::str::contains("@(2):name,score,active")); } #[test] fn toon_wrapper_reports_target_format_and_document_count() { cargo_command() .args(["--toon", "--from", "json", "--input-format", "jsonl"]) .arg(fixture("json-family/zon/records.jsonl")) .assert() .success() .stdout(predicate::str::contains("format: zon")) .stdout(predicate::str::contains("documents: 2")) .stdout(predicate::str::contains("@(2):name,score,active")); } #[test] fn invalid_zon_reports_line_and_unsupported_nested_blocks() { cargo_command() .args(["--from", "zon", "--to", "json"]) .arg(fixture("json-family/zon/invalid-nested.zon")) .assert() .failure() .stderr(predicate::str::contains("line 2")) .stderr(predicate::str::contains( "nested block values are not supported in zon v1", )); } #[test] fn empty_stdin_without_path_exits_with_usage_code_2() { cargo_command() .write_stdin("") .assert() .code(2) .stderr(predicate::str::contains("input is empty")); } #[test] fn invalid_stdin_path_stream_keeps_usage_code_2() { cargo_command() .args(["--input-format", "lines"]) .write_stdin(".\\definitely-missing-zon-input.json\n") .assert() .code(2) .stderr(predicate::str::contains("stdin path line 1 does not exist")); } #[test] fn help_names_zero_overhead_notation_and_examples() { cargo_command() .arg("--help") .assert() .success() .stdout(predicate::str::contains("Zero Overhead Notation")) .stdout(predicate::str::contains("not Zig Object Notation")) .stdout(predicate::str::contains( "bat --style=plain --paging=never .\\fixtures\\json-family\\zon\\records.jsonl | zon --input-format jsonl", )); }