use noyalib::{Value, from_str, load_all_as};
use serde::Deserialize;
const MULTI: &str = "a: 1\n---\nb: 2\n";
const EXPECTED_MESSAGE: &str =
"deserializing from YAML containing more than one document is not supported";
#[derive(Debug, Deserialize)]
struct A {
#[allow(dead_code)]
a: i32,
}
#[test]
fn typed_target_rejects_multi_document_stream() {
let err = from_str::<A>(MULTI).expect_err("a second document must be rejected");
assert_eq!(err.to_string(), EXPECTED_MESSAGE);
}
#[test]
fn value_target_rejects_multi_document_stream() {
let err = from_str::<Value>(MULTI).expect_err("a second document must be rejected");
assert_eq!(err.to_string(), EXPECTED_MESSAGE);
}
#[test]
fn single_document_with_leading_marker_still_parses() {
let v: Value = from_str("---\na: 1\n").expect("a single leading `---` is not multi-document");
assert_eq!(v.get("a").and_then(Value::as_i64), Some(1));
}
#[test]
fn single_document_with_trailing_end_marker_still_parses() {
let v: Value = from_str("a: 1\n...\n").expect("a trailing `...` is not multi-document");
assert_eq!(v.get("a").and_then(Value::as_i64), Some(1));
}
#[test]
fn single_document_with_both_markers_still_parses() {
let v: Value =
from_str("---\na: 1\n...\n").expect("both markers around one document still parse");
assert_eq!(v.get("a").and_then(Value::as_i64), Some(1));
}
#[test]
fn from_str_multi_still_returns_every_document() {
let docs: Vec<i32> =
load_all_as("1\n---\n2\n---\n3\n").expect("multi-doc entry point still works");
assert_eq!(docs, vec![1, 2, 3]);
}
#[derive(Debug, Deserialize)]
struct Role<'a> {
#[allow(dead_code)]
role: &'a str,
}
const TRAILING: [&str; 4] = [
"role: user\n---\nrole: admin\n",
"role: user\n...\n%YAML 1.2\n---\nrole: admin\n",
"role: user\n---\n[unterminated\n",
"role: user\n---\n{a: *undefined_alias}\n",
];
#[test]
fn from_str_borrowing_refuses_what_from_str_refuses() {
for yaml in TRAILING {
assert!(
from_str::<Value>(yaml).is_err(),
"from_str refuses {yaml:?}"
);
let err = noyalib::from_str_borrowing::<Role<'_>>(yaml)
.expect_err(&format!("from_str_borrowing must refuse {yaml:?}"));
if yaml.ends_with("admin\n") {
assert_eq!(err.to_string(), EXPECTED_MESSAGE, "{yaml:?}");
}
}
}
#[test]
fn streaming_deserializer_end_refuses_trailing_input() {
for yaml in TRAILING {
let mut de = noyalib::StreamingDeserializer::new(yaml);
let first = Role::deserialize(&mut de).expect("the first document reads");
assert_eq!(first.role, "user");
assert!(de.end().is_err(), "end() must refuse {yaml:?}");
}
}
#[test]
fn borrowing_still_accepts_one_document_with_markers() {
for yaml in [
"---\nrole: user\n",
"role: user\n...\n",
"---\nrole: user\n...\n",
] {
let r: Role<'_> = noyalib::from_str_borrowing(yaml).expect("one document");
assert_eq!(r.role, "user");
let mut de = noyalib::StreamingDeserializer::new(yaml);
let _ = Role::deserialize(&mut de).expect("reads");
de.end().expect("nothing trails one document");
}
}
#[test]
fn a_directly_built_streaming_deserializer_enforces_max_document_length() {
let yaml = format!("k: {}\n", "x".repeat(100));
let cfg = noyalib::ParserConfig::default().max_document_length(64);
let mut de = noyalib::StreamingDeserializer::with_config(&yaml, &cfg);
let err = Value::deserialize(&mut de).expect_err("over the length limit");
assert!(err.to_string().contains("maximum length of 64"), "{err}");
}