crate::ix!();
#[tracing::instrument(level = "trace", skip(skel))]
pub fn promote_missing_child_nodes_with_defaults(skel: &mut StringSkeleton) {
trace!("Scanning StringSkeleton for aggregator/dispatch children without map entries...");
let mut inserted = Vec::new();
let all_missing_names: Vec<String> = {
let mut missing = Vec::new();
for (map_key, node_data) in skel.map().iter() {
match node_data {
StringSkeletonNode::Aggregate { children, .. } => {
for (child_name, _child_spec) in children {
if !skel.map().contains_key(child_name) {
trace!(
"Aggregate node '{}' references child '{}' which is NOT in the map!",
map_key,
child_name
);
missing.push(child_name.clone());
}
}
}
StringSkeletonNode::Dispatch { children, .. } => {
for (child_name, _child_spec) in children {
if !skel.map().contains_key(child_name) {
trace!(
"Dispatch node '{}' references child '{}' which is NOT in the map!",
map_key,
child_name
);
missing.push(child_name.clone());
}
}
}
StringSkeletonNode::LeafHolder { .. } => {}
}
}
missing
};
for child_name in all_missing_names {
if skel.map().contains_key(&child_name) {
trace!(
"Child '{}' was already inserted or existed; skipping duplication.",
child_name
);
continue;
}
let synthetic_leaf = StringSkeletonNode::LeafHolder {
name: child_name.clone(),
ordering: None,
n_leaves: 9,
capstone: false,
};
skel.map_mut().insert(child_name.clone(), synthetic_leaf);
inserted.push(child_name.clone());
info!(
"Promoted missing child '{}' to a default LeafHolder(n_leaves=9, capstone=false).",
child_name
);
}
if inserted.is_empty() {
trace!("No missing child nodes were found. Nothing was promoted.");
} else {
trace!(
"Finished promoting {} aggregator/dispatch children as new LeafHolder nodes: {:?}",
inserted.len(),
inserted
);
}
}
#[cfg(test)]
mod test_promote_missing_child_nodes {
use super::*;
#[traced_test]
fn test_promote_children() {
let mut map = std::collections::HashMap::new();
let child_spec = AggregateChildSpecBuilder::default()
.psome_likelihood(100)
.optional(false)
.build()
.unwrap();
let mut children = std::collections::HashMap::new();
children.insert("MissingChild".to_string(), child_spec);
let agg_node = StringSkeletonNode::Aggregate {
name: "ParentAggregate".to_string(),
ordering: None,
children,
};
map.insert("Parent".to_string(), agg_node);
let skel = StringSkeletonBuilder::default()
.map(map)
.build()
.unwrap();
let stripped: StrippedStringSkeleton = (&skel).into();
let found_child = stripped
.items()
.iter()
.find(|item| item.name() == "MissingChild");
assert!(
found_child.is_some(),
"We expect 'MissingChild' to have been promoted, but it's missing in the stripped output!"
);
let child_node = found_child.unwrap();
assert_eq!(child_node.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*child_node.n_leaves(), Some(9));
assert_eq!(*child_node.capstone(), Some(false));
info!("test_promote_children passed! 'MissingChild' was successfully promoted to a LeafHolder node with default parameters.");
}
#[traced_test]
fn test_no_missing_children() {
trace!("Building a skeleton with no missing aggregator/dispatch children...");
let child_a = StringSkeletonNode::LeafHolder {
name: "ChildA".to_string(),
ordering: None,
n_leaves: 7,
capstone: false,
};
let mut dispatch_kids = HashMap::new();
dispatch_kids.insert("GrandChild".to_string(),
DispatchChildSpecBuilder::default()
.branch_selection_likelihood(42)
.build()
.unwrap()
);
let child_b = StringSkeletonNode::Dispatch {
name: "ChildB".to_string(),
ordering: None,
children: dispatch_kids,
};
let grandchild = StringSkeletonNode::LeafHolder {
name: "GrandChild".to_string(),
ordering: None,
n_leaves: 5,
capstone: true,
};
let mut agg_kids = HashMap::new();
let agg_spec = AggregateChildSpecBuilder::default()
.psome_likelihood(100)
.optional(false)
.build()
.unwrap();
agg_kids.insert("ChildA".to_string(), agg_spec.clone());
agg_kids.insert("ChildB".to_string(), agg_spec);
let root_agg = StringSkeletonNode::Aggregate {
name: "Root".to_string(),
ordering: None,
children: agg_kids,
};
let mut map = HashMap::new();
map.insert("Root".to_string(), root_agg);
map.insert("ChildA".to_string(), child_a);
map.insert("ChildB".to_string(), child_b);
map.insert("GrandChild".to_string(), grandchild);
let sk = StringSkeletonBuilder::default()
.map(map)
.build()
.unwrap();
info!("Converting skeleton => stripped...");
let stripped: StrippedStringSkeleton = (&sk).into();
assert_eq!(stripped.items().len(), 4, "Should have all 4 nodes, no promotions.");
let root_node = stripped.items().iter().find(|n| n.name() == "Root").unwrap();
assert_eq!(root_node.kind(), &SkillTreeNodeKind::Aggregate);
assert_eq!(root_node.children().as_ref().unwrap().len(), 2, "Root must have 2 children references: ChildA, ChildB");
let any_promotions = stripped.items().iter().any(|n| *n.n_leaves() == Some(9));
assert!(!any_promotions, "Expected zero promotions for a perfect skeleton reference set.");
info!("test_no_missing_children => PASSED");
}
#[traced_test]
fn test_single_missing_child_aggregate() {
trace!("Building skeleton => aggregator references 'PromotedChild' not in map...");
let mut agg_kids = HashMap::new();
let csp = AggregateChildSpecBuilder::default()
.psome_likelihood(55)
.optional(true)
.build()
.unwrap();
agg_kids.insert("PromotedChild".to_string(), csp);
let root_agg = StringSkeletonNode::Aggregate {
name: "RootAggregate".to_string(),
ordering: None,
children: agg_kids,
};
let existing_leaf = StringSkeletonNode::LeafHolder {
name: "ExistingLeaf".to_string(),
ordering: None,
n_leaves: 6,
capstone: false,
};
let mut map = HashMap::new();
map.insert("RootAggregate".to_string(), root_agg);
map.insert("ExistingLeaf".to_string(), existing_leaf);
let sk = StringSkeletonBuilder::default()
.map(map)
.build()
.unwrap();
info!("Convert skeleton => stripped => expect 1 promotion");
let stripped: StrippedStringSkeleton = (&sk).into();
assert_eq!(stripped.items().len(), 3, "We expect 3 total nodes after promotion.");
let promoted = stripped.items().iter().find(|x| x.name() == "PromotedChild");
assert!(promoted.is_some(), "The aggregator child 'PromotedChild' must be promoted to a new LeafHolder");
let promoted_nd = promoted.unwrap();
assert_eq!(promoted_nd.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*promoted_nd.n_leaves(), Some(9), "Promoted child should have default 9 leaves");
assert_eq!(*promoted_nd.capstone(), Some(false), "Promoted child => default capstone=false");
let root_nd = stripped.items().iter().find(|x| x.name() == "RootAggregate").unwrap();
let children = root_nd.children().as_ref().unwrap();
let child_spec = children.iter().find(|c| c.name() == "PromotedChild").unwrap();
assert_eq!(*child_spec.optional(), true);
assert_eq!(*child_spec.probability(), 55);
info!("test_single_missing_child_aggregate => PASSED");
}
#[traced_test]
fn test_single_missing_child_dispatch() {
trace!("Skeleton => dispatch references 'PromotedDispatchChild' not in the map...");
let mut dispatch_kids = HashMap::new();
dispatch_kids.insert("PromotedDispatchChild".to_string(),
DispatchChildSpecBuilder::default()
.branch_selection_likelihood(17)
.build()
.unwrap()
);
let root_disp = StringSkeletonNode::Dispatch {
name: "RootDispatch".to_string(),
ordering: None,
children: dispatch_kids,
};
let mut map = HashMap::new();
map.insert("RootDispatch".to_string(), root_disp);
let sk = StringSkeletonBuilder::default()
.map(map)
.build()
.unwrap();
let stripped: StrippedStringSkeleton = (&sk).into();
assert_eq!(stripped.items().len(), 2, "We expect RootDispatch + the new promoted child.");
let promoted = stripped.items().iter().find(|x| x.name() == "PromotedDispatchChild");
assert!(promoted.is_some(), "Should have promoted 'PromotedDispatchChild'.");
let pr = promoted.unwrap();
assert_eq!(pr.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*pr.n_leaves(), Some(9));
assert_eq!(*pr.capstone(), Some(false));
let root_node = stripped.items().iter().find(|n| n.name() == "RootDispatch").unwrap();
assert_eq!(root_node.kind(), &SkillTreeNodeKind::Dispatch);
let cd = root_node.children().as_ref().unwrap();
assert_eq!(cd.len(), 1);
assert_eq!(cd[0].name(), "PromotedDispatchChild");
assert!(!cd[0].optional(), "By default, dispatch child is set optional=false in the stripped aggregator logic.");
assert_eq!(*cd[0].probability(), 17);
info!("test_single_missing_child_dispatch => PASSED");
}
#[traced_test]
fn test_multiple_missing_children() {
trace!("Skeleton => aggregator references 'ChildX','ChildY','ChildZ' => but only ChildX is in the map. Y,Z are missing...");
let mut agg_map = HashMap::new();
agg_map.insert("ChildX".to_string(),
AggregateChildSpecBuilder::default()
.psome_likelihood(100)
.optional(false)
.build()
.unwrap()
);
agg_map.insert("ChildY".to_string(),
AggregateChildSpecBuilder::default()
.psome_likelihood(77)
.optional(true)
.build()
.unwrap()
);
agg_map.insert("ChildZ".to_string(),
AggregateChildSpecBuilder::default()
.psome_likelihood(33)
.optional(true)
.build()
.unwrap()
);
let root_agg = StringSkeletonNode::Aggregate {
name: "RootAgg".to_string(),
ordering: None,
children: agg_map,
};
let child_x = StringSkeletonNode::LeafHolder {
name: "ChildX".to_string(),
ordering: None,
n_leaves: 4,
capstone: false,
};
let mut map = HashMap::new();
map.insert("RootAgg".to_string(), root_agg);
map.insert("ChildX".to_string(), child_x);
let sk = StringSkeletonBuilder::default()
.map(map)
.build()
.unwrap();
let stripped: StrippedStringSkeleton = (&sk).into();
assert_eq!(stripped.items().len(), 4);
let child_y = stripped.items().iter().find(|x| x.name() == "ChildY").unwrap();
assert_eq!(child_y.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*child_y.n_leaves(), Some(9));
assert_eq!(*child_y.capstone(), Some(false));
let child_z = stripped.items().iter().find(|x| x.name() == "ChildZ").unwrap();
assert_eq!(child_z.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*child_z.n_leaves(), Some(9));
assert_eq!(*child_z.capstone(), Some(false));
let root_node = stripped.items().iter().find(|x| x.name() == "RootAgg").unwrap();
let kids = root_node.children().as_ref().unwrap();
assert_eq!(kids.len(), 3);
let ky = kids.iter().find(|c| c.name() == "ChildY").unwrap();
assert_eq!(*ky.probability(), 77);
assert!(ky.optional());
let kz = kids.iter().find(|c| c.name() == "ChildZ").unwrap();
assert_eq!(*kz.probability(), 33);
assert!(kz.optional());
info!("test_multiple_missing_children => PASSED");
}
#[traced_test]
fn test_duplicate_missing_child() {
trace!("We have 'Parent1' => aggregator referencing 'SharedChild', 'Parent2' => aggregator referencing 'SharedChild'. 'SharedChild' not in map => should be promoted once.");
let mut p1_kids = HashMap::new();
let csp1 = AggregateChildSpecBuilder::default()
.psome_likelihood(100)
.optional(false)
.build()
.unwrap();
p1_kids.insert("SharedChild".to_string(), csp1);
let parent1 = StringSkeletonNode::Aggregate {
name: "Parent1".to_string(),
ordering: None,
children: p1_kids,
};
let mut p2_kids = HashMap::new();
let csp2 = AggregateChildSpecBuilder::default()
.psome_likelihood(50)
.optional(true)
.build()
.unwrap();
p2_kids.insert("SharedChild".to_string(), csp2);
let parent2 = StringSkeletonNode::Aggregate {
name: "Parent2".to_string(),
ordering: None,
children: p2_kids,
};
let mut map = HashMap::new();
map.insert("Parent1".to_string(), parent1);
map.insert("Parent2".to_string(), parent2);
let sk = StringSkeletonBuilder::default().map(map).build().unwrap();
let stripped: StrippedStringSkeleton = (&sk).into();
assert_eq!(stripped.items().len(), 3);
let shared = stripped.items().iter().find(|x| x.name() == "SharedChild");
assert!(shared.is_some(), "We do expect 'SharedChild' to be promoted once.");
let sc = shared.unwrap();
assert_eq!(sc.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*sc.n_leaves(), Some(9));
let p1_stripped = stripped.items().iter().find(|x| x.name() == "Parent1").unwrap();
let p2_stripped = stripped.items().iter().find(|x| x.name() == "Parent2").unwrap();
assert_eq!(p1_stripped.children().as_ref().unwrap().len(), 1);
assert_eq!(p2_stripped.children().as_ref().unwrap().len(), 1);
assert_eq!(p1_stripped.children().as_ref().unwrap()[0].name(), "SharedChild");
assert_eq!(p2_stripped.children().as_ref().unwrap()[0].name(), "SharedChild");
info!("test_duplicate_missing_child => PASSED");
}
#[traced_test]
fn test_mixed_aggregate_dispatch() {
trace!("We construct a skeleton with a parent aggregator referencing {{ExistingLeaf,MissingLeaf,ChildDispatch}}, ChildDispatch references {{ExistingLeaf2,MissingDispatchLeaf}} etc.");
let mut agg_kids = HashMap::new();
let agg_childspec1 = AggregateChildSpecBuilder::default()
.psome_likelihood(100)
.optional(false)
.build()
.unwrap();
let agg_childspec2 = AggregateChildSpecBuilder::default()
.psome_likelihood(60)
.optional(true)
.build()
.unwrap();
agg_kids.insert("ExistingLeaf".to_string(), agg_childspec1.clone());
agg_kids.insert("MissingLeaf".to_string(), agg_childspec2.clone());
agg_kids.insert("ChildDispatch".to_string(), agg_childspec2);
let root_agg = StringSkeletonNode::Aggregate {
name: "RootAgg".to_string(),
ordering: None,
children: agg_kids,
};
let existing_leaf = StringSkeletonNode::LeafHolder {
name: "ExistingLeaf".to_string(),
ordering: None,
n_leaves: 4,
capstone: false,
};
let mut disp_kids = HashMap::new();
disp_kids.insert("ExistingLeaf2".to_string(),
DispatchChildSpecBuilder::default()
.branch_selection_likelihood(11)
.build()
.unwrap()
);
disp_kids.insert("MissingDispatchLeaf".to_string(),
DispatchChildSpecBuilder::default()
.branch_selection_likelihood(22)
.build()
.unwrap()
);
let child_dispatch = StringSkeletonNode::Dispatch {
name: "ChildDispatch".to_string(),
ordering: None,
children: disp_kids,
};
let existing_leaf2 = StringSkeletonNode::LeafHolder {
name: "ExistingLeaf2".to_string(),
ordering: None,
n_leaves: 6,
capstone: false,
};
let mut map = HashMap::new();
map.insert("RootAgg".to_string(), root_agg);
map.insert("ExistingLeaf".to_string(), existing_leaf);
map.insert("ChildDispatch".to_string(), child_dispatch);
map.insert("ExistingLeaf2".to_string(), existing_leaf2);
let sk = StringSkeletonBuilder::default().map(map).build().unwrap();
let stripped: StrippedStringSkeleton = (&sk).into();
assert_eq!(stripped.items().len(), 6);
let missing_leaf = stripped.items().iter().find(|x| x.name() == "MissingLeaf");
assert!(missing_leaf.is_some());
let ml = missing_leaf.unwrap();
assert_eq!(ml.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*ml.n_leaves(), Some(9));
assert_eq!(*ml.capstone(), Some(false));
let missing_disp = stripped.items().iter().find(|x| x.name() == "MissingDispatchLeaf");
assert!(missing_disp.is_some());
let md = missing_disp.unwrap();
assert_eq!(md.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*md.n_leaves(), Some(9));
assert_eq!(*md.capstone(), Some(false));
let root_agg_nd = stripped.items().iter().find(|x| x.name() == "RootAgg").unwrap();
assert_eq!(root_agg_nd.kind(), &SkillTreeNodeKind::Aggregate);
let rkids = root_agg_nd.children().as_ref().unwrap();
assert_eq!(rkids.len(), 3);
let re = rkids.iter().find(|c| c.name() == "ExistingLeaf").unwrap();
assert_eq!(*re.optional(), false);
assert_eq!(*re.probability(), 100);
let rm = rkids.iter().find(|c| c.name() == "MissingLeaf").unwrap();
assert_eq!(*rm.optional(), true);
assert_eq!(*rm.probability(), 60);
let rc = rkids.iter().find(|c| c.name() == "ChildDispatch").unwrap();
assert_eq!(*rc.optional(), true);
assert_eq!(*rc.probability(), 60);
let child_disp_nd = stripped.items().iter().find(|x| x.name() == "ChildDispatch").unwrap();
assert_eq!(child_disp_nd.kind(), &SkillTreeNodeKind::Dispatch);
let cdk = child_disp_nd.children().as_ref().unwrap();
assert_eq!(cdk.len(), 2);
let ed = cdk.iter().find(|ch| ch.name() == "ExistingLeaf2").unwrap();
assert!(!ed.optional());
assert_eq!(*ed.probability(), 11);
let md2 = cdk.iter().find(|ch| ch.name() == "MissingDispatchLeaf").unwrap();
assert!(!md2.optional());
assert_eq!(*md2.probability(), 22);
info!("test_mixed_aggregate_dispatch => PASSED");
}
#[traced_test]
fn test_recursive_aggregators_missing_at_end() {
trace!("Skeleton => A => aggregator => children=[B], B => aggregator => children=[C], C => aggregator => children=[MissingLeafC]. The rest is normal. This ensures no infinite loops or partial references.");
let mut c_kids = HashMap::new();
let csp = AggregateChildSpecBuilder::default()
.psome_likelihood(100)
.optional(false)
.build()
.unwrap();
c_kids.insert("MissingLeafC".to_string(), csp);
let node_c = StringSkeletonNode::Aggregate {
name: "C".to_string(),
ordering: None,
children: c_kids,
};
let mut b_kids = HashMap::new();
b_kids.insert("C".to_string(), csp);
let node_b = StringSkeletonNode::Aggregate {
name: "B".to_string(),
ordering: None,
children: b_kids,
};
let mut a_kids = HashMap::new();
a_kids.insert("B".to_string(), csp);
let node_a = StringSkeletonNode::Aggregate {
name: "A".to_string(),
ordering: None,
children: a_kids,
};
let mut map = HashMap::new();
map.insert("A".to_string(), node_a);
map.insert("B".to_string(), node_b);
map.insert("C".to_string(), node_c);
let sk = StringSkeletonBuilder::default().map(map).build().unwrap();
let stripped: StrippedStringSkeleton = (&sk).into();
assert_eq!(stripped.items().len(), 4);
let mlc = stripped.items().iter().find(|x| x.name() == "MissingLeafC");
assert!(mlc.is_some(), "We expect 'MissingLeafC' to be promoted => LeafHolder.");
let mlc_node = mlc.unwrap();
assert_eq!(mlc_node.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*mlc_node.n_leaves(), Some(9));
assert_eq!(*mlc_node.capstone(), Some(false));
let a_str = stripped.items().iter().find(|x| x.name() == "A").unwrap();
assert_eq!(a_str.kind(), &SkillTreeNodeKind::Aggregate);
assert_eq!(a_str.children().as_ref().unwrap().len(), 1);
assert_eq!(a_str.children().as_ref().unwrap()[0].name(), "B");
let b_str = stripped.items().iter().find(|x| x.name() == "B").unwrap();
assert_eq!(b_str.kind(), &SkillTreeNodeKind::Aggregate);
assert_eq!(b_str.children().as_ref().unwrap().len(), 1);
assert_eq!(b_str.children().as_ref().unwrap()[0].name(), "C");
let c_str = stripped.items().iter().find(|x| x.name() == "C").unwrap();
assert_eq!(c_str.kind(), &SkillTreeNodeKind::Aggregate);
let cch = c_str.children().as_ref().unwrap();
assert_eq!(cch.len(), 1);
assert_eq!(cch[0].name(), "MissingLeafC");
info!("test_recursive_aggregators_missing_at_end => PASSED");
}
#[traced_test]
fn test_missing_child_named_root() {
trace!("Aggregator => references child 'root' which is not in map => must be promoted to a LeafHolder. We do not treat 'root' as a reserved name here.");
let mut children_map = HashMap::new();
let csp = AggregateChildSpecBuilder::default()
.psome_likelihood(78)
.optional(true)
.build()
.unwrap();
children_map.insert("root".to_string(), csp);
let node_agg = StringSkeletonNode::Aggregate {
name: "MyAgg".to_string(),
ordering: None,
children: children_map,
};
let mut map = HashMap::new();
map.insert("MyAgg".to_string(), node_agg);
let sk = StringSkeletonBuilder::default().map(map).build().unwrap();
let stripped: StrippedStringSkeleton = (&sk).into();
assert_eq!(stripped.items().len(), 2);
let rnode = stripped.items().iter().find(|x| x.name() == "root");
assert!(rnode.is_some(), "Should have promoted the aggregator child named 'root'.");
let rr = rnode.unwrap();
assert_eq!(rr.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*rr.n_leaves(), Some(9));
assert_eq!(*rr.capstone(), Some(false));
let agg = stripped.items().iter().find(|x| x.name() == "MyAgg").unwrap();
let c = agg.children().as_ref().unwrap();
assert_eq!(c.len(), 1);
let c0 = &c[0];
assert_eq!(c0.name(), "root");
assert_eq!(*c0.optional(), true);
assert_eq!(*c0.probability(), 78);
info!("test_missing_child_named_root => PASSED");
}
#[traced_test]
fn test_stripped_conversion_coherence() {
trace!("We'll build a small skeleton with 2 aggregator references that are missing, and 1 dispatch child that is missing, plus 2 existing references. Then ensure the final shape is coherent overall.");
let leafholder_existing = StringSkeletonNode::LeafHolder {
name: "ExistingLeafOverall".to_string(),
ordering: None,
n_leaves: 3,
capstone: false,
};
let mut agg_kids = HashMap::new();
let csp = AggregateChildSpecBuilder::default()
.psome_likelihood(99)
.optional(true)
.build()
.unwrap();
agg_kids.insert("ChildA".to_string(), csp.clone());
agg_kids.insert("ChildB".to_string(), csp.clone());
let root_agg = StringSkeletonNode::Aggregate {
name: "AggTop".to_string(),
ordering: None,
children: agg_kids,
};
let child_a = StringSkeletonNode::LeafHolder {
name: "ChildA".to_string(),
ordering: Some(SubBranchOrdering::DifficultyAscending),
n_leaves: 2,
capstone: false,
};
let mut another_agg_kids = HashMap::new();
another_agg_kids.insert("ChildC".to_string(), csp.clone());
let another_agg = StringSkeletonNode::Aggregate {
name: "AnotherAgg".to_string(),
ordering: None,
children: another_agg_kids,
};
let mut dsp_map = HashMap::new();
dsp_map.insert("DispatchLeaf".to_string(),
DispatchChildSpecBuilder::default()
.branch_selection_likelihood(50)
.build()
.unwrap()
);
dsp_map.insert("ExistingLeafOverall".to_string(),
DispatchChildSpecBuilder::default()
.branch_selection_likelihood(50)
.build()
.unwrap()
);
let node_dispatch = StringSkeletonNode::Dispatch {
name: "DispatchNode".to_string(),
ordering: None,
children: dsp_map,
};
let mut big_map = HashMap::new();
big_map.insert("AggTop".to_string(), root_agg);
big_map.insert("ChildA".to_string(), child_a);
big_map.insert("AnotherAgg".to_string(), another_agg);
big_map.insert("DispatchNode".to_string(), node_dispatch);
big_map.insert("ExistingLeafOverall".to_string(), leafholder_existing);
let sk = StringSkeletonBuilder::default().map(big_map).build().unwrap();
let stripped: StrippedStringSkeleton = (&sk).into();
assert_eq!(stripped.items().len(), 8);
let b = stripped.items().iter().find(|x| x.name() == "ChildB").unwrap();
let c = stripped.items().iter().find(|x| x.name() == "ChildC").unwrap();
let dl = stripped.items().iter().find(|x| x.name() == "DispatchLeaf").unwrap();
assert_eq!(b.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(*b.n_leaves(), Some(9));
assert_eq!(c.kind(), &SkillTreeNodeKind::LeafHolder);
assert_eq!(dl.kind(), &SkillTreeNodeKind::LeafHolder);
let top = stripped.items().iter().find(|x| x.name() == "AggTop").unwrap();
assert_eq!(top.kind(), &SkillTreeNodeKind::Aggregate);
let topch = top.children().as_ref().unwrap();
assert_eq!(topch.len(), 2);
let ca = topch.iter().find(|cc| cc.name() == "ChildA").unwrap();
assert_eq!(*ca.probability(), 99);
assert!(ca.optional());
let cb = topch.iter().find(|cc| cc.name() == "ChildB").unwrap();
assert_eq!(*cb.probability(), 99);
assert!(cb.optional());
let aagg = stripped.items().iter().find(|x| x.name() == "AnotherAgg").unwrap();
assert_eq!(aagg.kind(), &SkillTreeNodeKind::Aggregate);
let ak = aagg.children().as_ref().unwrap();
assert_eq!(ak.len(), 1);
let cc2 = &ak[0];
assert_eq!(cc2.name(), "ChildC");
assert_eq!(*cc2.probability(), 99);
let dsp = stripped.items().iter().find(|x| x.name() == "DispatchNode").unwrap();
assert_eq!(dsp.kind(), &SkillTreeNodeKind::Dispatch);
let dch = dsp.children().as_ref().unwrap();
assert_eq!(dch.len(), 2);
let dlr = dch.iter().find(|ch| ch.name() == "DispatchLeaf").unwrap();
assert_eq!(*dlr.probability(), 50);
let exo = dch.iter().find(|ch| ch.name() == "ExistingLeafOverall").unwrap();
assert_eq!(*exo.probability(), 50);
info!("test_stripped_conversion_coherence => PASSED");
}
}