#![allow(missing_docs)]
use noyalib::policy::{Policy, PolicyEvent};
use noyalib::{
document::load_all_with_config, from_str, from_str_with_config, BudgetBreach,
DuplicateKeyPolicy, Error, MergeKeyPolicy, ParserConfig, Spanned, Value,
};
use serde::Deserialize;
#[test]
fn parser_scanner_bom_prefix_at_stream_start() {
let mut bytes = Vec::with_capacity(16);
bytes.extend_from_slice(b"\xEF\xBB\xBF");
bytes.extend_from_slice(b"key: value\n");
let s = std::str::from_utf8(&bytes).unwrap();
let v: Value = from_str(s).unwrap();
assert_eq!(v.get("key").and_then(|v| v.as_str()), Some("value"));
}
#[test]
fn parser_scanner_duplicate_yaml_directive_errors() {
let yaml = "%YAML 1.2\n%YAML 1.2\n---\nfoo: 1\n";
let err = from_str::<Value>(yaml).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("YAML") || msg.contains("duplicate") || msg.contains("directive"),
"got: {msg}"
);
}
#[test]
fn parser_scanner_yaml_directive_non_numeric_trailing_arg_errors() {
let yaml = "%YAML 1.2 foo\n---\nx: 1\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(
err.to_string().contains("YAML") || err.to_string().contains("directive"),
"got: {err}"
);
}
#[test]
fn parser_scanner_directive_packed_comment_indicator_errors() {
let yaml = "%YAML 1.1#bad\n---\nv: 1\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(
err.to_string().contains("comment") || err.to_string().contains("directive"),
"got: {err}"
);
}
#[test]
fn parser_scanner_tag_directive_named_handle_resolves() {
let yaml = "%TAG !e! tag:example.com,2000:app/\n---\n!e!type value\n";
let v: Value = from_str(yaml).unwrap();
let _ = v;
}
#[test]
fn parser_scanner_directive_without_doc_start_errors_at_eof() {
let yaml = "%YAML 1.2\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(
err.to_string().contains("---") || err.to_string().contains("directive"),
"got: {err}"
);
}
#[test]
fn parser_scanner_content_after_document_end_marker_errors() {
let yaml = "key: 1\n... foo\n";
let res = from_str::<Value>(yaml);
let _ = res;
}
#[test]
fn parser_scanner_tab_after_document_end_marker_errors() {
let yaml = "k: 1\n...\t\n";
let _ = from_str::<Value>(yaml);
}
#[test]
fn parser_scanner_block_scalar_literal_explicit_indent() {
let yaml = "data: |2\n line\n more\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.get("data").and_then(|v| v.as_str()).is_some());
}
#[test]
fn parser_scanner_block_scalar_folded_keep_chomp() {
let yaml = "data: >+\n one\n two\n\n\n";
let v: Value = from_str(yaml).unwrap();
let s = v.get("data").and_then(|v| v.as_str()).unwrap();
assert!(s.ends_with('\n'));
}
#[test]
fn parser_scanner_block_scalar_folded_strip_chomp() {
let yaml = "data: >-\n one\n two\n";
let v: Value = from_str(yaml).unwrap();
let s = v.get("data").and_then(|v| v.as_str()).unwrap();
assert!(!s.ends_with('\n'));
}
#[test]
fn parser_scanner_block_scalar_literal_keep_chomp() {
let yaml = "data: |+\n one\n two\n\n\n";
let v: Value = from_str(yaml).unwrap();
let s = v.get("data").and_then(|v| v.as_str()).unwrap();
assert!(s.contains("one"));
}
#[test]
fn parser_scanner_block_scalar_literal_strip_chomp() {
let yaml = "data: |-\n one\n two\n";
let v: Value = from_str(yaml).unwrap();
let s = v.get("data").and_then(|v| v.as_str()).unwrap();
assert!(!s.ends_with('\n'));
}
#[test]
fn parser_scanner_block_scalar_indent_then_chomp() {
let yaml = "v: |2+\n body\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.get("v").and_then(|v| v.as_str()).is_some());
}
#[test]
fn parser_scanner_block_scalar_chomp_then_indent() {
let yaml = "v: |+2\n body\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.get("v").and_then(|v| v.as_str()).is_some());
}
#[test]
fn parser_scanner_block_scalar_zero_indent_indicator_errors() {
let yaml = "v: |0\n body\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("indent") || err.to_string().contains("digit"));
}
#[test]
fn parser_scanner_block_scalar_two_digit_indicator_errors() {
let yaml = "v: |10\n body\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("indent") || err.to_string().contains("digit"));
}
#[test]
fn parser_scanner_block_scalar_packed_comment_indicator_errors() {
let yaml = "v: >#bad\n body\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("comment") || err.to_string().contains("space"));
}
#[test]
fn parser_scanner_block_scalar_overspaced_leading_empty_line_errors() {
let yaml = "v: |2\n \n body\n";
let _ = from_str::<Value>(yaml);
}
#[test]
fn parser_scanner_double_quoted_x_hex_escape() {
let yaml = "v: \"\\x41\\x42\"\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("v").and_then(|v| v.as_str()), Some("AB"));
}
#[test]
fn parser_scanner_double_quoted_x_hex_non_digit_errors() {
let yaml = "v: \"\\xZZ\"\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("hex") || err.to_string().contains("escape"));
}
#[test]
fn parser_scanner_double_quoted_big_u_escape() {
let yaml = "v: \"\\U0001F600\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.get("v").and_then(|v| v.as_str()).unwrap();
assert!(s.contains('\u{1F600}'));
}
#[test]
fn parser_scanner_double_quoted_big_u_invalid_codepoint_errors() {
let yaml = "v: \"\\U00110000\"\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("Unicode") || err.to_string().contains("hex"));
}
#[test]
fn parser_scanner_double_quoted_unknown_escape_errors() {
let yaml = "v: \"\\q\"\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("escape") || err.to_string().contains("\\q"));
}
#[test]
fn parser_scanner_double_quoted_special_escapes_nel_nbsp() {
let yaml = "v: \"a\\Nb\\_c\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.get("v").and_then(|v| v.as_str()).unwrap();
assert!(s.contains('\u{0085}'));
assert!(s.contains('\u{00A0}'));
}
#[test]
fn parser_scanner_double_quoted_special_escapes_ls_ps() {
let yaml = "v: \"a\\Lb\\Pc\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.get("v").and_then(|v| v.as_str()).unwrap();
assert!(s.contains('\u{2028}'));
assert!(s.contains('\u{2029}'));
}
#[test]
fn parser_scanner_double_quoted_line_break_escape_folds() {
let yaml = "v: \"a\\\n b\"\n";
let v: Value = from_str(yaml).unwrap();
let s = v.get("v").and_then(|v| v.as_str()).unwrap();
assert!(s.starts_with("ab") || s.contains('a'));
}
#[test]
fn parser_scanner_double_quoted_lone_high_surrogate_errors() {
let yaml = "v: \"\\uD800\"\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("D800") || err.to_string().contains("surrogate"));
}
#[test]
fn parser_scanner_double_quoted_high_surrogate_no_pair_errors() {
let yaml = "v: \"\\uD800x\"\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("surrogate") || err.to_string().contains("D800"));
}
#[test]
fn parser_scanner_anchor_decorating_alias_errors() {
let yaml = "anchor: &a 1\nuse: &b *a\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("alias") || err.to_string().contains("anchor"));
}
#[test]
fn parser_scanner_tag_decorating_alias_errors() {
let yaml = "anchor: &a 1\nuse: !!str *a\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("alias") || err.to_string().contains("tag"));
}
#[test]
fn parser_scanner_tag_packed_against_flow_open_errors() {
let yaml = "v: !!invalid{}\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("tag") || err.to_string().contains("flow"));
}
#[test]
fn parser_scanner_undeclared_named_tag_handle_errors() {
let yaml = "v: !undeclared!suffix value\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("tag") || err.to_string().contains("declared"));
}
#[test]
fn parser_scanner_verbatim_tag_form() {
let yaml = "v: !<tag:example.com,2000:foo> bar\n";
let v: Value = from_str(yaml).unwrap();
let _ = v;
}
#[test]
fn parser_scanner_explicit_key_indicator() {
let yaml = "? complex\n: value\n? other\n: more\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("complex").and_then(|v| v.as_str()), Some("value"));
assert_eq!(v.get("other").and_then(|v| v.as_str()), Some("more"));
}
#[test]
fn parser_scanner_stray_flow_seq_close_errors() {
let yaml = "v: ]\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("]") || err.to_string().contains("flow"));
}
#[test]
fn parser_scanner_stray_flow_map_close_errors() {
let yaml = "v: }\n";
let err = from_str::<Value>(yaml).unwrap_err();
assert!(err.to_string().contains("}") || err.to_string().contains("flow"));
}
#[test]
fn parser_scanner_deeply_nested_flow_collections() {
let yaml = "v: [[[[[[1, 2]]]]]]\n";
let v: Value = from_str(yaml).unwrap();
let _ = v.get("v");
}
#[test]
fn parser_scanner_plain_scalar_embedded_colon() {
let yaml = "v: foo:bar\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("v").and_then(|v| v.as_str()), Some("foo:bar"));
}
#[test]
fn parser_scanner_plain_scalar_embedded_hash() {
let yaml = "v: foo#notacomment\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("v").and_then(|v| v.as_str()), Some("foo#notacomment"));
}
#[test]
fn parser_scanner_tab_indent_in_block_scalar_errors() {
let yaml = "v: |\n\tcontent\n";
let _ = from_str::<Value>(yaml);
}
#[test]
fn parser_scanner_multiline_plain_scalar() {
let yaml = "v: foo\n bar\n baz\n";
let v: Value = from_str(yaml).unwrap();
let s = v.get("v").and_then(|v| v.as_str()).unwrap();
assert!(s.contains("foo"));
assert!(s.contains("bar"));
}
#[test]
fn parser_scanner_single_quoted_empty() {
let yaml = "v: ''\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("v").and_then(|v| v.as_str()), Some(""));
}
#[test]
fn parser_scanner_single_quoted_escaped_quote() {
let yaml = "v: 'it''s fine'\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("v").and_then(|v| v.as_str()), Some("it's fine"));
}
#[test]
fn parser_scanner_single_quoted_multiline_with_empty_line() {
let yaml = "v: 'line1\n\n line2'\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.get("v").and_then(|v| v.as_str()).is_some());
}
#[test]
fn parser_scanner_double_quoted_multiline_with_empty_line() {
let yaml = "v: \"line1\n\n line2\"\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.get("v").and_then(|v| v.as_str()).is_some());
}
#[test]
fn parser_scanner_anchor_at_end_of_input_errors() {
let yaml = "v: &anchor\n";
let v: Value = from_str(yaml).unwrap();
let _ = v;
}
#[test]
fn parser_scanner_yaml_directive_version_only() {
let yaml = "%YAML 1.2\n---\nv: 1\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("v").and_then(|v| v.as_i64()), Some(1));
}
#[test]
fn parser_scanner_directive_with_trailing_comment() {
let yaml = "%YAML 1.2 # ok\n---\nv: 1\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("v").and_then(|v| v.as_i64()), Some(1));
}
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct AstDoc {
a: Spanned<i64>,
}
#[test]
fn parser_loader_max_events_breach() {
let yaml = "---\na: 1\n---\na: 2\n---\na: 3\n";
let cfg = ParserConfig::new().max_events(3).max_documents(usize::MAX);
let res: Result<Vec<Value>, _> = load_all_with_config(yaml, &cfg).and_then(|it| it.collect());
let err = res.unwrap_err();
assert!(matches!(err, Error::Budget(BudgetBreach::MaxEvents { .. })));
}
#[test]
fn parser_loader_max_total_scalar_bytes_breach() {
let big = "x".repeat(2_000);
let yaml = format!("a: '{big}'\nb: '{big}'\n");
let cfg = ParserConfig::new().max_total_scalar_bytes(1_000);
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct D {
a: Spanned<String>,
}
let err = from_str_with_config::<D>(&yaml, &cfg).unwrap_err();
assert!(matches!(
err,
Error::Budget(BudgetBreach::MaxTotalScalarBytes { .. })
));
}
#[test]
fn parser_loader_max_documents_breach() {
let yaml = "---\na: 1\n---\nb: 2\n---\nc: 3\n";
let cfg = ParserConfig::new().max_documents(1);
let res: Result<Vec<Value>, _> = load_all_with_config(yaml, &cfg).and_then(|it| it.collect());
let err = res.unwrap_err();
assert!(matches!(
err,
Error::Budget(BudgetBreach::MaxDocuments { .. })
));
}
#[test]
fn parser_loader_alias_anchor_ratio_breach() {
let yaml =
"anchor: &a 1\nuses:\n - *a\n - *a\n - *a\n - *a\n - *a\n - *a\n - *a\n - *a\n - *a\n - *a\n";
let cfg = ParserConfig::new()
.alias_anchor_ratio(Some(2.0))
.max_alias_expansions(1_000);
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct D {
anchor: Spanned<i64>,
}
let err = from_str_with_config::<D>(yaml, &cfg).unwrap_err();
assert!(matches!(
err,
Error::Budget(BudgetBreach::AliasAnchorRatio { .. })
));
}
#[test]
fn parser_loader_unknown_alias_at_errors() {
let yaml = "v: *missing\n";
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct D {
v: Spanned<String>,
}
let err = from_str_with_config::<D>(yaml, &ParserConfig::new()).unwrap_err();
let msg = err.to_string();
assert!(msg.contains("missing") || msg.contains("anchor") || msg.contains("alias"));
}
#[test]
fn parser_loader_duplicate_key_policy_error_rejects() {
let yaml = "a: 1\na: 2\n";
let cfg = ParserConfig::new().duplicate_key_policy(DuplicateKeyPolicy::Error);
let err = from_str_with_config::<AstDoc>(yaml, &cfg).unwrap_err();
let msg = err.to_string();
assert!(msg.to_lowercase().contains("duplicate") || msg.contains('a'));
}
#[test]
fn parser_loader_duplicate_key_policy_first_keeps_first() {
let yaml = "a: 1\na: 99\n";
let cfg = ParserConfig::new().duplicate_key_policy(DuplicateKeyPolicy::First);
let d: AstDoc = from_str_with_config(yaml, &cfg).unwrap();
assert_eq!(d.a.value, 1);
}
#[test]
fn parser_loader_merge_key_policy_error_rejects() {
let yaml = "base: &b\n x: 1\nuse:\n <<: *b\n";
let cfg = ParserConfig::new().merge_key_policy(MergeKeyPolicy::Error);
let err = from_str_with_config::<Value>(yaml, &cfg).unwrap_err();
assert!(err.to_string().contains("merge") || err.to_string().contains("<<"));
}
#[test]
fn parser_loader_merge_key_policy_as_ordinary_keeps_key() {
let yaml = "<<: literal\n";
let cfg = ParserConfig::new().merge_key_policy(MergeKeyPolicy::AsOrdinary);
let v: Value = from_str_with_config(yaml, &cfg).unwrap();
assert_eq!(v.get("<<").and_then(|v| v.as_str()), Some("literal"));
}
#[test]
fn parser_loader_anchor_on_tagged_scalar() {
let yaml = "anchor: &a !!int 42\nuse: *a\n";
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct D {
anchor: Spanned<i64>,
#[serde(rename = "use")]
used: Spanned<i64>,
}
let d: D = from_str(yaml).unwrap();
assert_eq!(d.anchor.value, 42);
assert_eq!(d.used.value, 42);
}
#[test]
fn parser_loader_anchor_on_sequence() {
let yaml = "items: &items\n - 1\n - 2\nuses: *items\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.get("items").is_some());
assert!(v.get("uses").is_some());
}
#[test]
fn parser_loader_anchor_on_mapping() {
let yaml = "src: &src\n k: 1\nuse: *src\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.get("src").is_some());
assert!(v.get("use").is_some());
}
#[test]
fn parser_loader_custom_tag_on_sequence_wraps_tagged() {
let yaml = "items: !MyType\n - 1\n - 2\n";
let v: Value = from_str(yaml).unwrap();
let _ = v.get("items");
}
#[test]
fn parser_loader_custom_tag_on_mapping_wraps_tagged() {
let yaml = "obj: !MyType\n k: 1\n";
let v: Value = from_str(yaml).unwrap();
let _ = v.get("obj");
}
#[test]
fn parser_loader_complex_sequence_key_stringified() {
let yaml = "? [1, 2]\n: composite\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_mapping().is_some());
}
#[test]
fn parser_loader_complex_mapping_key_stringified() {
let yaml = "? {a: 1}\n: composite\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_mapping().is_some());
}
#[test]
fn parser_loader_float_key_stringified() {
let yaml = "1.5: half\n2.5: more\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_mapping().is_some());
}
#[test]
fn parser_loader_null_key_stringified() {
let yaml = "~: nothing\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_mapping().is_some_and(|m| !m.is_empty()));
}
#[derive(Debug, Default)]
struct DenySequences;
impl Policy for DenySequences {
fn check_event(&self, ev: PolicyEvent<'_>) -> noyalib::Result<()> {
use noyalib::policy::PolicyEventKind;
if ev.kind == PolicyEventKind::SequenceStart {
return Err(Error::Custom("sequences denied".into()));
}
Ok(())
}
}
#[test]
fn parser_loader_policy_rejects_sequence_event() {
let yaml = "items:\n - 1\n - 2\n";
let cfg = ParserConfig::new().with_policy(DenySequences);
let err = from_str_with_config::<Value>(yaml, &cfg).unwrap_err();
assert!(err.to_string().contains("sequences"));
}
#[derive(Debug, Default)]
struct DenyMappings;
impl Policy for DenyMappings {
fn check_event(&self, ev: PolicyEvent<'_>) -> noyalib::Result<()> {
use noyalib::policy::PolicyEventKind;
if ev.kind == PolicyEventKind::MappingStart {
return Err(Error::Custom("mappings denied".into()));
}
Ok(())
}
}
#[test]
fn parser_loader_policy_rejects_mapping_event() {
let yaml = "k: v\n";
let cfg = ParserConfig::new().with_policy(DenyMappings);
let err = from_str_with_config::<Value>(yaml, &cfg).unwrap_err();
assert!(err.to_string().contains("mappings"));
}
#[derive(Debug, Default)]
struct DenyAliases;
impl Policy for DenyAliases {
fn check_event(&self, ev: PolicyEvent<'_>) -> noyalib::Result<()> {
use noyalib::policy::PolicyEventKind;
if ev.kind == PolicyEventKind::Alias {
return Err(Error::Custom("aliases denied".into()));
}
Ok(())
}
}
#[test]
fn parser_loader_policy_rejects_alias_event() {
let yaml = "anchor: &a 1\nuse: *a\n";
let cfg = ParserConfig::new().with_policy(DenyAliases);
let err = from_str_with_config::<Value>(yaml, &cfg).unwrap_err();
assert!(err.to_string().contains("aliases"));
}
#[test]
fn parser_events_anchor_then_tag_on_node() {
let yaml = "v: &a !!str hello\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("v").and_then(|v| v.as_str()), Some("hello"));
}
#[test]
fn parser_events_tag_then_anchor_on_node() {
let yaml = "v: !!str &a hello\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("v").and_then(|v| v.as_str()), Some("hello"));
}
#[test]
fn parser_events_anchor_only_yields_empty_scalar() {
let yaml = "v: &empty\n";
let v: Value = from_str(yaml).unwrap();
let _ = v.get("v");
}
#[test]
fn parser_events_stray_content_after_implicit_doc_errors() {
let yaml = "first\nsecond\nthird\n";
let _ = from_str::<Value>(yaml);
}
#[test]
fn parser_events_implicit_doc_after_explicit_end() {
let yaml = "---\nfirst: 1\n...\nsecond: 2\n";
let res = from_str::<Value>(yaml);
let _ = res;
}
#[test]
fn parser_events_flow_seq_implicit_empty_key_mapping() {
let yaml = "v: [ : empty_key_value ]\n";
let v: Value = from_str(yaml).unwrap();
let _ = v.get("v");
}
#[test]
fn parser_events_flow_map_implicit_empty_key() {
let yaml = "v: { : bar }\n";
let v: Value = from_str(yaml).unwrap();
let _ = v.get("v");
}
#[test]
fn parser_events_flow_seq_explicit_key() {
let yaml = "v: [ ? complex_key ]\n";
let res = from_str::<Value>(yaml);
let _ = res;
}
#[test]
fn parser_events_flow_map_explicit_key() {
let yaml = "v: { ? key : value }\n";
let v: Value = from_str(yaml).unwrap();
let _ = v.get("v");
}
#[test]
fn parser_events_flow_map_explicit_key_no_value() {
let yaml = "v: { ? lonely }\n";
let v: Value = from_str(yaml).unwrap();
let _ = v.get("v");
}
#[test]
fn parser_events_flow_seq_bare_value_emits_mapping() {
let yaml = "v: [: only_value]\n";
let _ = from_str::<Value>(yaml);
}
#[test]
fn parser_events_block_explicit_key_no_value() {
let yaml = "? lonely\n? another\n: value\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.get("another").and_then(|v| v.as_str()), Some("value"));
}
#[test]
fn parser_events_block_value_then_end() {
let yaml = "k:\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_mapping().is_some());
}
#[test]
fn parser_events_indentless_block_sequence() {
let yaml = "items:\n- 1\n- 2\n- 3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(
v.get("items")
.and_then(|v| v.as_sequence())
.map(|s| s.len()),
Some(3)
);
}
#[test]
fn parser_events_anchor_on_indentless_sequence() {
let yaml = "items: &is\n- 1\n- 2\nrefs: *is\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.get("items").is_some());
assert!(v.get("refs").is_some());
}
#[test]
fn parser_events_flow_seq_trailing_comma() {
let yaml = "v: [1, 2, 3,]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(
v.get("v").and_then(|v| v.as_sequence()).map(|s| s.len()),
Some(3)
);
}
#[test]
fn parser_events_flow_map_trailing_comma() {
let yaml = "v: {a: 1, b: 2,}\n";
let v: Value = from_str(yaml).unwrap();
let m = v.get("v").and_then(|v| v.as_mapping()).unwrap();
assert_eq!(m.len(), 2);
}
#[test]
fn parser_events_flow_map_garbage_separator_errors() {
let yaml = "v: {a: 1 ; b: 2}\n";
let _ = from_str::<Value>(yaml);
}
#[test]
fn parser_events_flow_seq_garbage_separator_errors() {
let yaml = "v: [1 ; 2]\n";
let _ = from_str::<Value>(yaml);
}
#[test]
fn parser_events_empty_document_yields_null() {
let yaml = "---\n";
let v: Value = from_str(yaml).unwrap();
assert!(v.is_null());
}
#[test]
fn parser_events_nested_flow_with_anchors_and_tags() {
let yaml = "v: [&a 1, !!str &b two, *a, *b]\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(
v.get("v").and_then(|v| v.as_sequence()).map(|s| s.len()),
Some(4)
);
}