#![allow(
unused_comparisons,
unused_results,
clippy::approx_constant,
clippy::absurd_extreme_comparisons,
clippy::len_zero,
clippy::bool_assert_comparison
)]
use noyalib::{
DuplicateKeyPolicy, FlowStyle, Mapping, MappingAny, ParserConfig, ScalarStyle,
SerializerConfig, Spanned, Tag, TaggedValue, Value, from_str, from_str_with_config, from_value,
to_string, to_string_with_config,
};
use serde::{Deserialize, Serialize};
#[test]
fn single_quoted_escaped_quote() {
let v: Value = from_str("key: 'can''t stop'").unwrap();
assert_eq!(v["key"].as_str().unwrap(), "can't stop");
}
#[test]
fn single_quoted_line_folding() {
let yaml = "key: 'first\n second\n third'";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "first second third");
}
#[test]
fn single_quoted_multiple_breaks() {
let yaml = "key: 'para1\n\n\n para2'";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("\n\n"), "expected double newline, got: {s}");
}
#[test]
fn single_quoted_utf8_multibyte() {
let yaml = "key: 'café ñ 日本語'";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "café ñ 日本語");
}
#[test]
fn double_quoted_escape_null() {
let v: Value = from_str(r#"key: "null\0byte""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "null\0byte");
}
#[test]
fn double_quoted_escape_bell() {
let v: Value = from_str(r#"key: "\a""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\x07");
}
#[test]
fn double_quoted_escape_backspace() {
let v: Value = from_str(r#"key: "\b""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\x08");
}
#[test]
fn double_quoted_escape_tab() {
let v: Value = from_str(r#"key: "\t""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\t");
}
#[test]
fn double_quoted_escape_newline() {
let v: Value = from_str(r#"key: "line1\nline2""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "line1\nline2");
}
#[test]
fn double_quoted_escape_vertical_tab() {
let v: Value = from_str(r#"key: "\v""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\x0B");
}
#[test]
fn double_quoted_escape_form_feed() {
let v: Value = from_str(r#"key: "\f""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\x0C");
}
#[test]
fn double_quoted_escape_carriage_return() {
let v: Value = from_str(r#"key: "\r""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\r");
}
#[test]
fn double_quoted_escape_esc() {
let v: Value = from_str(r#"key: "\e""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\x1B");
}
#[test]
fn double_quoted_escape_space() {
let v: Value = from_str("key: \"\\ end\"").unwrap();
assert!(v["key"].as_str().unwrap().starts_with(' '));
}
#[test]
fn double_quoted_escape_double_quote() {
let v: Value = from_str(r#"key: "say \"hello\"""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), r#"say "hello""#);
}
#[test]
fn double_quoted_escape_slash() {
let v: Value = from_str(r#"key: "a\/b""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "a/b");
}
#[test]
fn double_quoted_escape_backslash() {
let v: Value = from_str(r#"key: "a\\b""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "a\\b");
}
#[test]
fn double_quoted_escape_nel() {
let v: Value = from_str(r#"key: "\N""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\u{0085}");
}
#[test]
fn double_quoted_escape_nbsp() {
let v: Value = from_str(r#"key: "\_""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\u{00A0}");
}
#[test]
fn double_quoted_escape_line_separator() {
let v: Value = from_str(r#"key: "\L""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\u{2028}");
}
#[test]
fn double_quoted_escape_paragraph_separator() {
let v: Value = from_str(r#"key: "\P""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "\u{2029}");
}
#[test]
fn double_quoted_escape_hex2() {
let v: Value = from_str(r#"key: "\x41\x42""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "AB");
}
#[test]
fn double_quoted_escape_unicode4() {
let v: Value = from_str(r#"key: "\u0041\u00E9""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "Aé");
}
#[test]
fn double_quoted_escape_unicode8() {
let v: Value = from_str(r#"key: "\U00000041""#).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "A");
}
#[test]
fn double_quoted_line_break_escape() {
let yaml = "key: \"line1\\\n line2\"";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "line1line2");
}
#[test]
fn double_quoted_unknown_escape_error() {
let result: Result<Value, _> = from_str(r#"key: "\q""#);
assert!(result.is_err());
let msg = result.unwrap_err().to_string();
assert!(msg.contains("escape") || msg.contains("unknown"), "{msg}");
}
#[test]
fn double_quoted_line_folding() {
let yaml = "key: \"first\n second\"";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "first second");
}
#[test]
fn double_quoted_multiple_breaks() {
let yaml = "key: \"first\n\n\n second\"";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("\n\n"), "expected double newline, got: {s}");
}
#[test]
fn double_quoted_whitespace_before_break() {
let yaml = "key: \"word \n more\"";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("word"), "{s}");
}
#[test]
fn double_quoted_hex_escape_invalid() {
let result: Result<Value, _> = from_str(r#"key: "\xGG""#);
assert!(result.is_err());
}
#[test]
fn double_quoted_hex_escape_invalid_codepoint() {
let result: Result<Value, _> = from_str(r#"key: "\UD800""#);
assert!(result.is_err());
}
#[test]
fn block_scalar_literal_basic() {
let yaml = "key: |\n line1\n line2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "line1\nline2\n");
}
#[test]
fn block_scalar_folded_basic() {
let yaml = "key: >\n line1\n line2\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "line1 line2\n");
}
#[test]
fn block_scalar_folded_multiple_breaks() {
let yaml = "key: >\n para1\n\n\n para2\n";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("\n\n"), "expected preserved breaks, got: {s}");
}
#[test]
fn block_scalar_folded_leading_blank() {
let yaml = "key: >\n normal\n indented\n back\n";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("indented"), "{s}");
assert!(s.contains("normal"), "{s}");
}
#[test]
fn block_scalar_keep_chomping() {
let yaml = "key: |+\n content\n\n\n";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.ends_with("\n\n\n") || s.ends_with("\n\n"), "got: {s:?}");
}
#[test]
fn block_scalar_strip_chomping() {
let yaml = "key: |-\n content\n\n\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "content");
}
#[test]
fn block_scalar_clip_chomping() {
let yaml = "key: |\n content\n\n\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "content\n");
}
#[test]
fn block_scalar_explicit_indent() {
let yaml = "key: |2\n content\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "content\n");
}
#[test]
fn block_scalar_autodetect_with_empty_lines() {
let yaml = "key: |\n\n content\n";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains("content"), "{s}");
}
#[test]
fn block_scalar_more_indented() {
let yaml = "key: |\n base\n extra\n";
let v: Value = from_str(yaml).unwrap();
let s = v["key"].as_str().unwrap();
assert!(s.contains(" extra"), "expected extra indent, got: {s:?}");
}
#[test]
fn block_scalar_comment_after_indicator() {
let yaml = "key: | # this is a comment\n content\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "content\n");
}
#[test]
fn anchor_name_length_limit() {
let long_name = "a".repeat(1030);
let yaml = format!("&{long_name} value");
let result: Result<Value, _> = from_str(&yaml);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("anchor"));
}
#[test]
fn tag_verbatim_length_limit() {
let long_uri = "x".repeat(1030);
let yaml = format!("!<{long_uri}> value");
let result: Result<Value, _> = from_str(&yaml);
assert!(result.is_err());
}
#[test]
fn tag_secondary_length_limit() {
let long_suffix = "x".repeat(1030);
let yaml = format!("!!{long_suffix} value");
let result: Result<Value, _> = from_str(&yaml);
assert!(result.is_err());
}
#[test]
fn tag_primary_length_limit() {
let long_suffix = "x".repeat(1030);
let yaml = format!("!{long_suffix} value");
let result: Result<Value, _> = from_str(&yaml);
assert!(result.is_err());
}
#[test]
fn tag_verbatim_normal() {
let yaml = "!<tag:example.com,2024:type> value";
let v: Value = from_str(yaml).unwrap();
assert!(!v.is_null(), "got: {v:?}");
}
#[test]
fn windows_line_endings_crlf() {
let yaml = "key: value\r\nkey2: value2\r\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key2"].as_str().unwrap(), "value2");
}
#[test]
fn tab_indentation_error() {
let yaml = "key:\n\t value";
let result: Result<Value, _> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn utf8_bom_at_start() {
let yaml = "\u{FEFF}key: value";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "value");
}
#[test]
fn explicit_document_start_markers() {
let yaml = "---\nfirst: 1\n...\n---\nsecond: 2\n...";
let docs = noyalib::load_all(yaml).unwrap();
assert_eq!(docs.len(), 2);
}
#[test]
fn empty_document_between_markers() {
let yaml = "---\n...\n---\nvalue\n...";
let docs = noyalib::load_all(yaml).unwrap();
assert!(docs.len() >= 1);
}
#[test]
fn flow_mapping_in_flow_sequence() {
let yaml = "[{a: 1}, {b: 2}]";
let v: Value = from_str(yaml).unwrap();
let seq = v.as_sequence().unwrap();
assert_eq!(seq.len(), 2);
}
#[test]
fn indentless_sequence_entry() {
let yaml = "key:\n - item1\n - item2";
let v: Value = from_str(yaml).unwrap();
let seq = v["key"].as_sequence().unwrap();
assert_eq!(seq.len(), 2);
}
#[test]
fn flow_sequence_with_mapping_entries() {
let yaml = "[a, b: c, d]";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_sequence().is_some());
}
#[test]
fn block_mapping_complex_key() {
let yaml = "? key1\n: value1\n? key2\n: value2";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key1"].as_str().unwrap(), "value1");
assert_eq!(v["key2"].as_str().unwrap(), "value2");
}
#[test]
fn flow_mapping_explicit_key() {
let yaml = "{a: 1, b: 2}";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_mapping().is_some());
assert_eq!(v["a"].as_i64().unwrap(), 1);
}
#[test]
fn node_with_anchor_and_tag() {
let yaml = "&anchor !!str tagged_value";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_str().is_some() || matches!(v, Value::Tagged(_)));
}
#[test]
fn block_mapping_implicit_value() {
let yaml = "key1:\nkey2: value2";
let v: Value = from_str(yaml).unwrap();
assert!(v["key1"].is_null());
assert_eq!(v["key2"].as_str().unwrap(), "value2");
}
#[test]
fn multi_document_lifecycle() {
let yaml = "---\na: 1\n---\nb: 2\n...";
let docs = noyalib::load_all(yaml).unwrap();
let collected: Vec<_> = docs.collect();
assert_eq!(collected.len(), 2);
}
#[test]
fn alias_expansion_byte_limit() {
let config = ParserConfig::new()
.max_alias_expansions(1024)
.max_document_length(1024);
let long_value = "x".repeat(200);
let mut yaml = format!("anchor: &a {long_value}\n");
for i in 0..10 {
yaml.push_str(&format!("ref{i}: *a\n"));
}
let result: Result<Value, _> = from_str_with_config(&yaml, &config);
assert!(result.is_err());
}
#[test]
fn duplicate_key_first_policy() {
let config = ParserConfig::new().duplicate_key_policy(DuplicateKeyPolicy::First);
let yaml = "a: first\na: second";
let v: Value = from_str_with_config(yaml, &config).unwrap();
assert_eq!(v["a"].as_str().unwrap(), "first");
}
#[test]
fn duplicate_key_last_policy_span_update() {
let yaml = "a: first\na: second";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"].as_str().unwrap(), "second");
}
#[test]
fn merge_key_from_mapping() {
let yaml =
"defaults: &defaults\n color: red\n size: large\nitem:\n <<: *defaults\n name: thing";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["item"]["color"].as_str().unwrap(), "red");
}
#[test]
fn merge_key_from_sequence_of_mappings() {
let yaml = "a: &a\n x: 1\nb: &b\n y: 2\nc:\n <<: [*a, *b]\n z: 3";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["c"]["x"].as_i64().unwrap(), 1);
assert_eq!(v["c"]["y"].as_i64().unwrap(), 2);
}
#[test]
fn merge_key_scalar_error() {
let yaml = "a: &a scalar\nb:\n <<: *a";
let result: Result<Value, _> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn sequence_with_anchored_value() {
let yaml = "&seq\n- a\n- b\n- c";
let v: Value = from_str(yaml).unwrap();
let seq = v.as_sequence().unwrap();
assert_eq!(seq.len(), 3);
}
#[test]
fn tagged_scalar_resolution() {
let yaml = "!!int 42";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_i64().unwrap(), 42);
}
#[test]
fn tagged_bool_resolution() {
let yaml = "!!bool true";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_bool().unwrap(), true);
}
#[test]
fn tagged_float_resolution() {
let yaml = "!!float 3.14";
let v: Value = from_str(yaml).unwrap();
assert!((v.as_f64().unwrap() - 3.14).abs() < 0.001);
}
#[test]
fn tagged_null_resolution() {
let yaml = "!!null ~";
let v: Value = from_str(yaml).unwrap();
assert!(v.is_null());
}
#[test]
fn tagged_str_resolution() {
let yaml = "!!str 42";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v.as_str().unwrap(), "42");
}
#[test]
fn tagged_float_special_values() {
let yaml = "!!float .inf";
let v: Value = from_str(yaml).unwrap();
assert!(v.as_f64().unwrap().is_infinite());
let yaml2 = "!!float .nan";
let v2: Value = from_str(yaml2).unwrap();
assert!(v2.as_f64().unwrap().is_nan());
}
#[test]
fn tagged_bool_invalid_error() {
let yaml = "!!bool notabool";
let result: Result<Value, _> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn tagged_int_invalid_error() {
let yaml = "!!int notanint";
let result: Result<Value, _> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn tagged_float_invalid_error() {
let yaml = "!!float notafloat";
let result: Result<Value, _> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn hex_integer_parsing() {
let v: Value = from_str("0xFF").unwrap();
assert_eq!(v.as_i64().unwrap(), 255);
}
#[test]
fn octal_integer_parsing() {
let v: Value = from_str("0o77").unwrap();
assert_eq!(v.as_i64().unwrap(), 63);
}
#[test]
fn large_integer_as_float() {
let v: Value = from_str("99999999999999999999").unwrap();
assert!(v.as_f64().is_some());
}
#[test]
fn deserialize_identifier_delegates_to_str() {
#[derive(Debug, Deserialize)]
enum Color {
Red,
Blue,
}
let yaml = "Red";
let c: Color = from_str(yaml).unwrap();
assert!(matches!(c, Color::Red));
}
#[test]
fn spanned_deserialize_all_fields() {
let yaml = "key: hello";
let spanned: Spanned<Value> = from_str(yaml).unwrap();
assert!(spanned.start.line() >= 1);
assert!(spanned.start.column() >= 0);
assert!(spanned.end.line() >= 1);
assert!(spanned.end.column() >= 0);
let _ = spanned.into_inner();
}
#[test]
fn spanned_nested_value() {
#[derive(Debug, Deserialize)]
struct Config {
name: Spanned<String>,
}
let yaml = "name: test";
let c: Config = from_str(yaml).unwrap();
assert_eq!(c.name.value, "test");
}
#[test]
fn serialize_custom_tag() {
let tagged = TaggedValue::new(Tag::new("!custom"), Value::String("val".into()));
let v = Value::Tagged(Box::new(tagged));
let s = to_string(&v).unwrap();
assert!(s.contains("!custom"), "got: {s}");
}
#[test]
fn serialize_flow_seq_non_sequence_fallback() {
use noyalib::fmt::FlowSeq;
let val = FlowSeq(42i32);
let s = to_string(&val).unwrap();
assert!(s.contains("42"), "got: {s}");
}
#[test]
fn serialize_flow_map_non_mapping_fallback() {
use noyalib::fmt::FlowMap;
let val = FlowMap(42i32);
let s = to_string(&val).unwrap();
assert!(s.contains("42"), "got: {s}");
}
#[test]
fn serialize_lit_str_non_string_fallback() {
use noyalib::fmt::LitString;
let ls = LitString::from("hello".to_string());
let s = to_string(&ls).unwrap();
assert!(s.contains("hello"), "got: {s}");
}
#[test]
fn serialize_fold_str_non_string_fallback() {
use noyalib::fmt::FoldString;
let fs = FoldString::from("hello".to_string());
let s = to_string(&fs).unwrap();
assert!(s.contains("hello"), "got: {s}");
}
#[test]
fn serialize_commented_value() {
use noyalib::Commented;
let c = Commented::new(42, "this is a comment");
let s = to_string(&c).unwrap();
assert!(s.contains("42"), "got: {s}");
assert!(s.contains("# this is a comment"), "got: {s}");
}
#[test]
fn serialize_space_after_value() {
use noyalib::SpaceAfter;
let sa = SpaceAfter(42);
let s = to_string(&sa).unwrap();
assert!(s.contains("42"), "got: {s}");
}
#[test]
fn serialize_unknown_internal_tag() {
let v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("__noya_unknown"),
Value::from("test"),
)));
let s = to_string(&v).unwrap();
assert!(s.contains("test"), "got: {s}");
}
#[test]
fn looks_like_number_empty_string() {
let v = Value::String(String::new());
let s = to_string(&v).unwrap();
assert!(s.contains("''") || s.contains("\"\"") || s.trim().is_empty());
}
#[test]
fn serialize_map_key_non_string_error() {
let mut map = std::collections::HashMap::new();
let _ = map.insert(vec![1, 2], "value");
let result = to_string(&map);
assert!(result.is_err());
}
#[test]
fn serialize_string_with_control_chars() {
let v = Value::String("hello\x07world".to_string());
let s = to_string(&v).unwrap();
assert!(s.contains('"') || s.contains("\\a"), "got: {s}");
}
#[test]
fn mapping_any_ord_different_keys() {
let mut m1 = MappingAny::new();
let mut m2 = MappingAny::new();
let _ = m1.insert(Value::from("a"), Value::from(1));
let _ = m2.insert(Value::from("b"), Value::from(1));
assert!(m1 < m2);
}
#[test]
fn mapping_any_ord_same_keys_different_values() {
let mut m1 = MappingAny::new();
let mut m2 = MappingAny::new();
let _ = m1.insert(Value::from("a"), Value::from(1));
let _ = m2.insert(Value::from("a"), Value::from(2));
assert!(m1 < m2);
}
#[test]
fn mapping_deserialize_type_error() {
let result: Result<Mapping, _> = serde_json::from_str("123");
assert!(result.is_err());
}
#[test]
fn mapping_any_deserialize_type_error() {
let result: Result<MappingAny, _> = serde_json::from_str("123");
assert!(result.is_err());
}
#[test]
fn tagged_value_deserialize_from_map() {
let yaml = "mytag: myvalue";
let tv: TaggedValue = from_str(yaml).unwrap();
assert_eq!(tv.tag().as_str(), "mytag");
}
#[test]
fn tagged_value_enum_deserialization() {
let tv: TaggedValue = from_str("mytag: myvalue").unwrap();
assert_eq!(tv.tag().as_str(), "mytag");
assert_eq!(tv.value().as_str(), Some("myvalue"));
}
#[test]
fn tagged_value_unit_variant() {
#[derive(Debug, Deserialize, PartialEq)]
enum Flag {
On,
Off,
}
let yaml = "On";
let flag: Flag = from_str(yaml).unwrap();
assert_eq!(flag, Flag::On);
}
#[test]
fn tagged_value_tuple_variant() {
#[derive(Debug, Deserialize, PartialEq)]
enum Data {
Pair(i32, i32),
}
let yaml = "Pair:\n - 1\n - 2";
let data: Data = from_str(yaml).unwrap();
assert_eq!(data, Data::Pair(1, 2));
}
#[test]
fn tagged_value_struct_variant() {
#[derive(Debug, Deserialize, PartialEq)]
enum Shape {
Circle { radius: f64 },
}
let yaml = "Circle:\n radius: 5.0";
let shape: Shape = from_str(yaml).unwrap();
assert_eq!(shape, Shape::Circle { radius: 5.0 });
}
#[test]
fn value_index_into_mut_tagged_sequence() {
let seq = Value::Sequence(vec![Value::from(1), Value::from(2)]);
let mut v = Value::Tagged(Box::new(TaggedValue::new(Tag::new("seq"), seq)));
let item = &mut v[0];
*item = Value::from(99);
if let Value::Tagged(t) = &v {
if let Value::Sequence(s) = t.value() {
assert_eq!(s[0].as_i64().unwrap(), 99);
}
}
}
#[test]
fn value_index_into_mut_tagged_mapping() {
let mut m = Mapping::new();
m.insert("key", Value::from(10));
let mut v = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("map"),
Value::Mapping(m),
)));
let item = &mut v["key"];
*item = Value::from(99);
}
#[test]
fn value_deserialize_any_for_tagged() {
let tagged = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("custom"),
Value::String("data".into()),
)));
let v2: Value = from_value(&tagged).unwrap();
assert!(!matches!(v2, Value::Null));
}
#[test]
fn value_deserialize_seq() {
let v = Value::Sequence(vec![Value::from(1), Value::from(2), Value::from(3)]);
let nums: Vec<i64> = from_value(&v).unwrap();
assert_eq!(nums, vec![1, 2, 3]);
}
#[test]
fn value_deserialize_map() {
let mut m = Mapping::new();
m.insert("a", Value::from(1));
m.insert("b", Value::from(2));
let v = Value::Mapping(m);
#[derive(Deserialize)]
struct Obj {
a: i64,
b: i64,
}
let obj: Obj = from_value(&v).unwrap();
assert_eq!(obj.a, 1);
assert_eq!(obj.b, 2);
}
#[test]
fn failsafe_schema_rejects_null() {
assert!(noyalib::validate_yaml_failsafe_schema(&Value::Null).is_err());
}
#[test]
fn failsafe_schema_rejects_bool() {
assert!(noyalib::validate_yaml_failsafe_schema(&Value::Bool(true)).is_err());
}
#[test]
fn failsafe_schema_rejects_number() {
assert!(noyalib::validate_yaml_failsafe_schema(&Value::from(42)).is_err());
}
#[test]
fn failsafe_schema_rejects_tagged() {
let tagged = Value::Tagged(Box::new(TaggedValue::new(Tag::new("t"), Value::Null)));
assert!(noyalib::validate_yaml_failsafe_schema(&tagged).is_err());
}
#[test]
fn failsafe_schema_validates_nested_strings() {
let seq = Value::Sequence(vec![Value::from("a"), Value::from("b")]);
assert!(noyalib::validate_yaml_failsafe_schema(&seq).is_ok());
let mut m = Mapping::new();
m.insert("k", Value::from("v"));
assert!(noyalib::validate_yaml_failsafe_schema(&Value::Mapping(m)).is_ok());
}
#[test]
fn failsafe_schema_rejects_nested_number() {
let seq = Value::Sequence(vec![Value::from(42)]);
assert!(noyalib::validate_yaml_failsafe_schema(&seq).is_err());
}
#[test]
fn json_schema_rejects_nan() {
assert!(!noyalib::is_yaml_json_compatible(&Value::from(f64::NAN)));
}
#[test]
fn json_schema_rejects_infinity() {
assert!(!noyalib::is_yaml_json_compatible(&Value::from(
f64::INFINITY
)));
}
#[test]
fn json_schema_rejects_tagged() {
let tagged = Value::Tagged(Box::new(TaggedValue::new(Tag::new("t"), Value::Null)));
assert!(noyalib::validate_yaml_json_schema(&tagged).is_err());
}
#[test]
fn json_schema_validates_nested() {
let seq = Value::Sequence(vec![Value::from(1), Value::from("a")]);
assert!(noyalib::validate_yaml_json_schema(&seq).is_ok());
let mut m = Mapping::new();
m.insert("k", Value::from(true));
assert!(noyalib::validate_yaml_json_schema(&Value::Mapping(m)).is_ok());
}
#[test]
fn core_schema_validates_everything() {
let seq = Value::Sequence(vec![Value::from(f64::NAN), Value::Null]);
assert!(noyalib::validate_yaml_core_schema(&seq).is_ok());
let mut m = Mapping::new();
m.insert("k", Value::from(true));
assert!(noyalib::validate_yaml_core_schema(&Value::Mapping(m)).is_ok());
let tagged = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("custom"),
Value::from("inner"),
)));
assert!(noyalib::validate_yaml_core_schema(&tagged).is_ok());
}
#[test]
fn is_failsafe_compatible_function() {
assert!(noyalib::is_yaml_failsafe_compatible(&Value::from("hello")));
assert!(!noyalib::is_yaml_failsafe_compatible(&Value::from(42)));
}
#[test]
fn path_display_and_parent() {
use noyalib::Path;
let root = Path::Root;
let display = format!("{root}");
assert_eq!(display, ".");
let key = root.key("name");
let key_display = format!("{key}");
assert!(key_display.contains("name"), "got: {key_display}");
let idx = root.index(0);
let idx_display = format!("{idx}");
assert!(idx_display.contains("0"), "got: {idx_display}");
let nested_alias = key.alias();
let alias_display = format!("{nested_alias}");
assert!(alias_display.contains("name"), "alias: {alias_display}");
let nested_unknown = key.unknown();
let unknown_display = format!("{nested_unknown}");
assert!(unknown_display.contains("?"), "unknown: {unknown_display}");
assert!(key.parent().is_some());
assert!(root.parent().is_none());
assert_eq!(root.depth(), 0);
assert!(key.depth() > 0);
assert!(root.is_root());
assert!(!key.is_root());
}
#[test]
fn spanned_struct_deserialization() {
let yaml = "42";
let spanned: Spanned<i32> = from_str(yaml).unwrap();
assert_eq!(spanned.value, 42);
assert!(spanned.start.line() >= 1);
}
#[test]
fn spanned_string_deserialization() {
let yaml = "hello world";
let spanned: Spanned<String> = from_str(yaml).unwrap();
assert_eq!(*spanned, "hello world");
}
#[test]
fn lit_str_as_str_and_into_inner() {
use noyalib::LitStr;
let ls = LitStr("hello");
assert_eq!(ls.as_str(), "hello");
assert_eq!(ls.into_inner(), "hello");
}
#[test]
fn fold_str_as_str_and_into_inner() {
use noyalib::FoldStr;
let fs = FoldStr("world");
assert_eq!(fs.as_str(), "world");
assert_eq!(fs.into_inner(), "world");
}
#[test]
fn singleton_map_recursive_with_tagged() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Action {
Run(String),
Stop,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Job {
#[serde(with = "noyalib::with::singleton_map_recursive")]
actions: Vec<Action>,
}
let job = Job {
actions: vec![Action::Run("test".into()), Action::Stop],
};
let yaml = to_string(&job).unwrap();
let roundtrip: Job = from_str(&yaml).unwrap();
assert_eq!(roundtrip, job);
}
#[test]
fn singleton_map_with_custom_transform() {
use noyalib::with::singleton_map_with::{
from_kebab_case, to_kebab_case, to_lowercase, to_pascal_case, to_snake_case, to_uppercase,
};
assert_eq!(to_snake_case("FooBar"), "foo_bar");
assert_eq!(to_snake_case("HTTPServer"), "h_t_t_p_server");
assert_eq!(to_pascal_case("foo_bar"), "FooBar");
assert_eq!(to_kebab_case("FooBar"), "foo-bar");
assert_eq!(to_lowercase("HELLO"), "hello");
assert_eq!(to_uppercase("hello"), "HELLO");
assert_eq!(from_kebab_case("foo-bar"), "FooBar");
}
#[test]
fn singleton_map_with_transform_tagged_keys() {
use noyalib::with::singleton_map_with::{to_snake_case, to_uppercase};
assert_eq!(to_snake_case("FooBar"), "foo_bar");
assert_eq!(to_uppercase("hello"), "HELLO");
}
#[test]
fn singleton_map_with_from_kebab_case() {
use noyalib::with::singleton_map_with::from_kebab_case;
assert_eq!(from_kebab_case("get-request"), "GetRequest");
assert_eq!(from_kebab_case("a-b-c"), "ABC");
}
#[test]
fn load_all_as_typed_deserialization() {
#[derive(Debug, Deserialize, PartialEq)]
struct Doc {
name: String,
}
let yaml = "---\nname: first\n---\nname: second";
let docs: Vec<Doc> = noyalib::load_all_as(yaml).unwrap();
assert_eq!(docs.len(), 2);
assert_eq!(docs[0].name, "first");
assert_eq!(docs[1].name, "second");
}
#[test]
fn document_iterator_len_and_empty() {
let yaml = "---\na: 1\n---\nb: 2";
let iter = noyalib::load_all(yaml).unwrap();
assert_eq!(iter.len(), 2);
assert!(!iter.is_empty());
let empty_iter = noyalib::load_all("---\n...").unwrap();
assert!(empty_iter.len() >= 1);
}
#[test]
fn try_load_all_alias() {
let yaml = "---\na: 1";
let iter = noyalib::try_load_all(yaml).unwrap();
assert_eq!(iter.len(), 1);
}
#[test]
fn from_reader_basic() {
let yaml = b"key: value";
let v: Value = noyalib::from_reader(&yaml[..]).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "value");
}
#[test]
fn from_reader_with_config() {
let yaml = b"key: value";
let config = ParserConfig::new().max_depth(10);
let v: Value = noyalib::from_reader_with_config(&yaml[..], &config).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "value");
}
#[test]
fn from_slice_basic() {
let yaml = b"key: value";
let v: Value = noyalib::from_slice(yaml).unwrap();
assert_eq!(v["key"].as_str().unwrap(), "value");
}
#[test]
fn value_get_path() {
let yaml = "a:\n b:\n c: deep";
let v: Value = from_str(yaml).unwrap();
let c = v.get_path("a.b.c");
assert_eq!(c.unwrap().as_str().unwrap(), "deep");
}
#[test]
fn value_merge_and_merge_concat() {
let mut a: Value = from_str("x: 1\ny: 2").unwrap();
let b: Value = from_str("y: 3\nz: 4").unwrap();
a.merge(b);
assert_eq!(a["y"].as_i64().unwrap(), 3);
assert_eq!(a["z"].as_i64().unwrap(), 4);
}
#[test]
fn value_insert_and_remove() {
let mut v: Value = from_str("a: 1").unwrap();
let _ = v.insert("b", Value::from(2));
assert_eq!(v["b"].as_i64().unwrap(), 2);
let _ = v.remove("a");
assert!(v.get("a").is_none());
}
#[test]
fn serializer_config_options() {
let config = SerializerConfig::new()
.indent(4)
.flow_style(FlowStyle::Block)
.scalar_style(ScalarStyle::DoubleQuoted)
.document_start(true)
.document_end(true);
let v = Value::from("hello");
let s = to_string_with_config(&v, &config).unwrap();
assert!(s.contains("---"), "got: {s}");
}
#[test]
fn to_writer_basic() {
let v = Value::from("test");
let mut buf = Vec::new();
noyalib::to_writer(&mut buf, &v).unwrap();
assert!(!buf.is_empty());
}
#[test]
fn to_writer_with_config() {
let config = SerializerConfig::new().document_start(true);
let v = Value::from("test");
let mut buf = Vec::new();
noyalib::to_writer_with_config(&mut buf, &v, &config).unwrap();
let s = String::from_utf8(buf).unwrap();
assert!(s.contains("---"), "got: {s}");
}
#[test]
fn multi_document_serialization() {
let docs = vec![Value::from("doc1"), Value::from("doc2")];
let s = noyalib::to_string_multi(&docs).unwrap();
assert!(s.contains("doc1") && s.contains("doc2"));
}
#[test]
fn multi_document_writer() {
let docs = vec![Value::from("doc1"), Value::from("doc2")];
let mut buf = Vec::new();
noyalib::to_writer_multi(&mut buf, &docs).unwrap();
let s = String::from_utf8(buf).unwrap();
assert!(s.contains("doc1") && s.contains("doc2"));
}