Feature/insight chat branching #109
@@ -2894,6 +2894,37 @@ mod tests {
|
|||||||
assert_eq!(f.node_id, u, "divergence node is the forked user turn");
|
assert_eq!(f.node_id, u, "divergence node is the forked user turn");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn render_tree_path_nested_forks_only_emits_at_divergence() {
|
||||||
|
// Regression: with .take() instead of .clone(), fork_info should only
|
||||||
|
// appear at the actual divergence point, not propagate downstream.
|
||||||
|
// Structure:
|
||||||
|
// u (user "q1")
|
||||||
|
// ├── a1 (assistant "answer 1")
|
||||||
|
// └── a2 (assistant "answer 2")
|
||||||
|
// ├── q2 (user "q2")
|
||||||
|
// └── r2 (assistant "reply 2") <- active leaf
|
||||||
|
//
|
||||||
|
// Expected fork_info: [None, Some, None, None]
|
||||||
|
// (only a2 has fork info since it's the divergence from a1)
|
||||||
|
let mut store = ChatHistoryStore::empty();
|
||||||
|
let u = store.append_node(None, ChatMessage::user("q1"));
|
||||||
|
let _a1 = store.append_node(Some(u), assistant_text("answer 1"));
|
||||||
|
let a2 = store.append_node(Some(u), assistant_text("answer 2"));
|
||||||
|
let q2 = store.append_node(Some(a2), ChatMessage::user("q2"));
|
||||||
|
let r2 = store.append_node(Some(q2), assistant_text("reply 2"));
|
||||||
|
store.active_leaf_id = r2;
|
||||||
|
|
||||||
|
let path = store.path_to_leaf(r2).expect("path to active leaf");
|
||||||
|
let (rendered, _turns, _node_ids, fork_info) = render_tree_path(&store, &path);
|
||||||
|
|
||||||
|
assert_eq!(rendered.len(), 4, "user + asst + user + asst");
|
||||||
|
assert!(fork_info[0].is_none(), "initial user has no fork");
|
||||||
|
assert!(fork_info[1].is_some(), "a2 is the fork point");
|
||||||
|
assert!(fork_info[2].is_none(), "q2 should NOT carry fork forward");
|
||||||
|
assert!(fork_info[3].is_none(), "r2 should NOT carry fork forward");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rewind_strips_assistant_and_tool_scaffolding() {
|
fn rewind_strips_assistant_and_tool_scaffolding() {
|
||||||
// Rendered: [user1, asst1, user2, asst2] → cut at rendered index 3
|
// Rendered: [user1, asst1, user2, asst2] → cut at rendered index 3
|
||||||
|
|||||||
@@ -743,4 +743,49 @@ mod tests {
|
|||||||
assert_eq!(store.nodes[1].parent_id, Some(parent));
|
assert_eq!(store.nodes[1].parent_id, Some(parent));
|
||||||
assert_eq!(store.nodes[1].id, child);
|
assert_eq!(store.nodes[1].id, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn branch_options_handles_nested_forks() {
|
||||||
|
// Regression: verify sub-fork display when there are multiple forks.
|
||||||
|
// Structure:
|
||||||
|
// root (user "q1")
|
||||||
|
// ├── a1 (assistant "answer 1")
|
||||||
|
// └── a2 (assistant "answer 2")
|
||||||
|
// ├── q2 (user "q2")
|
||||||
|
// │ ├── r1 (assistant "reply 1")
|
||||||
|
// │ └── r2 (assistant "reply 2") <- active leaf
|
||||||
|
//
|
||||||
|
// branch_options_at(root) should return [a1, a2] (2 branches)
|
||||||
|
// branch_options_at(q2) should return [r1, r2] (2 sub-branches)
|
||||||
|
let mut store = ChatHistoryStore::empty();
|
||||||
|
let root = store.append_node(None, mk_msg("user", "q1"));
|
||||||
|
let a1 = store.append_node(Some(root), mk_msg("assistant", "answer 1"));
|
||||||
|
let a2 = store.append_node(Some(root), mk_msg("assistant", "answer 2"));
|
||||||
|
let q2 = store.append_node(Some(a2), mk_msg("user", "q2"));
|
||||||
|
let r1 = store.append_node(Some(q2), mk_msg("assistant", "reply 1"));
|
||||||
|
let r2 = store.append_node(Some(q2), mk_msg("assistant", "reply 2"));
|
||||||
|
store.active_leaf_id = r2;
|
||||||
|
|
||||||
|
// Root fork: should show [a1, a2's subtree]
|
||||||
|
let opts = store.branch_options_at(root, r2);
|
||||||
|
assert_eq!(opts.len(), 2);
|
||||||
|
assert_eq!(opts[0].id, a1);
|
||||||
|
assert_eq!(opts[0].position, Some(1));
|
||||||
|
assert_eq!(opts[0].snippet, "answer 1");
|
||||||
|
// a2's subtree includes r2, so the rep should be r2 (preferred leaf)
|
||||||
|
assert_eq!(opts[1].id, r2);
|
||||||
|
assert_eq!(opts[1].position, Some(2));
|
||||||
|
assert_eq!(opts[1].snippet, "answer 2");
|
||||||
|
|
||||||
|
// Sub-fork at q2: should show [r1, r2]
|
||||||
|
let opts = store.branch_options_at(q2, r2);
|
||||||
|
assert_eq!(opts.len(), 2);
|
||||||
|
assert_eq!(opts[0].id, r1);
|
||||||
|
assert_eq!(opts[0].position, Some(1));
|
||||||
|
assert_eq!(opts[0].snippet, "reply 1");
|
||||||
|
assert_eq!(opts[1].id, r2);
|
||||||
|
assert_eq!(opts[1].position, Some(2));
|
||||||
|
assert_eq!(opts[1].snippet, "reply 2");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user