#![cfg(all(feature = "serialize", feature = "deserialize"))]
mod tests {
use serde_json::Value;
use serde_saphyr::Error;
use serde_saphyr::from_str_with_options;
use serde_saphyr::options::{DuplicateKeyPolicy, MergeKeyPolicy};
use std::collections::BTreeMap;
fn parse_first_wins_map(yaml: &str) -> Result<BTreeMap<String, Value>, Error> {
let opts = serde_saphyr::options! {
duplicate_keys: DuplicateKeyPolicy::FirstWins,
};
from_str_with_options::<BTreeMap<String, Value>>(yaml, opts)
}
fn parse_first_wins_map_rejecting_merge_keys(
yaml: &str,
) -> Result<BTreeMap<String, Value>, Error> {
let opts = serde_saphyr::options! {
duplicate_keys: DuplicateKeyPolicy::FirstWins,
merge_keys: MergeKeyPolicy::Error,
};
from_str_with_options::<BTreeMap<String, Value>>(yaml, opts)
}
#[test]
fn first_wins_skips_scalar_value_of_duplicate_key() {
let yaml = r#"
a: 1
a: 2
b: 3
"#;
let map = parse_first_wins_map(yaml).expect("parse ok");
assert_eq!(map.get("a"), Some(&Value::from(1)));
assert_eq!(map.get("b"), Some(&Value::from(3)));
assert_eq!(map.len(), 2);
}
#[test]
fn first_wins_skips_sequence_value_of_duplicate_key() {
let yaml = r#"
a: [1, 2]
a: [9, 9, 9] # should be skipped entirely
b: 3
"#;
let map = parse_first_wins_map(yaml).expect("parse ok");
assert_eq!(map.get("a"), Some(&Value::from(vec![1, 2])));
assert_eq!(map.get("b"), Some(&Value::from(3)));
assert_eq!(map.len(), 2);
}
#[test]
fn first_wins_skips_mapping_value_of_duplicate_key_including_nested() {
let yaml = r#"
a:
x: 1
y:
z: 2
a:
X: 9 # whole mapping must be skipped
Y: { Z: 10 } # including nested mapping
b: 42
"#;
let map = parse_first_wins_map(yaml).expect("parse ok");
let a = map.get("a").expect("key a present");
assert_eq!(
a,
&serde_json::json!({
"x": 1,
"y": { "z": 2 }
})
);
assert_eq!(map.get("b"), Some(&Value::from(42)));
assert_eq!(map.len(), 2);
}
#[test]
fn first_wins_handles_duplicate_in_middle_and_keeps_following_keys() {
let yaml = r#"
k1: start
dup:
inner: [1, 2, { q: 9 }]
dup:
should: be-ignored
and: "this too"
k2: end
"#;
let map = parse_first_wins_map(yaml).expect("parse ok");
assert_eq!(map.get("k1"), Some(&Value::from("start")));
assert_eq!(
map.get("dup"),
Some(&serde_json::json!({
"inner": [1, 2, { "q": 9 }]
}))
);
assert_eq!(map.get("k2"), Some(&Value::from("end")));
assert_eq!(map.len(), 3);
}
#[test]
fn first_wins_skips_nullish_scalar_duplicate_as_one_node_only() {
let yaml = r#"
a: { keep: true }
a: null
b: ok
"#;
let map = parse_first_wins_map(yaml).expect("parse ok");
assert_eq!(map.get("a"), Some(&serde_json::json!({ "keep": true })));
assert_eq!(map.get("b"), Some(&Value::from("ok")));
assert_eq!(map.len(), 2);
}
#[test]
fn first_wins_rejects_merge_key_inside_skipped_duplicate_value() {
let yaml = r#"
a: { keep: true }
a:
nested:
<<: { admin: true }
b: ok
"#;
let err = parse_first_wins_map_rejecting_merge_keys(yaml)
.expect_err("merge key in skipped duplicate value must be rejected");
assert!(matches!(
err.without_snippet(),
Error::MergeKeyNotAllowed { .. }
));
}
#[test]
fn first_wins_allows_quoted_merge_key_inside_skipped_duplicate_value() {
let yaml = r#"
a: { keep: true }
a:
nested:
"<<": { admin: true }
b: ok
"#;
let map = parse_first_wins_map_rejecting_merge_keys(yaml).expect("quoted key is literal");
assert_eq!(map.get("a"), Some(&serde_json::json!({ "keep": true })));
assert_eq!(map.get("b"), Some(&Value::from("ok")));
assert_eq!(map.len(), 2);
}
}