#![allow(missing_docs)]
use noyalib::{from_str, to_string, ParserConfig, Value};
#[test]
fn large_single_document_block_scalar() {
let mut yaml = String::from("payload: |\n");
let line: &str = " the quick brown fox jumps over the lazy dog 0123456789\n";
while yaml.len() < 1_000_000 {
yaml.push_str(line);
}
let v: Value = from_str(&yaml).unwrap();
let payload = v
.get("payload")
.and_then(Value::as_str)
.expect("payload string");
assert!(payload.len() > 900_000);
}
#[test]
fn wide_mapping_10k_entries() {
let mut yaml = String::new();
for i in 0..10_000 {
yaml.push_str(&format!("key{i:05}: {i}\n"));
}
let v: Value = from_str(&yaml).unwrap();
let m = v.as_mapping().expect("mapping");
assert_eq!(m.len(), 10_000);
assert_eq!(m.get("key00000").and_then(Value::as_i64), Some(0));
assert_eq!(m.get("key09999").and_then(Value::as_i64), Some(9_999));
}
#[test]
fn wide_sequence_10k_items() {
let mut yaml = String::new();
for i in 0..10_000 {
yaml.push_str(&format!("- {i}\n"));
}
let v: Value = from_str(&yaml).unwrap();
let s = v.as_sequence().expect("sequence");
assert_eq!(s.len(), 10_000);
assert_eq!(s[0].as_i64(), Some(0));
assert_eq!(s[9_999].as_i64(), Some(9_999));
}
#[test]
fn many_documents_one_thousand() {
let mut yaml = String::with_capacity(1_000 * 16);
for i in 0..1_000 {
yaml.push_str(&format!("---\nidx: {i}\n"));
}
let docs: Vec<Value> = noyalib::load_all_as(&yaml).unwrap();
assert_eq!(docs.len(), 1_000);
assert_eq!(docs[0].get("idx").and_then(Value::as_i64), Some(0));
assert_eq!(docs[999].get("idx").and_then(Value::as_i64), Some(999));
}
#[test]
fn deep_nesting_100_levels() {
let mut yaml = String::new();
for i in 0..100 {
yaml.push_str(&" ".repeat(i));
yaml.push_str(&format!("level{i}:\n"));
}
yaml.push_str(&" ".repeat(100));
yaml.push_str("leaf: 1\n");
let v: Value = from_str(&yaml).unwrap();
let mut cur = &v;
for i in 0..100 {
cur = cur.get(format!("level{i}")).expect("level present");
}
assert_eq!(cur.get("leaf").and_then(Value::as_i64), Some(1));
}
#[test]
fn recursion_limit_rejects_overdeep() {
let mut yaml = String::new();
for i in 0..10_000 {
yaml.push_str(&" ".repeat(i.min(2_000)));
yaml.push_str("k:\n");
}
let res: Result<Value, _> = from_str(&yaml);
assert!(res.is_err(), "10 000-deep nesting must be rejected");
}
#[test]
fn billion_laughs_alias_amplification_is_bounded() {
let yaml = r#"
a: &a ["lol", "lol", "lol", "lol", "lol", "lol", "lol", "lol", "lol", "lol"]
b: &b [*a, *a, *a, *a, *a, *a, *a, *a, *a, *a]
c: &c [*b, *b, *b, *b, *b, *b, *b, *b, *b, *b]
d: &d [*c, *c, *c, *c, *c, *c, *c, *c, *c, *c]
e: &e [*d, *d, *d, *d, *d, *d, *d, *d, *d, *d]
f: &f [*e, *e, *e, *e, *e, *e, *e, *e, *e, *e]
g: &g [*f, *f, *f, *f, *f, *f, *f, *f, *f, *f]
h: &h [*g, *g, *g, *g, *g, *g, *g, *g, *g, *g]
i: &i [*h, *h, *h, *h, *h, *h, *h, *h, *h, *h]
j: &j [*i, *i, *i, *i, *i, *i, *i, *i, *i, *i]
"#;
let res: Result<Value, _> = from_str(yaml);
assert!(
res.is_err(),
"billion-laughs amplification must hit the alias / document length limit"
);
}
#[test]
fn long_plain_scalar_1mb() {
let mut yaml = String::from("payload: \"");
yaml.push_str(&"x".repeat(1_000_000));
yaml.push_str("\"\n");
let v: Value = from_str(&yaml).unwrap();
let s = v.get("payload").and_then(Value::as_str).expect("payload");
assert_eq!(s.len(), 1_000_000);
}
#[test]
fn parse_emit_reparse_stability() {
let yaml = "name: noyalib\nport: 8080\nfeatures:\n - cst\n - schema\n - figment\n";
let v1: Value = from_str(yaml).unwrap();
let s1 = to_string(&v1).unwrap();
let v2: Value = from_str(&s1).unwrap();
let s2 = to_string(&v2).unwrap();
let v3: Value = from_str(&s2).unwrap();
assert_eq!(v1, v2, "round-trip 1 → 2");
assert_eq!(v2, v3, "round-trip 2 → 3");
assert_eq!(s1, s2, "emit is deterministic");
}
#[test]
fn many_iterations_no_drift() {
let yaml = "a: 1\nb: [2, 3]\nc: {d: 4}\n";
let baseline: Value = from_str(yaml).unwrap();
for _ in 0..100 {
let v: Value = from_str(yaml).unwrap();
assert_eq!(v, baseline, "no drift between iterations");
}
}
#[test]
fn unicode_heavy_document() {
let yaml = "\
name: \u{1F44B} hello\n\
emoji: \u{1F600}\u{1F60D}\u{1F914}\n\
japanese: \u{3053}\u{3093}\u{306B}\u{3061}\u{306F}\n\
rtl: \u{0645}\u{0631}\u{062D}\u{0628}\u{0627}\n\
cjk_long: \u{4F60}\u{597D}\u{4E16}\u{754C}\u{1F30D}\n\
";
let v: Value = from_str(yaml).unwrap();
assert!(v.get("emoji").and_then(Value::as_str).is_some());
assert!(v.get("japanese").and_then(Value::as_str).is_some());
assert!(v.get("rtl").and_then(Value::as_str).is_some());
}
#[test]
fn custom_low_max_depth_rejects() {
let yaml = "a:\n b:\n c:\n d: 1\n";
let cfg = ParserConfig::new().max_depth(2);
let res: Result<Value, _> = noyalib::from_str_with_config(yaml, &cfg);
assert!(res.is_err(), "depth=2 cap should reject nesting depth 4");
}
#[test]
fn many_aliases_within_budget() {
let mut yaml = String::from("anchors:\n");
for i in 0..1_000 {
yaml.push_str(&format!(" - &a{i:04} {i}\n"));
}
yaml.push_str("aliases:\n");
for i in 0..1_000 {
yaml.push_str(&format!(" - *a{i:04}\n"));
}
let v: Value = from_str(&yaml).unwrap();
let aliases = v.get("aliases").and_then(Value::as_sequence).unwrap();
assert_eq!(aliases.len(), 1_000);
assert_eq!(aliases[0].as_i64(), Some(0));
assert_eq!(aliases[999].as_i64(), Some(999));
}