use super::*;
#[test]
fn only_the_listed_formats_have_a_builtin() {
for format in BUILTIN_FORMATS {
assert!(has_builtin(format), "{format} should have one");
}
for unknown in ["a2ui", "graphql", "markdown", "text/vnd.acme+xml", ""] {
assert!(!has_builtin(unknown), "{unknown} should not have one");
}
}
#[test]
fn a_near_miss_label_is_not_validated() {
assert!(!has_builtin("json-lines"));
assert!(!has_builtin("xml-fragment"));
assert!(check(Some("json-lines"), "not json at all").is_ok());
}
#[test]
fn a_label_is_matched_case_and_whitespace_insensitively() {
assert!(has_builtin("JSON"));
assert!(has_builtin(" yaml "));
assert!(check(Some("JSON"), "{\"a\":1}").is_ok());
}
#[test]
fn no_format_validates_nothing() {
assert!(check(None, "anything at all").is_ok());
}
#[test]
fn json_accepts_valid_and_refuses_a_fenced_answer() {
assert!(check(Some("json"), r#"{"summary":"ok","rows":[1,2]}"#).is_ok());
let fenced = "```json\n{\"a\":1}\n```";
assert!(check(Some("json"), fenced).is_err());
}
#[test]
fn json_does_not_check_shape() {
assert!(check(Some("json"), r#"{"totally":"unexpected"}"#).is_ok());
}
#[test]
fn xml_accepts_valid_and_refuses_an_unclosed_tag() {
assert!(check(Some("xml"), "<report><finding severity=\"high\"/></report>").is_ok());
assert!(check(Some("xml"), "<report><finding></report>").is_err());
}
#[test]
fn xml_refuses_plain_prose() {
let err = check(Some("xml"), "Here are my findings, in prose.").expect_err("prose is not XML");
assert!(err.contains("plain text"), "{err}");
}
#[test]
fn xml_refuses_json_handed_back_by_mistake() {
assert!(check(Some("xml"), r#"{"finding":"high"}"#).is_err());
}
#[test]
fn yaml_accepts_valid_and_refuses_broken_indentation() {
assert!(check(Some("yaml"), "findings:\n - severity: high\n").is_ok());
assert!(check(Some("yml"), "findings:\n - severity: high\n").is_ok());
assert!(check(Some("yaml"), "a: [1, 2\nb: 3").is_err());
}
#[test]
fn an_alias_bomb_is_skipped_rather_than_expanded() {
let mut bomb = String::from("a: &a [x,x,x,x,x,x,x,x,x]\n");
let mut prev = "a".to_string();
for name in ["b", "c", "d", "e", "f", "g"] {
let refs = vec![format!("*{prev}"); 9].join(",");
bomb.push_str(&format!("{name}: &{name} [{refs}]\n"));
prev = name.to_string();
}
assert!(bomb.len() < 300, "the input is small; the expansion is not");
let started = std::time::Instant::now();
assert!(check(Some("yaml"), &bomb).is_ok(), "skipped, not rejected");
assert!(
started.elapsed() < std::time::Duration::from_millis(250),
"the document must not be expanded at all, took {:?}",
started.elapsed()
);
}
#[test]
fn ordinary_yaml_without_aliases_is_still_checked() {
assert!(check(Some("yaml"), "a: [1, 2\nb: 3").is_err());
assert!(check(Some("yaml"), "findings:\n - severity: high\n").is_ok());
}
#[test]
fn anchor_and_alias_detection_errs_toward_skipping() {
assert!(uses_anchors_or_aliases("base: &defaults\n a: 1\n"));
assert!(uses_anchors_or_aliases("copy: *defaults\n"));
assert!(uses_anchors_or_aliases("list: [*a, *b]\n"));
assert!(uses_anchors_or_aliases("note: 3 *4\n"));
assert!(!uses_anchors_or_aliases("findings:\n - severity: high\n"));
assert!(!uses_anchors_or_aliases("note: 3 * 4\n"));
}
#[test]
fn csv_accepts_valid_and_refuses_a_ragged_row() {
assert!(check(Some("csv"), "name,value\nalpha,1\nbeta,2\n").is_ok());
assert!(check(Some("csv"), "name,value\nalpha,1,extra\n").is_err());
}
#[test]
fn csv_refuses_an_empty_answer() {
assert!(check(Some("csv"), " \n").is_err());
}
#[test]
fn csv_refuses_an_unbalanced_quote() {
assert!(check(Some("csv"), "name,value\n\"unterminated,1\n").is_err());
}
#[test]
fn toml_accepts_valid_and_refuses_a_broken_table() {
assert!(check(Some("toml"), "[section]\nkey = \"value\"\n").is_ok());
assert!(check(Some("toml"), "[section\nkey = ").is_err());
}
#[test]
fn a_failure_says_what_was_wrong() {
for (format, bad) in [
("json", "not json"),
("xml", "<a>"),
("yaml", "a: [1, 2\nb: 3"),
("csv", "a,b\n1,2,3\n"),
("toml", "[oops"),
] {
let outcome = check(Some(format), bad);
assert!(
outcome.is_err(),
"{format} accepted {bad:?}, which it should not"
);
assert!(
!outcome.expect_err("asserted Err just above").is_empty(),
"{format} gave an empty reason"
);
}
}
#[test]
fn an_anchor_is_recognised_in_every_value_position() {
for content in [
"&a x", "k: &a x", "k:\t&a x", "k:\n &a x", "k:\r\n &a x", "k: [&a, b]", "k: {v: &a}", "k: [b, &a]", "- &a x", "k: *a", "k: &_private x", "k: &a1 x", ] {
assert!(
uses_anchors_or_aliases(content),
"missed the sigil in {content:?}"
);
}
}
#[test]
fn an_ampersand_that_is_not_a_sigil_is_left_alone() {
for content in [
"k: R&D", "k: rock & roll", "k: value &", "k: 2 * 3", "k: a&&b", "plain: document", ] {
assert!(
!uses_anchors_or_aliases(content),
"false positive on {content:?}"
);
}
}