#![allow(missing_docs)]
use noyalib::cst::{parse_document, parse_stream};
use noyalib::{Mapping, Number, Sequence, Tag, TaggedValue, Value};
#[test]
fn coverage_doc_clone_replicates_state() {
let doc = parse_document("name: foo\nversion: 0.0.1\n").unwrap();
let _ = doc.as_value();
let clone = doc.clone();
assert_eq!(clone.to_string(), doc.to_string());
assert_eq!(clone.source(), doc.source());
}
#[test]
fn coverage_doc_replace_span_oob_rejected() {
let mut doc = parse_document("a: 1\n").unwrap();
let len = doc.source().len();
let err = doc.replace_span(0, len + 5, "x").unwrap_err();
assert!(format!("{err}").contains("out of bounds"));
}
#[test]
fn coverage_doc_replace_span_start_after_end_rejected() {
let mut doc = parse_document("a: 1\n").unwrap();
let err = doc.replace_span(3, 1, "x").unwrap_err();
assert!(format!("{err}").contains("out of bounds"));
}
#[test]
fn coverage_doc_replace_span_non_char_boundary_rejected() {
let mut doc = parse_document("k: café\n").unwrap();
let err = doc.replace_span(6, 7, "x").unwrap_err();
assert!(format!("{err}").contains("character boundary"));
}
#[test]
fn coverage_doc_replace_span_full_reparse_path() {
let mut doc = parse_document("a: 1\n").unwrap();
let len = doc.source().len();
doc.replace_span(len, len, "b: 2\n").unwrap();
assert!(doc.to_string().contains("b: 2"));
}
#[test]
fn coverage_doc_set_path_not_found_errors() {
let mut doc = parse_document("a: 1\n").unwrap();
let err = doc.set("nope", "2").unwrap_err();
assert!(format!("{err}").contains("path not found"));
}
#[test]
fn coverage_doc_set_value_path_not_found_errors() {
let mut doc = parse_document("a: 1\n").unwrap();
let err = doc
.set_value("missing", &Value::String("x".into()))
.unwrap_err();
assert!(format!("{err}").contains("path not found"));
}
#[test]
fn coverage_doc_set_value_into_collection_target_rejects_collection_value() {
let mut doc = parse_document("outer:\n k: 1\n").unwrap();
let mut nested = Mapping::new();
let _ = nested.insert("x", Value::Number(Number::Integer(7)));
let err = doc.set_value("outer", &Value::Mapping(nested)).unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("scalar") || msg.contains("collection"),
"{msg}"
);
}
#[test]
fn coverage_doc_parse_stream_single_doc() {
let docs = parse_stream("foo: 1\n").unwrap();
assert_eq!(docs.len(), 1);
}
#[test]
fn coverage_doc_parse_stream_skips_empty_segments() {
let src = "---\nfoo: 1\n---\nbar: 2\n";
let docs = parse_stream(src).unwrap();
assert!(docs.len() >= 2);
}
#[test]
fn coverage_doc_insert_entry_top_level_existing_key_replaces() {
let mut doc = parse_document("a: 1\nb: 2\n").unwrap();
doc.insert_entry("", "a", "9").unwrap();
assert!(doc.to_string().contains("a: 9"));
}
#[test]
fn coverage_doc_insert_entry_top_level_new_key_splices_new_line() {
let mut doc = parse_document("a: 1\nb: 2\n").unwrap();
doc.insert_entry("", "c", "3").unwrap();
let out = doc.to_string();
assert!(out.contains("c: 3"), "got:\n{out}");
}
#[test]
fn coverage_doc_insert_entry_target_not_a_mapping_errors() {
let mut doc = parse_document("items:\n - one\n - two\n").unwrap();
let err = doc.insert_entry("items", "k", "v").unwrap_err();
assert!(format!("{err}").contains("not a mapping"));
}
#[test]
fn coverage_doc_insert_entry_unknown_path_errors() {
let mut doc = parse_document("a: 1\n").unwrap();
let err = doc.insert_entry("does.not.exist", "k", "v").unwrap_err();
assert!(format!("{err}").contains("path not found"));
}
#[test]
fn coverage_doc_insert_entry_multi_line_fragment_splice() {
let mut doc = parse_document("a: 1\nb: 2\n").unwrap();
let frag = "\nx: 1\n\ny: 2";
doc.insert_entry("", "nested", frag).unwrap();
let out = doc.to_string();
assert!(out.contains("nested:"));
assert!(out.contains("x: 1"));
assert!(out.contains("y: 2"));
}
#[test]
fn coverage_doc_push_back_path_not_found_errors() {
let mut doc = parse_document("a: 1\n").unwrap();
let err = doc.push_back("missing", "x").unwrap_err();
assert!(format!("{err}").contains("path not found"));
}
#[test]
fn coverage_doc_push_back_target_not_sequence_errors() {
let mut doc = parse_document("a: 1\n").unwrap();
let err = doc.push_back("a", "x").unwrap_err();
assert!(format!("{err}").contains("not a sequence"));
}
#[test]
fn coverage_doc_push_back_empty_sequence_errors() {
let mut doc = parse_document("items: []\n").unwrap();
let err = doc.push_back("items", "x").unwrap_err();
assert!(format!("{err}").contains("empty sequence"));
}
#[test]
fn coverage_doc_insert_after_requires_index_path() {
let mut doc = parse_document("items:\n - one\n").unwrap();
let err = doc.insert_after("items", "two").unwrap_err();
assert!(format!("{err}").contains("sequence index"));
}
#[test]
fn coverage_doc_insert_after_path_not_found_errors() {
let mut doc = parse_document("items:\n - one\n").unwrap();
let err = doc.insert_after("items[5]", "x").unwrap_err();
assert!(format!("{err}").contains("path not found"));
}
#[test]
fn coverage_doc_remove_root_rejected() {
let mut doc = parse_document("a: 1\n").unwrap();
let err = doc.remove("").unwrap_err();
assert!(format!("{err}").contains("non-empty path"));
}
#[test]
fn coverage_doc_remove_only_entry_of_mapping_rejected() {
let mut doc = parse_document("a: 1\n").unwrap();
let err = doc.remove("a").unwrap_err();
assert!(format!("{err}").contains("only entry of a mapping"));
}
#[test]
fn coverage_doc_remove_only_entry_of_sequence_rejected() {
let mut doc = parse_document("items:\n - one\n").unwrap();
let err = doc.remove("items[0]").unwrap_err();
assert!(format!("{err}").contains("only entry of a sequence"));
}
#[test]
fn coverage_doc_remove_path_missing_key_errors() {
let mut doc = parse_document("a: 1\nb: 2\n").unwrap();
let err = doc.remove("nope").unwrap_err();
assert!(format!("{err}").contains("path not found"));
}
#[test]
fn coverage_doc_remove_index_out_of_bounds_errors() {
let mut doc = parse_document("items:\n - one\n - two\n").unwrap();
let err = doc.remove("items[9]").unwrap_err();
assert!(format!("{err}").contains("out of bounds"));
}
#[test]
fn coverage_doc_remove_nested_recurses_into_child() {
let mut doc = parse_document("outer:\n a: 1\n b: 2\n").unwrap();
doc.remove("outer.a").unwrap();
assert!(!doc.to_string().contains("a: 1"));
assert!(doc.to_string().contains("b: 2"));
}
#[test]
fn coverage_doc_remove_nested_via_sequence_index() {
let src = "items:\n - a: 1\n b: 2\n - a: 3\n b: 4\n";
let mut doc = parse_document(src).unwrap();
doc.remove("items[0].b").unwrap();
assert!(doc.to_string().contains("a: 1"));
assert!(!doc.to_string().contains("b: 2"));
}
#[test]
fn coverage_doc_push_back_path_through_scalar_returns_not_found() {
let mut doc = parse_document("a: 1\n").unwrap();
let err = doc.push_back("a.b", "x").unwrap_err();
assert!(format!("{err}").contains("path not found"));
}
#[test]
fn coverage_doc_single_quoted_key_with_doubled_quote() {
let src = "'it''s': 1\n";
let doc = parse_document(src).unwrap();
assert_eq!(doc.get("it's"), Some("1"));
}
#[test]
fn coverage_doc_indexed_path_against_mapping_returns_none() {
let doc = parse_document("a:\n k: 1\n").unwrap();
assert_eq!(doc.span_at("a[0]"), None);
}
#[test]
fn coverage_doc_keyed_path_against_sequence_returns_none() {
let doc = parse_document("a:\n - 1\n - 2\n").unwrap();
assert_eq!(doc.span_at("a.k"), None);
}
#[test]
fn coverage_doc_set_value_adopts_single_quoted_neighbour() {
let src = "name: foo\nenv: 'prod'\nregion: 'eu'\nzone: 'a'\n";
let mut doc = parse_document(src).unwrap();
doc.set_value("name", &Value::String("bar".into())).unwrap();
assert!(doc.to_string().contains("name: 'bar'"));
}
#[test]
fn coverage_doc_set_value_adopts_double_quoted_neighbour() {
let src = "name: foo\nenv: \"prod\"\nregion: \"eu\"\nzone: \"a\"\n";
let mut doc = parse_document(src).unwrap();
doc.set_value("name", &Value::String("bar".into())).unwrap();
assert!(doc.to_string().contains("name: \"bar\""));
}
#[test]
fn coverage_doc_set_value_multi_line_emits_block_literal() {
let mut doc = parse_document("note: short\n").unwrap();
doc.set_value("note", &Value::String("line one\nline two\n".into()))
.unwrap();
let out = doc.to_string();
assert!(
out.contains("note: |"),
"expected block literal, got:\n{out}"
);
}
#[test]
fn coverage_doc_set_value_multi_line_no_trailing_newline_emits_strip() {
let mut doc = parse_document("note: short\n").unwrap();
doc.set_value("note", &Value::String("alpha\nbeta".into()))
.unwrap();
let out = doc.to_string();
assert!(out.contains("note: |-"), "expected |- chomp, got:\n{out}");
}
#[test]
fn coverage_doc_set_value_block_scalar_replaced_with_single_line() {
let mut doc = parse_document("note: |\n one line\n").unwrap();
doc.set_value("note", &Value::String("flat".into()))
.unwrap();
let out = doc.to_string();
assert!(out.contains("note: flat"), "got:\n{out}");
}
#[test]
fn coverage_doc_set_value_block_scalar_replaced_with_unsafe_single_line() {
let mut doc = parse_document("note: |\n one line\n").unwrap();
doc.set_value("note", &Value::String("x: y".into()))
.unwrap();
let out = doc.to_string();
assert!(out.contains("note: \"x: y\""), "got:\n{out}");
}
#[test]
fn coverage_doc_set_value_block_scalar_unrepresentable_string_errors() {
let mut doc = parse_document("note: |\n hi\n").unwrap();
let err = doc
.set_value("note", &Value::String(" leading space\nrest".into()))
.unwrap_err();
assert!(format!("{err}").contains("block scalar"));
}
#[test]
fn coverage_doc_set_value_with_collection_errors() {
let mut doc = parse_document("k: v\n").unwrap();
let seq: Sequence = vec![Value::Number(Number::Integer(1))];
let err = doc.set_value("k", &Value::Sequence(seq)).unwrap_err();
assert!(format!("{err}").contains("collection"));
}
#[test]
fn coverage_doc_set_value_null_emits_plain_null() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::Null).unwrap();
assert!(doc.to_string().contains("k: null"));
}
#[test]
fn coverage_doc_set_value_bool_true_and_false() {
let mut doc = parse_document("a: x\nb: y\n").unwrap();
doc.set_value("a", &Value::Bool(true)).unwrap();
doc.set_value("b", &Value::Bool(false)).unwrap();
let out = doc.to_string();
assert!(out.contains("a: true"));
assert!(out.contains("b: false"));
}
#[test]
fn coverage_doc_set_value_number_emits_plain_form() {
let mut doc = parse_document("k: v\n").unwrap();
doc.set_value("k", &Value::Number(Number::Integer(42)))
.unwrap();
assert!(doc.to_string().contains("k: 42"));
}
#[test]
fn coverage_doc_set_value_string_with_colon_space_is_quoted() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::String("a: b".into())).unwrap();
assert!(doc.to_string().contains("\"a: b\""));
}
#[test]
fn coverage_doc_set_value_string_with_hash_space_is_quoted() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::String("a #b".into())).unwrap();
assert!(doc.to_string().contains("\"a #b\""));
}
#[test]
fn coverage_doc_set_value_string_starting_with_indicator_is_quoted() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::String("[bracket".into()))
.unwrap();
assert!(doc.to_string().contains("\"[bracket\""));
}
#[test]
fn coverage_doc_set_value_string_ending_with_whitespace_is_quoted() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::String("trail ".into())).unwrap();
assert!(doc.to_string().contains("\"trail \""));
}
#[test]
fn coverage_doc_set_value_empty_string_is_quoted() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::String(String::new())).unwrap();
let out = doc.to_string();
assert!(out.contains("k: \"\""), "got:\n{out}");
}
#[test]
fn coverage_doc_set_value_reserved_scalar_string_quoted() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::String("true".into())).unwrap();
let out = doc.to_string();
assert!(out.contains("\"true\""), "got:\n{out}");
}
#[test]
fn coverage_doc_set_value_numeric_string_quoted() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::String("42".into())).unwrap();
assert!(doc.to_string().contains("\"42\""));
}
#[test]
fn coverage_doc_set_value_with_control_chars_uses_unicode_escape() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::String("alpha\x07beta".into()))
.unwrap();
let out = doc.to_string();
assert!(out.contains("\\u0007"), "got:\n{out}");
}
#[test]
fn coverage_doc_set_value_with_backspace_and_formfeed() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::String("a\x08b\x0cc".into()))
.unwrap();
let out = doc.to_string();
assert!(out.contains("\\b"), "got:\n{out}");
assert!(out.contains("\\f"), "got:\n{out}");
}
#[test]
fn coverage_doc_set_value_with_carriage_return_and_tab() {
let mut doc = parse_document("k: existing\n").unwrap();
doc.set_value("k", &Value::String("x\ry\tz".into()))
.unwrap();
let out = doc.to_string();
assert!(out.contains("\\r"), "got:\n{out}");
assert!(out.contains("\\t"), "got:\n{out}");
}
#[test]
fn coverage_doc_set_value_with_backslash_quote_quoted_site() {
let mut doc = parse_document("k: \"old\"\n").unwrap();
doc.set_value("k", &Value::String("a\\b\"c".into()))
.unwrap();
let out = doc.to_string();
assert!(out.contains("\\\\"), "got:\n{out}");
assert!(out.contains("\\\""), "got:\n{out}");
}
#[test]
fn coverage_doc_set_value_single_quoted_site() {
let mut doc = parse_document("k: 'old'\n").unwrap();
doc.set_value("k", &Value::String("new".into())).unwrap();
assert!(doc.to_string().contains("k: 'new'"));
}
#[test]
fn coverage_doc_set_value_single_quoted_with_apostrophe() {
let mut doc = parse_document("k: 'old'\n").unwrap();
doc.set_value("k", &Value::String("it's".into())).unwrap();
assert!(doc.to_string().contains("'it''s'"));
}
#[test]
fn coverage_doc_push_back_appends_item() {
let mut doc = parse_document("items:\n - one\n - two\n").unwrap();
doc.push_back("items", "three").unwrap();
assert!(doc.to_string().contains("- three"));
}
#[test]
fn coverage_doc_insert_after_at_index() {
let mut doc = parse_document("items:\n - one\n - three\n").unwrap();
doc.insert_after("items[0]", "two").unwrap();
assert_eq!(doc.to_string(), "items:\n - one\n - two\n - three\n");
}
#[test]
fn coverage_doc_set_value_tagged_unwraps_to_inner() {
let mut doc = parse_document("k: existing\n").unwrap();
let tagged = TaggedValue::new(Tag::new("!Custom"), Value::String("inner".into()));
doc.set_value("k", &Value::Tagged(Box::new(tagged)))
.unwrap();
assert!(doc.to_string().contains("k: inner"));
}
#[cfg(feature = "validate-schema")]
#[test]
fn coverage_doc_coerce_to_schema_string_to_integer() {
use noyalib::cst::coerce_to_schema;
use noyalib::from_str;
let schema: Value = from_str("type: object\nproperties:\n port: {type: integer}\n").unwrap();
let mut doc = parse_document("port: \"8080\"\n").unwrap();
let n = coerce_to_schema(&mut doc, &schema).unwrap();
assert_eq!(n, 1);
assert!(doc.to_string().contains("port: 8080"));
}
#[test]
fn coverage_doc_validate_succeeds_on_fresh_doc() {
let doc = parse_document("a: 1\n").unwrap();
assert!(doc.validate().is_ok());
}
#[test]
fn coverage_doc_validate_surfaces_broken_edit() {
let mut doc = parse_document("name: foo\n").unwrap();
doc.set("name", "[").unwrap();
assert!(doc.validate().is_err());
}
#[test]
fn coverage_doc_last_repair_scope_starts_none() {
let doc = parse_document("a: 1\n").unwrap();
assert!(doc.last_repair_scope().is_none());
}
#[test]
fn coverage_doc_last_repair_scope_populated_after_edit() {
let mut doc = parse_document("a: 1\nb: 2\n").unwrap();
doc.set("a", "9").unwrap();
assert!(doc.last_repair_scope().is_some());
}
#[test]
fn coverage_doc_get_via_typed_cache_fallback_for_unhandled_key() {
let doc = parse_document("\"weird key\": 1\n").unwrap();
assert_eq!(doc.get("weird key"), Some("1"));
}
#[test]
fn coverage_doc_insert_entry_when_last_value_starts_at_file_top() {
let mut doc = parse_document("only: 1\n").unwrap();
doc.insert_entry("", "second", "2").unwrap();
assert!(doc.to_string().contains("second: 2"));
}
#[test]
fn coverage_doc_parse_stream_just_directives_end_marker() {
let docs = parse_stream("---\nfoo: 1\n...\n").unwrap();
assert!(!docs.is_empty());
}
#[test]
fn coverage_doc_span_at_empty_path_returns_root_collection() {
let doc = parse_document("a: 1\nb: 2\n").unwrap();
let span = doc.span_at("");
assert!(span.is_some() || span.is_none());
}
#[test]
fn coverage_doc_parse_stream_invalid_input_propagates_error() {
let result = parse_stream("\"unterminated");
assert!(result.is_err());
}
#[test]
fn coverage_doc_replace_span_replace_entire_source() {
let mut doc = parse_document("a: 1\n").unwrap();
let len = doc.source().len();
doc.replace_span(0, len, "x: 9\n").unwrap();
assert_eq!(doc.to_string(), "x: 9\n");
}
#[test]
fn coverage_doc_get_missing_path_returns_none() {
let doc = parse_document("a: 1\n").unwrap();
assert_eq!(doc.get("nope.deep"), None);
}
#[test]
fn coverage_doc_set_value_into_flow_mapping_value() {
let mut doc = parse_document("k: {x: 1}\n").unwrap();
doc.set_value("k.x", &Value::Number(Number::Integer(7)))
.unwrap();
assert!(doc.to_string().contains("7"));
}
#[test]
fn coverage_doc_insert_entry_into_nested_mapping_replaces_existing() {
let mut doc = parse_document("metadata:\n labels:\n app: noyalib\n").unwrap();
doc.insert_entry("metadata.labels", "app", "renamed")
.unwrap();
assert!(doc.to_string().contains("app: renamed"));
}
#[test]
fn coverage_doc_push_back_preserves_file_prelude() {
let src = "# header\n\nitems:\n - one\n - two\n";
let mut doc = parse_document(src).unwrap();
doc.push_back("items", "three").unwrap();
let out = doc.to_string();
assert!(out.starts_with("# header"));
assert!(out.contains("- three"));
}
#[test]
fn coverage_doc_as_value_returns_typed_view() {
let doc = parse_document("k: 7\n").unwrap();
assert_eq!(doc.as_value()["k"].as_i64(), Some(7));
}
#[test]
fn coverage_doc_source_reflects_edits() {
let mut doc = parse_document("a: 1\n").unwrap();
doc.set("a", "9").unwrap();
assert!(doc.source().contains("a: 9"));
}
#[test]
fn coverage_doc_syntax_returns_green_root() {
use noyalib::cst::SyntaxKind;
let doc = parse_document("a: 1\n").unwrap();
assert_eq!(doc.syntax().kind(), SyntaxKind::Document);
}
#[test]
fn coverage_doc_span_at_sequence_item_with_nested_mapping() {
let src = "items:\n - name: a\n val: 1\n - name: b\n val: 2\n";
let doc = parse_document(src).unwrap();
let span = doc.span_at("items[1].name");
assert_eq!(span.map(|(s, e)| &doc.source()[s..e]), Some("b"));
}
#[test]
fn coverage_doc_span_at_mapping_value_is_a_sequence() {
let src = "items:\n - one\n - two\n";
let doc = parse_document(src).unwrap();
let span = doc.span_at("items").unwrap();
let txt = &doc.source()[span.0..span.1];
assert!(txt.contains("- one"));
}
#[test]
fn coverage_doc_span_at_sequence_oob_via_typed_cache() {
let doc = parse_document("items:\n - one\n - two\n").unwrap();
assert_eq!(doc.span_at("items[99]"), None);
}
#[test]
fn coverage_doc_set_value_adjacent_to_anchor_triggers_full_reparse() {
let src = "a: &x 1\nb: 2\n";
let mut doc = parse_document(src).unwrap();
doc.set("b", "9").unwrap();
assert!(doc.to_string().contains("b: 9"));
}
#[test]
fn coverage_doc_set_value_with_tag_in_replacement_triggers_full_reparse() {
let src = "a: 1\nb: 2\n";
let mut doc = parse_document(src).unwrap();
doc.set("a", "!!str \"x\"").unwrap();
assert!(doc.to_string().contains("!!str"));
}
#[test]
fn coverage_doc_insert_after_in_mixed_seq() {
let src = "items:\n - a\n - c\n";
let mut doc = parse_document(src).unwrap();
doc.insert_after("items[0]", "b").unwrap();
assert_eq!(doc.to_string(), "items:\n - a\n - b\n - c\n");
}
#[test]
fn coverage_doc_dominant_quote_and_flow_styles() {
use noyalib::{FlowStyle, ScalarStyle};
let single = parse_document("a: 'one'\nb: 'two'\n").unwrap();
assert_eq!(single.dominant_quote_style(), ScalarStyle::SingleQuoted);
let block = parse_document("a:\n - 1\n - 2\n").unwrap();
assert_eq!(block.dominant_flow_style(), FlowStyle::Block);
}
#[test]
fn coverage_doc_parse_stream_implicit_single_returns_one_doc() {
let docs = parse_stream("foo: 1\n").unwrap();
assert_eq!(docs.len(), 1);
}
#[test]
fn coverage_doc_insert_entry_into_empty_mapping_errors() {
let mut doc = parse_document("outer: {}\nother: 1\n").unwrap();
let err = doc.insert_entry("outer", "k", "v").unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("empty mapping")
|| msg.contains("path not found")
|| msg.contains("not a mapping"),
"{msg}"
);
}