#![cfg(feature = "tmux-it")]
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
use bosun::tmux::attach::{
clear_ctrl_q_bound, clear_session_cycle_bound, ensure_ctrl_q_bound, ensure_session_cycle_bound,
};
fn unique_socket(tag: &str) -> String {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
format!("bosun-bind-{}-{}-{}", tag, std::process::id(), nanos)
}
fn tmux(socket: &str, args: &[&str]) -> std::process::Output {
Command::new("tmux")
.arg("-L")
.arg(socket)
.args(args)
.output()
.expect("spawn tmux")
}
fn list_keys_root(socket: &str) -> String {
let out = tmux(socket, &["list-keys", "-T", "root"]);
String::from_utf8_lossy(&out.stdout).to_string()
}
fn count_ctrl_q_bindings(socket: &str) -> usize {
let keys = list_keys_root(socket);
keys.lines().filter(|l| l.contains("C-q")).count()
}
fn list_keys_prefix(socket: &str) -> String {
let out = tmux(socket, &["list-keys", "-T", "root"]);
String::from_utf8_lossy(&out.stdout).to_string()
}
fn count_cycle_bindings(socket: &str) -> usize {
let keys = list_keys_prefix(socket);
keys.lines()
.filter(|l| (l.contains("S-Left") || l.contains("S-Right")) && l.contains("run-shell"))
.count()
}
fn kill_server(socket: &str) {
let _ = tmux(socket, &["kill-server"]);
}
#[test]
fn ensure_then_clear_leaves_no_binding() {
let sock = unique_socket("solo");
tmux(&sock, &["new-session", "-d", "-s", "dummy"]);
let before = count_ctrl_q_bindings(&sock);
assert_eq!(before, 0, "expected no C-q binding before test");
ensure_ctrl_q_bound(Some(&sock));
assert!(
count_ctrl_q_bindings(&sock) >= 1,
"expected C-q binding to be installed, got: {}",
list_keys_root(&sock)
);
clear_ctrl_q_bound(Some(&sock));
assert_eq!(
count_ctrl_q_bindings(&sock),
0,
"expected C-q binding gone after clear, got:\n{}",
list_keys_root(&sock)
);
kill_server(&sock);
}
#[test]
fn ensure_is_idempotent() {
let sock = unique_socket("idem");
tmux(&sock, &["new-session", "-d", "-s", "dummy"]);
for _ in 0..5 {
ensure_ctrl_q_bound(Some(&sock));
}
assert_eq!(
count_ctrl_q_bindings(&sock),
1,
"repeated ensure_ctrl_q_bound should produce exactly 1 binding, got:\n{}",
list_keys_root(&sock)
);
clear_ctrl_q_bound(Some(&sock));
assert_eq!(count_ctrl_q_bindings(&sock), 0);
kill_server(&sock);
}
#[test]
fn cycle_bindings_install_and_clear() {
let sock = unique_socket("cycle");
tmux(&sock, &["new-session", "-d", "-s", "dummy"]);
assert_eq!(
count_cycle_bindings(&sock),
0,
"expected no S-Left/S-Right bindings before test"
);
ensure_session_cycle_bound(Some(&sock));
assert_eq!(
count_cycle_bindings(&sock),
2,
"expected exactly 2 cycle bindings (S-Left + S-Right) after install, got:\n{}",
list_keys_prefix(&sock)
);
for _ in 0..3 {
ensure_session_cycle_bound(Some(&sock));
}
assert_eq!(
count_cycle_bindings(&sock),
2,
"repeated ensure_session_cycle_bound must not duplicate bindings, got:\n{}",
list_keys_prefix(&sock)
);
clear_session_cycle_bound(Some(&sock));
assert_eq!(
count_cycle_bindings(&sock),
0,
"expected cycle bindings gone after clear, got:\n{}",
list_keys_prefix(&sock)
);
kill_server(&sock);
}