#![expect(
clippy::panic,
clippy::expect_used,
clippy::indexing_slicing,
clippy::wildcard_enum_match_arm,
reason = "test code"
)]
use rlsp_yaml_parser::loader::LoaderBuilder;
use rlsp_yaml_parser::{LoadError, Node, Schema};
fn scalar_tag(input: &str, schema: Schema) -> Option<String> {
let docs = LoaderBuilder::new()
.schema(schema)
.build()
.load(input)
.expect("load failed");
assert_eq!(docs.len(), 1, "expected exactly one document");
match &docs[0].root {
Node::Scalar { tag, .. } => tag.clone(),
other => panic!("expected root scalar, got: {other:?}"),
}
}
fn mapping_tag(input: &str, schema: Schema) -> Option<String> {
let docs = LoaderBuilder::new()
.schema(schema)
.build()
.load(input)
.expect("load failed");
assert_eq!(docs.len(), 1, "expected exactly one document");
match &docs[0].root {
Node::Mapping { tag, .. } => tag.clone(),
other => panic!("expected root mapping, got: {other:?}"),
}
}
fn sequence_tag(input: &str, schema: Schema) -> Option<String> {
let docs = LoaderBuilder::new()
.schema(schema)
.build()
.load(input)
.expect("load failed");
assert_eq!(docs.len(), 1, "expected exactly one document");
match &docs[0].root {
Node::Sequence { tag, .. } => tag.clone(),
other => panic!("expected root sequence, got: {other:?}"),
}
}
fn load_with_schema(
input: &str,
schema: Schema,
) -> Result<Vec<rlsp_yaml_parser::Document<rlsp_yaml_parser::Span>>, rlsp_yaml_parser::LoadError> {
LoaderBuilder::new().schema(schema).build().load(input)
}
#[test]
fn core_plain_integer_resolves_to_int() {
let tag = scalar_tag("42\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:int"));
}
#[test]
fn core_plain_bool_resolves_to_bool() {
let tag = scalar_tag("true\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:bool"));
}
#[test]
fn core_plain_string_resolves_to_str() {
let tag = scalar_tag("hello\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn core_plain_float_resolves_to_float() {
let tag = scalar_tag("3.14\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:float"));
}
#[test]
fn core_plain_null_lowercase_resolves_to_null() {
let tag = scalar_tag("null\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:null"));
}
#[test]
fn core_plain_tilde_resolves_to_null() {
let tag = scalar_tag("~\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:null"));
}
#[test]
fn core_plain_empty_resolves_to_null() {
let tag = scalar_tag("---\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:null"));
}
#[test]
fn core_double_quoted_integer_resolves_to_str() {
let tag = scalar_tag("\"42\"\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn core_block_literal_resolves_to_str() {
let tag = scalar_tag("|\n hello\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn core_explicit_str_tag_on_integer_value_preserved() {
let tag = scalar_tag("!!str 42\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn core_explicit_int_tag_on_quoted_string_preserved() {
let tag = scalar_tag("!!int \"123\"\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:int"));
}
#[test]
fn core_bare_excl_on_plain_scalar_resolves_to_str() {
let tag = scalar_tag("! 42\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn core_bare_excl_on_bool_value_resolves_to_str() {
let tag = scalar_tag("! true\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn core_bare_excl_on_sequence_resolves_to_seq() {
let tag = sequence_tag("! [a, b]\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:seq"));
}
#[test]
fn core_bare_excl_on_mapping_resolves_to_map() {
let tag = mapping_tag("! {a: b}\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:map"));
}
#[test]
fn core_untagged_mapping_resolves_to_map() {
let tag = mapping_tag("a: 1\nb: 2\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:map"));
}
#[test]
fn core_untagged_sequence_resolves_to_seq() {
let tag = sequence_tag("- a\n- b\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:seq"));
}
#[test]
fn core_nested_structure_all_tags_resolved() {
let yaml = "numbers:\n - 1\n - 3.14\n - hello\n - true\n";
let docs = load_with_schema(yaml, Schema::Core).expect("load failed");
assert_eq!(docs.len(), 1);
let Node::Mapping {
entries,
tag: map_tag,
..
} = &docs[0].root
else {
panic!("expected root mapping");
};
assert_eq!(map_tag.as_deref(), Some("tag:yaml.org,2002:map"));
let (_key, value) = &entries[0];
let Node::Sequence {
items,
tag: seq_tag,
..
} = value
else {
panic!("expected sequence value");
};
assert_eq!(seq_tag.as_deref(), Some("tag:yaml.org,2002:seq"));
let expected_tags = [
"tag:yaml.org,2002:int",
"tag:yaml.org,2002:float",
"tag:yaml.org,2002:str",
"tag:yaml.org,2002:bool",
];
assert_eq!(items.len(), expected_tags.len());
for (item, expected) in items.iter().zip(expected_tags.iter()) {
let Node::Scalar { tag, .. } = item else {
panic!("expected scalar item");
};
assert_eq!(
tag.as_deref(),
Some(*expected),
"wrong tag for item {item:?}"
);
}
}
#[test]
fn core_octal_resolves_to_int() {
let tag = scalar_tag("0o777\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:int"));
}
#[test]
fn core_hex_resolves_to_int() {
let tag = scalar_tag("0x1A\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:int"));
}
#[test]
fn core_inf_resolves_to_float() {
let tag = scalar_tag(".inf\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:float"));
}
#[test]
fn core_nan_resolves_to_float() {
let tag = scalar_tag(".nan\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:float"));
}
#[test]
fn core_positive_signed_int_resolves_to_int() {
let tag = scalar_tag("+12\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:int"));
}
#[test]
fn core_negative_zero_resolves_to_int() {
let tag = scalar_tag("-0\n", Schema::Core);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:int"));
}
#[test]
fn load_default_plain_integer_resolves_to_int() {
let docs = rlsp_yaml_parser::loader::load("42\n").expect("load failed");
let Node::Scalar { tag, .. } = &docs[0].root else {
panic!("expected scalar");
};
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:int"));
}
#[test]
fn load_default_plain_null_resolves_to_null() {
let docs = rlsp_yaml_parser::loader::load("null\n").expect("load failed");
let Node::Scalar { tag, .. } = &docs[0].root else {
panic!("expected scalar");
};
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:null"));
}
#[test]
fn load_default_mapping_resolves_to_map() {
let docs = rlsp_yaml_parser::loader::load("a: 1\n").expect("load failed");
let Node::Mapping { tag, .. } = &docs[0].root else {
panic!("expected mapping");
};
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:map"));
}
#[test]
fn loader_builder_default_uses_core_schema() {
let docs = LoaderBuilder::new()
.build()
.load("42\n")
.expect("load failed");
let Node::Scalar { tag, .. } = &docs[0].root else {
panic!("expected scalar");
};
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:int"));
}
#[test]
fn loader_builder_explicit_core_same_as_default() {
let input = "true\n";
let via_default = LoaderBuilder::new()
.build()
.load(input)
.expect("default failed");
let via_explicit = LoaderBuilder::new()
.schema(Schema::Core)
.build()
.load(input)
.expect("explicit failed");
assert_eq!(via_default, via_explicit);
}
#[test]
fn load_default_empty_document_resolves_to_null() {
let docs = rlsp_yaml_parser::loader::load("---\n").expect("load failed");
let Node::Scalar { tag, .. } = &docs[0].root else {
panic!("expected scalar");
};
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:null"));
}
#[test]
fn json_plain_int_resolves_to_int() {
let tag = scalar_tag("42\n", Schema::Json);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:int"));
}
#[test]
fn json_plain_bool_resolves_to_bool() {
let tag = scalar_tag("true\n", Schema::Json);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:bool"));
}
#[test]
fn json_plain_null_resolves_to_null() {
let tag = scalar_tag("null\n", Schema::Json);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:null"));
}
#[test]
fn json_plain_float_resolves_to_float() {
let tag = scalar_tag("3.14\n", Schema::Json);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:float"));
}
#[test]
fn json_plain_false_resolves_to_bool() {
let tag = scalar_tag("false\n", Schema::Json);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:bool"));
}
#[test]
fn json_plain_zero_resolves_to_int() {
let tag = scalar_tag("0\n", Schema::Json);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:int"));
}
#[test]
fn json_plain_string_returns_unresolved_scalar_error() {
let result = load_with_schema("hello\n", Schema::Json);
assert!(
matches!(
result,
Err(rlsp_yaml_parser::LoadError::UnresolvedScalar { .. })
),
"expected UnresolvedScalar, got: {result:?}"
);
}
#[test]
fn json_octal_plain_returns_unresolved_scalar_error() {
let result = load_with_schema("0o777\n", Schema::Json);
assert!(
matches!(
result,
Err(rlsp_yaml_parser::LoadError::UnresolvedScalar { .. })
),
"expected UnresolvedScalar for octal, got: {result:?}"
);
}
#[test]
fn json_plus_prefix_int_returns_unresolved_scalar_error() {
let result = load_with_schema("+42\n", Schema::Json);
assert!(
matches!(
result,
Err(rlsp_yaml_parser::LoadError::UnresolvedScalar { .. })
),
"expected UnresolvedScalar for +42, got: {result:?}"
);
}
#[test]
fn json_tilde_returns_unresolved_scalar_error() {
let result = load_with_schema("~\n", Schema::Json);
assert!(
matches!(
result,
Err(rlsp_yaml_parser::LoadError::UnresolvedScalar { .. })
),
"expected UnresolvedScalar for ~, got: {result:?}"
);
}
#[test]
fn json_uppercase_bool_returns_unresolved_scalar_error() {
let result = load_with_schema("TRUE\n", Schema::Json);
assert!(
matches!(
result,
Err(rlsp_yaml_parser::LoadError::UnresolvedScalar { .. })
),
"expected UnresolvedScalar for TRUE, got: {result:?}"
);
}
#[test]
fn json_inf_notation_returns_unresolved_scalar_error() {
let result = load_with_schema(".inf\n", Schema::Json);
assert!(
matches!(
result,
Err(rlsp_yaml_parser::LoadError::UnresolvedScalar { .. })
),
"expected UnresolvedScalar for .inf, got: {result:?}"
);
}
#[test]
fn json_nan_notation_returns_unresolved_scalar_error() {
let result = load_with_schema(".nan\n", Schema::Json);
assert!(
matches!(
result,
Err(rlsp_yaml_parser::LoadError::UnresolvedScalar { .. })
),
"expected UnresolvedScalar for .nan, got: {result:?}"
);
}
#[test]
fn json_double_quoted_string_resolves_to_str() {
let tag = scalar_tag("\"hello\"\n", Schema::Json);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn json_untagged_sequence_resolves_to_seq() {
let tag = sequence_tag("[1, 2]\n", Schema::Json);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:seq"));
}
#[test]
fn json_untagged_mapping_resolves_to_map() {
let tag = mapping_tag("{\"a\": 1}\n", Schema::Json);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:map"));
}
#[test]
fn json_empty_document_returns_unresolved_scalar_error() {
let result = load_with_schema("---\n", Schema::Json);
assert!(
matches!(
result,
Err(rlsp_yaml_parser::LoadError::UnresolvedScalar { .. })
),
"expected UnresolvedScalar for empty doc under JSON schema, got: {result:?}"
);
}
#[test]
fn json_negative_zero_resolves_to_float() {
let tag = scalar_tag("-0\n", Schema::Json);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:float"));
}
#[test]
fn failsafe_plain_int_resolves_to_str() {
let tag = scalar_tag("42\n", Schema::Failsafe);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn failsafe_plain_bool_resolves_to_str() {
let tag = scalar_tag("true\n", Schema::Failsafe);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn failsafe_quoted_string_resolves_to_str() {
let tag = scalar_tag("\"hello\"\n", Schema::Failsafe);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn failsafe_plain_null_resolves_to_str() {
let tag = scalar_tag("null\n", Schema::Failsafe);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn failsafe_untagged_sequence_resolves_to_seq() {
let tag = sequence_tag("- a\n- b\n", Schema::Failsafe);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:seq"));
}
#[test]
fn failsafe_untagged_mapping_resolves_to_map() {
let tag = mapping_tag("a: 1\n", Schema::Failsafe);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:map"));
}
#[test]
fn failsafe_bare_excl_on_scalar_resolves_to_str() {
let tag = scalar_tag("! 42\n", Schema::Failsafe);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:str"));
}
#[test]
fn failsafe_bare_excl_on_sequence_resolves_to_seq() {
let tag = sequence_tag("! [a, b]\n", Schema::Failsafe);
assert_eq!(tag.as_deref(), Some("tag:yaml.org,2002:seq"));
}
#[test]
fn json_unresolved_scalar_propagates_from_nested_sequence_item() {
let result = load_with_schema("[1, hello, 3]\n", Schema::Json);
assert!(
matches!(
result,
Err(rlsp_yaml_parser::LoadError::UnresolvedScalar { .. })
),
"expected UnresolvedScalar propagating from sequence, got: {result:?}"
);
}
#[test]
fn json_unresolved_scalar_propagates_from_nested_mapping_value() {
let result = load_with_schema("{\"key\": hello}\n", Schema::Json);
assert!(
matches!(
result,
Err(rlsp_yaml_parser::LoadError::UnresolvedScalar { .. })
),
"expected UnresolvedScalar propagating from mapping value, got: {result:?}"
);
}
#[test]
fn json_unresolved_scalar_display_message_is_exact() {
let err = load_with_schema("hello\n", Schema::Json).expect_err("expected error");
assert_eq!(
err.to_string(),
"JSON schema: plain scalar does not match any type pattern"
);
}
#[test]
fn json_unresolved_scalar_pos_reflects_actual_position() {
let err =
load_with_schema("42\n---\nhello\n", Schema::Json).expect_err("expected UnresolvedScalar");
let LoadError::UnresolvedScalar { pos, .. } = err else {
panic!("expected UnresolvedScalar, got: {err:?}");
};
assert!(
pos.line >= 3,
"pos.line should be >= 3 for a scalar on line 3, got: {pos:?}"
);
}
#[test]
fn json_unresolved_scalar_value_field_contains_scalar_content() {
let err = load_with_schema("hello\n", Schema::Json).expect_err("expected error");
let LoadError::UnresolvedScalar { value, .. } = err else {
panic!("expected UnresolvedScalar, got: {err:?}");
};
assert_eq!(value, "hello");
}
#[test]
fn json_unresolved_scalar_truncates_long_value() {
let long_value = "a".repeat(200);
let input = format!("{long_value}\n");
let err = load_with_schema(&input, Schema::Json).expect_err("expected error");
let LoadError::UnresolvedScalar { value, .. } = err else {
panic!("expected UnresolvedScalar");
};
assert!(
value.ends_with("..."),
"truncated value should end with '...', got: {value:?}"
);
assert_eq!(
value.len(),
131,
"truncated value should be 131 chars, got len={}",
value.len()
);
}