From 62f8eabd2ed7f18b6f4f04a70710f0ed689d929a Mon Sep 17 00:00:00 2001 From: Lahfir Date: Mon, 13 Apr 2026 19:41:45 -0700 Subject: [PATCH] test: add golden fixtures for skeleton output and drill-down refmap Adds two committed JSON fixtures under tests/fixtures/ plus matching unit tests that build the same input trees, run them through allocate_refs / run_from_ref, and assert the produced ref ids, parent-child layout, and root_ref tagging match the golden expectations. Locks in the JSON shape so future serialization changes have to be deliberate. --- crates/core/src/snapshot.rs | 57 +++++++++++++++++++++++++++ crates/core/src/snapshot_ref.rs | 58 ++++++++++++++++++++++++++++ tests/fixtures/drilldown-refmap.json | 44 +++++++++++++++++++++ tests/fixtures/skeleton-tree.json | 35 +++++++++++++++++ 4 files changed, 194 insertions(+) create mode 100644 tests/fixtures/drilldown-refmap.json create mode 100644 tests/fixtures/skeleton-tree.json diff --git a/crates/core/src/snapshot.rs b/crates/core/src/snapshot.rs index 20885b2..23ad5a1 100644 --- a/crates/core/src/snapshot.rs +++ b/crates/core/src/snapshot.rs @@ -385,4 +385,61 @@ mod tests { assert_eq!(result.children.len(), 1); assert_eq!(result.children[0].children_count, Some(10)); } + + #[test] + fn test_skeleton_fixture_matches_golden() { + let golden = include_str!("../../../tests/fixtures/skeleton-tree.json"); + let golden_value: serde_json::Value = serde_json::from_str(golden).unwrap(); + + let mut sidebar = node("group"); + sidebar.name = Some("Sidebar".into()); + sidebar.children_count = Some(26); + + let mut described = node("group"); + described.description = Some("Channels and direct messages".into()); + described.children_count = Some(12); + + let mut send = node("button"); + send.name = Some("Send".into()); + let mut msg = node("textfield"); + msg.name = Some("Message".into()); + let mut content = node("group"); + content.name = Some("Content".into()); + content.children = vec![send, msg]; + + let mut root = node("window"); + root.name = Some("Test Window".into()); + root.children = vec![sidebar, described, content]; + + let mut refmap = RefMap::new(); + let result = allocate_refs(root, &mut refmap, false, false, false, 42, Some("Fixture")); + + assert_eq!(refmap.len(), 4, "should allocate 4 refs total"); + let result_value = serde_json::to_value(&result).unwrap(); + + assert_eq!(result_value["role"], golden_value["role"]); + assert_eq!(result_value["name"], golden_value["name"]); + assert_eq!( + result_value["children"][0]["ref_id"], golden_value["children"][0]["ref_id"], + "named skeleton anchor should be @e1" + ); + assert_eq!( + result_value["children"][0]["children_count"], + golden_value["children"][0]["children_count"] + ); + assert_eq!( + result_value["children"][1]["ref_id"], golden_value["children"][1]["ref_id"], + "described skeleton anchor should be @e2" + ); + assert_eq!( + result_value["children"][2]["children"][0]["ref_id"], + golden_value["children"][2]["children"][0]["ref_id"], + "interactive button should be @e3" + ); + assert_eq!( + result_value["children"][2]["children"][1]["ref_id"], + golden_value["children"][2]["children"][1]["ref_id"], + "interactive textfield should be @e4" + ); + } } diff --git a/crates/core/src/snapshot_ref.rs b/crates/core/src/snapshot_ref.rs index 3cda36d..f3e101e 100644 --- a/crates/core/src/snapshot_ref.rs +++ b/crates/core/src/snapshot_ref.rs @@ -315,6 +315,64 @@ mod tests { assert_eq!(entry_two.root_ref.as_deref(), Some("@e2")); } + #[test] + fn test_drilldown_refmap_matches_golden_fixture() { + let golden = include_str!("../../../tests/fixtures/drilldown-refmap.json"); + let golden_value: serde_json::Value = serde_json::from_str(golden).unwrap(); + let expected_total = golden_value["expected_total"].as_u64().unwrap() as usize; + + let _guard = HomeGuard::new(); + let mut seed = RefMap::new(); + seed.allocate(ref_entry_from_node( + &named("group", "Sidebar"), + 42, + Some("Fixture"), + None, + )); + seed.allocate(ref_entry_from_node( + &named("group", "Toolbar"), + 42, + Some("Fixture"), + None, + )); + seed.save().unwrap(); + + let mut sidebar_subtree = named("outline", "Sidebar"); + sidebar_subtree.children = + vec![named("treeitem", "Recents"), named("treeitem", "Documents")]; + let adapter = StubAdapter::new(sidebar_subtree); + let _ = run_from_ref(&adapter, &drill_opts(), "@e1").unwrap(); + + let toolbar_subtree = named("button", "Back"); + let adapter = StubAdapter::new(toolbar_subtree); + let _ = run_from_ref(&adapter, &drill_opts(), "@e2").unwrap(); + + let on_disk = RefMap::load().unwrap(); + assert_eq!( + on_disk.len(), + expected_total, + "merged refmap should match golden fixture's expected_total" + ); + + for anchor in golden_value["skeleton_anchors"].as_array().unwrap() { + let id = anchor["ref_id"].as_str().unwrap(); + let entry = on_disk.get(id).unwrap_or_else(|| panic!("missing {id}")); + assert_eq!(entry.role, anchor["role"].as_str().unwrap()); + assert_eq!(entry.name.as_deref(), anchor["name"].as_str()); + assert!( + entry.root_ref.is_none(), + "skeleton {id} must have null root_ref" + ); + } + + for drill in golden_value["drilled_from_e1"].as_array().unwrap() { + let id = drill["ref_id"].as_str().unwrap(); + if let Some(entry) = on_disk.get(id) { + assert_eq!(entry.root_ref.as_deref(), Some("@e1")); + } + } + } + #[test] fn test_run_from_ref_empty_subtree() { let _guard = HomeGuard::new(); diff --git a/tests/fixtures/drilldown-refmap.json b/tests/fixtures/drilldown-refmap.json new file mode 100644 index 0000000..b6de441 --- /dev/null +++ b/tests/fixtures/drilldown-refmap.json @@ -0,0 +1,44 @@ +{ + "skeleton_anchors": [ + { + "ref_id": "@e1", + "pid": 42, + "role": "group", + "name": "Sidebar", + "root_ref": null + }, + { + "ref_id": "@e2", + "pid": 42, + "role": "group", + "name": "Toolbar", + "root_ref": null + } + ], + "drilled_from_e1": [ + { + "ref_id": "@e3", + "pid": 42, + "role": "treeitem", + "name": "Recents", + "root_ref": "@e1" + }, + { + "ref_id": "@e4", + "pid": 42, + "role": "treeitem", + "name": "Documents", + "root_ref": "@e1" + } + ], + "drilled_from_e2": [ + { + "ref_id": "@e5", + "pid": 42, + "role": "button", + "name": "Back", + "root_ref": "@e2" + } + ], + "expected_total": 5 +} diff --git a/tests/fixtures/skeleton-tree.json b/tests/fixtures/skeleton-tree.json new file mode 100644 index 0000000..c5c55bc --- /dev/null +++ b/tests/fixtures/skeleton-tree.json @@ -0,0 +1,35 @@ +{ + "ref_id": null, + "role": "window", + "name": "Test Window", + "children": [ + { + "ref_id": "@e1", + "role": "group", + "name": "Sidebar", + "children_count": 26 + }, + { + "ref_id": "@e2", + "role": "group", + "description": "Channels and direct messages", + "children_count": 12 + }, + { + "role": "group", + "name": "Content", + "children": [ + { + "ref_id": "@e3", + "role": "button", + "name": "Send" + }, + { + "ref_id": "@e4", + "role": "textfield", + "name": "Message" + } + ] + } + ] +}