Files
IdeA/crates/domain/tests/window.rs
Blomios 3ed0f6b45f feat(terminals): reprise de conversation par cellule + fix ordre d'écriture
Permet de recharger la conversation CLI précédente de chaque cellule à la
réouverture du projet, de façon universelle (indépendant du modèle/CLI).

- profil AgentRuntime: bloc déclaratif optionnel `session { assignFlag, resumeFlag }`
- LeafCell: `conversationId` (persistant, distinct du SessionId PTY) + `agentWasRunning`
- runtime: SessionPlan (None/Assign/Resume) + composition pure des args
- LaunchAgent: décide Assign vs Resume, génère l'UUID, remonte l'id assigné
  (persistance par l'appelant via setCellConversation — découplage SRP)
- close: SnapshotRunningAgents fige `agentWasRunning` avant le kill-all
  (statut clot/en cours universel, sans parsing CLI)
- SessionInspector: port optionnel best-effort + adapter ClaudeTranscriptInspector
- popup de reprise par cellule (statut + sujet/tokens si dispo), intercalée
  avant le Resume auto, jamais sur le chemin reattach

fix(terminals): sérialise les écritures PTY (file FIFO par handle) — corrige
les caractères mélangés/accents dus au réordonnancement des invoke Tauri concurrents

fix(layout): l'opération `move` préservait mal les champs du leaf (perdait `agent`)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:27:08 +02:00

96 lines
3.1 KiB
Rust

//! 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,
conversation_id: None,
agent_was_running: false,
}))
}
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);
}