forked from Crockan/MercuryToolbox
151 lines
4.4 KiB
Rust
151 lines
4.4 KiB
Rust
//! Contract tests for shared CLI helpers.
|
|
|
|
use common::{
|
|
CliError, ColorChoice, CommonArgs, ExitCode, InputFormat, RenderMode, emit_json,
|
|
emit_structured, formats, map_result_count, parse_color_choice, parse_format_choice,
|
|
parse_input_format, should_read_stdin,
|
|
};
|
|
use serde::Serialize;
|
|
use serde_json::{Map, Value, json};
|
|
|
|
#[test]
|
|
fn parses_shared_choice_values() {
|
|
let parsed = CommonArgs {
|
|
json: true,
|
|
format: None,
|
|
input_format: parse_input_format("jsonl").expect("input format"),
|
|
color: parse_color_choice("never").expect("color choice"),
|
|
quiet: false,
|
|
};
|
|
|
|
assert!(parsed.json);
|
|
assert_eq!(parsed.input_format, InputFormat::Jsonl);
|
|
assert_eq!(parsed.color, ColorChoice::Never);
|
|
assert_eq!(parsed.render_mode(), RenderMode::Json);
|
|
assert_eq!(
|
|
parse_format_choice("toon").expect("format choice"),
|
|
RenderMode::Toon
|
|
);
|
|
|
|
let input_error = parse_input_format("yaml").expect_err("invalid input format");
|
|
let color_error = parse_color_choice("always").expect_err("invalid color choice");
|
|
let format_error = parse_format_choice("yaml").expect_err("invalid format choice");
|
|
assert!(matches!(input_error, CliError::Usage(_)));
|
|
assert!(matches!(color_error, CliError::Usage(_)));
|
|
assert!(matches!(format_error, CliError::Usage(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn auto_stdin_reads_only_without_explicit_input_and_when_not_terminal() {
|
|
assert!(should_read_stdin(false, false));
|
|
assert!(!should_read_stdin(false, true));
|
|
assert!(!should_read_stdin(true, false));
|
|
}
|
|
|
|
#[test]
|
|
fn maps_result_count_to_exit_code() {
|
|
assert_eq!(map_result_count(1), ExitCode::Success);
|
|
assert_eq!(map_result_count(0), ExitCode::NoResults);
|
|
}
|
|
|
|
#[test]
|
|
fn emits_single_json_document() {
|
|
#[derive(Serialize)]
|
|
struct Demo<'a> {
|
|
name: &'a str,
|
|
}
|
|
|
|
let rendered = emit_json(&Demo { name: "jsonlgrep" }).expect("json output");
|
|
|
|
assert_eq!(rendered, "{\"name\":\"jsonlgrep\"}\n");
|
|
}
|
|
|
|
#[test]
|
|
fn emits_structured_toon_document() {
|
|
#[derive(Serialize)]
|
|
struct Demo<'a> {
|
|
name: &'a str,
|
|
count: u8,
|
|
}
|
|
|
|
let rendered = emit_structured(
|
|
&Demo {
|
|
name: "jsonlgrep",
|
|
count: 2,
|
|
},
|
|
RenderMode::Toon,
|
|
)
|
|
.expect("toon output");
|
|
|
|
let lines = rendered.lines().collect::<Vec<_>>();
|
|
assert_eq!(lines.len(), 2);
|
|
assert!(lines.contains(&"name: jsonlgrep"));
|
|
assert!(lines.contains(&"count: 2"));
|
|
assert!(rendered.ends_with('\n'));
|
|
}
|
|
|
|
#[test]
|
|
fn structured_toon_render_rejects_values_past_explicit_depth_limit() {
|
|
let error = emit_structured(&deeply_nested_value(260), RenderMode::Toon)
|
|
.expect_err("deep TOON encode should fail");
|
|
|
|
assert!(matches!(
|
|
error,
|
|
CliError::Runtime(message) if message.contains("maximum TOON encoding depth")
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn exposes_json_family_format_modules() {
|
|
let records = vec![json!({"id": "user-1", "active": true})];
|
|
|
|
let ison = formats::ison::encode_records(&records, "record").expect("ison records");
|
|
assert!(ison.contains("object.record|"));
|
|
assert!(ison.contains("id:str"));
|
|
assert!(ison.contains("active:bool"));
|
|
assert_eq!(
|
|
formats::ison::decode_record_line(&ison, 1).expect("ison decode"),
|
|
records[0]
|
|
);
|
|
|
|
let zon = formats::zon::encode_value(&records[0]).expect("zon");
|
|
assert!(zon.contains("id:\"user-1\""));
|
|
assert_eq!(
|
|
formats::zon::decode_str(&zon).expect("zon decode"),
|
|
records[0]
|
|
);
|
|
|
|
let tonl = formats::tonl::encode_documents(&records).expect("tonl");
|
|
assert!(tonl.contains("id = \"user-1\""));
|
|
assert_eq!(
|
|
formats::tonl::decode_documents(&tonl).expect("tonl decode"),
|
|
records
|
|
);
|
|
}
|
|
|
|
fn deeply_nested_value(depth: usize) -> Value {
|
|
let mut value = json!(1);
|
|
for index in 0..depth {
|
|
let mut object = Map::new();
|
|
object.insert(format!("k{index}"), value);
|
|
value = Value::Object(object);
|
|
}
|
|
value
|
|
}
|
|
|
|
#[test]
|
|
fn isonl_roundtrips_pipe_inside_quoted_string() {
|
|
let records = vec![json!({"message": "left|right", "ok": true})];
|
|
|
|
let ison = formats::ison::encode_records(&records, "record").expect("ison records");
|
|
|
|
assert_eq!(
|
|
formats::ison::decode_record_line(ison.trim_end(), 1).expect("ison decode"),
|
|
records[0]
|
|
);
|
|
assert_eq!(
|
|
formats::ison::decode_records(&ison).expect("ison batch decode"),
|
|
records
|
|
);
|
|
}
|