use super::*;
#[tokio::test]
async fn attached_prefix_d_dispatches_detach_client() {
let handler = RequestHandler::new();
let requester_pid = std::process::id();
let alpha = session_name("alpha");
let mut control_rx = create_attached_session(&handler, requester_pid, &alpha).await;
handler
.handle_attached_live_input_for_test(requester_pid, b"\x02d")
.await
.expect("prefix d dispatches");
assert!(
matches!(control_rx.try_recv(), Ok(AttachControl::Detach)),
"C-b d must detach the attached client"
);
}
#[tokio::test]
async fn attached_prefix_d_dispatches_detach_client_across_separate_reads() {
let handler = RequestHandler::new();
let requester_pid = std::process::id();
let alpha = session_name("alpha");
let mut control_rx = create_attached_session(&handler, requester_pid, &alpha).await;
handler
.handle_attached_live_input_for_test(requester_pid, b"\x02")
.await
.expect("prefix key input");
handler
.handle_attached_live_input_for_test(requester_pid, b"d")
.await
.expect("prefix d input");
assert!(
matches!(control_rx.try_recv(), Ok(AttachControl::Detach)),
"C-b d must still detach when prefix and command arrive in separate reads"
);
}
#[tokio::test]
async fn attached_prefix_c_creates_window_across_separate_reads() {
let handler = RequestHandler::new();
let requester_pid = std::process::id();
let alpha = session_name("alpha");
let _control_rx = create_attached_session(&handler, requester_pid, &alpha).await;
handler
.handle_attached_live_input_for_test(requester_pid, b"\x02")
.await
.expect("prefix key input");
handler
.handle_attached_live_input_for_test(requester_pid, b"c")
.await
.expect("prefix c input");
assert_eq!(
active_windows(&handler, &alpha).await,
"0:0\n1:1\n",
"C-b c must still create a new window when keys arrive in separate reads"
);
}
#[tokio::test]
async fn attached_kill_last_pane_exits_the_session() {
let handler = RequestHandler::new();
let requester_pid = std::process::id();
let alpha = session_name("alpha");
let mut control_rx = create_attached_session(&handler, requester_pid, &alpha).await;
let killed = handler
.handle(Request::KillPane(rmux_proto::KillPaneRequest {
target: PaneTarget::new(alpha.clone(), 0),
kill_all_except: false,
}))
.await;
assert_eq!(
killed,
Response::KillPane(rmux_proto::KillPaneResponse {
target: PaneTarget::new(alpha.clone(), 0),
window_destroyed: true,
})
);
tokio::time::timeout(Duration::from_secs(5), async {
loop {
match control_rx.recv().await {
Some(AttachControl::Exited) => break,
Some(_) => {}
None => panic!("attach control channel closed before exit notification"),
}
}
})
.await
.expect("timed out waiting for attach exit notification");
wait_for_session_removed(&handler, &alpha).await;
}
#[tokio::test]
async fn attached_resize_resizes_session_and_refreshes_status_frame() {
let handler = RequestHandler::new();
let requester_pid = std::process::id();
let alpha = session_name("alpha");
let mut control_rx = create_attached_session(&handler, requester_pid, &alpha).await;
handler
.handle_attached_resize(
requester_pid,
TerminalSize {
cols: 132,
rows: 43,
},
)
.await
.expect("attached resize succeeds");
{
let client_size = {
let active_attach = handler.active_attach.lock().await;
active_attach
.by_pid
.get(&requester_pid)
.expect("attached client is tracked")
.client_size
};
let state = handler.state.lock().await;
let size = state
.sessions
.session(&alpha)
.expect("session exists")
.window()
.size();
assert_eq!(
client_size,
TerminalSize {
cols: 132,
rows: 43
}
);
assert_eq!(
size,
TerminalSize {
cols: 132,
rows: 43
}
);
}
assert_eq!(
pane_terminal_size(&handler, &alpha, 0, 0).await,
TerminalSize {
cols: 132,
rows: 42
}
);
let frame = take_render_frame(control_rx.try_recv().expect("resize refresh"));
assert!(
frame.contains("[alpha]"),
"resize should redraw status for the attached client, got {frame:?}"
);
}
#[tokio::test]
async fn attached_resize_ignores_zero_sized_terminal_reports() {
let handler = RequestHandler::new();
let requester_pid = std::process::id();
let alpha = session_name("alpha");
let mut control_rx = create_attached_session(&handler, requester_pid, &alpha).await;
handler
.handle_attached_resize(requester_pid, TerminalSize { cols: 0, rows: 0 })
.await
.expect("zero-sized resize is ignored");
let (client_size, session_size) = {
let active_attach = handler.active_attach.lock().await;
let client_size = active_attach
.by_pid
.get(&requester_pid)
.expect("attached client is tracked")
.client_size;
drop(active_attach);
let state = handler.state.lock().await;
let session_size = state
.sessions
.session(&alpha)
.expect("session exists")
.window()
.size();
(client_size, session_size)
};
assert_eq!(client_size, TerminalSize { cols: 80, rows: 24 });
assert_eq!(session_size, TerminalSize { cols: 80, rows: 24 });
assert!(
control_rx.try_recv().is_err(),
"ignored zero-sized resize must not emit a refresh frame"
);
}