use std::collections::HashMap;
use noyalib::{from_str, Value};
#[test]
fn invalid_indentation() {
let result: Result<Value, _> = from_str("a: 1\n b: 2\n");
let _ = result;
}
#[test]
fn unclosed_flow_sequence() {
let result: Result<Vec<i64>, _> = from_str("[1, 2, 3");
assert!(result.is_err());
}
#[test]
fn unclosed_flow_mapping() {
let result: Result<HashMap<String, i64>, _> = from_str("{a: 1, b: 2");
assert!(result.is_err());
}
#[test]
fn tab_as_indentation() {
let result: Result<Value, _> = from_str("a:\n\tb: 1\n");
assert!(result.is_err());
}
#[test]
fn type_mismatch_string_as_int() {
let result: Result<i64, _> = from_str("hello");
assert!(result.is_err());
}
#[test]
fn type_mismatch_mapping_as_seq() {
let result: Result<Vec<String>, _> = from_str("a: 1\nb: 2\n");
assert!(result.is_err());
}
#[test]
fn type_mismatch_seq_as_mapping() {
let result: Result<HashMap<String, String>, _> = from_str("- a\n- b\n");
assert!(result.is_err());
}
#[test]
fn empty_yaml_is_error() {
let result: Result<i64, _> = from_str("");
assert!(result.is_err());
}
#[test]
fn stray_scalar_after_mapping() {
let result: Result<HashMap<String, String>, _> = from_str("foo: bar\ninvalid\n");
let _ = result;
}
#[test]
fn max_depth_exceeded() {
use noyalib::{from_str_with_config, ParserConfig};
let yaml = "a:\n b:\n c:\n d:\n e:\n f: 1\n";
let config = ParserConfig::new().max_depth(5);
let result: Result<Value, _> = from_str_with_config(yaml, &config);
assert!(result.is_err(), "should reject excessive nesting");
}
#[test]
fn max_document_length_exceeded() {
use noyalib::{from_str_with_config, ParserConfig};
let config = ParserConfig::new().max_document_length(10);
let yaml = "this is more than 10 bytes";
let result: Result<Value, _> = from_str_with_config(yaml, &config);
assert!(result.is_err(), "should reject oversized document");
}
#[test]
fn missing_required_struct_field() {
use serde::Deserialize;
#[allow(dead_code)]
#[derive(Debug, Deserialize)]
struct Required {
name: String,
age: i64,
}
let result: Result<Required, _> = from_str("name: John\n");
assert!(result.is_err());
}
#[test]
fn wrong_type_in_sequence() {
let result: Result<Vec<i64>, _> = from_str("- 1\n- hello\n- 3\n");
assert!(result.is_err());
}
#[test]
fn invalid_escape_in_double_quote() {
let result: Result<String, _> = from_str("\"\\z\"");
assert!(result.is_err());
}
#[test]
fn no_panic_on_any_input() {
let inputs = [
"",
"---",
"...",
"[",
"{",
"- - -",
"!!",
"&",
"*",
"---\n---",
"key: [unclosed",
"key: {unclosed",
":\n:",
"- :\n - :",
];
for input in inputs {
let _ = from_str::<Value>(input);
}
}
#[test]
fn comment_indicator_must_be_preceded_by_whitespace() {
let result: Result<Value, _> = from_str("key: \"value\"# invalid comment\n");
assert!(
result.is_err(),
"expected rejection of inline `#` without preceding whitespace"
);
}
#[test]
fn block_scalar_header_rejects_adjacent_hash() {
let result: Result<Value, _> = from_str("block: >#comment\n scalar\n");
assert!(
result.is_err(),
"expected rejection of `>#` with no whitespace"
);
}
#[test]
fn duplicate_yaml_directive_rejected() {
let result: Result<Value, _> = from_str("%YAML 1.2\n%YAML 1.2\n---\n");
assert!(
result.is_err(),
"expected rejection of duplicate %YAML directive"
);
}
#[test]
fn tab_before_block_indicator_rejected() {
for input in &["-\t-\n", "- \t-\n", "?\t-\n", "? -\n:\t-\n"] {
let result: Result<Value, _> = from_str(input);
assert!(
result.is_err(),
"expected rejection of tab-as-indentation in {input:?}"
);
}
}
#[test]
fn tab_as_inline_separation_accepted() {
let v: Value = from_str("? a\n: -\tb\n - -\tc\n - d\n").unwrap();
let outer = v.as_mapping().expect("mapping");
let seq = outer.get("a").expect("key 'a'").as_sequence().expect("seq");
assert_eq!(seq[0].as_str(), Some("b"));
}
#[test]
fn document_end_marker_rejects_trailing_content() {
let result: Result<Value, _> = from_str("---\nkey: value\n... invalid\n");
assert!(result.is_err());
}
#[test]
fn doc_marker_inside_quoted_scalar_rejected() {
let r1: Result<Value, _> = from_str("---\n'\n...\n'\n");
assert!(r1.is_err(), "single-quoted scalar containing `...`");
let r2: Result<Value, _> = from_str("---\n\"\n---\n\"\n");
assert!(r2.is_err(), "double-quoted scalar containing `---`");
}
#[test]
fn block_scalar_indent_indicator_validation() {
let r1: Result<Value, _> = from_str("--- |0\n");
assert!(r1.is_err(), "indent indicator 0 is invalid");
let r2: Result<Value, _> = from_str("--- |10\n");
assert!(r2.is_err(), "two-digit indent indicator is invalid");
}
#[test]
fn stray_flow_close_outside_flow_rejected() {
let r1: Result<Value, _> = from_str("[ a, b, c ] ]\n");
assert!(r1.is_err());
let r2: Result<Value, _> = from_str("{ a: 1 } }\n");
assert!(r2.is_err());
}
#[test]
fn yaml_directive_rejects_non_numeric_extras() {
let r: Result<Value, _> = from_str("%YAML 1.2 foo\n---\n");
assert!(r.is_err());
let _: Value = from_str("%YAML 1.1 1.2\n---\n").unwrap();
}
#[test]
fn between_levels_indentation_rejected() {
let r: Result<Value, _> = from_str("key:\n - ok\n - also ok\n - wrong\n");
assert!(r.is_err());
let r: Result<Value, _> = from_str("k1: v1\n k2: v2\n");
assert!(r.is_err());
let r: Result<Value, _> = from_str("key:\n ok: 1\n wrong: 2\n");
assert!(r.is_err());
}
#[test]
fn correctly_indented_blocks_still_parse() {
let _: Value = from_str("a:\n b:\n c: 1\n").unwrap();
let _: Value = from_str("a: 1\nb: 2\nc: 3\n").unwrap();
let _: Value = from_str("xs:\n - 1\n - 2\n - 3\n").unwrap();
let _: Value = from_str("# comment\nkey: value\n").unwrap();
}
#[test]
fn quoted_scalar_continuation_must_be_indented() {
let r: Result<Value, _> = from_str("---\nquoted: \"a\nb\nc\"\n");
assert!(r.is_err());
let _: Value = from_str("---\nquoted: \"a\n b\n c\"\n").unwrap();
}
#[test]
fn multiline_implicit_key_rejected_in_block_context() {
let r: Result<Value, _> = from_str("\"a\\nb\": 1\n\"c\n d\": 1\n");
assert!(r.is_err());
let r: Result<Value, _> = from_str("'a\\nb': 1\n'c\n d': 1\n");
assert!(r.is_err());
let r: Result<Value, _> = from_str("a\\nb: 1\nc\n d: 1\n");
assert!(r.is_err());
}
#[test]
fn empty_implicit_key_after_anchored_value_parses() {
let _: Value = from_str("? &a a\n: &b b\n: *a\n").unwrap();
}
#[test]
fn block_collection_inline_with_doc_start_rejected() {
let r: Result<Value, _> = from_str("--- &anchor a: b\n");
assert!(r.is_err());
let r: Result<Value, _> = from_str("--- key1: value1\n key2: value2\n");
assert!(r.is_err());
let _: Value = from_str("--- text\n").unwrap();
let _: Value = from_str("--- {a: 1}\n").unwrap();
}
#[test]
fn directive_without_document_rejected() {
let r: Result<Value, _> = from_str("%YAML 1.2\n");
assert!(r.is_err());
}
#[test]
fn directive_without_doc_end_marker_rejected() {
let r: Result<Value, _> = from_str("---\nkey: value\n%YAML 1.2\n---\n");
assert!(r.is_err());
let r: Result<Value, _> =
from_str("!foo \"bar\"\n%TAG ! tag:example.com,2000:app/\n---\n!foo \"bar\"\n");
assert!(r.is_err());
let _: Vec<Value> =
noyalib::load_all_as("---\nfoo: bar\n...\n%YAML 1.2\n---\nbaz: qux\n").unwrap();
}
#[test]
fn directive_comment_without_whitespace_rejected() {
let r: Result<Value, _> = from_str("%YAML 1.1#...\n---\n");
assert!(r.is_err());
let _: Value = from_str("%YAML 1.1 # ok\n---\nfoo: 1\n").unwrap();
}
#[test]
fn alias_decorated_by_anchor_rejected() {
let r: Result<Value, _> = from_str("key1: &a value\nkey2: &b *a\n");
assert!(r.is_err());
let r: Result<Value, _> = from_str("top: &n3\n *alias : scalar3\n");
if let Err(e) = r {
assert!(
!e.to_string().contains("alias cannot be decorated"),
"line-broken anchor → alias-key must not trigger the adjacency guard"
);
}
}
#[test]
fn tag_followed_by_flow_indicator_rejected() {
let r: Result<Value, _> = from_str("---\n!invalid{}tag scalar\n");
assert!(r.is_err());
let _: Value = from_str("---\n!foo {a: 1}\n").unwrap();
}
#[test]
fn stray_content_after_first_implicit_document_rejected() {
let r: Result<Value, _> = from_str("word1 # comment\nword2\n");
assert!(r.is_err());
let r: Result<Value, _> = from_str("---\n[\nseq\n]\nstray\n");
assert!(r.is_err());
let _: Vec<Value> = noyalib::load_all_as("---\nscalar1\n...\nkey: value\n").unwrap();
}
#[test]
fn flow_continuation_must_be_indented_more_than_parent() {
let r: Result<Value, _> = from_str("---\nflow: [a,\nb,\nc]\n");
assert!(r.is_err());
let _: Value = from_str("---\nflow: [a,\n b,\n c]\n").unwrap();
let _: Value = from_str("[\n a,\n b\n]\n").unwrap();
}
#[test]
fn from_str_drains_trailing_events_to_surface_errors() {
let r: Result<Value, _> = from_str("--- key1: value1\n key2: value2\n");
assert!(
r.is_err(),
"expected from_str to surface the lazy-only-accept error"
);
let r: Result<Value, _> = from_str("--- &anchor a: b\n");
assert!(r.is_err());
}