//! L10 tests for the pure `Workspace::move_tab_to_new_window` operation //! (ARCHITECTURE ยง10): a tab is *moved*, never duplicated; an emptied source //! window is dropped; an active moved tab hands activity back to a sibling. use domain::{ LayoutNode, LayoutTree, LayoutError, LeafCell, NodeId, ProjectId, Tab, TabId, Window, WindowId, Workspace, }; use uuid::Uuid; fn tid(n: u128) -> TabId { TabId::from_uuid(Uuid::from_u128(n)) } fn wid(n: u128) -> WindowId { WindowId::from_uuid(Uuid::from_u128(n)) } fn leaf_tree() -> LayoutTree { LayoutTree::new(LayoutNode::Leaf(LeafCell { id: NodeId::from_uuid(Uuid::from_u128(900)), session: None, agent: None, })) } fn tab(n: u128) -> Tab { Tab { id: tid(n), project_id: ProjectId::from_uuid(Uuid::from_u128(1000 + n)), layout: leaf_tree(), } } /// Count how many windows contain a tab with the given id. fn occurrences(ws: &Workspace, tab: TabId) -> usize { ws.windows .iter() .filter(|w| w.tabs.iter().any(|t| t.id == tab)) .count() } #[test] fn move_tab_from_multi_tab_window_keeps_source_and_creates_new() { let src = Window::new(wid(1), vec![tab(1), tab(2)], tid(1)).unwrap(); let ws = Workspace { windows: vec![src] }; let next = ws.move_tab_to_new_window(tid(1), wid(99)).unwrap(); assert_eq!(next.windows.len(), 2, "source kept + new window"); // The moved tab appears exactly once (moved, not duplicated). assert_eq!(occurrences(&next, tid(1)), 1); // Source window kept tab 2 and fell back its active tab to it. let source = next.windows.iter().find(|w| w.id == wid(1)).unwrap(); assert_eq!(source.tabs.len(), 1); assert_eq!(source.active_tab, tid(2)); // New window holds the moved tab, active. let detached = next.windows.iter().find(|w| w.id == wid(99)).unwrap(); assert_eq!(detached.tabs.len(), 1); assert_eq!(detached.active_tab, tid(1)); } #[test] fn move_only_tab_removes_the_emptied_source_window() { let src = Window::new(wid(1), vec![tab(1)], tid(1)).unwrap(); let ws = Workspace { windows: vec![src] }; let next = ws.move_tab_to_new_window(tid(1), wid(99)).unwrap(); assert_eq!(next.windows.len(), 1, "emptied source dropped"); assert_eq!(next.windows[0].id, wid(99)); assert_eq!(occurrences(&next, tid(1)), 1); } #[test] fn move_unknown_tab_is_rejected() { let ws = Workspace { windows: vec![Window::new(wid(1), vec![tab(1)], tid(1)).unwrap()], }; assert!(matches!( ws.move_tab_to_new_window(tid(404), wid(99)).unwrap_err(), LayoutError::TabNotFound(t) if t == tid(404) )); } #[test] fn move_non_active_tab_leaves_source_active_unchanged() { let src = Window::new(wid(1), vec![tab(1), tab(2)], tid(1)).unwrap(); let ws = Workspace { windows: vec![src] }; let next = ws.move_tab_to_new_window(tid(2), wid(99)).unwrap(); let source = next.windows.iter().find(|w| w.id == wid(1)).unwrap(); assert_eq!(source.active_tab, tid(1), "active tab unchanged"); assert_eq!(source.tabs.len(), 1); }